mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 17:48:47 +01:00
Compare commits
135 Commits
commit-ser
...
release-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc2b6f414f | ||
|
|
90c83dc0c3 | ||
|
|
dd3545b79b | ||
|
|
73f9171107 | ||
|
|
1804306f94 | ||
|
|
e96f32d233 | ||
|
|
212d320363 | ||
|
|
d881ee7894 | ||
|
|
8e818188bd | ||
|
|
b07a61a2a9 | ||
|
|
151ee6a956 | ||
|
|
aa27a1ec6b | ||
|
|
4b9404ba8d | ||
|
|
df36505caa | ||
|
|
9a7a7cebdd | ||
|
|
39ec73e12a | ||
|
|
4fad51f895 | ||
|
|
44e5aa010a | ||
|
|
eb77b7028c | ||
|
|
2e7fbb0428 | ||
|
|
423ad6ba43 | ||
|
|
a28a57dadf | ||
|
|
59db3998e2 | ||
|
|
9552034a80 | ||
|
|
53570cbd14 | ||
|
|
26a868f76d | ||
|
|
703e32fafe | ||
|
|
f8c2e48a03 | ||
|
|
d774c422de | ||
|
|
2cb08f5c26 | ||
|
|
d3955f4f2a | ||
|
|
312cea0504 | ||
|
|
98203002ba | ||
|
|
a316a413ae | ||
|
|
c8f21e383a | ||
|
|
e2df7315fb | ||
|
|
9e4a0f523e | ||
|
|
7c21486b31 | ||
|
|
caab433303 | ||
|
|
d6cb4e903f | ||
|
|
710ee9a218 | ||
|
|
e0ff56d89f | ||
|
|
91bb155ed2 | ||
|
|
7ff0ccc34e | ||
|
|
b17323b41e | ||
|
|
1701f7edd3 | ||
|
|
ed5d9c3874 | ||
|
|
14f681e3ee | ||
|
|
8281b18831 | ||
|
|
5bbb51ab42 | ||
|
|
6e181d72b3 | ||
|
|
c415d3f3d5 | ||
|
|
759bd2888d | ||
|
|
2a410f3441 | ||
|
|
689a73a729 | ||
|
|
fbb6b20418 | ||
|
|
f05a6b7906 | ||
|
|
7aac4ba0f0 | ||
|
|
7cac5a8946 | ||
|
|
ba62a0a86d | ||
|
|
3fa66ec42c | ||
|
|
cc672e7609 | ||
|
|
cce1e97434 | ||
|
|
f43122d3cd | ||
|
|
f9436641a6 | ||
|
|
7561cc1cdd | ||
|
|
bb06722d98 | ||
|
|
997f89a92c | ||
|
|
f569aa105e | ||
|
|
dac68ac593 | ||
|
|
45d4a071ae | ||
|
|
a10cb870f8 | ||
|
|
8436954759 | ||
|
|
ab3b2e780e | ||
|
|
0e65b848da | ||
|
|
9075601a68 | ||
|
|
31860d09d4 | ||
|
|
65dec01de0 | ||
|
|
6eba5be864 | ||
|
|
cc56c9e8a2 | ||
|
|
30f68e1041 | ||
|
|
e63273e4c1 | ||
|
|
d5eaaa3527 | ||
|
|
c5ea5c4df5 | ||
|
|
c2c9746050 | ||
|
|
78cd50b2c7 | ||
|
|
dd86b08369 | ||
|
|
0ca43663c9 | ||
|
|
d4c37e2521 | ||
|
|
ba60fadd94 | ||
|
|
58b04e5e11 | ||
|
|
3acd5ee30d | ||
|
|
45d5de702e | ||
|
|
b8efc8b1ab | ||
|
|
9cf0c69bbe | ||
|
|
871b045368 | ||
|
|
7f9be43b8b | ||
|
|
78ad599120 | ||
|
|
557871d223 | ||
|
|
f29e4fbea3 | ||
|
|
766316ef74 | ||
|
|
eb0afcbc3d | ||
|
|
0083647b8b | ||
|
|
5289315c3f | ||
|
|
83ec3bfbf7 | ||
|
|
5c86f758c3 | ||
|
|
67e1e04afb | ||
|
|
266e92e3a1 | ||
|
|
6648d31671 | ||
|
|
82185106a2 | ||
|
|
b14837e58e | ||
|
|
9c4a90af91 | ||
|
|
3750adefa7 | ||
|
|
dc3d08e626 | ||
|
|
eaa9af21d7 | ||
|
|
55e5d6bf3e | ||
|
|
85422bbb17 | ||
|
|
79901a4e84 | ||
|
|
5506e8520c | ||
|
|
0a97e150d8 | ||
|
|
e1ac2f6071 | ||
|
|
8c3f38a97d | ||
|
|
8b9c448786 | ||
|
|
80baeb8a6c | ||
|
|
72f7b14594 | ||
|
|
b9bf46dfb9 | ||
|
|
d7c489b9cc | ||
|
|
38eb17a027 | ||
|
|
ea3402962f | ||
|
|
b3fabc23cd | ||
|
|
69d6d1064b | ||
|
|
d09621d36b | ||
|
|
728205618e | ||
|
|
912a2db05c | ||
|
|
d105196075 |
@@ -8,7 +8,6 @@ ignore:
|
||||
- "pkg/client/.*"
|
||||
- "vendor/.*"
|
||||
- "test/.*"
|
||||
- "**/mocks/*"
|
||||
coverage:
|
||||
status:
|
||||
# we've found this not to be useful
|
||||
|
||||
@@ -18,10 +18,8 @@ hack/
|
||||
docs/
|
||||
examples/
|
||||
.github/
|
||||
!test/container
|
||||
!test/e2e/testdata
|
||||
!test/fixture
|
||||
!test/remote
|
||||
!test/container
|
||||
!hack/installers
|
||||
!hack/gpg-wrapper.sh
|
||||
!hack/git-verify-wrapper.sh
|
||||
|
||||
13
.gitattributes
vendored
13
.gitattributes
vendored
@@ -1,13 +0,0 @@
|
||||
**/*.pb.go linguist-generated=true
|
||||
**/mocks/*.go linguist-generated=true
|
||||
assets/swagger.json linguist-generated=true
|
||||
docs/operator-manual/resource_actions_builtin.md linguist-generated=true
|
||||
docs/operator-manual/server-commands/argocd-*.md linguist-generated=true
|
||||
docs/user-guide/commands/argocd_*.md linguist-generated=true
|
||||
manifests/core-install.yaml linguist-generated=true
|
||||
manifests/crds/*-crd.yaml linguist-generated=true
|
||||
manifests/ha/install.yaml linguist-generated=true
|
||||
manifests/ha/namespace-install.yaml linguist-generated=true
|
||||
manifests/install.yaml linguist-generated=true
|
||||
manifests/namespace-install.yaml linguist-generated=true
|
||||
pkg/apis/api-rules/violation_exceptions.list linguist-generated=true
|
||||
43
.github/ISSUE_TEMPLATE/new_dev_tool.md
vendored
43
.github/ISSUE_TEMPLATE/new_dev_tool.md
vendored
@@ -1,43 +0,0 @@
|
||||
---
|
||||
name: New Dev Tool Request
|
||||
about: This is a request for adding a new tool for setting up a dev environment.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
Checklist:
|
||||
|
||||
* [ ] I am willing to maintain this tool, or have another Argo CD maintainer who is.
|
||||
* [ ] I have another Argo CD maintainer who is willing to help maintain this tool (there needs to be at least two maintainers willing to maintain this tool)
|
||||
* [ ] I have a lead sponsor who is a core Argo CD maintainer
|
||||
* [ ] There is a PR which adds said tool - this is so that the maintainers can assess the impact of having this in the tree
|
||||
* [ ] I have given a motivation why this should be added
|
||||
|
||||
### The proposer
|
||||
|
||||
<-- The username(s) of the person(s) proposing the tool -->
|
||||
|
||||
### The proposed tool
|
||||
|
||||
<!-- The tool itself, with a link to the tool’s website -->
|
||||
|
||||
### Motivation
|
||||
|
||||
<!-- Why this tool would be useful to have in the tree. -->
|
||||
|
||||
### Link to PR (Optional)
|
||||
|
||||
<!-- A PR adding the tool to the tree -->
|
||||
|
||||
### Lead Sponsor(s)
|
||||
|
||||
Final approval requires sponsorship from at least one core maintainer.
|
||||
|
||||
- @<sponsor-1>
|
||||
|
||||
### Co-sponsors
|
||||
|
||||
These will be the co-maintainers of the specified tool.
|
||||
|
||||
- @<sponsor-1>
|
||||
6
.github/ISSUE_TEMPLATE/release.md
vendored
6
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -9,6 +9,12 @@ assignees: ''
|
||||
Target RC1 date: ___. __, ____
|
||||
Target GA date: ___. __, ____
|
||||
|
||||
- [ ] Create new section in the [Release Planning doc](https://docs.google.com/document/d/1trJIomcgXcfvLw0aYnERrFWfPjQOfYMDJOCh1S8nMBc/edit?usp=sharing)
|
||||
- [ ] Schedule a Release Planning meeting roughly two weeks before the scheduled Release freeze date by adding it to the community calendar (or delegate this task to someone with write access to the community calendar)
|
||||
- [ ] Include Zoom link in the invite
|
||||
- [ ] Post in #argo-cd and #argo-contributors one week before the meeting
|
||||
- [ ] Post again one hour before the meeting
|
||||
- [ ] At the meeting, remove issues/PRs from the project's column for that release which have not been “claimed” by at least one Approver (add it to the next column if Approver requests that)
|
||||
- [ ] 1wk before feature freeze post in #argo-contributors that PRs must be merged by DD-MM-YYYY to be included in the release - ask approvers to drop items from milestone they can’t merge
|
||||
- [ ] At least two days before RC1 date, draft RC blog post and submit it for review (or delegate this task)
|
||||
- [ ] Cut RC1 (or delegate this task to an Approver and coordinate timing)
|
||||
|
||||
15
.github/dependabot.yml
vendored
15
.github/dependabot.yml
vendored
@@ -4,13 +4,8 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 20
|
||||
ignore:
|
||||
- dependency-name: k8s.io/*
|
||||
groups:
|
||||
otel:
|
||||
patterns:
|
||||
- "^go.opentelemetry.io/.*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
@@ -22,20 +17,10 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/ui-test/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
ignore:
|
||||
# We use consistent go and node versions across a lot of different files, and updating via dependabot would cause
|
||||
# drift among those files, instead we let renovate bot handle them.
|
||||
- dependency-name: "library/golang"
|
||||
- dependency-name: "library/node"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/test/container/"
|
||||
|
||||
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@@ -13,12 +13,11 @@ Checklist:
|
||||
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
|
||||
* [ ] Does this PR require documentation updates?
|
||||
* [ ] I've updated documentation as required by this PR.
|
||||
* [ ] Optional. My organization is added to USERS.md.
|
||||
* [ ] I have signed off all my commits as required by [DCO](https://github.com/argoproj/argoproj/blob/master/community/CONTRIBUTING.md#legal)
|
||||
* [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
|
||||
* [ ] My build is green ([troubleshooting builds](https://argo-cd.readthedocs.io/en/latest/developer-guide/ci/)).
|
||||
* [ ] My new feature complies with the [feature status](https://github.com/argoproj/argoproj/blob/master/community/feature-status.md) guidelines.
|
||||
* [ ] I have added a brief description of why this PR is necessary and/or what this PR solves.
|
||||
* [ ] Optional. My organization is added to USERS.md.
|
||||
* [ ] Optional. For bug fixes, I've indicated what older releases this fix should be cherry-picked into (this may or may not happen depending on risk/complexity).
|
||||
|
||||
<!-- Please see [Contribution FAQs](https://argo-cd.readthedocs.io/en/latest/developer-guide/faq/) if you have questions about your pull-request. -->
|
||||
|
||||
3
.github/workflows/README.md
vendored
3
.github/workflows/README.md
vendored
@@ -6,10 +6,9 @@
|
||||
| codeql.yaml | CodeQL analysis |
|
||||
| image-reuse.yaml | Build, push, and Sign container images |
|
||||
| image.yaml | Build container image for PR's & publish for push events |
|
||||
| init-release.yaml | Build manifests and version then create a PR for release branch|
|
||||
| pr-title-check.yaml| Lint PR for semantic information |
|
||||
| init-release.yaml | Build manifests and version then create a PR for release branch|
|
||||
| release.yaml | Build images, cli-binaries, provenances, and post actions |
|
||||
| scorecard.yaml | Generate scorecard for supply-chain security |
|
||||
| update-snyk.yaml | Scheduled snyk reports |
|
||||
|
||||
# Reusable workflows
|
||||
|
||||
246
.github/workflows/ci-build.yaml
vendored
246
.github/workflows/ci-build.yaml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Integration tests
|
||||
on:
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
@@ -13,8 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.23.3'
|
||||
GOLANG_VERSION: '1.21'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -24,65 +23,36 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
backend: ${{ steps.filter.outputs.backend_any_changed }}
|
||||
frontend: ${{ steps.filter.outputs.frontend_any_changed }}
|
||||
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: tj-actions/changed-files@bab30c2299617f6615ec02a68b9a40d10bd21366 # v45.0.5
|
||||
id: filter
|
||||
with:
|
||||
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
||||
files_yaml: |
|
||||
backend:
|
||||
- '!ui/**'
|
||||
- '!**.md'
|
||||
- '!**/*.md'
|
||||
- '!docs/**'
|
||||
frontend:
|
||||
- 'ui/**'
|
||||
- Dockerfile
|
||||
docs:
|
||||
- 'docs/**'
|
||||
check-go:
|
||||
name: Ensure Go modules synchronicity
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
run: |
|
||||
go mod download
|
||||
- name: Check for tidiness of go.mod and go.sum
|
||||
- name: Check for tidyness of go.mod and go.sum
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code -- .
|
||||
|
||||
build-go:
|
||||
name: Build & cache Go code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -97,43 +67,37 @@ jobs:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
||||
name: Lint Go code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
|
||||
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
|
||||
with:
|
||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||
version: v1.62.2
|
||||
args: --verbose
|
||||
version: v1.54.0
|
||||
args: --enable gofmt --timeout 10m --exclude SA5011 --verbose --max-issues-per-linter 0 --max-same-issues 0
|
||||
|
||||
test-go:
|
||||
name: Run unit tests for Go packages
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -153,7 +117,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -173,31 +137,34 @@ jobs:
|
||||
go mod download
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate code coverage artifacts
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: code-coverage
|
||||
path: coverage.out
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
path: test-results/
|
||||
|
||||
test-go-race:
|
||||
name: Run unit tests with -race for Go packages
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
@@ -217,7 +184,7 @@ jobs:
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -238,22 +205,19 @@ jobs:
|
||||
- name: Run all unit tests
|
||||
run: make test-race-local
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: race-results
|
||||
path: test-results/
|
||||
|
||||
codegen:
|
||||
name: Check changes to generated code
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.docs == 'true'}}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
@@ -296,22 +260,17 @@ jobs:
|
||||
|
||||
build-ui:
|
||||
name: Build, test & lint UI code
|
||||
# We run UI logic for backend changes so that we have a complete set of coverage documents to send to codecov.
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- changes
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
# renovate: datasource=node-version packageName=node versioning=node
|
||||
node-version: '22.9.0'
|
||||
node-version: '20.7.0'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
@@ -326,8 +285,6 @@ jobs:
|
||||
NODE_ENV: production
|
||||
NODE_ONLINE_ENV: online
|
||||
HOST_ARCH: amd64
|
||||
# If we're on the master branch, set the codecov token so that we upload bundle analysis
|
||||
CODECOV_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.CODECOV_TOKEN || '' }}
|
||||
working-directory: ui/
|
||||
- name: Run ESLint
|
||||
run: yarn lint
|
||||
@@ -335,86 +292,78 @@ jobs:
|
||||
|
||||
analyze:
|
||||
name: Process & analyze test artifacts
|
||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- test-go
|
||||
- build-ui
|
||||
- changes
|
||||
- test-e2e
|
||||
env:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Remove other node_modules directory
|
||||
run: |
|
||||
rm -rf ui/node_modules/argo-ui/node_modules
|
||||
- name: Get e2e code coverage
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
- name: Create test-results directory
|
||||
run: |
|
||||
mkdir -p test-results
|
||||
- name: Get code coverage artifiact
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: e2e-code-coverage
|
||||
- name: Get unit test code coverage
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
name: code-coverage
|
||||
- name: Get test result artifact
|
||||
uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
- name: combine-go-coverage
|
||||
# We generate coverage reports for all Argo CD components, but only the applicationset-controller,
|
||||
# app-controller, repo-server, and commit-server report contain coverage data. The other components currently
|
||||
# don't shut down gracefully, so no coverage data is produced. Once those components are fixed, we can add
|
||||
# references to their coverage output directories.
|
||||
run: |
|
||||
go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
|
||||
- name: Upload code coverage information to codecov.io
|
||||
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
|
||||
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
|
||||
with:
|
||||
file: test-results/full-coverage.out
|
||||
fail_ci_if_error: true
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Upload test results to Codecov
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
|
||||
uses: codecov/test-results-action@9739113ad922ea0a9abb4b2c0f8bf6a4aa8ef820 # v1.0.1
|
||||
with:
|
||||
file: test-results/junit.xml
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: coverage.out
|
||||
- name: Perform static code analysis using SonarCloud
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
uses: SonarSource/sonarqube-scan-action@1b442ee39ac3fa7c2acdd410208dcb2bcfaae6c4 # v4.1.0
|
||||
SCANNER_VERSION: 4.2.0.1873
|
||||
SCANNER_PATH: /tmp/cache/scanner
|
||||
OS: linux
|
||||
run: |
|
||||
# We do not use the provided action, because it does contain an old
|
||||
# version of the scanner, and also takes time to build.
|
||||
set -e
|
||||
mkdir -p ${SCANNER_PATH}
|
||||
export SONAR_USER_HOME=${SCANNER_PATH}/.sonar
|
||||
if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then
|
||||
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip
|
||||
unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH}
|
||||
fi
|
||||
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java
|
||||
|
||||
# Explicitly set NODE_MODULES
|
||||
export NODE_MODULES=${PWD}/ui/node_modules
|
||||
export NODE_PATH=${PWD}/ui/node_modules
|
||||
|
||||
${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
if: env.sonar_secret != ''
|
||||
|
||||
test-e2e:
|
||||
name: Run end-to-end tests
|
||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
k3s:
|
||||
- version: v1.31.0
|
||||
# We designate the latest version because we only collect code coverage for that version.
|
||||
latest: true
|
||||
- version: v1.30.4
|
||||
latest: false
|
||||
- version: v1.29.8
|
||||
latest: false
|
||||
- version: v1.28.13
|
||||
latest: false
|
||||
needs:
|
||||
k3s-version: [v1.28.2, v1.27.6, v1.26.9, v1.25.14]
|
||||
needs:
|
||||
- build-go
|
||||
- changes
|
||||
env:
|
||||
GOPATH: /home/runner/go
|
||||
ARGOCD_FAKE_IN_CLUSTER: "true"
|
||||
@@ -424,15 +373,15 @@ jobs:
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
ARGOCD_IN_CI: "true"
|
||||
ARGOCD_E2E_APISERVER_PORT: "8088"
|
||||
ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external,argocd-e2e-external-2"
|
||||
ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external"
|
||||
ARGOCD_SERVER: "127.0.0.1:8088"
|
||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
@@ -440,7 +389,7 @@ jobs:
|
||||
sudo pkill mono || true
|
||||
- name: Install K3S
|
||||
env:
|
||||
INSTALL_K3S_VERSION: ${{ matrix.k3s.version }}+k3s1
|
||||
INSTALL_K3S_VERSION: ${{ matrix.k3s-version }}+k3s1
|
||||
run: |
|
||||
set -x
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
@@ -451,7 +400,7 @@ jobs:
|
||||
sudo chmod go-r $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
@@ -477,7 +426,7 @@ jobs:
|
||||
git config --global user.email "john.doe@example.com"
|
||||
- name: Pull Docker image required for tests
|
||||
run: |
|
||||
docker pull ghcr.io/dexidp/dex:v2.41.1
|
||||
docker pull ghcr.io/dexidp/dex:v2.37.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:7.0.15-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
@@ -492,7 +441,7 @@ jobs:
|
||||
# port 8080 which is not visible in netstat -tulpen, but still there
|
||||
# with a HTTP listener. We have API server listening on port 8088
|
||||
# instead.
|
||||
make start-e2e-local COVERAGE_ENABLED=true 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log &
|
||||
make start-e2e-local 2>&1 | sed -r "s/[[:cntrl:]]\[[0-9]{1,3}m//g" > /tmp/e2e-server.log &
|
||||
count=1
|
||||
until curl -f http://127.0.0.1:8088/healthz; do
|
||||
sleep 10;
|
||||
@@ -506,40 +455,9 @@ jobs:
|
||||
run: |
|
||||
set -x
|
||||
make test-e2e-local
|
||||
goreman run stop-all || echo "goreman trouble"
|
||||
sleep 30
|
||||
- name: Upload e2e coverage report
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: e2e-code-coverage
|
||||
path: /tmp/coverage
|
||||
if: ${{ matrix.k3s.latest }}
|
||||
- name: Upload e2e-server logs
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
||||
name: e2e-server-k8s${{ matrix.k3s-version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
if: ${{ failure() }}
|
||||
|
||||
# workaround for status checks -- check this one job instead of each individual E2E job in the matrix
|
||||
# this allows us to skip the entire matrix when it doesn't need to run while still having accurate status checks
|
||||
# see:
|
||||
# https://github.com/argoproj/argo-workflows/pull/12006
|
||||
# https://github.com/orgs/community/discussions/9141#discussioncomment-2296809
|
||||
# https://github.com/orgs/community/discussions/26822#discussioncomment-3305794
|
||||
test-e2e-composite-result:
|
||||
name: E2E Tests - Composite result
|
||||
if: ${{ always() }}
|
||||
needs:
|
||||
- test-e2e
|
||||
- changes
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- run: |
|
||||
result="${{ needs.test-e2e.result }}"
|
||||
# mark as successful even if skipped
|
||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
17
.github/workflows/codeql.yml
vendored
17
.github/workflows/codeql.yml
vendored
@@ -23,23 +23,18 @@ jobs:
|
||||
actions: read # for github/codeql-action/init to get workflow details
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||
if: github.repository == 'argoproj/argo-cd' || vars.enable_codeql
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
|
||||
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/init@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -47,7 +42,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/autobuild@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -61,4 +56,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/analyze@8aff97f12c99086bdb92ff62ae06dbbcdf07941b # v2.1.33
|
||||
|
||||
22
.github/workflows/image-reuse.yaml
vendored
22
.github/workflows/image-reuse.yaml
vendored
@@ -58,26 +58,26 @@ jobs:
|
||||
image-digest: ${{ steps.image.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ github.ref_type == 'tag'}}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
if: ${{ github.ref_type != 'tag'}}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
||||
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 # v3.4.0
|
||||
|
||||
- uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
||||
- uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1
|
||||
- uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2.2.0
|
||||
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
- name: Setup tags for container image as a CSV type
|
||||
run: |
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
echo 'EOF' >> $GITHUB_ENV
|
||||
|
||||
- name: Login to Quay.io
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.quay_username }}
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
if: ${{ inputs.quay_image_name && inputs.push }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.ghcr_username }}
|
||||
@@ -120,7 +120,7 @@ jobs:
|
||||
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
||||
|
||||
- name: Login to dockerhub Container Registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.docker_username }}
|
||||
password: ${{ secrets.docker_password }}
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
uses: jlumbroso/free-disk-space@4d9e71b726748f254fe64fa44d273194bd18ec91
|
||||
with:
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
|
||||
- name: Build and push container image
|
||||
id: image
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 #v6.10.0
|
||||
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 #v4.1.1
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ inputs.platforms }}
|
||||
|
||||
12
.github/workflows/image.yaml
vendored
12
.github/workflows/image.yaml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
image-tag: ${{ steps.image.outputs.tag}}
|
||||
platforms: ${{ steps.platforms.outputs.platforms }}
|
||||
steps:
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
|
||||
- name: Set image tag for ghcr
|
||||
run: echo "tag=$(cat ./VERSION)-${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
|
||||
@@ -52,8 +52,7 @@ jobs:
|
||||
uses: ./.github/workflows/image-reuse.yaml
|
||||
with:
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.23.3
|
||||
go-version: 1.21
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: false
|
||||
|
||||
@@ -69,8 +68,7 @@ jobs:
|
||||
quay_image_name: quay.io/argoproj/argocd:latest
|
||||
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.23.3
|
||||
go-version: 1.21
|
||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||
push: true
|
||||
secrets:
|
||||
@@ -88,7 +86,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
|
||||
with:
|
||||
image: ghcr.io/argoproj/argo-cd/argocd
|
||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||
@@ -106,7 +104,7 @@ jobs:
|
||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
|
||||
4
.github/workflows/init-release.yaml
vendored
4
.github/workflows/init-release.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
git stash pop
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
|
||||
with:
|
||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||
|
||||
2
.github/workflows/pr-title-check.yml
vendored
2
.github/workflows/pr-title-check.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
name: Validate PR Title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: thehanimo/pr-title-checker@7fbfe05602bdd86f926d3fb3bccb6f3aed43bc70 # v1.4.3
|
||||
- uses: thehanimo/pr-title-checker@0cf5902181e78341bb97bb06646396e5bd354b3f # v1.4.0
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
configuration_path: ".github/pr-title-checker-config.json"
|
||||
|
||||
70
.github/workflows/release.yaml
vendored
70
.github/workflows/release.yaml
vendored
@@ -10,8 +10,7 @@ on:
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
GOLANG_VERSION: '1.23.3' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
GOLANG_VERSION: '1.21' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||
|
||||
jobs:
|
||||
argocd-image:
|
||||
@@ -24,8 +23,7 @@ jobs:
|
||||
with:
|
||||
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||
# renovate: datasource=golang-version packageName=golang
|
||||
go-version: 1.23.3
|
||||
go-version: 1.21
|
||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||
push: true
|
||||
secrets:
|
||||
@@ -40,7 +38,7 @@ jobs:
|
||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
|
||||
with:
|
||||
image: quay.io/argoproj/argocd
|
||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||
@@ -61,7 +59,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -69,15 +67,19 @@ jobs:
|
||||
- name: Fetch all tags
|
||||
run: git fetch --force --tags
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in release branches.
|
||||
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in realease branches.
|
||||
run: |
|
||||
set -xue
|
||||
echo "GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }})" >> $GITHUB_ENV
|
||||
if echo ${{ github.ref_name }} | grep -E -- '-rc1+$';then
|
||||
echo "GORELEASER_PREVIOUS_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n 2 | head -n 1)" >> $GITHUB_ENV
|
||||
else
|
||||
echo "This is not the first release on the branch, Using GoReleaser defaults"
|
||||
fi
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
- name: Set environment variables for ldflags
|
||||
id: set_ldflag
|
||||
@@ -85,23 +87,15 @@ jobs:
|
||||
echo "KUBECTL_VERSION=$(go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)" >> $GITHUB_ENV
|
||||
echo "GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)" >> $GITHUB_ENV
|
||||
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||
with:
|
||||
large-packages: false
|
||||
docker-images: false
|
||||
swap-storage: false
|
||||
tool-cache: false
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||
id: run-goreleaser
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --timeout 55m
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
||||
KUBECTL_VERSION: ${{ env.KUBECTL_VERSION }}
|
||||
GIT_TREE_STATE: ${{ env.GIT_TREE_STATE }}
|
||||
|
||||
- name: Generate subject for provenance
|
||||
@@ -126,7 +120,7 @@ jobs:
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
||||
provenance-name: "argocd-cli.intoto.jsonl"
|
||||
@@ -145,13 +139,13 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
||||
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
with:
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
@@ -184,7 +178,7 @@ jobs:
|
||||
fi
|
||||
|
||||
cd /tmp && tar -zcf sbom.tar.gz *.spdx
|
||||
|
||||
|
||||
- name: Generate SBOM hash
|
||||
shell: bash
|
||||
id: sbom-hash
|
||||
@@ -193,15 +187,15 @@ jobs:
|
||||
# base64 -w0 encodes to base64 and outputs on a single line.
|
||||
# sha256sum /tmp/sbom.tar.gz ... | base64 -w0
|
||||
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
|
||||
- name: Upload SBOM
|
||||
uses: softprops/action-gh-release@7b4da11513bf3f43f9999e90eabced41ab8bb048 # v2.2.0
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: |
|
||||
/tmp/sbom.tar.gz
|
||||
|
||||
|
||||
sbom-provenance:
|
||||
needs: [generate-sbom]
|
||||
permissions:
|
||||
@@ -209,13 +203,13 @@ jobs:
|
||||
id-token: write # Needed for provenance signing and ID
|
||||
contents: write # Needed for release uploads
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
# Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
|
||||
provenance-name: "argocd-sbom.intoto.jsonl"
|
||||
upload-assets: true
|
||||
|
||||
|
||||
post-release:
|
||||
needs:
|
||||
- argocd-image
|
||||
@@ -228,7 +222,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -271,13 +265,11 @@ jobs:
|
||||
set -xue
|
||||
SOURCE_TAG=${{ github.ref_name }}
|
||||
VERSION_REF="${SOURCE_TAG#*v}"
|
||||
COMMIT_HASH=$(git rev-parse HEAD)
|
||||
if echo "$VERSION_REF" | grep -E -- '^[0-9]+\.[0-9]+\.0-rc1';then
|
||||
VERSION=$(awk 'BEGIN {FS=OFS="."} {$2++; print}' <<< "${VERSION_REF%-rc1}")
|
||||
echo "Updating VERSION to: $VERSION"
|
||||
echo "UPDATE_VERSION=true" >> $GITHUB_ENV
|
||||
echo "NEW_VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "COMMIT_HASH=$COMMIT_HASH" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Not updating VERSION"
|
||||
echo "UPDATE_VERSION=false" >> $GITHUB_ENV
|
||||
@@ -286,14 +278,10 @@ jobs:
|
||||
- name: Update VERSION on master branch
|
||||
run: |
|
||||
echo ${{ env.NEW_VERSION }} > VERSION
|
||||
# Replace the 'project-release: vX.X.X-rcX' line in SECURITY-INSIGHTS.yml
|
||||
sed -i "s/project-release: v.*$/project-release: v${{ env.NEW_VERSION }}/" SECURITY-INSIGHTS.yml
|
||||
# Update the 'commit-hash: XXXXXXX' line in SECURITY-INSIGHTS.yml
|
||||
sed -i "s/commit-hash: .*/commit-hash: ${{ env.COMMIT_HASH }}/" SECURITY-INSIGHTS.yml
|
||||
if: ${{ env.UPDATE_VERSION == 'true' }}
|
||||
|
||||
- name: Create PR to update VERSION on master branch
|
||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
|
||||
with:
|
||||
commit-message: Bump version in master
|
||||
title: "chore: Bump version in master"
|
||||
|
||||
8
.github/workflows/scorecard.yaml
vendored
8
.github/workflows/scorecard.yaml
vendored
@@ -30,12 +30,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -62,6 +62,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@8fcfedf57053e09257688fce7a0beeb18b1b9ae3 # v2.17.2
|
||||
uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2.2.1
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
2
.github/workflows/update-snyk.yaml
vendored
2
.github/workflows/update-snyk.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build reports
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
.vscode/
|
||||
.idea/
|
||||
.DS_Store
|
||||
.run/
|
||||
vendor/
|
||||
dist/*
|
||||
ui/dist/app/*
|
||||
@@ -20,7 +19,6 @@ node_modules/
|
||||
./test/cmp/*.sock
|
||||
.envrc.remote
|
||||
.*.swp
|
||||
rerunreport.txt
|
||||
|
||||
# ignore built binaries
|
||||
cmd/argocd/argocd
|
||||
|
||||
2
.gitpod.Dockerfile
vendored
2
.gitpod.Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM gitpod/workspace-full@sha256:230285e0b949e6d728d384b2029a4111db7b9c87c182f22f32a0be9e36b225df
|
||||
FROM gitpod/workspace-full@sha256:511cecde4dc129ca9eb4cc4c479d61f95e5485ebe320a07f5b902f11899956a3
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
issues:
|
||||
exclude:
|
||||
- SA5011
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude-rules:
|
||||
- path: '(.+)_test\.go'
|
||||
linters:
|
||||
- unparam
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- errorlint
|
||||
- gocritic
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- perfsprint
|
||||
- staticcheck
|
||||
- testifylint
|
||||
- thelper
|
||||
- unparam
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- whitespace
|
||||
linters-settings:
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- appendAssign
|
||||
- assignOp # Keep it disabled for readability
|
||||
- badCond
|
||||
- commentFormatting
|
||||
- exitAfterDefer
|
||||
- ifElseChain
|
||||
- mapKey
|
||||
- singleCaseSwitch
|
||||
- typeSwitchVar
|
||||
goimports:
|
||||
local-prefixes: github.com/argoproj/argo-cd/v2
|
||||
perfsprint:
|
||||
# Optimizes even if it requires an int or uint type cast.
|
||||
int-conversion: true
|
||||
# Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.
|
||||
err-error: false
|
||||
# Optimizes `fmt.Errorf`.
|
||||
errorf: false
|
||||
# Optimizes `fmt.Sprintf` with only one argument.
|
||||
sprintf1: true
|
||||
# Optimizes into strings concatenation.
|
||||
strconcat: false
|
||||
testifylint:
|
||||
enable-all: true
|
||||
disable:
|
||||
- go-require
|
||||
run:
|
||||
timeout: 50m
|
||||
@@ -1,5 +1,3 @@
|
||||
version: 2
|
||||
|
||||
project_name: argocd
|
||||
|
||||
before:
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# global config
|
||||
filename: "{{.InterfaceName}}.go"
|
||||
dir: "{{.InterfaceDir}}/mocks"
|
||||
outpkg: "mocks"
|
||||
mockname: "{{.InterfaceName}}"
|
||||
with-expecter: false
|
||||
# individual interface config
|
||||
packages:
|
||||
github.com/argoproj/argo-cd/v2/applicationset/generators:
|
||||
interfaces:
|
||||
Generator:
|
||||
github.com/argoproj/argo-cd/v2/applicationset/services:
|
||||
interfaces:
|
||||
Repos:
|
||||
github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider:
|
||||
config:
|
||||
dir: "applicationset/services/scm_provider/aws_codecommit/mocks"
|
||||
interfaces:
|
||||
AWSCodeCommitClient:
|
||||
AWSTaggingClient:
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops/git:
|
||||
config:
|
||||
dir: "applicationset/services/scm_provider/azure_devops/git/mocks"
|
||||
interfaces:
|
||||
Client:
|
||||
github.com/argoproj/argo-cd/v2/applicationset/utils:
|
||||
interfaces:
|
||||
Renderer:
|
||||
github.com/argoproj/argo-cd/v2/commitserver/commit:
|
||||
interfaces:
|
||||
RepoClientFactory:
|
||||
github.com/argoproj/argo-cd/v2/commitserver/apiclient:
|
||||
interfaces:
|
||||
CommitServiceClient:
|
||||
Clientset:
|
||||
github.com/argoproj/argo-cd/v2/controller/cache:
|
||||
interfaces:
|
||||
LiveStateCache:
|
||||
github.com/argoproj/argo-cd/v2/reposerver/apiclient:
|
||||
interfaces:
|
||||
RepoServerServiceClient:
|
||||
RepoServerService_GenerateManifestWithFilesClient:
|
||||
github.com/argoproj/argo-cd/v2/server/application:
|
||||
interfaces:
|
||||
Broadcaster:
|
||||
github.com/argoproj/argo-cd/v2/server/extension:
|
||||
interfaces:
|
||||
ApplicationGetter:
|
||||
ExtensionMetricsRegistry:
|
||||
ProjectGetter:
|
||||
RbacEnforcer:
|
||||
SettingsGetter:
|
||||
UserGetter:
|
||||
github.com/argoproj/argo-cd/v2/util/db:
|
||||
interfaces:
|
||||
ArgoDB:
|
||||
github.com/argoproj/argo-cd/v2/util/git:
|
||||
interfaces:
|
||||
Client:
|
||||
github.com/argoproj/argo-cd/v2/util/helm:
|
||||
interfaces:
|
||||
Client:
|
||||
github.com/argoproj/argo-cd/v2/util/io:
|
||||
interfaces:
|
||||
TempPaths:
|
||||
github.com/argoproj/argo-cd/v2/util/notification/argocd:
|
||||
interfaces:
|
||||
Service:
|
||||
# These mocks are not currently used, but they are part of the public API of this package.
|
||||
github.com/argoproj/argo-cd/v2/pkg/apiclient/session:
|
||||
interfaces:
|
||||
SessionServiceServer:
|
||||
SessionServiceClient:
|
||||
github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster:
|
||||
interfaces:
|
||||
ClusterServiceServer:
|
||||
@@ -8,4 +8,4 @@ python:
|
||||
build:
|
||||
os: "ubuntu-22.04"
|
||||
tools:
|
||||
python: "3.12"
|
||||
python: "3.7"
|
||||
11
CODEOWNERS
11
CODEOWNERS
@@ -2,13 +2,8 @@
|
||||
** @argoproj/argocd-approvers
|
||||
|
||||
# Docs
|
||||
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/USERS.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/README.md @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/mkdocs.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
/docs/** @argoproj/argocd-approvers @argoproj/argocd-approvers-docs
|
||||
|
||||
# CI
|
||||
/.codecov.yml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
/sonar-project.properties @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/code-contributions/)
|
||||
15
Dockerfile
15
Dockerfile
@@ -1,10 +1,10 @@
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:22.04@sha256:0bced47fffa3361afa981854fcabcd4577cd43cebbb808cea2b1f33a3dd7f508
|
||||
####################################################################################################
|
||||
# Builder image
|
||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||
# Also used as the image in CI jobs so needs all dependencies
|
||||
####################################################################################################
|
||||
FROM docker.io/library/golang:1.23.3@sha256:d56c3e08fe5b27729ee3834854ae8f7015af48fd651cd25d1e3bcf3c19830174 AS builder
|
||||
FROM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS builder
|
||||
|
||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -28,7 +28,7 @@ WORKDIR /tmp
|
||||
COPY hack/install.sh hack/tool-versions.sh ./
|
||||
COPY hack/installers installers
|
||||
|
||||
RUN ./install.sh helm && \
|
||||
RUN ./install.sh helm-linux && \
|
||||
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||
|
||||
####################################################################################################
|
||||
@@ -51,7 +51,7 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install -y \
|
||||
git git-lfs tini gpg tzdata connect-proxy && \
|
||||
git git-lfs tini gpg tzdata && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
@@ -83,7 +83,7 @@ WORKDIR /home/argocd
|
||||
####################################################################################################
|
||||
# Argo CD UI stage
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:23.0.0@sha256:e643c0b70dca9704dff42e12b17f5b719dbe4f95e6392fc2dfa0c5f02ea8044d AS argocd-ui
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/node:20.6.1@sha256:14bd39208dbc0eb171cbfb26ccb9ac09fa1b2eba04ccd528ab5d12983fd9ee24 AS argocd-ui
|
||||
|
||||
WORKDIR /src
|
||||
COPY ["ui/package.json", "ui/yarn.lock", "./"]
|
||||
@@ -101,7 +101,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.23.3@sha256:d56c3e08fe5b27729ee3834854ae8f7015af48fd651cd25d1e3bcf3c19830174 AS argocd-build
|
||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.10@sha256:16438a8e66c0c984f732e815ee5b7d715b8e33e81bac6d6a3750b1067744e7ca AS argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
@@ -140,8 +140,7 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-notifications && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-applicationset-controller && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-k8s-auth && \
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-k8s-auth
|
||||
|
||||
USER $ARGOCD_USER_ID
|
||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||
|
||||
145
Makefile
145
Makefile
@@ -3,7 +3,6 @@ CURRENT_DIR=$(shell pwd)
|
||||
DIST_DIR=${CURRENT_DIR}/dist
|
||||
CLI_NAME=argocd
|
||||
BIN_NAME=argocd
|
||||
CGO_FLAG=0
|
||||
|
||||
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
||||
|
||||
@@ -23,21 +22,14 @@ KUBECTL_VERSION=$(shell go list -m k8s.io/client-go | head -n 1 | rev | cut -d'
|
||||
GOPATH?=$(shell if test -x `which go`; then go env GOPATH; else echo "$(HOME)/go"; fi)
|
||||
GOCACHE?=$(HOME)/.cache/go-build
|
||||
|
||||
# Docker command to use
|
||||
DOCKER?=docker
|
||||
ifeq ($(DOCKER),podman)
|
||||
PODMAN_ARGS=--userns keep-id
|
||||
else
|
||||
PODMAN_ARGS=
|
||||
endif
|
||||
|
||||
DOCKER_SRCDIR?=$(GOPATH)/src
|
||||
DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd
|
||||
|
||||
ARGOCD_PROCFILE?=Procfile
|
||||
|
||||
# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml
|
||||
MKDOCS_DOCKER_IMAGE?=python:3.7-alpine
|
||||
# Strict mode has been disabled in latest versions of mkdocs-material.
|
||||
# Thus pointing to the older image of mkdocs-material matching the version used by argo-cd.
|
||||
MKDOCS_DOCKER_IMAGE?=squidfunk/mkdocs-material:4.1.1
|
||||
MKDOCS_RUN_ARGS?=
|
||||
|
||||
# Configuration for building argocd-test-tools image
|
||||
@@ -57,7 +49,7 @@ ARGOCD_E2E_DEX_PORT?=5556
|
||||
ARGOCD_E2E_YARN_HOST?=localhost
|
||||
ARGOCD_E2E_DISABLE_AUTH?=
|
||||
|
||||
ARGOCD_E2E_TEST_TIMEOUT?=90m
|
||||
ARGOCD_E2E_TEST_TIMEOUT?=45m
|
||||
|
||||
ARGOCD_IN_CI?=false
|
||||
ARGOCD_TEST_E2E?=true
|
||||
@@ -84,7 +76,7 @@ SUDO?=
|
||||
# Runs any command in the argocd-test-utils container in server mode
|
||||
# Server mode container will start with uid 0 and drop privileges during runtime
|
||||
define run-in-test-server
|
||||
$(SUDO) $(DOCKER) run --rm -it \
|
||||
$(SUDO) docker run --rm -it \
|
||||
--name argocd-test-server \
|
||||
-u $(CONTAINER_UID):$(CONTAINER_GID) \
|
||||
-e USER_ID=$(CONTAINER_UID) \
|
||||
@@ -109,14 +101,13 @@ define run-in-test-server
|
||||
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
||||
-p 4000:4000 \
|
||||
-p 5000:5000 \
|
||||
$(PODMAN_ARGS) \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
endef
|
||||
|
||||
# Runs any command in the argocd-test-utils container in client mode
|
||||
define run-in-test-client
|
||||
$(SUDO) $(DOCKER) run --rm -it \
|
||||
$(SUDO) docker run --rm -it \
|
||||
--name argocd-test-client \
|
||||
-u $(CONTAINER_UID):$(CONTAINER_GID) \
|
||||
-e HOME=/home/user \
|
||||
@@ -131,14 +122,13 @@ define run-in-test-client
|
||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||
-w ${DOCKER_WORKDIR} \
|
||||
$(PODMAN_ARGS) \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
endef
|
||||
|
||||
#
|
||||
define exec-in-test-server
|
||||
$(SUDO) $(DOCKER) exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
|
||||
$(SUDO) docker exec -it -u $(CONTAINER_UID):$(CONTAINER_GID) -e ARGOCD_E2E_RECORD=$(ARGOCD_E2E_RECORD) -e ARGOCD_E2E_K3S=$(ARGOCD_E2E_K3S) argocd-test-server $(1)
|
||||
endef
|
||||
|
||||
PATH:=$(PATH):$(PWD)/hack
|
||||
@@ -153,13 +143,6 @@ DEV_IMAGE?=false
|
||||
ARGOCD_GPG_ENABLED?=true
|
||||
ARGOCD_E2E_APISERVER_PORT?=8080
|
||||
|
||||
ifeq (${COVERAGE_ENABLED}, true)
|
||||
# We use this in the cli-local target to enable code coverage for e2e tests.
|
||||
COVERAGE_FLAG=-cover
|
||||
else
|
||||
COVERAGE_FLAG=
|
||||
endif
|
||||
|
||||
override LDFLAGS += \
|
||||
-X ${PACKAGE}.version=${VERSION} \
|
||||
-X ${PACKAGE}.buildDate=${BUILD_DATE} \
|
||||
@@ -192,25 +175,29 @@ endif
|
||||
.PHONY: all
|
||||
all: cli image
|
||||
|
||||
.PHONY: mockgen
|
||||
mockgen:
|
||||
./hack/generate-mock.sh
|
||||
# We have some legacy requirements for being checked out within $GOPATH.
|
||||
# The ensure-gopath target can be used as dependency to ensure we are running
|
||||
# within these boundaries.
|
||||
.PHONY: ensure-gopath
|
||||
ensure-gopath:
|
||||
ifneq ("$(PWD)","$(LEGACY_PATH)")
|
||||
@echo "Due to legacy requirements for codegen, repository needs to be checked out within \$$GOPATH"
|
||||
@echo "Location of this repo should be '$(LEGACY_PATH)' but is '$(PWD)'"
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
.PHONY: gogen
|
||||
gogen:
|
||||
gogen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
go generate ./...
|
||||
go generate ./util/argo/...
|
||||
|
||||
.PHONY: protogen
|
||||
protogen: mod-vendor-local protogen-fast
|
||||
|
||||
.PHONY: protogen-fast
|
||||
protogen-fast:
|
||||
protogen: ensure-gopath mod-vendor-local
|
||||
export GO111MODULE=off
|
||||
./hack/generate-proto.sh
|
||||
|
||||
.PHONY: openapigen
|
||||
openapigen:
|
||||
openapigen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
./hack/update-openapi.sh
|
||||
|
||||
@@ -225,25 +212,19 @@ notification-docs:
|
||||
|
||||
|
||||
.PHONY: clientgen
|
||||
clientgen:
|
||||
clientgen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
./hack/update-codegen.sh
|
||||
|
||||
.PHONY: clidocsgen
|
||||
clidocsgen:
|
||||
clidocsgen: ensure-gopath
|
||||
go run tools/cmd-docs/main.go
|
||||
|
||||
.PHONY: actionsdocsgen
|
||||
actionsdocsgen:
|
||||
hack/generate-actions-list.sh
|
||||
|
||||
.PHONY: codegen-local
|
||||
codegen-local: mod-vendor-local mockgen gogen protogen clientgen openapigen clidocsgen actionsdocsgen manifests-local notification-docs notification-catalog
|
||||
codegen-local: ensure-gopath mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog
|
||||
rm -rf vendor/
|
||||
|
||||
.PHONY: codegen-local-fast
|
||||
codegen-local-fast: mockgen gogen protogen-fast clientgen openapigen clidocsgen manifests-local notification-docs notification-catalog
|
||||
|
||||
.PHONY: codegen
|
||||
codegen: test-tools-image
|
||||
$(call run-in-test-client,make codegen-local)
|
||||
@@ -254,11 +235,11 @@ cli: test-tools-image
|
||||
|
||||
.PHONY: cli-local
|
||||
cli-local: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -gcflags="all=-N -l" $(COVERAGE_FLAG) -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
|
||||
.PHONY: gen-resources-cli-local
|
||||
gen-resources-cli-local: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
|
||||
|
||||
.PHONY: release-cli
|
||||
release-cli: clean-debug build-ui
|
||||
@@ -273,8 +254,8 @@ release-cli: clean-debug build-ui
|
||||
.PHONY: test-tools-image
|
||||
test-tools-image:
|
||||
ifndef SKIP_TEST_TOOLS_IMAGE
|
||||
$(SUDO) $(DOCKER) build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
|
||||
$(SUDO) $(DOCKER) tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG)
|
||||
$(SUDO) docker build --build-arg UID=$(CONTAINER_UID) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
|
||||
$(SUDO) docker tag $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG)
|
||||
endif
|
||||
|
||||
.PHONY: manifests-local
|
||||
@@ -288,25 +269,25 @@ manifests: test-tools-image
|
||||
# consolidated binary for cli, util, server, repo-server, controller
|
||||
.PHONY: argocd-all
|
||||
argocd-all: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||
|
||||
.PHONY: server
|
||||
server: clean-debug
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
|
||||
|
||||
.PHONY: repo-server
|
||||
repo-server:
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
|
||||
|
||||
.PHONY: controller
|
||||
controller:
|
||||
CGO_ENABLED=${CGO_FLAG} GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
CGO_ENABLED=0 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
|
||||
.PHONY: build-ui
|
||||
build-ui:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui .
|
||||
DOCKER_BUILDKIT=1 docker build -t argocd-ui --platform=$(TARGET_ARCH) --target argocd-ui .
|
||||
find ./ui/dist -type f -not -name gitkeep -delete
|
||||
$(DOCKER) run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
docker run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -315,29 +296,29 @@ ifeq ($(DEV_IMAGE), true)
|
||||
# the dist directory is under .dockerignore.
|
||||
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
|
||||
image: build-ui
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base .
|
||||
CGO_ENABLED=${CGO_FLAG} GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
DOCKER_BUILDKIT=1 docker build --platform=$(TARGET_ARCH) -t argocd-base --target argocd-base .
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GODEBUG="tarinsecurepath=0,zipinsecurepath=0" go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-cmp-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-dex
|
||||
cp Dockerfile.dev dist
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
DOCKER_BUILDKIT=1 docker build --platform=$(TARGET_ARCH) -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
else
|
||||
image:
|
||||
DOCKER_BUILDKIT=1 $(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
|
||||
DOCKER_BUILDKIT=1 docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) --platform=$(TARGET_ARCH) .
|
||||
endif
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: armimage
|
||||
armimage:
|
||||
$(DOCKER) build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
|
||||
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
|
||||
|
||||
.PHONY: builder-image
|
||||
builder-image:
|
||||
$(DOCKER) build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then $(DOCKER) push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi
|
||||
docker build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: mod-download
|
||||
mod-download: test-tools-image
|
||||
@@ -371,7 +352,7 @@ lint-local:
|
||||
golangci-lint --version
|
||||
# NOTE: If you get a "Killed" OOM message, try reducing the value of GOGC
|
||||
# See https://github.com/golangci/golangci-lint#memory-usage-of-golangci-lint
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --fix --verbose
|
||||
GOGC=$(ARGOCD_LINT_GOGC) GOMAXPROCS=2 golangci-lint run --enable gofmt --fix --verbose --timeout 3000s --max-issues-per-linter 0 --max-same-issues 0
|
||||
|
||||
.PHONY: lint-ui
|
||||
lint-ui: test-tools-image
|
||||
@@ -405,9 +386,9 @@ test: test-tools-image
|
||||
.PHONY: test-local
|
||||
test-local:
|
||||
if test "$(TEST_MODULE)" = ""; then \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results"; \
|
||||
./hack/test.sh -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
|
||||
else \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -args -test.gocoverdir="$(PWD)/test-results" "$(TEST_MODULE)"; \
|
||||
./hack/test.sh -coverprofile=coverage.out "$(TEST_MODULE)"; \
|
||||
fi
|
||||
|
||||
.PHONY: test-race
|
||||
@@ -419,9 +400,9 @@ test-race: test-tools-image
|
||||
.PHONY: test-race-local
|
||||
test-race-local:
|
||||
if test "$(TEST_MODULE)" = ""; then \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES=`go list ./... | grep -v 'test/e2e'` ./hack/test.sh -race -args -test.gocoverdir="$(PWD)/test-results"; \
|
||||
./hack/test.sh -race -coverprofile=coverage.out `go list ./... | grep -v 'test/e2e'`; \
|
||||
else \
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=0 PACKAGES="$(TEST_MODULE)" ./hack/test.sh -race -args -test.gocoverdir="$(PWD)/test-results"; \
|
||||
./hack/test.sh -race -coverprofile=coverage.out "$(TEST_MODULE)"; \
|
||||
fi
|
||||
|
||||
# Run the E2E test suite. E2E test servers (see start-e2e target) must be
|
||||
@@ -435,7 +416,7 @@ test-e2e:
|
||||
test-e2e-local: cli-local
|
||||
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
||||
export GO111MODULE=off
|
||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
|
||||
ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e
|
||||
|
||||
# Spawns a shell in the test server container for debugging purposes
|
||||
debug-test-server: test-tools-image
|
||||
@@ -448,7 +429,7 @@ debug-test-client: test-tools-image
|
||||
# Starts e2e server in a container
|
||||
.PHONY: start-e2e
|
||||
start-e2e: test-tools-image
|
||||
$(DOCKER) version
|
||||
docker version
|
||||
mkdir -p ${GOCACHE}
|
||||
$(call run-in-test-server,make ARGOCD_PROCFILE=test/container/Procfile start-e2e-local)
|
||||
|
||||
@@ -457,7 +438,6 @@ start-e2e: test-tools-image
|
||||
start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
||||
kubectl create ns argocd-e2e || true
|
||||
kubectl create ns argocd-e2e-external || true
|
||||
kubectl create ns argocd-e2e-external-2 || true
|
||||
kubectl config set-context --current --namespace=argocd-e2e
|
||||
kustomize build test/manifests/base | kubectl apply -f -
|
||||
kubectl apply -f https://raw.githubusercontent.com/open-cluster-management/api/a6845f2ebcb186ec26b832f60c988537a58f3859/cluster/v1alpha1/0000_04_clusters.open-cluster-management.io_placementdecisions.crd.yaml
|
||||
@@ -466,13 +446,6 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
||||
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
|
||||
mkdir -p /tmp/coverage/repo-server
|
||||
mkdir -p /tmp/coverage/applicationset-controller
|
||||
mkdir -p /tmp/coverage/notification
|
||||
mkdir -p /tmp/coverage/commit-server
|
||||
# set paths for locally managed ssh known hosts and tls certs data
|
||||
ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \
|
||||
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
|
||||
@@ -485,13 +458,11 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
|
||||
BIN_MODE=$(ARGOCD_BIN_MODE) \
|
||||
ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external,argocd-e2e-external-2 \
|
||||
ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES=argocd-e2e-external,argocd-e2e-external-2 \
|
||||
ARGOCD_APPLICATIONSET_CONTROLLER_TOKENREF_STRICT_MODE=true \
|
||||
ARGOCD_APPLICATION_NAMESPACES=argocd-e2e-external \
|
||||
ARGOCD_APPLICATIONSET_CONTROLLER_NAMESPACES=argocd-e2e-external \
|
||||
ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \
|
||||
ARGOCD_E2E_TEST=true \
|
||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||
ls -lrt /tmp/coverage
|
||||
|
||||
# Cleans VSCode debug.test files from sub-dirs to prevent them from being included in by golang embed
|
||||
.PHONY: clean-debug
|
||||
@@ -504,7 +475,7 @@ clean: clean-debug
|
||||
|
||||
.PHONY: start
|
||||
start: test-tools-image
|
||||
$(DOCKER) version
|
||||
docker version
|
||||
$(call run-in-test-server,make ARGOCD_PROCFILE=test/container/Procfile start-local ARGOCD_START=${ARGOCD_START})
|
||||
|
||||
# Starts a local instance of ArgoCD
|
||||
@@ -517,11 +488,9 @@ start-local: mod-vendor-local dep-ui-local cli-local
|
||||
mkdir -p /tmp/argocd-local
|
||||
mkdir -p /tmp/argocd-local/gpg/keys && chmod 0700 /tmp/argocd-local/gpg/keys
|
||||
mkdir -p /tmp/argocd-local/gpg/source
|
||||
REDIS_PASSWORD=$(shell kubectl get secret argocd-redis -o jsonpath='{.data.auth}' | base64 -d) \
|
||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||
ARGOCD_IN_CI=false \
|
||||
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
||||
BIN_MODE=$(ARGOCD_BIN_MODE) \
|
||||
ARGOCD_E2E_TEST=false \
|
||||
ARGOCD_APPLICATION_NAMESPACES=$(ARGOCD_APPLICATION_NAMESPACES) \
|
||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||
@@ -555,7 +524,7 @@ build-docs-local:
|
||||
|
||||
.PHONY: build-docs
|
||||
build-docs:
|
||||
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install mkdocs; pip install $$(mkdocs get-deps); mkdocs build'
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -v ${CURRENT_DIR}:/docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install -r docs/requirements.txt; mkdocs build'
|
||||
|
||||
.PHONY: serve-docs-local
|
||||
serve-docs-local:
|
||||
@@ -563,7 +532,8 @@ serve-docs-local:
|
||||
|
||||
.PHONY: serve-docs
|
||||
serve-docs:
|
||||
$(DOCKER) run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs -w /docs --entrypoint "" ${MKDOCS_DOCKER_IMAGE} sh -c 'pip install mkdocs; pip install $$(mkdocs get-deps); mkdocs serve -a $$(ip route get 1 | awk '\''{print $$7}'\''):8000'
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}/site:/site -w /site --entrypoint "" ${MKDOCS_DOCKER_IMAGE} python3 -m http.server --bind 0.0.0.0 8000
|
||||
|
||||
|
||||
# Verify that kubectl can connect to your K8s cluster from Docker
|
||||
.PHONY: verify-kube-connect
|
||||
@@ -586,8 +556,7 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal
|
||||
.PHONY: install-test-tools-local
|
||||
install-test-tools-local:
|
||||
./hack/install.sh kustomize
|
||||
./hack/install.sh helm
|
||||
./hack/install.sh gotestsum
|
||||
./hack/install.sh helm-linux
|
||||
|
||||
# Installs all tools required for running codegen (Linux packages)
|
||||
.PHONY: install-codegen-tools-local
|
||||
@@ -615,7 +584,7 @@ list:
|
||||
|
||||
.PHONY: applicationset-controller
|
||||
applicationset-controller:
|
||||
GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=${CGO_FLAG} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd
|
||||
GODEBUG="tarinsecurepath=0,zipinsecurepath=0" CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-applicationset-controller ./cmd
|
||||
|
||||
.PHONY: checksums
|
||||
checksums:
|
||||
|
||||
3
OWNERS
3
OWNERS
@@ -1,12 +1,10 @@
|
||||
owners:
|
||||
- alexmt
|
||||
- crenshaw-dev
|
||||
- jessesuen
|
||||
|
||||
approvers:
|
||||
- alexec
|
||||
- alexmt
|
||||
- gdsoumya
|
||||
- jannfis
|
||||
- jessesuen
|
||||
- jgwest
|
||||
@@ -32,3 +30,4 @@ reviewers:
|
||||
- zachaller
|
||||
- 34fathombelow
|
||||
- alexef
|
||||
- gdsoumya
|
||||
|
||||
13
Procfile
13
Procfile
@@ -1,13 +1,12 @@
|
||||
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/app-controller} HOSTNAME=testappcontroller-1 FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'}"
|
||||
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
|
||||
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
|
||||
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
||||
redis: hack/start-redis-with-password.sh
|
||||
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}"
|
||||
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" = 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} docker.io/library/redis:$(grep "image: redis" manifests/base/redis/argocd-redis-deployment.yaml | cut -d':' -f3) --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
|
||||
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_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'
|
||||
git-server: test/fixture/testrepos/start-git.sh
|
||||
helm-registry: test/fixture/testrepos/start-helm-registry.sh
|
||||
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
|
||||
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "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'}"
|
||||
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug"
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
[](https://github.com/argoproj/argo-cd/actions?query=workflow%3A%22Integration+tests%22)
|
||||
[](https://codecov.io/gh/argoproj/argo-cd)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/4486)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/argoproj/argo-cd)
|
||||
[](https://api.securityscorecards.dev/projects/github.com/argoproj/argo-cd)
|
||||
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fargoproj%2Fargo-cd?ref=badge_shield)
|
||||
|
||||
**Social:**
|
||||
[](https://twitter.com/argoproj)
|
||||
[](https://argoproj.github.io/community/join-slack)
|
||||
[](https://www.linkedin.com/company/argoproj/)
|
||||
|
||||
# Argo CD - Declarative Continuous Delivery for Kubernetes
|
||||
|
||||
@@ -56,7 +56,7 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
### Blogs and Presentations
|
||||
|
||||
1. [Awesome-Argo: A Curated List of Awesome Projects and Resources Related to Argo](https://github.com/terrytangyuan/awesome-argo)
|
||||
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://akuity.io/blog/secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argocd/)
|
||||
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://akuity.io/blog/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argocd-kubecon-china-2021/)
|
||||
1. [GitOps Without Pipelines With ArgoCD Image Updater](https://youtu.be/avPUQin9kzU)
|
||||
1. [Combining Argo CD (GitOps), Crossplane (Control Plane), And KubeVela (OAM)](https://youtu.be/eEcgn_gU3SM)
|
||||
1. [How to Apply GitOps to Everything - Combining Argo CD and Crossplane](https://youtu.be/yrj4lmScKHQ)
|
||||
@@ -85,5 +85,4 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
1. [Getting Started with ArgoCD for GitOps Deployments](https://youtu.be/AvLuplh1skA)
|
||||
1. [Using Argo CD & Datree for Stable Kubernetes CI/CD Deployments](https://youtu.be/17894DTru2Y)
|
||||
1. [How to create Argo CD Applications Automatically using ApplicationSet? "Automation of GitOps"](https://amralaayassen.medium.com/how-to-create-argocd-applications-automatically-using-applicationset-automation-of-the-gitops-59455eaf4f72)
|
||||
1. [Progressive Delivery with Service Mesh – Argo Rollouts with Istio](https://www.cncf.io/blog/2022/12/16/progressive-delivery-with-service-mesh-argo-rollouts-with-istio/)
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
header:
|
||||
schema-version: 1.0.0
|
||||
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
|
||||
last-updated: '2023-10-27'
|
||||
last-reviewed: '2023-10-27'
|
||||
commit-hash: 74a367d10e7110209610ba3ec225539ebe5f7522
|
||||
project-url: https://github.com/argoproj/argo-cd
|
||||
project-release: v2.14.0
|
||||
changelog: https://github.com/argoproj/argo-cd/releases
|
||||
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
|
||||
project-lifecycle:
|
||||
status: active
|
||||
roadmap: https://github.com/orgs/argoproj/projects/25
|
||||
bug-fixes-only: false
|
||||
core-maintainers:
|
||||
- https://github.com/argoproj/argoproj/blob/master/MAINTAINERS.md
|
||||
release-cycle: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/
|
||||
release-process: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#release-process
|
||||
contribution-policy:
|
||||
accepts-pull-requests: true
|
||||
accepts-automated-pull-requests: true
|
||||
automated-tools-list:
|
||||
- automated-tool: dependabot
|
||||
action: allowed
|
||||
path:
|
||||
- /
|
||||
- automated-tool: snyk-report
|
||||
action: allowed
|
||||
path:
|
||||
- docs/snyk
|
||||
comment: |
|
||||
This tool runs Snyk and generates a report of vulnerabilities in the project's dependencies. The report is
|
||||
placed in the project's documentation. The workflow is defined here:
|
||||
https://github.com/argoproj/argo-cd/blob/master/.github/workflows/update-snyk.yaml
|
||||
contributing-policy: https://argo-cd.readthedocs.io/en/stable/developer-guide/code-contributions/
|
||||
code-of-conduct: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
|
||||
documentation:
|
||||
- https://argo-cd.readthedocs.io/
|
||||
distribution-points:
|
||||
- https://github.com/argoproj/argo-cd/releases
|
||||
- https://quay.io/repository/argoproj/argocd
|
||||
security-artifacts:
|
||||
threat-model:
|
||||
threat-model-created: true
|
||||
evidence-url:
|
||||
- https://github.com/argoproj/argoproj/blob/master/docs/argo_threat_model.pdf
|
||||
- https://github.com/argoproj/argoproj/blob/master/docs/end_user_threat_model.pdf
|
||||
self-assessment:
|
||||
self-assessment-created: false
|
||||
comment: |
|
||||
An extensive self-assessment was performed for CNCF graduation. Because the self-assessment process was evolving
|
||||
at the time, no standardized document has been published.
|
||||
security-testing:
|
||||
- tool-type: sca
|
||||
tool-name: Dependabot
|
||||
tool-version: "2"
|
||||
tool-url: https://github.com/dependabot
|
||||
integration:
|
||||
ad-hoc: false
|
||||
ci: false
|
||||
before-release: false
|
||||
tool-rulesets:
|
||||
- https://github.com/argoproj/argo-cd/blob/master/.github/dependabot.yml
|
||||
- tool-type: sca
|
||||
tool-name: Snyk
|
||||
tool-version: latest
|
||||
tool-url: https://snyk.io/
|
||||
integration:
|
||||
ad-hoc: true
|
||||
ci: true
|
||||
before-release: false
|
||||
- tool-type: sast
|
||||
tool-name: CodeQL
|
||||
tool-version: latest
|
||||
tool-url: https://codeql.github.com/
|
||||
integration:
|
||||
ad-hoc: false
|
||||
ci: true
|
||||
before-release: false
|
||||
comment: |
|
||||
We use the default configuration with the latest version.
|
||||
security-assessments:
|
||||
- auditor-name: Trail of Bits
|
||||
auditor-url: https://trailofbits.com
|
||||
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/argo_security_final_report.pdf
|
||||
report-year: 2021
|
||||
- auditor-name: Ada Logics
|
||||
auditor-url: https://adalogics.com
|
||||
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/argo_security_audit_2022.pdf
|
||||
report-year: 2022
|
||||
- auditor-name: Ada Logics
|
||||
auditor-url: https://adalogics.com
|
||||
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/audit_fuzzer_adalogics_2022.pdf
|
||||
report-year: 2022
|
||||
comment: |
|
||||
Part of the audit was performed by Ada Logics, focussed on fuzzing.
|
||||
- auditor-name: Chainguard
|
||||
auditor-url: https://chainguard.dev
|
||||
auditor-report: https://github.com/argoproj/argoproj/blob/master/docs/software_supply_chain_slsa_assessment_chainguard_2023.pdf
|
||||
report-year: 2023
|
||||
comment: |
|
||||
Confirmed the project's release process as achieving SLSA (v0.1) level 3.
|
||||
security-contacts:
|
||||
- type: email
|
||||
value: cncf-argo-security@lists.cncf.io
|
||||
primary: true
|
||||
vulnerability-reporting:
|
||||
accepts-vulnerability-reports: true
|
||||
email-contact: cncf-argo-security@lists.cncf.io
|
||||
security-policy: https://github.com/argoproj/argo-cd/security/policy
|
||||
bug-bounty-available: true
|
||||
bug-bounty-url: https://hackerone.com/ibb/policy_scopes
|
||||
out-scope:
|
||||
- vulnerable and outdated components # See https://github.com/argoproj/argo-cd/blob/master/SECURITY.md#a-word-about-security-scanners
|
||||
- security logging and monitoring failures
|
||||
dependencies:
|
||||
third-party-packages: true
|
||||
dependencies-lists:
|
||||
- https://github.com/argoproj/argo-cd/blob/master/go.mod
|
||||
- https://github.com/argoproj/argo-cd/blob/master/Dockerfile
|
||||
- https://github.com/argoproj/argo-cd/blob/master/ui/package.json
|
||||
sbom:
|
||||
- sbom-file: https://github.com/argoproj/argo-cd/releases # Every release's assets include SBOMs.
|
||||
sbom-format: SPDX
|
||||
dependencies-lifecycle:
|
||||
policy-url: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#dependencies-lifecycle-policy
|
||||
env-dependencies-policy:
|
||||
policy-url: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#dependencies-lifecycle-policy
|
||||
85
USERS.md
85
USERS.md
@@ -11,76 +11,55 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [7shifts](https://www.7shifts.com/)
|
||||
1. [Adevinta](https://www.adevinta.com/)
|
||||
1. [Adfinis](https://adfinis.com)
|
||||
1. [Adobe](https://www.adobe.com/)
|
||||
1. [Adventure](https://jp.adventurekk.com/)
|
||||
1. [Adyen](https://www.adyen.com)
|
||||
1. [AirQo](https://airqo.net/)
|
||||
1. [Akuity](https://akuity.io/)
|
||||
1. [Alarm.com](https://alarm.com/)
|
||||
1. [Alauda](https://alauda.io/)
|
||||
1. [Albert Heijn](https://ah.nl/)
|
||||
1. [Alibaba Group](https://www.alibabagroup.com/)
|
||||
1. [Allianz Direct](https://www.allianzdirect.de/)
|
||||
1. [AlphaSense](https://www.alpha-sense.com/)
|
||||
1. [Amadeus IT Group](https://amadeus.com/)
|
||||
1. [Ambassador Labs](https://www.getambassador.io/)
|
||||
1. [Ancestry](https://www.ancestry.com/)
|
||||
1. [Andgo Systems](https://www.andgosystems.com/)
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [Ant Group](https://www.antgroup.com/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
2. [Arturia](https://www.arturia.com)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||
1. [Augury](https://www.augury.com/)
|
||||
1. [Autodesk](https://www.autodesk.com)
|
||||
1. [Axians ACSP](https://www.axians.fr)
|
||||
2. [Autodesk](https://www.autodesk.com)
|
||||
1. [Axual B.V.](https://axual.com)
|
||||
1. [Back Market](https://www.backmarket.com)
|
||||
1. [Bajaj Finserv Health Ltd.](https://www.bajajfinservhealth.in)
|
||||
1. [Baloise](https://www.baloise.com)
|
||||
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
||||
1. [Beat](https://thebeat.co/en/)
|
||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
||||
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
||||
1. [Believable Bots](https://believablebots.io)
|
||||
1. [BigPanda](https://bigpanda.io)
|
||||
1. [BioBox Analytics](https://biobox.io)
|
||||
1. [BMW Group](https://www.bmwgroup.com/)
|
||||
1. [Boozt](https://www.booztgroup.com/)
|
||||
1. [Bosch](https://www.bosch.com/)
|
||||
1. [Boticario](https://www.boticario.com.br/)
|
||||
1. [Broker Consulting, a.s.](https://www.bcas.cz/en/)
|
||||
1. [Bulder Bank](https://bulderbank.no)
|
||||
1. [Cabify](https://cabify.com/en)
|
||||
1. [CAM](https://cam-inc.co.jp)
|
||||
1. [Camptocamp](https://camptocamp.com)
|
||||
1. [Candis](https://www.candis.io)
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [CARFAX](https://www.carfax.com)
|
||||
1. [CARFAX Europe](https://www.carfax.eu)
|
||||
1. [Carrefour Group](https://www.carrefour.com)
|
||||
1. [Casavo](https://casavo.com)
|
||||
1. [Celonis](https://www.celonis.com/)
|
||||
1. [CERN](https://home.cern/)
|
||||
1. [Chainnodes](https://chainnodes.org)
|
||||
1. [Chargetrip](https://chargetrip.com)
|
||||
1. [Chainnodes](https://chainnodes.org)
|
||||
1. [Chime](https://www.chime.com)
|
||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||
1. [Cloud Posse](https://www.cloudposse.com/)
|
||||
1. [Cloud Scale](https://cloudscaleinc.com/)
|
||||
1. [CloudScript](https://www.cloudscript.com.br/)
|
||||
1. [CloudGeometry](https://www.cloudgeometry.io/)
|
||||
1. [Cloudmate](https://cloudmt.co.kr/)
|
||||
1. [Cloudogu](https://cloudogu.com/)
|
||||
1. [Cobalt](https://www.cobalt.io/)
|
||||
1. [Codefresh](https://www.codefresh.io/)
|
||||
1. [Codility](https://www.codility.com/)
|
||||
1. [Cognizant](https://www.cognizant.com/)
|
||||
1. [Commonbond](https://commonbond.co/)
|
||||
1. [Compatio.AI](https://compatio.ai/)
|
||||
1. [Contlo](https://contlo.com/)
|
||||
1. [Coralogix](https://coralogix.com/)
|
||||
1. [Crédit Agricole CIB](https://www.ca-cib.com)
|
||||
1. [CROZ d.o.o.](https://croz.net/)
|
||||
@@ -89,7 +68,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [D2iQ](https://www.d2iq.com)
|
||||
1. [DaoCloud](https://daocloud.io/)
|
||||
1. [Datarisk](https://www.datarisk.io/)
|
||||
1. [Daydream](https://daydream.ing)
|
||||
1. [Deloitte](https://www.deloitte.com/)
|
||||
1. [Deutsche Telekom AG](https://telekom.com)
|
||||
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
||||
@@ -114,19 +92,14 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Fave](https://myfave.com)
|
||||
1. [Flexport](https://www.flexport.com/)
|
||||
1. [Flip](https://flip.id)
|
||||
1. [Fly Security](https://www.flysecurity.com.br/)
|
||||
1. [Fonoa](https://www.fonoa.com/)
|
||||
1. [Fortra](https://www.fortra.com)
|
||||
1. [freee](https://corp.freee.co.jp/en/company/)
|
||||
1. [Freshop, Inc](https://www.freshop.com/)
|
||||
1. [Future PLC](https://www.futureplc.com/)
|
||||
1. [Flagler Health](https://www.flaglerhealth.io/)
|
||||
1. [G DATA CyberDefense AG](https://www.gdata-software.com/)
|
||||
1. [G-Research](https://www.gresearch.com/teams/open-source-software/)
|
||||
1. [Garner](https://www.garnercorp.com)
|
||||
1. [Generali Deutschland AG](https://www.generali.de/)
|
||||
1. [Gepardec](https://gepardec.com/)
|
||||
1. [Getir](https://getir.com)
|
||||
1. [GetYourGuide](https://www.getyourguide.com/)
|
||||
1. [Gitpod](https://www.gitpod.io)
|
||||
1. [Gllue](https://gllue.com)
|
||||
@@ -136,14 +109,13 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [GlueOps](https://glueops.dev)
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Gojek](https://www.gojek.io/)
|
||||
1. [GoTo Financial](https://gotofinancial.com/)
|
||||
1. [GoTo](https://www.goto.com/)
|
||||
1. [GoTo Financial](https://gotofinancial.com/)
|
||||
1. [Greenpass](https://www.greenpass.com.br/)
|
||||
1. [Gridfuse](https://gridfuse.com/)
|
||||
1. [Groww](https://groww.in)
|
||||
1. [Grupo MasMovil](https://grupomasmovil.com/en/)
|
||||
1. [Handelsbanken](https://www.handelsbanken.se)
|
||||
1. [Hazelcast](https://hazelcast.com/)
|
||||
1. [Healy](https://www.healyworld.net)
|
||||
1. [Helio](https://helio.exchange)
|
||||
1. [Hetki](https://hetki.ai)
|
||||
@@ -151,18 +123,14 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Hiya](https://hiya.com)
|
||||
1. [Honestbank](https://honestbank.com)
|
||||
1. [Hostinger](https://www.hostinger.com)
|
||||
1. [IABAI](https://www.iab.ai)
|
||||
1. [IBM](https://www.ibm.com/)
|
||||
1. [Ibotta](https://home.ibotta.com)
|
||||
1. [IFS](https://www.ifs.com)
|
||||
1. [IITS-Consulting](https://iits-consulting.de)
|
||||
1. [IllumiDesk](https://www.illumidesk.com)
|
||||
1. [imaware](https://imaware.health)
|
||||
1. [Indeed](https://indeed.com)
|
||||
1. [Index Exchange](https://www.indexexchange.com/)
|
||||
1. [Info Support](https://www.infosupport.com/)
|
||||
1. [InsideBoard](https://www.insideboard.com)
|
||||
1. [Instruqt](https://www.instruqt.com)
|
||||
1. [Intuit](https://www.intuit.com/)
|
||||
1. [Jellysmack](https://www.jellysmack.com)
|
||||
1. [Joblift](https://joblift.com/)
|
||||
@@ -172,21 +140,16 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Karrot](https://www.daangn.com/)
|
||||
1. [KarrotPay](https://www.daangnpay.com/)
|
||||
1. [Kasa](https://kasa.co.kr/)
|
||||
1. [Kave Home](https://kavehome.com)
|
||||
1. [Keeeb](https://www.keeeb.com/)
|
||||
1. [KelkooGroup](https://www.kelkoogroup.com)
|
||||
1. [Keptn](https://keptn.sh)
|
||||
1. [Kinguin](https://www.kinguin.net/)
|
||||
1. [KintoHub](https://www.kintohub.com/)
|
||||
1. [KompiTech GmbH](https://www.kompitech.com/)
|
||||
1. [Kong Inc.](https://konghq.com/)
|
||||
1. [KPMG](https://kpmg.com/uk)
|
||||
1. [KubeSphere](https://github.com/kubesphere)
|
||||
1. [Kurly](https://www.kurly.com/)
|
||||
1. [Kvist](https://kvistsolutions.com)
|
||||
1. [Kyriba](https://www.kyriba.com/)
|
||||
1. [LeFigaro](https://www.lefigaro.fr/)
|
||||
1. [Lely](https://www.lely.com/)
|
||||
1. [LexisNexis](https://www.lexisnexis.com/)
|
||||
1. [Lian Chu Securities](https://lczq.com)
|
||||
1. [Liatrio](https://www.liatrio.com)
|
||||
@@ -206,8 +169,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Meican](https://meican.com/)
|
||||
1. [Meilleurs Agents](https://www.meilleursagents.com/)
|
||||
1. [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
|
||||
1. [Mercedes-Benz.io](https://www.mercedes-benz.io/)
|
||||
1. [Metacore Games](https://metacoregames.com/)
|
||||
1. [Metanet](http://www.metanet.co.kr/en/)
|
||||
1. [MindSpore](https://mindspore.cn)
|
||||
1. [Mirantis](https://mirantis.com/)
|
||||
@@ -216,28 +177,20 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Moengage](https://www.moengage.com/)
|
||||
1. [Money Forward](https://corp.moneyforward.com/en/)
|
||||
1. [MOO Print](https://www.moo.com/)
|
||||
1. [Mozilla](https://www.mozilla.org)
|
||||
1. [MTN Group](https://www.mtn.com/)
|
||||
1. [Municipality of The Hague](https://www.denhaag.nl/)
|
||||
1. [My Job Glasses](https://myjobglasses.com)
|
||||
1. [Natura &Co](https://naturaeco.com/)
|
||||
1. [Nethopper](https://nethopper.io)
|
||||
1. [New Relic](https://newrelic.com/)
|
||||
1. [Nextbasket](https://nextbasket.com)
|
||||
1. [Nextdoor](https://nextdoor.com/)
|
||||
1. [Next Fit Sistemas](https://nextfit.com.br/)
|
||||
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
|
||||
1. [Nitro](https://gonitro.com)
|
||||
1. [NYCU, CS IT Center](https://it.cs.nycu.edu.tw)
|
||||
1. [Objective](https://www.objective.com.br/)
|
||||
1. [OCCMundial](https://occ.com.mx)
|
||||
1. [Octadesk](https://octadesk.com)
|
||||
1. [Octopus Deploy](https://octopus.com)
|
||||
1. [Olfeo](https://www.olfeo.com/)
|
||||
1. [omegaUp](https://omegaUp.com)
|
||||
1. [Omni](https://omni.se/)
|
||||
1. [Oncourse Home Solutions](https://oncoursehome.com/)
|
||||
1. [Open Analytics](https://openanalytics.eu)
|
||||
1. [openEuler](https://openeuler.org)
|
||||
1. [openGauss](https://opengauss.org/)
|
||||
1. [OpenGov](https://opengov.com)
|
||||
@@ -249,73 +202,53 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Optoro](https://www.optoro.com/)
|
||||
1. [Orbital Insight](https://orbitalinsight.com/)
|
||||
1. [Oscar Health Insurance](https://hioscar.com/)
|
||||
1. [Outpost24](https://outpost24.com/)
|
||||
1. [p3r](https://www.p3r.one/)
|
||||
1. [Packlink](https://www.packlink.com/)
|
||||
1. [PagerDuty](https://www.pagerduty.com/)
|
||||
1. [Pandosearch](https://www.pandosearch.com/en/home)
|
||||
1. [Patreon](https://www.patreon.com/)
|
||||
1. [PayIt](https://payitgov.com/)
|
||||
1. [PayPay](https://paypay.ne.jp/)
|
||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||
1. [Percona](https://percona.com/)
|
||||
1. [PGS](https://www.pgs.com)
|
||||
1. [Pigment](https://www.gopigment.com/)
|
||||
1. [Pipedrive](https://www.pipedrive.com/)
|
||||
1. [Pipefy](https://www.pipefy.com/)
|
||||
1. [Pipekit](https://pipekit.io/)
|
||||
1. [Pismo](https://pismo.io/)
|
||||
1. [PITS Globale Datenrettungsdienste](https://www.pitsdatenrettung.de/)
|
||||
1. [Platform9 Systems](https://platform9.com/)
|
||||
1. [Polarpoint.io](https://polarpoint.io)
|
||||
1. [PostFinance](https://github.com/postfinance)
|
||||
1. [Preferred Networks](https://preferred.jp/en/)
|
||||
1. [Previder BV](https://previder.nl)
|
||||
1. [Priceline](https://priceline.com)
|
||||
1. [Procore](https://www.procore.com)
|
||||
1. [Productboard](https://www.productboard.com/)
|
||||
1. [Prudential](https://prudential.com.sg)
|
||||
1. [PT Boer Technology (Btech)](https://btech.id/)
|
||||
1. [PUBG](https://www.pubg.com)
|
||||
1. [Puzzle ITC](https://www.puzzle.ch/)
|
||||
1. [Pvotal Technologies](https://pvotal.tech/)
|
||||
1. [Qonto](https://qonto.com)
|
||||
1. [QuintoAndar](https://quintoandar.com.br)
|
||||
1. [Quipper](https://www.quipper.com/)
|
||||
1. [RapidAPI](https://www.rapidapi.com/)
|
||||
1. [rebuy](https://www.rebuy.de/)
|
||||
1. [Recreation.gov](https://www.recreation.gov/)
|
||||
1. [Red Hat](https://www.redhat.com/)
|
||||
1. [Redpill Linpro](https://www.redpill-linpro.com/)
|
||||
1. [Reenigne Cloud](https://reenigne.ca)
|
||||
1. [reev.com](https://www.reev.com/)
|
||||
1. [Relex Solutions](https://www.relexsolutions.com/)
|
||||
1. [RightRev](https://rightrev.com/)
|
||||
1. [Rijkswaterstaat](https://www.rijkswaterstaat.nl/en)
|
||||
1. [Rise](https://www.risecard.eu/)
|
||||
1. [Riskified](https://www.riskified.com/)
|
||||
1. [Robotinfra](https://www.robotinfra.com)
|
||||
1. [Rocket.Chat](https://rocket.chat)
|
||||
1. [Rogo](https://rogodata.com)
|
||||
1. [Rubin Observatory](https://www.lsst.org)
|
||||
1. [Saildrone](https://www.saildrone.com/)
|
||||
1. [Salad Technologies](https://salad.com/)
|
||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [Sauce Labs](https://saucelabs.com/)
|
||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
||||
1. [SEEK](https://seek.com.au)
|
||||
1. [SEKAI](https://www.sekai.io/)
|
||||
1. [Semgrep](https://semgrep.com)
|
||||
1. [Shield](https://shield.com)
|
||||
1. [SI Analytics](https://si-analytics.ai)
|
||||
1. [Sidewalk Entertainment](https://sidewalkplay.com/)
|
||||
1. [Skit](https://skit.ai/)
|
||||
1. [Skribble](https://skribble.com)
|
||||
1. [Skyscanner](https://www.skyscanner.net/)
|
||||
1. [Smart Pension](https://www.smartpension.co.uk/)
|
||||
1. [Smilee.io](https://smilee.io)
|
||||
1. [Smilegate Stove](https://www.onstove.com/)
|
||||
1. [Smood.ch](https://www.smood.ch/)
|
||||
1. [Snapp](https://snapp.ir/)
|
||||
1. [Snyk](https://snyk.io/)
|
||||
@@ -325,8 +258,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Spendesk](https://spendesk.com/)
|
||||
1. [Splunk](https://splunk.com/)
|
||||
1. [Spores Labs](https://spores.app)
|
||||
1. [Statsig](https://statsig.com)
|
||||
1. [SternumIOT](https://sternumiot.com)
|
||||
1. [StreamNative](https://streamnative.io)
|
||||
1. [Stuart](https://stuart.com/)
|
||||
1. [Sumo Logic](https://sumologic.com/)
|
||||
@@ -335,16 +266,12 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Swisscom](https://www.swisscom.ch)
|
||||
1. [Swissquote](https://github.com/swissquote)
|
||||
1. [Syncier](https://syncier.com/)
|
||||
1. [Syself](https://syself.com)
|
||||
1. [TableCheck](https://tablecheck.com/)
|
||||
1. [Tailor Brands](https://www.tailorbrands.com)
|
||||
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
||||
1. [TBC Bank](https://tbcbank.ge/)
|
||||
1. [Techcombank](https://www.techcombank.com.vn/trang-chu)
|
||||
1. [Technacy](https://www.technacy.it/)
|
||||
1. [Telavita](https://www.telavita.com.br/)
|
||||
1. [Tesla](https://tesla.com/)
|
||||
1. [TextNow](https://www.textnow.com/)
|
||||
1. [The Scale Factory](https://www.scalefactory.com/)
|
||||
1. [ThousandEyes](https://www.thousandeyes.com/)
|
||||
1. [Ticketmaster](https://ticketmaster.com)
|
||||
@@ -367,7 +294,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Urbantz](https://urbantz.com/)
|
||||
1. [Vectra](https://www.vectra.ai)
|
||||
1. [Veepee](https://www.veepee.com)
|
||||
1. [Verkada](https://www.verkada.com)
|
||||
1. [Viaduct](https://www.viaduct.ai/)
|
||||
1. [VietMoney](https://vietmoney.vn/)
|
||||
1. [Vinted](https://vinted.com/)
|
||||
@@ -392,5 +318,4 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Yubo](https://www.yubo.live/)
|
||||
1. [ZDF](https://www.zdf.de/)
|
||||
1. [Zimpler](https://www.zimpler.com/)
|
||||
1. [ZipRecuiter](https://www.ziprecruiter.com/)
|
||||
1. [ZOZO](https://corp.zozo.com/)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -14,43 +12,43 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// clusterSecretEventHandler is used when watching Secrets to check if they are ArgoCD Cluster Secrets, and if so
|
||||
// requeue any related ApplicationSets.
|
||||
type clusterSecretEventHandler struct {
|
||||
// handler.EnqueueRequestForOwner
|
||||
//handler.EnqueueRequestForOwner
|
||||
Log log.FieldLogger
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
func (h *clusterSecretEventHandler) Create(e event.CreateEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.Object)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Update(ctx context.Context, e event.UpdateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.ObjectNew)
|
||||
func (h *clusterSecretEventHandler) Update(e event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.ObjectNew)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Delete(ctx context.Context, e event.DeleteEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
func (h *clusterSecretEventHandler) Delete(e event.DeleteEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.Object)
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) Generic(ctx context.Context, e event.GenericEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
h.queueRelatedAppGenerators(ctx, q, e.Object)
|
||||
func (h *clusterSecretEventHandler) Generic(e event.GenericEvent, q workqueue.RateLimitingInterface) {
|
||||
h.queueRelatedAppGenerators(q, e.Object)
|
||||
}
|
||||
|
||||
// addRateLimitingInterface defines the Add method of workqueue.RateLimitingInterface, allow us to easily mock
|
||||
// it for testing purposes.
|
||||
type addRateLimitingInterface[T comparable] interface {
|
||||
Add(item T)
|
||||
type addRateLimitingInterface interface {
|
||||
Add(item interface{})
|
||||
}
|
||||
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Context, q addRateLimitingInterface[reconcile.Request], object client.Object) {
|
||||
func (h *clusterSecretEventHandler) queueRelatedAppGenerators(q addRateLimitingInterface, object client.Object) {
|
||||
// Check for label, lookup all ApplicationSets that might match the cluster, queue them all
|
||||
if object.GetLabels()[common.LabelKeySecretType] != common.LabelValueSecretTypeCluster {
|
||||
if object.GetLabels()[generators.ArgoCDSecretTypeLabel] != generators.ArgoCDSecretTypeCluster {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
||||
}).Info("processing event for cluster secret")
|
||||
|
||||
appSetList := &argoprojiov1alpha1.ApplicationSetList{}
|
||||
err := h.Client.List(ctx, appSetList)
|
||||
err := h.Client.List(context.Background(), appSetList)
|
||||
if err != nil {
|
||||
h.Log.WithError(err).Error("unable to list ApplicationSets")
|
||||
return
|
||||
@@ -68,6 +66,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
||||
|
||||
h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets")
|
||||
for _, appSet := range appSetList.Items {
|
||||
|
||||
foundClusterGenerator := false
|
||||
for _, generator := range appSet.Spec.Generators {
|
||||
if generator.Clusters != nil {
|
||||
@@ -110,6 +109,7 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
||||
}
|
||||
}
|
||||
if foundClusterGenerator {
|
||||
|
||||
// TODO: only queue the AppGenerator if the labels match this cluster
|
||||
req := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: appSet.Namespace, Name: appSet.Name}}
|
||||
q.Add(req)
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
argocommon "github.com/argoproj/argo-cd/v2/common"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -18,16 +14,18 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestClusterEventHandler(t *testing.T) {
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := argov1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = argov1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -43,7 +41,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -71,7 +69,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -114,7 +112,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -158,7 +156,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -219,7 +217,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -255,7 +253,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -305,7 +303,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -356,7 +354,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -390,7 +388,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -426,7 +424,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -476,7 +474,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -527,7 +525,7 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
Namespace: "argocd",
|
||||
Name: "my-secret",
|
||||
Labels: map[string]string{
|
||||
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||
generators.ArgoCDSecretTypeLabel: generators.ArgoCDSecretTypeCluster,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -536,7 +534,9 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
appSetList := argov1alpha1.ApplicationSetList{
|
||||
Items: test.items,
|
||||
}
|
||||
@@ -550,20 +550,28 @@ func TestClusterEventHandler(t *testing.T) {
|
||||
|
||||
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
||||
|
||||
handler.queueRelatedAppGenerators(context.Background(), &mockAddRateLimitingInterface, &test.secret)
|
||||
handler.queueRelatedAppGenerators(&mockAddRateLimitingInterface, &test.secret)
|
||||
|
||||
assert.False(t, mockAddRateLimitingInterface.errorOccurred)
|
||||
assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add checks the type, and adds it to the internal list of received additions
|
||||
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
|
||||
obj.addedItems = append(obj.addedItems, item)
|
||||
func (obj *mockAddRateLimitingInterface) Add(item interface{}) {
|
||||
if req, ok := item.(ctrl.Request); ok {
|
||||
obj.addedItems = append(obj.addedItems, req)
|
||||
} else {
|
||||
obj.errorOccurred = true
|
||||
}
|
||||
}
|
||||
|
||||
type mockAddRateLimitingInterface struct {
|
||||
addedItems []reconcile.Request
|
||||
errorOccurred bool
|
||||
addedItems []ctrl.Request
|
||||
}
|
||||
|
||||
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
|
||||
@@ -573,7 +581,7 @@ func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T)
|
||||
|
||||
hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, hasClusterGenerator)
|
||||
}
|
||||
|
||||
@@ -600,7 +608,7 @@ func TestNestedGeneratorHasClusterGenerator_NestedMergeGenerator(t *testing.T) {
|
||||
|
||||
hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, hasClusterGenerator)
|
||||
}
|
||||
|
||||
@@ -627,6 +635,6 @@ func TestNestedGeneratorHasClusterGenerator_NestedMergeGeneratorWithInvalidJSON(
|
||||
|
||||
hasClusterGenerator, err := nestedGeneratorHasClusterGenerator(nested)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.NotNil(t, err)
|
||||
assert.False(t, hasClusterGenerator)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -17,7 +16,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
@@ -27,7 +25,7 @@ func TestRequeueAfter(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
scheme := runtime.NewScheme()
|
||||
err := argov1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
gvrToListKind := map[schema.GroupVersionResource]string{{
|
||||
Group: "mallard.io",
|
||||
Version: "v1",
|
||||
@@ -57,14 +55,14 @@ func TestRequeueAfter(t *testing.T) {
|
||||
},
|
||||
}
|
||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
|
||||
scmConfig := generators.NewSCMConfig("", []string{""}, true, nil, true)
|
||||
|
||||
terminalGenerators := map[string]generators.Generator{
|
||||
"List": generators.NewListGenerator(),
|
||||
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
|
||||
"Git": generators.NewGitGenerator(mockServer, "namespace"),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
|
||||
"Git": generators.NewGitGenerator(mockServer),
|
||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}),
|
||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, scmConfig),
|
||||
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}),
|
||||
}
|
||||
|
||||
nestedGenerators := map[string]generators.Generator{
|
||||
@@ -90,18 +88,15 @@ func TestRequeueAfter(t *testing.T) {
|
||||
}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).Build()
|
||||
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
|
||||
r := ApplicationSetReconciler{
|
||||
Client: client,
|
||||
Scheme: scheme,
|
||||
Recorder: record.NewFakeRecorder(0),
|
||||
Generators: topLevelGenerators,
|
||||
Metrics: metrics,
|
||||
}
|
||||
|
||||
type args struct {
|
||||
appset *argov1alpha1.ApplicationSet
|
||||
requeueAfterOverride string
|
||||
appset *argov1alpha1.ApplicationSet
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -109,13 +104,11 @@ func TestRequeueAfter(t *testing.T) {
|
||||
want time.Duration
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{name: "Cluster", args: args{
|
||||
appset: &argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{{Clusters: &argov1alpha1.ClusterGenerator{}}},
|
||||
},
|
||||
}, requeueAfterOverride: "",
|
||||
}, want: generators.NoRequeueAfter, wantErr: assert.NoError},
|
||||
{name: "Cluster", args: args{appset: &argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{{Clusters: &argov1alpha1.ClusterGenerator{}}},
|
||||
},
|
||||
}}, want: generators.NoRequeueAfter, wantErr: assert.NoError},
|
||||
{name: "ClusterMergeNested", args: args{&argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
@@ -130,7 +123,7 @@ func TestRequeueAfter(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, ""}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||
{name: "ClusterMatrixNested", args: args{&argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
@@ -145,65 +138,15 @@ func TestRequeueAfter(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, ""}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||
{name: "ListGenerator", args: args{appset: &argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{{List: &argov1alpha1.ListGenerator{}}},
|
||||
},
|
||||
}}, want: generators.NoRequeueAfter, wantErr: assert.NoError},
|
||||
{name: "DuckGenerator", args: args{appset: &argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{{ClusterDecisionResource: &argov1alpha1.DuckTypeGenerator{}}},
|
||||
},
|
||||
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
||||
{name: "OverrideRequeueDuck", args: args{
|
||||
appset: &argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{{ClusterDecisionResource: &argov1alpha1.DuckTypeGenerator{}}},
|
||||
},
|
||||
}, requeueAfterOverride: "1h",
|
||||
}, want: 1 * time.Hour, wantErr: assert.NoError},
|
||||
{name: "OverrideRequeueGit", args: args{&argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{Git: &argov1alpha1.GitGenerator{}},
|
||||
},
|
||||
},
|
||||
}, "1h"}, want: 1 * time.Hour, wantErr: assert.NoError},
|
||||
{name: "OverrideRequeueMatrix", args: args{&argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{Clusters: &argov1alpha1.ClusterGenerator{}},
|
||||
{Merge: &argov1alpha1.MergeGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||
Git: &argov1alpha1.GitGenerator{},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, "5m"}, want: 5 * time.Minute, wantErr: assert.NoError},
|
||||
{name: "OverrideRequeueMerge", args: args{&argov1alpha1.ApplicationSet{
|
||||
Spec: argov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||
{Clusters: &argov1alpha1.ClusterGenerator{}},
|
||||
{Merge: &argov1alpha1.MergeGenerator{
|
||||
Generators: []argov1alpha1.ApplicationSetNestedGenerator{
|
||||
{
|
||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||
Git: &argov1alpha1.GitGenerator{},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}, "12s"}, want: 12 * time.Second, wantErr: assert.NoError},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Setenv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", tt.args.requeueAfterOverride)
|
||||
assert.Equalf(t, tt.want, r.getMinRequeueAfter(tt.args.appset), "getMinRequeueAfter(%v)", tt.args.appset)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Application, error) {
|
||||
appString, err := json.Marshal(app)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while marhsalling Application %w", err)
|
||||
}
|
||||
|
||||
convertedTemplatePatch, err := utils.ConvertYAMLToJSON(templatePatch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while converting template to json %q: %w", convertedTemplatePatch, err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(convertedTemplatePatch), &appv1.Application{}); err != nil {
|
||||
return nil, fmt.Errorf("invalid templatePatch %q: %w", convertedTemplatePatch, err)
|
||||
}
|
||||
|
||||
data, err := strategicpatch.StrategicMergePatch(appString, []byte(convertedTemplatePatch), appv1.Application{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while applying templatePatch template to json %q: %w", convertedTemplatePatch, err)
|
||||
}
|
||||
|
||||
finalApp := appv1.Application{}
|
||||
err = json.Unmarshal(data, &finalApp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while unmarhsalling patched application: %w", err)
|
||||
}
|
||||
|
||||
// Prevent changes to the `project` field. This helps prevent malicious template patches
|
||||
finalApp.Spec.Project = app.Spec.Project
|
||||
|
||||
return &finalApp, nil
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_ApplyTemplatePatch(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
appTemplate *appv1.Application
|
||||
templatePatch string
|
||||
expectedApp *appv1.Application
|
||||
}{
|
||||
{
|
||||
name: "patch with JSON",
|
||||
appTemplate: &appv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: "namespace",
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "guestbook",
|
||||
},
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
},
|
||||
},
|
||||
templatePatch: `{
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"annotation-some-key": "annotation-some-value"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"source": {
|
||||
"helm": {
|
||||
"valueFiles": [
|
||||
"values.test.yaml",
|
||||
"values.big.yaml"
|
||||
]
|
||||
}
|
||||
},
|
||||
"syncPolicy": {
|
||||
"automated": {
|
||||
"prune": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
expectedApp: &appv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: "namespace",
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Annotations: map[string]string{
|
||||
"annotation-some-key": "annotation-some-value",
|
||||
},
|
||||
},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "guestbook",
|
||||
Helm: &appv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{
|
||||
"values.test.yaml",
|
||||
"values.big.yaml",
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
SyncPolicy: &appv1.SyncPolicy{
|
||||
Automated: &appv1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "patch with YAML",
|
||||
appTemplate: &appv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: "namespace",
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "guestbook",
|
||||
},
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
},
|
||||
},
|
||||
templatePatch: `
|
||||
metadata:
|
||||
annotations:
|
||||
annotation-some-key: annotation-some-value
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
valueFiles:
|
||||
- values.test.yaml
|
||||
- values.big.yaml
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true`,
|
||||
expectedApp: &appv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: "namespace",
|
||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
||||
Annotations: map[string]string{
|
||||
"annotation-some-key": "annotation-some-value",
|
||||
},
|
||||
},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "guestbook",
|
||||
Helm: &appv1.ApplicationSourceHelm{
|
||||
ValueFiles: []string{
|
||||
"values.test.yaml",
|
||||
"values.big.yaml",
|
||||
},
|
||||
},
|
||||
},
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
SyncPolicy: &appv1.SyncPolicy{
|
||||
Automated: &appv1.SyncPolicyAutomated{
|
||||
Prune: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project field isn't overwritten",
|
||||
appTemplate: &appv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "guestbook",
|
||||
},
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
},
|
||||
},
|
||||
templatePatch: `
|
||||
spec:
|
||||
project: my-project`,
|
||||
expectedApp: &appv1.Application{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Application",
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-cluster-guestbook",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Source: &appv1.ApplicationSource{
|
||||
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
|
||||
TargetRevision: "HEAD",
|
||||
Path: "guestbook",
|
||||
},
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "guestbook",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tcc := tc
|
||||
t.Run(tcc.name, func(t *testing.T) {
|
||||
result, err := applyTemplatePatch(tcc.appTemplate, tcc.templatePatch)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, *tcc.expectedApp, *result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
app := &appv1.Application{}
|
||||
|
||||
result, err := applyTemplatePatch(app, "hello world")
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
|
||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func GenerateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet, g map[string]generators.Generator, renderer utils.Renderer, client client.Client) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
|
||||
var res []argov1alpha1.Application
|
||||
|
||||
var firstError error
|
||||
var applicationSetReason argov1alpha1.ApplicationSetReasonType
|
||||
|
||||
for _, requestedGenerator := range applicationSetInfo.Spec.Generators {
|
||||
t, err := generators.Transform(requestedGenerator, g, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{}, client)
|
||||
if err != nil {
|
||||
logCtx.WithError(err).WithField("generator", requestedGenerator).
|
||||
Error("error generating application from params")
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
applicationSetReason = argov1alpha1.ApplicationSetReasonApplicationParamsGenerationError
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for _, a := range t {
|
||||
tmplApplication := GetTempApplication(a.Template)
|
||||
|
||||
for _, p := range a.Params {
|
||||
app, err := renderer.RenderTemplateParams(tmplApplication, applicationSetInfo.Spec.SyncPolicy, p, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
logCtx.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
|
||||
Error("error generating application from params")
|
||||
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if applicationSetInfo.Spec.TemplatePatch != nil {
|
||||
patchedApplication, err := renderTemplatePatch(renderer, app, applicationSetInfo, p)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("params", a.Params).WithField("generator", requestedGenerator).
|
||||
Error("error generating application from params")
|
||||
|
||||
if firstError == nil {
|
||||
firstError = err
|
||||
applicationSetReason = argov1alpha1.ApplicationSetReasonRenderTemplateParamsError
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
app = patchedApplication
|
||||
}
|
||||
|
||||
// The app's namespace must be the same as the AppSet's namespace to preserve the appsets-in-any-namespace
|
||||
// security boundary.
|
||||
app.Namespace = applicationSetInfo.Namespace
|
||||
res = append(res, *app)
|
||||
}
|
||||
}
|
||||
if log.IsLevelEnabled(log.DebugLevel) {
|
||||
logCtx.WithField("generator", requestedGenerator).Debugf("apps from generator: %+v", res)
|
||||
} else {
|
||||
logCtx.Infof("generated %d applications", len(res))
|
||||
}
|
||||
}
|
||||
|
||||
return res, applicationSetReason, firstError
|
||||
}
|
||||
|
||||
func renderTemplatePatch(r utils.Renderer, app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]interface{}) (*argov1alpha1.Application, error) {
|
||||
replacedTemplate, err := r.Replace(*applicationSetInfo.Spec.TemplatePatch, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error replacing values in templatePatch: %w", err)
|
||||
}
|
||||
|
||||
return applyTemplatePatch(app, replacedTemplate)
|
||||
}
|
||||
|
||||
func GetTempApplication(applicationSetTemplate argov1alpha1.ApplicationSetTemplate) *argov1alpha1.Application {
|
||||
var tmplApplication argov1alpha1.Application
|
||||
tmplApplication.Annotations = applicationSetTemplate.Annotations
|
||||
tmplApplication.Labels = applicationSetTemplate.Labels
|
||||
tmplApplication.Namespace = applicationSetTemplate.Namespace
|
||||
tmplApplication.Name = applicationSetTemplate.Name
|
||||
tmplApplication.Spec = applicationSetTemplate.Spec
|
||||
tmplApplication.Finalizers = applicationSetTemplate.Finalizers
|
||||
|
||||
return &tmplApplication
|
||||
}
|
||||
@@ -1,350 +0,0 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
||||
genmock "github.com/argoproj/argo-cd/v2/applicationset/generators/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
rendmock "github.com/argoproj/argo-cd/v2/applicationset/utils/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestGenerateApplications(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
params []map[string]interface{}
|
||||
template v1alpha1.ApplicationSetTemplate
|
||||
generateParamsError error
|
||||
rendererError error
|
||||
expectErr bool
|
||||
expectedReason v1alpha1.ApplicationSetReasonType
|
||||
}{
|
||||
{
|
||||
name: "Generate two applications",
|
||||
params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}},
|
||||
template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
Labels: map[string]string{"label_name": "label_value"},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{},
|
||||
},
|
||||
expectedReason: "",
|
||||
},
|
||||
{
|
||||
name: "Handles error from the generator",
|
||||
generateParamsError: fmt.Errorf("error"),
|
||||
expectErr: true,
|
||||
expectedReason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError,
|
||||
},
|
||||
{
|
||||
name: "Handles error from the render",
|
||||
params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}},
|
||||
template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
Labels: map[string]string{"label_name": "label_value"},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{},
|
||||
},
|
||||
rendererError: fmt.Errorf("error"),
|
||||
expectErr: true,
|
||||
expectedReason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
|
||||
},
|
||||
} {
|
||||
cc := c
|
||||
app := v1alpha1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: "argoproj.io/v1alpha1",
|
||||
},
|
||||
}
|
||||
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
generatorMock := genmock.Generator{}
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
List: &v1alpha1.ListGenerator{},
|
||||
}
|
||||
|
||||
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||
Return(cc.params, cc.generateParamsError)
|
||||
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
rendererMock := rendmock.Renderer{}
|
||||
|
||||
var expectedApps []v1alpha1.Application
|
||||
|
||||
if cc.generateParamsError == nil {
|
||||
for _, p := range cc.params {
|
||||
if cc.rendererError != nil {
|
||||
rendererMock.On("RenderTemplateParams", GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)).
|
||||
Return(nil, cc.rendererError)
|
||||
} else {
|
||||
rendererMock.On("RenderTemplateParams", GetTempApplication(cc.template), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), p, false, []string(nil)).
|
||||
Return(&app, nil)
|
||||
expectedApps = append(expectedApps, app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generators := map[string]generators.Generator{
|
||||
"List": &generatorMock,
|
||||
}
|
||||
renderer := &rendererMock
|
||||
|
||||
got, reason, err := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{generator},
|
||||
Template: cc.template,
|
||||
},
|
||||
},
|
||||
generators,
|
||||
renderer,
|
||||
nil,
|
||||
)
|
||||
|
||||
if cc.expectErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, expectedApps, got)
|
||||
assert.Equal(t, cc.expectedReason, reason)
|
||||
generatorMock.AssertNumberOfCalls(t, "GenerateParams", 1)
|
||||
|
||||
if cc.generateParamsError == nil {
|
||||
rendererMock.AssertNumberOfCalls(t, "RenderTemplateParams", len(cc.params))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeTemplateApplications(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
params []map[string]interface{}
|
||||
template v1alpha1.ApplicationSetTemplate
|
||||
overrideTemplate v1alpha1.ApplicationSetTemplate
|
||||
expectedMerged v1alpha1.ApplicationSetTemplate
|
||||
expectedApps []v1alpha1.Application
|
||||
}{
|
||||
{
|
||||
name: "Generate app",
|
||||
params: []map[string]interface{}{{"name": "app1"}},
|
||||
template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
Labels: map[string]string{"label_name": "label_value"},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{},
|
||||
},
|
||||
overrideTemplate: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "test",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{},
|
||||
},
|
||||
expectedMerged: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "test",
|
||||
Namespace: "namespace",
|
||||
Labels: map[string]string{"label_name": "label_value", "foo": "bar"},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{},
|
||||
},
|
||||
expectedApps: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "test",
|
||||
Labels: map[string]string{"foo": "bar"},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
cc := c
|
||||
|
||||
t.Run(cc.name, func(t *testing.T) {
|
||||
generatorMock := genmock.Generator{}
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
List: &v1alpha1.ListGenerator{},
|
||||
}
|
||||
|
||||
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||
Return(cc.params, nil)
|
||||
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
Return(&cc.overrideTemplate)
|
||||
|
||||
rendererMock := rendmock.Renderer{}
|
||||
|
||||
rendererMock.On("RenderTemplateParams", GetTempApplication(cc.expectedMerged), mock.AnythingOfType("*v1alpha1.ApplicationSetSyncPolicy"), cc.params[0], false, []string(nil)).
|
||||
Return(&cc.expectedApps[0], nil)
|
||||
|
||||
generators := map[string]generators.Generator{
|
||||
"List": &generatorMock,
|
||||
}
|
||||
renderer := &rendererMock
|
||||
|
||||
got, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{generator},
|
||||
Template: cc.template,
|
||||
},
|
||||
},
|
||||
generators,
|
||||
renderer,
|
||||
nil,
|
||||
)
|
||||
|
||||
assert.Equal(t, cc.expectedApps, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test app generation from a go template application set using a pull request generator
|
||||
func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||
for _, cases := range []struct {
|
||||
name string
|
||||
params []map[string]interface{}
|
||||
template v1alpha1.ApplicationSetTemplate
|
||||
expectedApp []v1alpha1.Application
|
||||
}{
|
||||
{
|
||||
name: "Generate an application from a go template application set manifest using a pull request generator",
|
||||
params: []map[string]interface{}{
|
||||
{
|
||||
"number": "1",
|
||||
"title": "title1",
|
||||
"branch": "branch1",
|
||||
"branch_slug": "branchSlug1",
|
||||
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
"head_short_sha": "089d92cb",
|
||||
"branch_slugify_default": "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
"branch_slugify_smarttruncate_disabled": "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature",
|
||||
"branch_slugify_smarttruncate_enabled": "feat/testwithsmarttruncateenabledramdomlonglistofcharacters",
|
||||
"labels": []string{"label1"},
|
||||
},
|
||||
},
|
||||
template: v1alpha1.ApplicationSetTemplate{
|
||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||
Name: "AppSet-{{.branch}}-{{.number}}",
|
||||
Labels: map[string]string{
|
||||
"app1": "{{index .labels 0}}",
|
||||
"branch-test1": "AppSet-{{.branch_slugify_default | slugify }}",
|
||||
"branch-test2": "AppSet-{{.branch_slugify_smarttruncate_disabled | slugify 49 false }}",
|
||||
"branch-test3": "AppSet-{{.branch_slugify_smarttruncate_enabled | slugify 50 true }}",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://testurl/testRepo",
|
||||
TargetRevision: "{{.head_short_sha}}",
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "AppSet-{{.branch_slug}}-{{.head_sha}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedApp: []v1alpha1.Application{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "AppSet-branch1-1",
|
||||
Labels: map[string]string{
|
||||
"app1": "label1",
|
||||
"branch-test1": "AppSet-feat-a-really-long-pull-request-name-to-test-argo",
|
||||
"branch-test2": "AppSet-feat-areallylongpullrequestnametotestargoslugific",
|
||||
"branch-test3": "AppSet-feat",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Source: &v1alpha1.ApplicationSource{
|
||||
RepoURL: "https://testurl/testRepo",
|
||||
TargetRevision: "089d92cb",
|
||||
},
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: "https://kubernetes.default.svc",
|
||||
Namespace: "AppSet-branchSlug1-089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(cases.name, func(t *testing.T) {
|
||||
generatorMock := genmock.Generator{}
|
||||
generator := v1alpha1.ApplicationSetGenerator{
|
||||
PullRequest: &v1alpha1.PullRequestGenerator{},
|
||||
}
|
||||
|
||||
generatorMock.On("GenerateParams", &generator, mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).
|
||||
Return(cases.params, nil)
|
||||
|
||||
generatorMock.On("GetTemplate", &generator).
|
||||
Return(&cases.template, nil)
|
||||
|
||||
generators := map[string]generators.Generator{
|
||||
"PullRequest": &generatorMock,
|
||||
}
|
||||
renderer := &utils.Render{}
|
||||
|
||||
gotApp, _, _ := GenerateApplications(log.NewEntry(log.StandardLogger()), v1alpha1.ApplicationSet{
|
||||
Spec: v1alpha1.ApplicationSetSpec{
|
||||
GoTemplate: true,
|
||||
Generators: []v1alpha1.ApplicationSetGenerator{{
|
||||
PullRequest: &v1alpha1.PullRequestGenerator{},
|
||||
}},
|
||||
Template: cases.template,
|
||||
},
|
||||
},
|
||||
generators,
|
||||
renderer,
|
||||
nil,
|
||||
)
|
||||
assert.EqualValues(t, cases.expectedApp[0].ObjectMeta.Name, gotApp[0].ObjectMeta.Name)
|
||||
assert.EqualValues(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision)
|
||||
assert.EqualValues(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace)
|
||||
assert.True(t, maps.Equal(cases.expectedApp[0].ObjectMeta.Labels, gotApp[0].ObjectMeta.Labels))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,14 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argoappsetv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
ArgoCDSecretTypeLabel = "argocd.argoproj.io/secret-type"
|
||||
ArgoCDSecretTypeCluster = "cluster"
|
||||
)
|
||||
|
||||
var _ Generator = (*ClusterGenerator)(nil)
|
||||
|
||||
// ClusterGenerator generates Applications for some or all clusters registered with ArgoCD.
|
||||
@@ -34,6 +38,7 @@ type ClusterGenerator struct {
|
||||
var render = &utils.Render{}
|
||||
|
||||
func NewClusterGenerator(c client.Client, ctx context.Context, clientset kubernetes.Interface, namespace string) Generator {
|
||||
|
||||
settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
|
||||
|
||||
g := &ClusterGenerator{
|
||||
@@ -48,7 +53,7 @@ func NewClusterGenerator(c client.Client, ctx context.Context, clientset kuberne
|
||||
|
||||
// GetRequeueAfter never requeue the cluster generator because the `clusterSecretEventHandler` will requeue the appsets
|
||||
// when the cluster secrets change
|
||||
func (g *ClusterGenerator) GetRequeueAfter(_ *argoappsetv1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
func (g *ClusterGenerator) GetRequeueAfter(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
return NoRequeueAfter
|
||||
}
|
||||
|
||||
@@ -56,8 +61,8 @@ func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.Appli
|
||||
return &appSetGenerator.Clusters.Template
|
||||
}
|
||||
|
||||
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
logCtx := log.WithField("applicationset", appSet.GetName()).WithField("namespace", appSet.GetNamespace())
|
||||
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -80,44 +85,37 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
clusterSecrets, err := g.getSecretsByClusterName(logCtx, appSetGenerator)
|
||||
clusterSecrets, err := g.getSecretsByClusterName(appSetGenerator)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := []map[string]interface{}{}
|
||||
|
||||
secretsFound := []corev1.Secret{}
|
||||
|
||||
isFlatMode := appSetGenerator.Clusters.FlatList
|
||||
logCtx.Debugf("Using flat mode = %t for cluster generator", isFlatMode)
|
||||
clustersParams := make([]map[string]interface{}, 0)
|
||||
|
||||
for _, cluster := range clustersFromArgoCD.Items {
|
||||
|
||||
// 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]interface{}{}
|
||||
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)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isFlatMode {
|
||||
clustersParams = append(clustersParams, params)
|
||||
} else {
|
||||
res = append(res, params)
|
||||
}
|
||||
res = append(res, params)
|
||||
|
||||
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
||||
log.WithField("cluster", "local cluster").Info("matched local cluster")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,13 +127,6 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
||||
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
|
||||
params["server"] = string(cluster.Data["server"])
|
||||
|
||||
project, ok := cluster.Data["project"]
|
||||
if ok {
|
||||
params["project"] = string(project)
|
||||
} else {
|
||||
params["project"] = ""
|
||||
}
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
meta := map[string]interface{}{}
|
||||
|
||||
@@ -159,39 +150,31 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
||||
|
||||
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isFlatMode {
|
||||
clustersParams = append(clustersParams, params)
|
||||
} else {
|
||||
res = append(res, params)
|
||||
}
|
||||
res = append(res, params)
|
||||
|
||||
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
|
||||
log.WithField("cluster", cluster.Name).Info("matched cluster secret")
|
||||
}
|
||||
|
||||
if isFlatMode {
|
||||
res = append(res, map[string]interface{}{
|
||||
"clusters": clustersParams,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
||||
func (g *ClusterGenerator) getSecretsByClusterName(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
||||
// List all Clusters:
|
||||
clusterSecretList := &corev1.SecretList{}
|
||||
|
||||
selector := metav1.AddLabelToSelector(&appSetGenerator.Clusters.Selector, common.LabelKeySecretType, common.LabelValueSecretTypeCluster)
|
||||
selector := metav1.AddLabelToSelector(&appSetGenerator.Clusters.Selector, ArgoCDSecretTypeLabel, ArgoCDSecretTypeCluster)
|
||||
secretSelector, err := metav1.LabelSelectorAsSelector(selector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting label selector: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := g.Client.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
||||
log.Debug("clusters matching labels", "count", len(clusterSecretList.Items))
|
||||
|
||||
res := map[string]corev1.Secret{}
|
||||
|
||||
@@ -202,4 +185,5 @@ func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerat
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type possiblyErroringFakeCtrlRuntimeClient struct {
|
||||
@@ -76,20 +75,18 @@ func TestGenerateParams(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config": []byte("{}"),
|
||||
"name": []byte("production_01/west"),
|
||||
"server": []byte("https://production-01.example.com"),
|
||||
"project": []byte("prod-project"),
|
||||
"config": []byte("{}"),
|
||||
"name": []byte("production_01/west"),
|
||||
"server": []byte("https://production-01.example.com"),
|
||||
},
|
||||
Type: corev1.SecretType("Opaque"),
|
||||
},
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
selector metav1.LabelSelector
|
||||
isFlatMode bool
|
||||
values map[string]string
|
||||
expected []map[string]interface{}
|
||||
name string
|
||||
selector metav1.LabelSelector
|
||||
values map[string]string
|
||||
expected []map[string]interface{}
|
||||
// clientError is true if a k8s client error should be simulated
|
||||
clientError bool
|
||||
expectedError error
|
||||
@@ -107,16 +104,13 @@ func TestGenerateParams(t *testing.T) {
|
||||
"aaa": "{{ server }}",
|
||||
"no-op": "{{ this-does-not-exist }}",
|
||||
}, expected: []map[string]interface{}{
|
||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc", "project": ""},
|
||||
{
|
||||
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||
},
|
||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
|
||||
|
||||
{
|
||||
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "staging", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "staging", "values.aaa": "https://staging-01.example.com", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||
},
|
||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "staging", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "staging", "values.aaa": "https://staging-01.example.com", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
|
||||
|
||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc"},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
@@ -130,15 +124,11 @@ func TestGenerateParams(t *testing.T) {
|
||||
},
|
||||
values: nil,
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||
},
|
||||
{"name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
|
||||
|
||||
{
|
||||
"name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||
},
|
||||
{"name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
@@ -154,10 +144,8 @@ func TestGenerateParams(t *testing.T) {
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||
},
|
||||
{"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
@@ -180,14 +168,10 @@ func TestGenerateParams(t *testing.T) {
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||
},
|
||||
{
|
||||
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||
},
|
||||
{"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
|
||||
{"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production"},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
@@ -213,10 +197,8 @@ func TestGenerateParams(t *testing.T) {
|
||||
"name": "baz",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||
},
|
||||
{"values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging"},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
@@ -227,75 +209,7 @@ func TestGenerateParams(t *testing.T) {
|
||||
values: nil,
|
||||
expected: nil,
|
||||
clientError: true,
|
||||
expectedError: fmt.Errorf("error getting cluster secrets: could not list Secrets"),
|
||||
},
|
||||
{
|
||||
name: "flat mode without selectors",
|
||||
selector: metav1.LabelSelector{},
|
||||
values: map[string]string{
|
||||
"lol1": "lol",
|
||||
"lol2": "{{values.lol1}}{{values.lol1}}",
|
||||
"lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}",
|
||||
"foo": "bar",
|
||||
"bar": "{{ metadata.annotations.foo.argoproj.io }}",
|
||||
"bat": "{{ metadata.labels.environment }}",
|
||||
"aaa": "{{ server }}",
|
||||
"no-op": "{{ this-does-not-exist }}",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"clusters": []map[string]interface{}{
|
||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc", "project": ""},
|
||||
{
|
||||
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||
},
|
||||
|
||||
{
|
||||
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "staging", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "staging", "values.aaa": "https://staging-01.example.com", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isFlatMode: true,
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "production or staging with flat mode",
|
||||
selector: metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "environment",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"production",
|
||||
"staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isFlatMode: true,
|
||||
values: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"clusters": []map[string]interface{}{
|
||||
{
|
||||
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||
},
|
||||
{
|
||||
"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
expectedError: fmt.Errorf("could not list Secrets"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -306,7 +220,9 @@ func TestGenerateParams(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
@@ -315,7 +231,7 @@ func TestGenerateParams(t *testing.T) {
|
||||
testCase.clientError,
|
||||
}
|
||||
|
||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -328,16 +244,16 @@ func TestGenerateParams(t *testing.T) {
|
||||
Clusters: &argoprojiov1alpha1.ClusterGenerator{
|
||||
Selector: testCase.selector,
|
||||
Values: testCase.values,
|
||||
FlatList: testCase.isFlatMode,
|
||||
},
|
||||
}, &applicationSetInfo, nil)
|
||||
}, &applicationSetInfo)
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
assert.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -394,11 +310,10 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
selector metav1.LabelSelector
|
||||
values map[string]string
|
||||
isFlatMode bool
|
||||
expected []map[string]interface{}
|
||||
name string
|
||||
selector metav1.LabelSelector
|
||||
values map[string]string
|
||||
expected []map[string]interface{}
|
||||
// clientError is true if a k8s client error should be simulated
|
||||
clientError bool
|
||||
expectedError error
|
||||
@@ -420,7 +335,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -446,7 +360,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -472,7 +385,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"nameNormalized": "in-cluster",
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
"project": "",
|
||||
"values": map[string]string{
|
||||
"lol1": "lol",
|
||||
"lol2": "<no value><no value>",
|
||||
@@ -501,7 +413,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -517,7 +428,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -548,7 +458,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -589,7 +498,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -608,7 +516,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -652,7 +559,6 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
@@ -677,163 +583,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
values: nil,
|
||||
expected: nil,
|
||||
clientError: true,
|
||||
expectedError: fmt.Errorf("error getting cluster secrets: could not list Secrets"),
|
||||
},
|
||||
{
|
||||
name: "Clusters with flat list mode and no selector",
|
||||
selector: metav1.LabelSelector{},
|
||||
isFlatMode: true,
|
||||
values: map[string]string{
|
||||
"lol1": "lol",
|
||||
"lol2": "{{ .values.lol1 }}{{ .values.lol1 }}",
|
||||
"lol3": "{{ .values.lol2 }}{{ .values.lol2 }}{{ .values.lol2 }}",
|
||||
"foo": "bar",
|
||||
"bar": "{{ if not (empty .metadata) }}{{index .metadata.annotations \"foo.argoproj.io\" }}{{ end }}",
|
||||
"bat": "{{ if not (empty .metadata) }}{{.metadata.labels.environment}}{{ end }}",
|
||||
"aaa": "{{ .server }}",
|
||||
"no-op": "{{ .thisDoesNotExist }}",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"clusters": []map[string]interface{}{
|
||||
{
|
||||
"nameNormalized": "in-cluster",
|
||||
"name": "in-cluster",
|
||||
"server": "https://kubernetes.default.svc",
|
||||
"project": "",
|
||||
"values": map[string]string{
|
||||
"lol1": "lol",
|
||||
"lol2": "<no value><no value>",
|
||||
"lol3": "<no value><no value><no value>",
|
||||
"foo": "bar",
|
||||
"bar": "",
|
||||
"bat": "",
|
||||
"aaa": "https://kubernetes.default.svc",
|
||||
"no-op": "<no value>",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"lol1": "lol",
|
||||
"lol2": "<no value><no value>",
|
||||
"lol3": "<no value><no value><no value>",
|
||||
"foo": "bar",
|
||||
"bar": "production",
|
||||
"bat": "production",
|
||||
"aaa": "https://production-01.example.com",
|
||||
"no-op": "<no value>",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"lol1": "lol",
|
||||
"lol2": "<no value><no value>",
|
||||
"lol3": "<no value><no value><no value>",
|
||||
"foo": "bar",
|
||||
"bar": "staging",
|
||||
"bat": "staging",
|
||||
"aaa": "https://staging-01.example.com",
|
||||
"no-op": "<no value>",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "production or staging with flat mode",
|
||||
selector: metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "environment",
|
||||
Operator: "In",
|
||||
Values: []string{
|
||||
"production",
|
||||
"staging",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isFlatMode: true,
|
||||
values: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"clusters": []map[string]interface{}{
|
||||
{
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "production",
|
||||
"org": "bar",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "production",
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "staging-01",
|
||||
"nameNormalized": "staging-01",
|
||||
"server": "https://staging-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"argocd.argoproj.io/secret-type": "cluster",
|
||||
"environment": "staging",
|
||||
"org": "foo",
|
||||
},
|
||||
"annotations": map[string]string{
|
||||
"foo.argoproj.io": "staging",
|
||||
},
|
||||
},
|
||||
"values": map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
clientError: false,
|
||||
expectedError: nil,
|
||||
expectedError: fmt.Errorf("could not list Secrets"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -844,7 +594,9 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
|
||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||
|
||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||
@@ -853,7 +605,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
testCase.clientError,
|
||||
}
|
||||
|
||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -868,16 +620,16 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
||||
Clusters: &argoprojiov1alpha1.ClusterGenerator{
|
||||
Selector: testCase.selector,
|
||||
Values: testCase.values,
|
||||
FlatList: testCase.isFlatMode,
|
||||
},
|
||||
}, &applicationSetInfo, nil)
|
||||
}, &applicationSetInfo)
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
assert.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
|
||||
@@ -33,6 +32,7 @@ type DuckTypeGenerator struct {
|
||||
}
|
||||
|
||||
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
|
||||
|
||||
settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
|
||||
|
||||
g := &DuckTypeGenerator{
|
||||
@@ -46,20 +46,22 @@ func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clie
|
||||
}
|
||||
|
||||
func (g *DuckTypeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
|
||||
// Return a requeue default of 3 minutes, if no override is specified.
|
||||
|
||||
if appSetGenerator.ClusterDecisionResource.RequeueAfterSeconds != nil {
|
||||
return time.Duration(*appSetGenerator.ClusterDecisionResource.RequeueAfterSeconds) * time.Second
|
||||
}
|
||||
|
||||
return getDefaultRequeueAfter()
|
||||
return DefaultRequeueAfterSeconds
|
||||
}
|
||||
|
||||
func (g *DuckTypeGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||
return &appSetGenerator.ClusterDecisionResource.Template
|
||||
}
|
||||
|
||||
func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -81,6 +83,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
|
||||
// Read the configMapRef
|
||||
cm, err := g.clientset.CoreV1().ConfigMaps(g.namespace).Get(g.ctx, appSetGenerator.ClusterDecisionResource.ConfigMapRef, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading configMapRef: %w", err)
|
||||
}
|
||||
@@ -101,6 +104,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
|
||||
if (resourceName == "" && labelSelector.MatchLabels == nil && labelSelector.MatchExpressions == nil) ||
|
||||
(resourceName != "" && (labelSelector.MatchExpressions != nil || labelSelector.MatchLabels != nil)) {
|
||||
|
||||
log.Warningf("You must choose either resourceName=%v, labelSelector.matchLabels=%v or labelSelect.matchExpressions=%v", resourceName, labelSelector.MatchLabels, labelSelector.MatchExpressions)
|
||||
return nil, fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator")
|
||||
}
|
||||
@@ -118,14 +122,15 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
log.WithField("listOptions.LabelSelector", listOptions.LabelSelector).Info("selection type")
|
||||
} else {
|
||||
listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", resourceName).String()
|
||||
// metav1.Convert_fields_Selector_To_string(fields.).Sprintf("metadata.name=%s", resourceName)
|
||||
//metav1.Convert_fields_Selector_To_string(fields.).Sprintf("metadata.name=%s", resourceName)
|
||||
log.WithField("listOptions.FieldSelector", listOptions.FieldSelector).Info("selection type")
|
||||
}
|
||||
|
||||
duckResources, err := g.dynClient.Resource(duckGVR).Namespace(g.namespace).List(g.ctx, listOptions)
|
||||
|
||||
if err != nil {
|
||||
log.WithField("GVK", duckGVR).Warning("resources were not found")
|
||||
return nil, fmt.Errorf("failed to get dynamic resources: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(duckResources.Items) == 0 {
|
||||
@@ -144,6 +149,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
if matchKey == "" {
|
||||
log.WithField("matchKey", matchKey).Warning("matchKey not found in " + cm.Name)
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
res := []map[string]interface{}{}
|
||||
@@ -161,6 +167,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
log.WithField("duckResourceStatus", duckResource.Object["status"]).Debug("found resource")
|
||||
|
||||
clusterDecisions = append(clusterDecisions, duckResource.Object["status"].(map[string]interface{})[statusListKey].([]interface{})...)
|
||||
|
||||
}
|
||||
log.Infof("Number of decisions found: %v", len(clusterDecisions))
|
||||
|
||||
@@ -169,6 +176,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
|
||||
if len(clusterDecisions) > 0 {
|
||||
for _, cluster := range clusterDecisions {
|
||||
|
||||
// generated instance of cluster params
|
||||
params := map[string]interface{}{}
|
||||
|
||||
@@ -186,6 +194,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
|
||||
for _, argoCluster := range argoClusters {
|
||||
if argoCluster.Name == strMatchValue {
|
||||
|
||||
log.WithField(matchKey, argoCluster.Name).Info("matched cluster in ArgoCD")
|
||||
params["name"] = argoCluster.Name
|
||||
params["server"] = argoCluster.Server
|
||||
@@ -193,6 +202,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
found = true
|
||||
break // Stop looking
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !found {
|
||||
@@ -218,7 +228,7 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
||||
res = append(res, params)
|
||||
}
|
||||
} else {
|
||||
log.Warningf("clusterDecisionResource status.%s missing", statusListKey)
|
||||
log.Warningf("clusterDecisionResource status." + statusListKey + " missing")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -19,11 +18,9 @@ import (
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
resourceApiVersion = "mallard.io/v1"
|
||||
resourceKind = "ducks"
|
||||
resourceName = "quak"
|
||||
)
|
||||
const resourceApiVersion = "mallard.io/v1"
|
||||
const resourceKind = "ducks"
|
||||
const resourceName = "quak"
|
||||
|
||||
func TestGenerateParamsForDuckType(t *testing.T) {
|
||||
clusters := []client.Object{
|
||||
@@ -282,7 +279,9 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
|
||||
appClientset := kubefake.NewSimpleClientset(append(runtimeClusters, configMap)...)
|
||||
|
||||
gvrToListKind := map[schema.GroupVersionResource]string{{
|
||||
@@ -293,7 +292,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
||||
|
||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||
|
||||
duckTypeGenerator := NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace")
|
||||
var duckTypeGenerator = NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -309,12 +308,12 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
||||
LabelSelector: testCase.labelSelector,
|
||||
Values: testCase.values,
|
||||
},
|
||||
}, &applicationSetInfo, nil)
|
||||
}, &applicationSetInfo)
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
assert.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
})
|
||||
@@ -578,7 +577,9 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
|
||||
appClientset := kubefake.NewSimpleClientset(append(runtimeClusters, configMap)...)
|
||||
|
||||
gvrToListKind := map[schema.GroupVersionResource]string{{
|
||||
@@ -589,7 +590,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
||||
|
||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||
|
||||
duckTypeGenerator := NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace")
|
||||
var duckTypeGenerator = NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -607,12 +608,12 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
||||
LabelSelector: testCase.labelSelector,
|
||||
Values: testCase.values,
|
||||
},
|
||||
}, &applicationSetInfo, nil)
|
||||
}, &applicationSetInfo)
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
assert.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/jeremywohl/flatten"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
|
||||
@@ -27,7 +26,7 @@ type TransformResult struct {
|
||||
}
|
||||
|
||||
// Transform a spec generator to list of paramSets and a template
|
||||
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}, client client.Client) ([]TransformResult, error) {
|
||||
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) {
|
||||
// This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied
|
||||
// verbatim from that package, with the difference that we do not have any restrictions on label values. This is done
|
||||
// so that, among other things, we can match on cluster urls.
|
||||
@@ -65,7 +64,7 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
|
||||
continue
|
||||
}
|
||||
}
|
||||
params, err = g.GenerateParams(interpolatedGenerator, appSet, client)
|
||||
params, err = g.GenerateParams(interpolatedGenerator, appSet)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("generator", g).
|
||||
Error("error generating params")
|
||||
|
||||
@@ -65,8 +65,8 @@ func TestMatchValues(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
listGenerator := NewListGenerator()
|
||||
data := map[string]Generator{
|
||||
var listGenerator = NewListGenerator()
|
||||
var data = map[string]Generator{
|
||||
"List": listGenerator,
|
||||
}
|
||||
|
||||
@@ -84,13 +84,12 @@ func TestMatchValues(t *testing.T) {
|
||||
List: &argov1alpha1.ListGenerator{
|
||||
Elements: testCase.elements,
|
||||
Template: emptyTemplate(),
|
||||
},
|
||||
},
|
||||
}},
|
||||
data,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil, nil)
|
||||
&applicationSetInfo, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
@@ -149,8 +148,8 @@ func TestMatchValuesGoTemplate(t *testing.T) {
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
listGenerator := NewListGenerator()
|
||||
data := map[string]Generator{
|
||||
var listGenerator = NewListGenerator()
|
||||
var data = map[string]Generator{
|
||||
"List": listGenerator,
|
||||
}
|
||||
|
||||
@@ -168,13 +167,12 @@ func TestMatchValuesGoTemplate(t *testing.T) {
|
||||
List: &argov1alpha1.ListGenerator{
|
||||
Elements: testCase.elements,
|
||||
Template: emptyTemplate(),
|
||||
},
|
||||
},
|
||||
}},
|
||||
data,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil, nil)
|
||||
&applicationSetInfo, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
@@ -199,7 +197,6 @@ func TestTransForm(t *testing.T) {
|
||||
"name": "production_01/west",
|
||||
"nameNormalized": "production-01-west",
|
||||
"server": "https://production-01.example.com",
|
||||
"project": "",
|
||||
}},
|
||||
},
|
||||
{
|
||||
@@ -215,7 +212,6 @@ func TestTransForm(t *testing.T) {
|
||||
"name": "some-really-long-server-url",
|
||||
"nameNormalized": "some-really-long-server-url",
|
||||
"server": "https://some-really-long-url-that-will-exceed-63-characters.com",
|
||||
"project": "",
|
||||
}},
|
||||
},
|
||||
}
|
||||
@@ -240,13 +236,12 @@ func TestTransForm(t *testing.T) {
|
||||
Selector: metav1.LabelSelector{},
|
||||
Template: argov1alpha1.ApplicationSetTemplate{},
|
||||
Values: nil,
|
||||
},
|
||||
},
|
||||
}},
|
||||
testGenerators,
|
||||
emptyTemplate(),
|
||||
&applicationSetInfo, nil, nil)
|
||||
&applicationSetInfo, nil)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, results[0].Params)
|
||||
})
|
||||
}
|
||||
@@ -348,11 +343,12 @@ func getMockClusterGenerator() Generator {
|
||||
func getMockGitGenerator() Generator {
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything).Return([]string{"app1", "app2", "app_3", "p1/app4"}, nil)
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
return gitGenerator
|
||||
}
|
||||
|
||||
func TestGetRelevantGenerators(t *testing.T) {
|
||||
|
||||
testGenerators := map[string]Generator{
|
||||
"Clusters": getMockClusterGenerator(),
|
||||
"Git": getMockGitGenerator(),
|
||||
@@ -365,8 +361,7 @@ func TestGetRelevantGenerators(t *testing.T) {
|
||||
requestedGenerator := &argov1alpha1.ApplicationSetGenerator{
|
||||
List: &argov1alpha1.ListGenerator{
|
||||
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
||||
},
|
||||
}
|
||||
}}
|
||||
|
||||
relevantGenerators := GetRelevantGenerators(requestedGenerator, testGenerators)
|
||||
assert.Len(t, relevantGenerators, 1)
|
||||
@@ -409,8 +404,7 @@ func TestInterpolateGenerator(t *testing.T) {
|
||||
"path-basename": "{{path.basename}}",
|
||||
"path-zero": "{{path[0]}}",
|
||||
"path-full": "{{path}}",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
gitGeneratorParams := map[string]interface{}{
|
||||
@@ -464,8 +458,7 @@ func TestInterpolateGenerator_go(t *testing.T) {
|
||||
"path-zero": "{{index .path.segments 0}}",
|
||||
"path-full": "{{.path.path}}",
|
||||
"kubernetes.io/environment": `{{default "foo" .my_label}}`,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
gitGeneratorParams := map[string]interface{}{
|
||||
@@ -557,7 +550,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := InterpolateGenerator(tt.args.requestedGenerator, tt.args.params, tt.args.useGoTemplate, tt.args.goTemplateOptions)
|
||||
if tt.expectedErrStr != "" {
|
||||
require.EqualError(t, err, tt.expectedErrStr)
|
||||
assert.EqualError(t, err, tt.expectedErrStr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -11,29 +11,23 @@ import (
|
||||
|
||||
"github.com/jeremywohl/flatten"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
)
|
||||
|
||||
var _ Generator = (*GitGenerator)(nil)
|
||||
|
||||
type GitGenerator struct {
|
||||
repos services.Repos
|
||||
namespace string
|
||||
repos services.Repos
|
||||
}
|
||||
|
||||
func NewGitGenerator(repos services.Repos, namespace string) Generator {
|
||||
func NewGitGenerator(repos services.Repos) Generator {
|
||||
g := &GitGenerator{
|
||||
repos: repos,
|
||||
namespace: namespace,
|
||||
repos: repos,
|
||||
}
|
||||
|
||||
return g
|
||||
}
|
||||
|
||||
@@ -42,16 +36,18 @@ func (g *GitGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicati
|
||||
}
|
||||
|
||||
func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
|
||||
// Return a requeue default of 3 minutes, if no default is specified.
|
||||
|
||||
if appSetGenerator.Git.RequeueAfterSeconds != nil {
|
||||
return time.Duration(*appSetGenerator.Git.RequeueAfterSeconds) * time.Second
|
||||
}
|
||||
|
||||
return getDefaultRequeueAfter()
|
||||
return DefaultRequeueAfterSeconds
|
||||
}
|
||||
|
||||
func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -62,31 +58,12 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
|
||||
noRevisionCache := appSet.RefreshRequired()
|
||||
|
||||
verifyCommit := false
|
||||
|
||||
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
|
||||
// but git generator cannot be called without verifying the commit signature.
|
||||
// In this case, we skip the signature verification.
|
||||
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||
project := appSet.Spec.Template.Spec.Project
|
||||
appProject := &argoprojiov1alpha1.AppProject{}
|
||||
namespace := g.namespace
|
||||
if namespace == "" {
|
||||
namespace = appSet.Namespace
|
||||
}
|
||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
|
||||
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
||||
}
|
||||
// we need to verify the signature on the Git revision if GPG is enabled
|
||||
verifyCommit = len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
|
||||
}
|
||||
|
||||
var err error
|
||||
var res []map[string]interface{}
|
||||
if len(appSetGenerator.Git.Directories) != 0 {
|
||||
res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
} else if len(appSetGenerator.Git.Files) != 0 {
|
||||
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||
} else {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -97,9 +74,10 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
|
||||
// Directories, not files
|
||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache, verifyCommit)
|
||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
||||
}
|
||||
@@ -122,11 +100,12 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
|
||||
// Get all files that match the requested path string, removing duplicates
|
||||
allFiles := make(map[string][]byte)
|
||||
for _, requestedPath := range appSetGenerator.Git.Files {
|
||||
files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache, verifyCommit)
|
||||
files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -146,10 +125,11 @@ func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1al
|
||||
// Generate params from each path, and return
|
||||
res := []map[string]interface{}{}
|
||||
for _, path := range allPaths {
|
||||
|
||||
// A JSON / YAML file path can contain multiple sets of parameters (ie it is an array)
|
||||
paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to process file '%s': %w", path, err)
|
||||
return nil, fmt.Errorf("unable to process file '%s': %v", path, err)
|
||||
}
|
||||
|
||||
res = append(res, paramsArray...)
|
||||
@@ -167,17 +147,15 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
singleObj := make(map[string]interface{})
|
||||
err = yaml.Unmarshal(fileContent, &singleObj)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse file: %w", err)
|
||||
return nil, fmt.Errorf("unable to parse file: %v", err)
|
||||
}
|
||||
objectsFound = append(objectsFound, singleObj)
|
||||
} else if len(objectsFound) == 0 {
|
||||
// If file is valid but empty, add a default empty item
|
||||
objectsFound = append(objectsFound, map[string]interface{}{})
|
||||
}
|
||||
|
||||
res := []map[string]interface{}{}
|
||||
|
||||
for _, objectFound := range objectsFound {
|
||||
|
||||
params := map[string]interface{}{}
|
||||
|
||||
if useGoTemplate {
|
||||
@@ -233,13 +211,13 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
|
||||
func (g *GitGenerator) filterApps(Directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
|
||||
res := []string{}
|
||||
for _, appPath := range allPaths {
|
||||
appInclude := false
|
||||
appExclude := false
|
||||
// Iterating over each appPath and check whether directories object has requestedPath that matches the appPath
|
||||
for _, requestedPath := range directories {
|
||||
for _, requestedPath := range Directories {
|
||||
match, err := path.Match(requestedPath.Path, appPath)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("requestedPath", requestedPath).
|
||||
@@ -264,6 +242,7 @@ func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryG
|
||||
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
||||
res := make([]map[string]interface{}, len(requestedApps))
|
||||
for i, a := range requestedApps {
|
||||
|
||||
params := make(map[string]interface{}, 5)
|
||||
|
||||
if useGoTemplate {
|
||||
|
||||
@@ -6,179 +6,121 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_generateParamsFromGitFile(t *testing.T) {
|
||||
defaultContent := []byte(`
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`)
|
||||
type args struct {
|
||||
filePath string
|
||||
fileContent []byte
|
||||
values map[string]string
|
||||
useGoTemplate bool
|
||||
goTemplateOptions []string
|
||||
pathParamPrefix string
|
||||
`), values, false, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []map[string]interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
assert.Equal(t, []map[string]interface{}{
|
||||
{
|
||||
name: "empty file returns path parameters",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: []byte(""),
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid json/yaml file returns error",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: []byte("this is not json or yaml"),
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "file parameters are added to params",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo.bar": "baz",
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path parameter are prefixed",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: false,
|
||||
pathParamPrefix: "myRepo",
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo.bar": "baz",
|
||||
"myRepo.path": "path/dir",
|
||||
"myRepo.path.basename": "dir",
|
||||
"myRepo.path.filename": "file_name.yaml",
|
||||
"myRepo.path.basenameNormalized": "dir",
|
||||
"myRepo.path.filenameNormalized": "file-name.yaml",
|
||||
"myRepo.path[0]": "path",
|
||||
"myRepo.path[1]": "dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "file parameters are added to params with go template",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: true,
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "path parameter are prefixed with go template",
|
||||
args: args{
|
||||
filePath: "path/dir/file_name.yaml",
|
||||
fileContent: defaultContent,
|
||||
values: map[string]string{},
|
||||
useGoTemplate: true,
|
||||
pathParamPrefix: "myRepo",
|
||||
},
|
||||
want: []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"myRepo": map[string]interface{}{
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"foo.bar": "baz",
|
||||
"path": "path/dir",
|
||||
"path.basename": "dir",
|
||||
"path.filename": "file_name.yaml",
|
||||
"path.basenameNormalized": "dir",
|
||||
"path.filenameNormalized": "file-name.yaml",
|
||||
"path[0]": "path",
|
||||
"path[1]": "dir",
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func Test_generatePrefixedParamsFromGitFile(t *testing.T) {
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`), values, false, nil, "myRepo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile(tt.args.filePath, tt.args.fileContent, tt.args.values, tt.args.useGoTemplate, tt.args.goTemplateOptions, tt.args.pathParamPrefix)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GitGenerator.generateParamsFromGitFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, params)
|
||||
})
|
||||
assert.Equal(t, []map[string]interface{}{
|
||||
{
|
||||
"foo.bar": "baz",
|
||||
"myRepo.path": "path/dir",
|
||||
"myRepo.path.basename": "dir",
|
||||
"myRepo.path.filename": "file_name.yaml",
|
||||
"myRepo.path.basenameNormalized": "dir",
|
||||
"myRepo.path.filenameNormalized": "file-name.yaml",
|
||||
"myRepo.path[0]": "path",
|
||||
"myRepo.path[1]": "dir",
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func Test_generateParamsFromGitFileGoTemplate(t *testing.T) {
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`), values, true, nil, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func Test_generatePrefixedParamsFromGitFileGoTemplate(t *testing.T) {
|
||||
values := map[string]string{}
|
||||
params, err := (*GitGenerator)(nil).generateParamsFromGitFile("path/dir/file_name.yaml", []byte(`
|
||||
foo:
|
||||
bar: baz
|
||||
`), values, true, nil, "myRepo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, []map[string]interface{}{
|
||||
{
|
||||
"foo": map[string]interface{}{
|
||||
"bar": "baz",
|
||||
},
|
||||
"myRepo": map[string]interface{}{
|
||||
"path": map[string]interface{}{
|
||||
"path": "path/dir",
|
||||
"basename": "dir",
|
||||
"filename": "file_name.yaml",
|
||||
"basenameNormalized": "dir",
|
||||
"filenameNormalized": "file-name.yaml",
|
||||
"segments": []string{
|
||||
"path",
|
||||
"dir",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, params)
|
||||
}
|
||||
|
||||
func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
|
||||
@@ -321,9 +263,9 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -341,19 +283,12 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client)
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
if testCaseCopy.expectedError != nil {
|
||||
require.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
assert.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
@@ -363,6 +298,7 @@ func TestGitGenerateParamsFromDirectories(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
|
||||
@@ -562,6 +498,7 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
},
|
||||
repoError: nil,
|
||||
expected: []map[string]interface{}{
|
||||
|
||||
{
|
||||
"path": map[string]interface{}{
|
||||
"path": "app1",
|
||||
@@ -622,9 +559,9 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCaseCopy.repoApps, testCaseCopy.repoError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -642,28 +579,23 @@ func TestGitGenerateParamsFromDirectoriesGoTemplate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client)
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
if testCaseCopy.expectedError != nil {
|
||||
require.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
assert.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGitGenerateParamsFromFiles(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
// files is the list of paths/globs to match
|
||||
@@ -986,10 +918,10 @@ cluster:
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1006,20 +938,13 @@ cluster:
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client)
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
fmt.Println(got, err)
|
||||
|
||||
if testCaseCopy.expectedError != nil {
|
||||
require.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
assert.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
@@ -1029,6 +954,7 @@ cluster:
|
||||
}
|
||||
|
||||
func TestGitGenerateParamsFromFilesGoTemplate(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
// files is the list of paths/globs to match
|
||||
@@ -1342,10 +1268,10 @@ cluster:
|
||||
t.Parallel()
|
||||
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
argoCDServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
|
||||
Return(testCaseCopy.repoFileContents, testCaseCopy.repoPathsError)
|
||||
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "")
|
||||
var gitGenerator = NewGitGenerator(&argoCDServiceMock)
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -1362,20 +1288,13 @@ cluster:
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, client)
|
||||
got, err := gitGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
fmt.Println(got, err)
|
||||
|
||||
if testCaseCopy.expectedError != nil {
|
||||
require.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
assert.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
@@ -1383,114 +1302,3 @@ cluster:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitGenerator_GenerateParams(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
directories []argoprojiov1alpha1.GitDirectoryGeneratorItem
|
||||
pathParamPrefix string
|
||||
repoApps []string
|
||||
repoPathsError error
|
||||
repoFileContents map[string][]byte
|
||||
values map[string]string
|
||||
expected []map[string]interface{}
|
||||
expectedError error
|
||||
appset argoprojiov1alpha1.ApplicationSet
|
||||
callGetDirectories bool
|
||||
}{
|
||||
{
|
||||
name: "Signature Verification - ignores templated project field",
|
||||
repoApps: []string{
|
||||
"app1",
|
||||
},
|
||||
repoPathsError: nil,
|
||||
appset: argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
Git: &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||
PathParamPrefix: "",
|
||||
Values: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argoprojiov1alpha1.ApplicationSpec{
|
||||
Project: "{{.project}}",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
callGetDirectories: true,
|
||||
expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Signature Verification - Checks for non-templated project field",
|
||||
repoApps: []string{
|
||||
"app1",
|
||||
},
|
||||
repoPathsError: nil,
|
||||
appset: argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
Git: &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||
PathParamPrefix: "",
|
||||
Values: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{
|
||||
Spec: argoprojiov1alpha1.ApplicationSpec{
|
||||
Project: "project",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
callGetDirectories: false,
|
||||
expected: []map[string]interface{}{{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "path[0]": "app1", "values.foo": "bar"}},
|
||||
expectedError: fmt.Errorf("error getting project project: appprojects.argoproj.io \"project\" not found"),
|
||||
},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
argoCDServiceMock := mocks.Repos{}
|
||||
|
||||
if testCase.callGetDirectories {
|
||||
argoCDServiceMock.On("GetDirectories", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCase.repoApps, testCase.repoPathsError)
|
||||
}
|
||||
gitGenerator := NewGitGenerator(&argoCDServiceMock, "namespace")
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
got, err := gitGenerator.GenerateParams(&testCase.appset.Spec.Generators[0], &testCase.appset, client)
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testCase.expected, got)
|
||||
}
|
||||
|
||||
argoCDServiceMock.AssertExpectations(t)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
)
|
||||
|
||||
// Generator defines the interface implemented by all ApplicationSet generators.
|
||||
@@ -15,7 +12,7 @@ type Generator interface {
|
||||
// GenerateParams interprets the ApplicationSet and generates all relevant parameters for the application template.
|
||||
// The expected / desired list of parameters is returned, it then will be render and reconciled
|
||||
// against the current state of the Applications in the cluster.
|
||||
GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error)
|
||||
GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error)
|
||||
|
||||
// GetRequeueAfter is the generator can controller the next reconciled loop
|
||||
// In case there is more then one generator the time will be the minimum of the times.
|
||||
@@ -26,16 +23,10 @@ type Generator interface {
|
||||
GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate
|
||||
}
|
||||
|
||||
var (
|
||||
EmptyAppSetGeneratorError = fmt.Errorf("ApplicationSet is empty")
|
||||
NoRequeueAfter time.Duration
|
||||
)
|
||||
var EmptyAppSetGeneratorError = fmt.Errorf("ApplicationSet is empty")
|
||||
var NoRequeueAfter time.Duration
|
||||
|
||||
// DefaultRequeueAfterSeconds is used when GetRequeueAfter is not specified, it is the default time to wait before the next reconcile loop
|
||||
const (
|
||||
DefaultRequeueAfterSeconds = 3 * time.Minute
|
||||
)
|
||||
|
||||
func getDefaultRequeueAfter() time.Duration {
|
||||
// Default is 3 minutes, min is 1 second, max is 1 year
|
||||
return env.ParseDurationFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", DefaultRequeueAfterSeconds, 1*time.Second, 8760*time.Hour)
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_getDefaultRequeueAfter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
requeueAfterEnv string
|
||||
want time.Duration
|
||||
}{
|
||||
{name: "Default", requeueAfterEnv: "", want: DefaultRequeueAfterSeconds},
|
||||
{name: "Min", requeueAfterEnv: "1s", want: 1 * time.Second},
|
||||
{name: "Max", requeueAfterEnv: "8760h", want: 8760 * time.Hour},
|
||||
{name: "Override", requeueAfterEnv: "10m", want: 10 * time.Minute},
|
||||
{name: "LessThanMin", requeueAfterEnv: "1ms", want: DefaultRequeueAfterSeconds},
|
||||
{name: "MoreThanMax", requeueAfterEnv: "8761h", want: DefaultRequeueAfterSeconds},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Setenv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", tt.requeueAfterEnv)
|
||||
assert.Equalf(t, tt.want, getDefaultRequeueAfter(), "getDefaultRequeueAfter()")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -13,7 +12,8 @@ import (
|
||||
|
||||
var _ Generator = (*ListGenerator)(nil)
|
||||
|
||||
type ListGenerator struct{}
|
||||
type ListGenerator struct {
|
||||
}
|
||||
|
||||
func NewListGenerator() Generator {
|
||||
g := &ListGenerator{}
|
||||
@@ -28,7 +28,7 @@ func (g *ListGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicat
|
||||
return &appSetGenerator.List.Template
|
||||
}
|
||||
|
||||
func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
||||
var element map[string]interface{}
|
||||
err := json.Unmarshal(tmpItem.Raw, &element)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshling list element %w", err)
|
||||
return nil, fmt.Errorf("error unmarshling list element %v", err)
|
||||
}
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
@@ -59,14 +59,14 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
||||
for k, v := range values {
|
||||
value, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error parsing value as string %w", err)
|
||||
return nil, fmt.Errorf("error parsing value as string %v", err)
|
||||
}
|
||||
params[fmt.Sprintf("values.%s", k)] = value
|
||||
}
|
||||
} else {
|
||||
v, ok := value.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error parsing value as string %w", err)
|
||||
return nil, fmt.Errorf("error parsing value as string %v", err)
|
||||
}
|
||||
params[key] = v
|
||||
}
|
||||
@@ -77,10 +77,11 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
||||
|
||||
// Append elements from ElementsYaml to the response
|
||||
if len(appSetGenerator.List.ElementsYaml) > 0 {
|
||||
|
||||
var yamlElements []map[string]interface{}
|
||||
err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %w", err)
|
||||
return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %v", err)
|
||||
}
|
||||
res = append(res, yamlElements...)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -26,7 +25,8 @@ func TestGenerateListParams(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
listGenerator := NewListGenerator()
|
||||
|
||||
var listGenerator = NewListGenerator()
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -38,11 +38,11 @@ func TestGenerateListParams(t *testing.T) {
|
||||
got, err := listGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
List: &argoprojiov1alpha1.ListGenerator{
|
||||
Elements: testCase.elements,
|
||||
},
|
||||
}, &applicationSetInfo, nil)
|
||||
}}, &applicationSetInfo)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,8 @@ func TestGenerateListParamsGoTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
listGenerator := NewListGenerator()
|
||||
|
||||
var listGenerator = NewListGenerator()
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -75,10 +76,9 @@ func TestGenerateListParamsGoTemplate(t *testing.T) {
|
||||
got, err := listGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
List: &argoprojiov1alpha1.ListGenerator{
|
||||
Elements: testCase.elements,
|
||||
},
|
||||
}, &applicationSetInfo, nil)
|
||||
}}, &applicationSetInfo)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.ElementsMatch(t, testCase.expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -33,7 +32,8 @@ func NewMatrixGenerator(supportedGenerators map[string]Generator) Generator {
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
||||
func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
|
||||
if appSetGenerator.Matrix == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -48,16 +48,17 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
|
||||
res := []map[string]interface{}{}
|
||||
|
||||
g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil, client)
|
||||
g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error failed to get params for first generator in matrix generator: %w", err)
|
||||
}
|
||||
for _, a := range g0 {
|
||||
g1, err := m.getParams(appSetGenerator.Matrix.Generators[1], appSet, a, client)
|
||||
g1, err := m.getParams(appSetGenerator.Matrix.Generators[1], appSet, a)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get params for second generator in the matrix generator: %w", err)
|
||||
}
|
||||
for _, b := range g1 {
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
tmp := map[string]interface{}{}
|
||||
if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil {
|
||||
@@ -80,7 +81,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]interface{}, client client.Client) ([]map[string]interface{}, error) {
|
||||
func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]interface{}) ([]map[string]interface{}, error) {
|
||||
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -118,10 +119,10 @@ func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Appli
|
||||
m.supportedGenerators,
|
||||
argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
appSet,
|
||||
params,
|
||||
client)
|
||||
params)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err)
|
||||
return nil, fmt.Errorf("child generator returned an error on parameter generation: %v", err)
|
||||
}
|
||||
|
||||
if len(t) == 0 {
|
||||
@@ -171,6 +172,7 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
|
||||
} else {
|
||||
return NoRequeueAfter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) {
|
||||
|
||||
@@ -19,11 +19,11 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestMatrixGenerate(t *testing.T) {
|
||||
|
||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
@@ -147,11 +147,12 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
|
||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
List: g.List,
|
||||
}
|
||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]interface{}{
|
||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{
|
||||
{
|
||||
"path": "app1",
|
||||
"path.basename": "app1",
|
||||
@@ -168,7 +169,7 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": genMock,
|
||||
"List": &ListGenerator{},
|
||||
@@ -180,19 +181,22 @@ func TestMatrixGenerate(t *testing.T) {
|
||||
Generators: testCaseCopy.baseGenerators,
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}, appSet, nil)
|
||||
}, appSet)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
assert.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
|
||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
@@ -356,11 +360,12 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
|
||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
List: g.List,
|
||||
}
|
||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]interface{}{
|
||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{
|
||||
{
|
||||
"path": map[string]string{
|
||||
"path": "app1",
|
||||
@@ -381,7 +386,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": genMock,
|
||||
"List": &ListGenerator{},
|
||||
@@ -393,19 +398,22 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||
Generators: testCaseCopy.baseGenerators,
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}, appSet, nil)
|
||||
}, appSet)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
assert.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
|
||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
@@ -522,7 +530,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
mock.On("GetRequeueAfter", &gitGeneratorSpec).Return(testCaseCopy.gitGetRequeueAfter, nil)
|
||||
}
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": mock,
|
||||
"List": &ListGenerator{},
|
||||
@@ -540,7 +548,9 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||
})
|
||||
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,8 +588,8 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: []map[string]interface{}{
|
||||
{"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json", "path.basename": "dev", "path.basenameNormalized": "dev", "name": "dev-01", "nameNormalized": "dev-01", "server": "https://dev-01.example.com", "metadata.labels.environment": "dev", "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "project": ""},
|
||||
{"path": "examples/git-generator-files-discovery/cluster-config/prod/config.json", "path.basename": "prod", "path.basenameNormalized": "prod", "name": "prod-01", "nameNormalized": "prod-01", "server": "https://prod-01.example.com", "metadata.labels.environment": "prod", "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "project": ""},
|
||||
{"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json", "path.basename": "dev", "path.basenameNormalized": "dev", "name": "dev-01", "nameNormalized": "dev-01", "server": "https://dev-01.example.com", "metadata.labels.environment": "dev", "metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
|
||||
{"path": "examples/git-generator-files-discovery/cluster-config/prod/config.json", "path.basename": "prod", "path.basenameNormalized": "prod", "name": "prod-01", "nameNormalized": "prod-01", "server": "https://prod-01.example.com", "metadata.labels.environment": "prod", "metadata.labels.argocd.argoproj.io/secret-type": "cluster"},
|
||||
},
|
||||
clientError: false,
|
||||
},
|
||||
@@ -645,9 +655,10 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||
fakeClient,
|
||||
testCase.clientError,
|
||||
}
|
||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
|
||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
Clusters: g.Clusters,
|
||||
@@ -667,7 +678,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": genMock,
|
||||
"Clusters": clusterGenerator,
|
||||
@@ -679,14 +690,15 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||
Generators: testCaseCopy.baseGenerators,
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}, appSet, nil)
|
||||
}, appSet)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
assert.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -734,7 +746,6 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
"name": "dev-01",
|
||||
"nameNormalized": "dev-01",
|
||||
"server": "https://dev-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"environment": "dev",
|
||||
@@ -751,7 +762,6 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
"name": "prod-01",
|
||||
"nameNormalized": "prod-01",
|
||||
"server": "https://prod-01.example.com",
|
||||
"project": "",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"environment": "prod",
|
||||
@@ -828,14 +838,16 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
fakeClient,
|
||||
testCase.clientError,
|
||||
}
|
||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
var clusterGenerator = NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
|
||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
Clusters: g.Clusters,
|
||||
}
|
||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{
|
||||
|
||||
{
|
||||
"path": map[string]string{
|
||||
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||
@@ -854,7 +866,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
}
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": genMock,
|
||||
"Clusters": clusterGenerator,
|
||||
@@ -866,19 +878,22 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||
Generators: testCaseCopy.baseGenerators,
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}, appSet, nil)
|
||||
}, appSet)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
assert.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
|
||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
||||
RepoURL: "RepoURL",
|
||||
Revision: "Revision",
|
||||
@@ -982,6 +997,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, g := range testCaseCopy.baseGenerators {
|
||||
|
||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Git: g.Git,
|
||||
List: g.List,
|
||||
@@ -1013,9 +1029,10 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
}}, nil)
|
||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
|
||||
}
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(
|
||||
var matrixGenerator = NewMatrixGenerator(
|
||||
map[string]Generator{
|
||||
"Git": genMock,
|
||||
"List": &ListGenerator{},
|
||||
@@ -1027,15 +1044,17 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||
Generators: testCaseCopy.baseGenerators,
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}, appSet, nil)
|
||||
}, appSet)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
assert.ErrorIs(t, err, testCaseCopy.expectedErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,7 +1068,7 @@ func (g *generatorMock) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicat
|
||||
return args.Get(0).(*argoprojiov1alpha1.ApplicationSetTemplate)
|
||||
}
|
||||
|
||||
func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
args := g.Called(appSetGenerator, appSet)
|
||||
|
||||
return args.Get(0).([]map[string]interface{}), args.Error(1)
|
||||
@@ -1059,6 +1078,7 @@ func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
args := g.Called(appSetGenerator)
|
||||
|
||||
return args.Get(0).(time.Duration)
|
||||
|
||||
}
|
||||
|
||||
func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
@@ -1075,7 +1095,7 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
// of that bug.
|
||||
|
||||
listGeneratorMock := &generatorMock{}
|
||||
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]interface{}{
|
||||
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet")).Return([]map[string]interface{}{
|
||||
{"some": "value"},
|
||||
}, nil)
|
||||
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
||||
@@ -1088,10 +1108,10 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
}
|
||||
|
||||
repoServiceMock := &mocks.Repos{}
|
||||
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||
"some/path.json": []byte("test: content"),
|
||||
}, nil)
|
||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||
gitGenerator := NewGitGenerator(repoServiceMock)
|
||||
|
||||
matrixGenerator := NewMatrixGenerator(map[string]Generator{
|
||||
"List": listGeneratorMock,
|
||||
@@ -1114,17 +1134,9 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
err := v1alpha1.AddToScheme(scheme)
|
||||
require.NoError(t, err)
|
||||
appProject := argoprojiov1alpha1.AppProject{}
|
||||
|
||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||
|
||||
params, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Matrix: matrixGeneratorSpec,
|
||||
}, &argoprojiov1alpha1.ApplicationSet{}, client)
|
||||
}, &argoprojiov1alpha1.ApplicationSet{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []map[string]interface{}{{
|
||||
"path": "some",
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -37,10 +36,10 @@ func NewMergeGenerator(supportedGenerators map[string]Generator) Generator {
|
||||
|
||||
// getParamSetsForAllGenerators generates params for each child generator in a MergeGenerator. Param sets are returned
|
||||
// in slices ordered according to the order of the given generators.
|
||||
func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([][]map[string]interface{}, error) {
|
||||
func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([][]map[string]interface{}, error) {
|
||||
var paramSets [][]map[string]interface{}
|
||||
for i, generator := range generators {
|
||||
generatorParamSets, err := m.getParams(generator, appSet, client)
|
||||
generatorParamSets, err := m.getParams(generator, appSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting params from generator %d of %d: %w", i+1, len(generators), err)
|
||||
}
|
||||
@@ -51,7 +50,7 @@ func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1a
|
||||
}
|
||||
|
||||
// GenerateParams gets the params produced by the MergeGenerator.
|
||||
func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
||||
func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
if appSetGenerator.Merge == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -60,7 +59,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
return nil, ErrLessThanTwoGeneratorsInMerge
|
||||
}
|
||||
|
||||
paramSetsFromGenerators, err := m.getParamSetsForAllGenerators(appSetGenerator.Merge.Generators, appSet, client)
|
||||
paramSetsFromGenerators, err := m.getParamSetsForAllGenerators(appSetGenerator.Merge.Generators, appSet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting param sets from generators: %w", err)
|
||||
}
|
||||
@@ -78,6 +77,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
|
||||
for mergeKeyValue, baseParamSet := range baseParamSetsByMergeKey {
|
||||
if overrideParamSet, exists := paramSetsByMergeKey[mergeKeyValue]; exists {
|
||||
|
||||
if appSet.Spec.GoTemplate {
|
||||
if err := mergo.Merge(&baseParamSet, overrideParamSet, mergo.WithOverride); err != nil {
|
||||
return nil, fmt.Errorf("error merging base param set with override param set: %w", err)
|
||||
@@ -95,7 +95,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
||||
}
|
||||
|
||||
mergedParamSets := make([]map[string]interface{}, len(baseParamSetsByMergeKey))
|
||||
i := 0
|
||||
var i = 0
|
||||
for _, mergedParamSet := range baseParamSetsByMergeKey {
|
||||
mergedParamSets[i] = mergedParamSet
|
||||
i += 1
|
||||
@@ -138,7 +138,7 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface
|
||||
}
|
||||
|
||||
// getParams get the parameters generated by this generator.
|
||||
func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
||||
func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -176,9 +176,10 @@ func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Applic
|
||||
m.supportedGenerators,
|
||||
argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
appSet,
|
||||
map[string]interface{}{}, client)
|
||||
map[string]interface{}{})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err)
|
||||
return nil, fmt.Errorf("child generator returned an error on parameter generation: %v", err)
|
||||
}
|
||||
|
||||
if len(t) == 0 {
|
||||
@@ -226,6 +227,7 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App
|
||||
} else {
|
||||
return NoRequeueAfter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MergeGenerator, error) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -50,6 +49,7 @@ func listOfMapsToSet(maps []map[string]interface{}) (map[string]bool, error) {
|
||||
}
|
||||
|
||||
func TestMergeGenerate(t *testing.T) {
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
||||
@@ -156,7 +156,7 @@ func TestMergeGenerate(t *testing.T) {
|
||||
|
||||
appSet := &argoprojiov1alpha1.ApplicationSet{}
|
||||
|
||||
mergeGenerator := NewMergeGenerator(
|
||||
var mergeGenerator = NewMergeGenerator(
|
||||
map[string]Generator{
|
||||
"List": &ListGenerator{},
|
||||
"Matrix": &MatrixGenerator{
|
||||
@@ -178,18 +178,18 @@ func TestMergeGenerate(t *testing.T) {
|
||||
MergeKeys: testCaseCopy.mergeKeys,
|
||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||
},
|
||||
}, appSet, nil)
|
||||
}, appSet)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.EqualError(t, err, testCaseCopy.expectedErr.Error())
|
||||
assert.EqualError(t, err, testCaseCopy.expectedErr.Error())
|
||||
} else {
|
||||
expectedSet, err := listOfMapsToSet(testCaseCopy.expected)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
actualSet, err := listOfMapsToSet(got)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedSet, actualSet)
|
||||
}
|
||||
})
|
||||
@@ -197,7 +197,7 @@ func TestMergeGenerate(t *testing.T) {
|
||||
}
|
||||
|
||||
func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON {
|
||||
t.Helper()
|
||||
|
||||
resVal, err := json.Marshal(g)
|
||||
if err != nil {
|
||||
t.Error("unable to unmarshal json", g)
|
||||
@@ -339,11 +339,13 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
||||
got, err := getParamSetsByMergeKey(testCaseCopy.mergeKeys, testCaseCopy.paramSets)
|
||||
|
||||
if testCaseCopy.expectedErr != nil {
|
||||
require.EqualError(t, err, testCaseCopy.expectedErr.Error())
|
||||
assert.EqualError(t, err, testCaseCopy.expectedErr.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
client "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
time "time"
|
||||
|
||||
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// Generator is an autogenerated mock type for the Generator type
|
||||
type Generator struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GenerateParams provides a mock function with given fields: appSetGenerator, applicationSetInfo, _a2
|
||||
func (_m *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, _a2 client.Client) ([]map[string]interface{}, error) {
|
||||
ret := _m.Called(appSetGenerator, applicationSetInfo, _a2)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GenerateParams")
|
||||
}
|
||||
|
||||
var r0 []map[string]interface{}
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]interface{}, error)); ok {
|
||||
return rf(appSetGenerator, applicationSetInfo, _a2)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]interface{}); ok {
|
||||
r0 = rf(appSetGenerator, applicationSetInfo, _a2)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]map[string]interface{})
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok {
|
||||
r1 = rf(appSetGenerator, applicationSetInfo, _a2)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRequeueAfter provides a mock function with given fields: appSetGenerator
|
||||
func (_m *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
ret := _m.Called(appSetGenerator)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRequeueAfter")
|
||||
}
|
||||
|
||||
var r0 time.Duration
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok {
|
||||
r0 = rf(appSetGenerator)
|
||||
} else {
|
||||
r0 = ret.Get(0).(time.Duration)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetTemplate provides a mock function with given fields: appSetGenerator
|
||||
func (_m *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
||||
ret := _m.Called(appSetGenerator)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetTemplate")
|
||||
}
|
||||
|
||||
var r0 *v1alpha1.ApplicationSetTemplate
|
||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok {
|
||||
r0 = rf(appSetGenerator)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewGenerator creates a new instance of Generator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewGenerator(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Generator {
|
||||
mock := &Generator{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -55,7 +55,8 @@ func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applic
|
||||
return &appSetGenerator.Plugin.Template
|
||||
}
|
||||
|
||||
func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -93,7 +94,7 @@ func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName
|
||||
}
|
||||
token, err := g.getToken(ctx, cm["token"])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
|
||||
var requestTimeout int
|
||||
@@ -116,6 +117,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
res := []map[string]interface{}{}
|
||||
|
||||
for _, objectFound := range objectsFound {
|
||||
|
||||
params := map[string]interface{}{}
|
||||
|
||||
if useGoTemplate {
|
||||
@@ -150,6 +152,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
|
||||
}
|
||||
|
||||
func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string, error) {
|
||||
|
||||
if tokenRef == "" || !strings.HasPrefix(tokenRef, "$") {
|
||||
return "", fmt.Errorf("token is empty, or does not reference a secret key starting with '$': %v", tokenRef)
|
||||
}
|
||||
@@ -164,8 +167,9 @@ func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string
|
||||
Namespace: g.namespace,
|
||||
},
|
||||
secret)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error fetching secret %s/%s: %w", g.namespace, secretName, err)
|
||||
return "", fmt.Errorf("error fetching secret %s/%s: %v", g.namespace, secretName, err)
|
||||
}
|
||||
|
||||
secretValues := make(map[string]string, len(secret.Data))
|
||||
@@ -188,6 +192,7 @@ func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef string)
|
||||
Namespace: g.namespace,
|
||||
},
|
||||
cm)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -631,7 +631,9 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
|
||||
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||
Plugin: &argoprojiov1alpha1.PluginGenerator{
|
||||
ConfigMapRef: argoprojiov1alpha1.PluginConfigMapRef{Name: testCase.configmap.Name},
|
||||
@@ -643,9 +645,10 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
}
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
_, tokenKey := plugin.ParseSecretKey(testCase.configmap.Data["token"])
|
||||
expectedToken := testCase.secret.Data[strings.ReplaceAll(tokenKey, "$", "")]
|
||||
expectedToken := testCase.secret.Data[strings.Replace(tokenKey, "$", "", -1)]
|
||||
if authHeader != "Bearer "+string(expectedToken) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
@@ -654,7 +657,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err := w.Write(testCase.content)
|
||||
if err != nil {
|
||||
require.NoError(t, fmt.Errorf("Error Write %w", err))
|
||||
assert.NoError(t, fmt.Errorf("Error Write %v", err))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -670,7 +673,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
|
||||
fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build()
|
||||
|
||||
pluginGenerator := NewPluginGenerator(fakeClientWithCache, ctx, fakeClient, "default")
|
||||
var pluginGenerator = NewPluginGenerator(fakeClientWithCache, ctx, fakeClient, "default")
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -681,20 +684,21 @@ func TestPluginGenerateParams(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
got, err := pluginGenerator.GenerateParams(&generatorConfig, &applicationSetInfo, nil)
|
||||
got, err := pluginGenerator.GenerateParams(&generatorConfig, &applicationSetInfo)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
if testCase.expectedError != nil {
|
||||
require.EqualError(t, err, testCase.expectedError.Error())
|
||||
assert.EqualError(t, err, testCase.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
expectedJson, err := json.Marshal(testCase.expected)
|
||||
require.NoError(t, err)
|
||||
gotJson, err := json.Marshal(got)
|
||||
require.NoError(t, err)
|
||||
assert.JSONEq(t, string(expectedJson), string(gotJson))
|
||||
assert.Equal(t, string(expectedJson), string(gotJson))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
|
||||
pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request"
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -24,13 +24,17 @@ const (
|
||||
type PullRequestGenerator struct {
|
||||
client client.Client
|
||||
selectServiceProviderFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
||||
SCMConfig
|
||||
auth SCMAuthProviders
|
||||
scmRootCAPath string
|
||||
allowedSCMProviders []string
|
||||
}
|
||||
|
||||
func NewPullRequestGenerator(client client.Client, scmConfig SCMConfig) Generator {
|
||||
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string) Generator {
|
||||
g := &PullRequestGenerator{
|
||||
client: client,
|
||||
SCMConfig: scmConfig,
|
||||
client: client,
|
||||
auth: auth,
|
||||
scmRootCAPath: scmRootCAPath,
|
||||
allowedSCMProviders: allowedScmProviders,
|
||||
}
|
||||
g.selectServiceProviderFunc = g.selectServiceProvider
|
||||
return g
|
||||
@@ -50,7 +54,7 @@ func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A
|
||||
return &appSetGenerator.PullRequest.Template
|
||||
}
|
||||
|
||||
func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -62,12 +66,12 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
ctx := context.Background()
|
||||
svc, err := g.selectServiceProviderFunc(ctx, appSetGenerator.PullRequest, applicationSetInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to select pull request service provider: %w", err)
|
||||
return nil, fmt.Errorf("failed to select pull request service provider: %v", err)
|
||||
}
|
||||
|
||||
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing repos: %w", err)
|
||||
return nil, fmt.Errorf("error listing repos: %v", err)
|
||||
}
|
||||
params := make([]map[string]interface{}, 0, len(pulls))
|
||||
|
||||
@@ -97,7 +101,6 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
|
||||
paramMap := map[string]interface{}{
|
||||
"number": strconv.Itoa(pull.Number),
|
||||
"title": pull.Title,
|
||||
"branch": pull.Branch,
|
||||
"branch_slug": slug.Make(pull.Branch),
|
||||
"target_branch": pull.TargetBranch,
|
||||
@@ -105,7 +108,6 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
"head_sha": pull.HeadSHA,
|
||||
"head_short_sha": pull.HeadSHA[:shortSHALength],
|
||||
"head_short_sha_7": pull.HeadSHA[:shortSHALength7],
|
||||
"author": pull.Author,
|
||||
}
|
||||
|
||||
// PR lables will only be supported for Go Template appsets, since fasttemplate will be deprecated.
|
||||
@@ -119,78 +121,61 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
|
||||
// selectServiceProvider selects the provider to get pull requests from the configuration
|
||||
func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, generatorConfig *argoprojiov1alpha1.PullRequestGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||
if !g.enableSCMProviders {
|
||||
return nil, ErrSCMProvidersDisabled
|
||||
}
|
||||
if err := ScmProviderAllowed(applicationSetInfo, generatorConfig, g.allowedSCMProviders); err != nil {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %w", err)
|
||||
}
|
||||
|
||||
if generatorConfig.Github != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, generatorConfig.Github.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Github.API)
|
||||
}
|
||||
return g.github(ctx, generatorConfig.Github, applicationSetInfo)
|
||||
}
|
||||
if generatorConfig.GitLab != nil {
|
||||
providerConfig := generatorConfig.GitLab
|
||||
var caCerts []byte
|
||||
var prErr error
|
||||
if providerConfig.CARef != nil {
|
||||
caCerts, prErr = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace)
|
||||
if prErr != nil {
|
||||
return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", prErr)
|
||||
}
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
|
||||
}
|
||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure)
|
||||
}
|
||||
if generatorConfig.Gitea != nil {
|
||||
providerConfig := generatorConfig.Gitea
|
||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Gitea.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
return pullrequest.NewGiteaService(ctx, token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Insecure)
|
||||
}
|
||||
if generatorConfig.BitbucketServer != nil {
|
||||
providerConfig := generatorConfig.BitbucketServer
|
||||
var caCerts []byte
|
||||
var prErr error
|
||||
if providerConfig.CARef != nil {
|
||||
caCerts, prErr = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace)
|
||||
if prErr != nil {
|
||||
return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", prErr)
|
||||
}
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
|
||||
}
|
||||
if providerConfig.BearerToken != nil {
|
||||
appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
if providerConfig.BasicAuth != nil {
|
||||
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
return pullrequest.NewBitbucketServiceBearerToken(ctx, appToken, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
} else if providerConfig.BasicAuth != nil {
|
||||
password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
}
|
||||
return pullrequest.NewBitbucketServiceBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
return pullrequest.NewBitbucketServiceBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.Repo)
|
||||
} else {
|
||||
return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo)
|
||||
}
|
||||
}
|
||||
if generatorConfig.Bitbucket != nil {
|
||||
providerConfig := generatorConfig.Bitbucket
|
||||
if providerConfig.BearerToken != nil {
|
||||
appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
appToken, err := g.getSecretRef(ctx, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret Bearer token: %v", err)
|
||||
}
|
||||
return pullrequest.NewBitbucketCloudServiceBearerToken(providerConfig.API, appToken, providerConfig.Owner, providerConfig.Repo)
|
||||
} else if providerConfig.BasicAuth != nil {
|
||||
password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
return pullrequest.NewBitbucketCloudServiceBasicAuth(providerConfig.API, providerConfig.BasicAuth.Username, password, providerConfig.Owner, providerConfig.Repo)
|
||||
} else {
|
||||
@@ -199,9 +184,9 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
||||
}
|
||||
if generatorConfig.AzureDevOps != nil {
|
||||
providerConfig := generatorConfig.AzureDevOps
|
||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
return pullrequest.NewAzureDevOpsService(ctx, token, providerConfig.API, providerConfig.Organization, providerConfig.Project, providerConfig.Repo, providerConfig.Labels)
|
||||
}
|
||||
@@ -211,17 +196,41 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
||||
func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alpha1.PullRequestGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||
// use an app if it was configured
|
||||
if cfg.AppSecretName != "" {
|
||||
auth, err := g.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName)
|
||||
auth, err := g.auth.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting GitHub App secret: %w", err)
|
||||
return nil, fmt.Errorf("error getting GitHub App secret: %v", err)
|
||||
}
|
||||
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||
}
|
||||
|
||||
// always default to token, even if not set (public access)
|
||||
token, err := utils.GetSecretRef(ctx, g.client, cfg.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
token, err := g.getSecretRef(ctx, cfg.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
return pullrequest.NewGithubService(ctx, token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||
}
|
||||
|
||||
// getSecretRef gets the value of the key for the specified Secret resource.
|
||||
func (g *PullRequestGenerator) getSecretRef(ctx context.Context, ref *argoprojiov1alpha1.SecretRef, namespace string) (string, error) {
|
||||
if ref == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
err := g.client.Get(
|
||||
ctx,
|
||||
client.ObjectKey{
|
||||
Name: ref.SecretName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
secret)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error fetching secret %s/%s: %v", namespace, ref.SecretName, err)
|
||||
}
|
||||
tokenBytes, ok := secret.Data[ref.Key]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("key %q in secret %s/%s not found", ref.Key, namespace, ref.SecretName)
|
||||
}
|
||||
return string(tokenBytes), nil
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
@@ -28,11 +29,9 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "title1",
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "testName",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -41,7 +40,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"number": "1",
|
||||
"title": "title1",
|
||||
"branch": "branch1",
|
||||
"branch_slug": "branch1",
|
||||
"target_branch": "master",
|
||||
@@ -49,7 +47,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
"head_short_sha": "089d92cb",
|
||||
"head_short_sha_7": "089d92c",
|
||||
"author": "testName",
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -61,11 +58,9 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
Number: 2,
|
||||
Title: "title2",
|
||||
Branch: "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
TargetBranch: "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
HeadSHA: "9b34ff5bd418e57d58891eb0aa0728043ca1e8be",
|
||||
Author: "testName",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -74,7 +69,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"number": "2",
|
||||
"title": "title2",
|
||||
"branch": "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
"branch_slug": "feat-areally-long-pull-request-name-to-test-argo",
|
||||
"target_branch": "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
|
||||
@@ -82,7 +76,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
"head_sha": "9b34ff5bd418e57d58891eb0aa0728043ca1e8be",
|
||||
"head_short_sha": "9b34ff5b",
|
||||
"head_short_sha_7": "9b34ff5",
|
||||
"author": "testName",
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -94,11 +87,9 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "title1",
|
||||
Branch: "a-very-short-sha",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "abcd",
|
||||
Author: "testName",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -107,7 +98,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"number": "1",
|
||||
"title": "title1",
|
||||
"branch": "a-very-short-sha",
|
||||
"branch_slug": "a-very-short-sha",
|
||||
"target_branch": "master",
|
||||
@@ -115,7 +105,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
"head_sha": "abcd",
|
||||
"head_short_sha": "abcd",
|
||||
"head_short_sha_7": "abcd",
|
||||
"author": "testName",
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -138,12 +127,10 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "title1",
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Labels: []string{"preview"},
|
||||
Author: "testName",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -152,7 +139,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"number": "1",
|
||||
"title": "title1",
|
||||
"branch": "branch1",
|
||||
"branch_slug": "branch1",
|
||||
"target_branch": "master",
|
||||
@@ -161,7 +147,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
"head_short_sha": "089d92cb",
|
||||
"head_short_sha_7": "089d92c",
|
||||
"labels": []string{"preview"},
|
||||
"author": "testName",
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -179,12 +164,10 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
[]*pullrequest.PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "title1",
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Labels: []string{"preview"},
|
||||
Author: "testName",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -193,7 +176,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
expected: []map[string]interface{}{
|
||||
{
|
||||
"number": "1",
|
||||
"title": "title1",
|
||||
"branch": "branch1",
|
||||
"branch_slug": "branch1",
|
||||
"target_branch": "master",
|
||||
@@ -201,7 +183,6 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
"head_sha": "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
"head_short_sha": "089d92cb",
|
||||
"head_short_sha_7": "089d92c",
|
||||
"author": "testName",
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@@ -222,20 +203,82 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{},
|
||||
}
|
||||
|
||||
got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet, nil)
|
||||
if c.expectedErr != nil {
|
||||
assert.Equal(t, c.expectedErr.Error(), gotErr.Error())
|
||||
} else {
|
||||
require.NoError(t, gotErr)
|
||||
}
|
||||
got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet)
|
||||
assert.Equal(t, c.expectedErr, gotErr)
|
||||
assert.ElementsMatch(t, c.expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullRequestGetSecretRef(t *testing.T) {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-secret", Namespace: "test"},
|
||||
Data: map[string][]byte{
|
||||
"my-token": []byte("secret"),
|
||||
},
|
||||
}
|
||||
gen := &PullRequestGenerator{client: fake.NewClientBuilder().WithObjects(secret).Build()}
|
||||
ctx := context.Background()
|
||||
|
||||
cases := []struct {
|
||||
name, namespace, token string
|
||||
ref *argoprojiov1alpha1.SecretRef
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "valid ref",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"},
|
||||
namespace: "test",
|
||||
token: "secret",
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "nil ref",
|
||||
ref: nil,
|
||||
namespace: "test",
|
||||
token: "",
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "wrong name",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "other", Key: "my-token"},
|
||||
namespace: "test",
|
||||
token: "",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "wrong key",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "other-token"},
|
||||
namespace: "test",
|
||||
token: "",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "wrong namespace",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"},
|
||||
namespace: "other",
|
||||
token: "",
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
token, err := gen.getSecretRef(ctx, c.ref, c.namespace)
|
||||
if c.hasError {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
assert.Equal(t, c.token, token)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
providerConfig *argoprojiov1alpha1.PullRequestGenerator
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Error Github",
|
||||
@@ -244,6 +287,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitlab",
|
||||
@@ -252,6 +296,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitea",
|
||||
@@ -260,6 +305,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Bitbucket",
|
||||
@@ -268,6 +314,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -277,13 +324,13 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pullRequestGenerator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{
|
||||
pullRequestGenerator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{
|
||||
"github.myorg.com",
|
||||
"gitlab.myorg.com",
|
||||
"gitea.myorg.com",
|
||||
"bitbucket.myorg.com",
|
||||
"azuredevops.myorg.com",
|
||||
}, true, nil, true))
|
||||
})
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -296,33 +343,10 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
_, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
|
||||
_, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
require.Error(t, err, "Must return an error")
|
||||
var expectedError ErrDisallowedSCMProvider
|
||||
assert.ErrorAs(t, err, &expectedError)
|
||||
assert.Error(t, err, "Must return an error")
|
||||
assert.Equal(t, testCaseCopy.expectedError, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
|
||||
generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, nil, true))
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
|
||||
Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
|
||||
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package generators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -28,38 +28,27 @@ type SCMProviderGenerator struct {
|
||||
client client.Client
|
||||
// Testing hooks.
|
||||
overrideProvider scm_provider.SCMProviderService
|
||||
SCMConfig
|
||||
}
|
||||
type SCMConfig struct {
|
||||
SCMAuthProviders
|
||||
scmRootCAPath string
|
||||
allowedSCMProviders []string
|
||||
enableSCMProviders bool
|
||||
GitHubApps github_app_auth.Credentials
|
||||
tokenRefStrictMode bool
|
||||
}
|
||||
|
||||
func NewSCMConfig(scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool, gitHubApps github_app_auth.Credentials, tokenRefStrictMode bool) SCMConfig {
|
||||
return SCMConfig{
|
||||
type SCMAuthProviders struct {
|
||||
GitHubApps github_app_auth.Credentials
|
||||
}
|
||||
|
||||
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string) Generator {
|
||||
return &SCMProviderGenerator{
|
||||
client: client,
|
||||
SCMAuthProviders: providers,
|
||||
scmRootCAPath: scmRootCAPath,
|
||||
allowedSCMProviders: allowedSCMProviders,
|
||||
enableSCMProviders: enableSCMProviders,
|
||||
GitHubApps: gitHubApps,
|
||||
tokenRefStrictMode: tokenRefStrictMode,
|
||||
}
|
||||
}
|
||||
|
||||
func NewSCMProviderGenerator(client client.Client, scmConfig SCMConfig) Generator {
|
||||
return &SCMProviderGenerator{
|
||||
client: client,
|
||||
SCMConfig: scmConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// Testing generator
|
||||
func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator {
|
||||
return &SCMProviderGenerator{overrideProvider: overrideProvider, SCMConfig: SCMConfig{
|
||||
enableSCMProviders: true,
|
||||
}}
|
||||
return &SCMProviderGenerator{overrideProvider: overrideProvider}
|
||||
}
|
||||
|
||||
func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||
@@ -76,34 +65,14 @@ func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A
|
||||
return &appSetGenerator.SCMProvider.Template
|
||||
}
|
||||
|
||||
var ErrSCMProvidersDisabled = errors.New("scm providers are disabled")
|
||||
|
||||
type ErrDisallowedSCMProvider struct {
|
||||
Provider string
|
||||
Allowed []string
|
||||
}
|
||||
|
||||
func NewErrDisallowedSCMProvider(provider string, allowed []string) ErrDisallowedSCMProvider {
|
||||
return ErrDisallowedSCMProvider{
|
||||
Provider: provider,
|
||||
Allowed: allowed,
|
||||
}
|
||||
}
|
||||
|
||||
func (e ErrDisallowedSCMProvider) Error() string {
|
||||
return fmt.Sprintf("scm provider %q not allowed, must use one of the following: %s", e.Provider, strings.Join(e.Allowed, ", "))
|
||||
}
|
||||
|
||||
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, generator SCMGeneratorWithCustomApiUrl, allowedScmProviders []string) error {
|
||||
url := generator.CustomApiUrl()
|
||||
|
||||
func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, url string, allowedScmProviders []string) bool {
|
||||
if url == "" || len(allowedScmProviders) == 0 {
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
|
||||
for _, allowedScmProvider := range allowedScmProviders {
|
||||
if url == allowedScmProvider {
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,12 +80,12 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
|
||||
common.SecurityField: common.SecurityMedium,
|
||||
"applicationset": applicationSetInfo.Name,
|
||||
"appSetNamespace": applicationSetInfo.Namespace,
|
||||
}).Debugf("attempted to use disallowed SCM %q, must use one of the following: %s", url, strings.Join(allowedScmProviders, ", "))
|
||||
}).Debugf("attempted to use disallowed SCM %q", url)
|
||||
|
||||
return NewErrDisallowedSCMProvider(url, allowedScmProviders)
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
||||
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
|
||||
if appSetGenerator == nil {
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
@@ -125,105 +94,90 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
return nil, EmptyAppSetGeneratorError
|
||||
}
|
||||
|
||||
if !g.enableSCMProviders {
|
||||
return nil, ErrSCMProvidersDisabled
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// Create the SCM provider helper.
|
||||
providerConfig := appSetGenerator.SCMProvider
|
||||
|
||||
if err := ScmProviderAllowed(applicationSetInfo, providerConfig, g.allowedSCMProviders); err != nil {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
var provider scm_provider.SCMProviderService
|
||||
if g.overrideProvider != nil {
|
||||
provider = g.overrideProvider
|
||||
} else if providerConfig.Github != nil {
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Github.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Github.API)
|
||||
}
|
||||
var err error
|
||||
provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scm provider: %w", err)
|
||||
}
|
||||
} else if providerConfig.Gitlab != nil {
|
||||
providerConfig := providerConfig.Gitlab
|
||||
var caCerts []byte
|
||||
var scmError error
|
||||
if providerConfig.CARef != nil {
|
||||
caCerts, scmError = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace)
|
||||
if scmError != nil {
|
||||
return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", scmError)
|
||||
}
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitlab.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitlab.API)
|
||||
}
|
||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
token, err := g.getSecretRef(ctx, providerConfig.Gitlab.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Gitlab token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Gitlab token: %v", err)
|
||||
}
|
||||
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Group, token, providerConfig.API, providerConfig.AllBranches, providerConfig.IncludeSubgroups, providerConfig.WillIncludeSharedProjects(), providerConfig.Insecure, g.scmRootCAPath, providerConfig.Topic, caCerts)
|
||||
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups, providerConfig.Gitlab.WillIncludeSharedProjects(), providerConfig.Gitlab.Insecure, g.scmRootCAPath, providerConfig.Gitlab.Topic)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing Gitlab service: %w", err)
|
||||
return nil, fmt.Errorf("error initializing Gitlab service: %v", err)
|
||||
}
|
||||
} else if providerConfig.Gitea != nil {
|
||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitea.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitea.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Gitea token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Gitea token: %v", err)
|
||||
}
|
||||
provider, err = scm_provider.NewGiteaProvider(ctx, providerConfig.Gitea.Owner, token, providerConfig.Gitea.API, providerConfig.Gitea.AllBranches, providerConfig.Gitea.Insecure)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing Gitea service: %w", err)
|
||||
return nil, fmt.Errorf("error initializing Gitea service: %v", err)
|
||||
}
|
||||
} else if providerConfig.BitbucketServer != nil {
|
||||
providerConfig := providerConfig.BitbucketServer
|
||||
var caCerts []byte
|
||||
var scmError error
|
||||
if providerConfig.CARef != nil {
|
||||
caCerts, scmError = utils.GetConfigMapData(ctx, g.client, providerConfig.CARef, applicationSetInfo.Namespace)
|
||||
if scmError != nil {
|
||||
return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", scmError)
|
||||
}
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API)
|
||||
}
|
||||
if providerConfig.BearerToken != nil {
|
||||
appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
var scmError error
|
||||
if providerConfig.BasicAuth != nil {
|
||||
password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Secret token: %v", err)
|
||||
}
|
||||
provider, scmError = scm_provider.NewBitbucketServerProviderBearerToken(ctx, appToken, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
} else if providerConfig.BasicAuth != nil {
|
||||
password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||
}
|
||||
provider, scmError = scm_provider.NewBitbucketServerProviderBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
provider, scmError = scm_provider.NewBitbucketServerProviderBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.AllBranches)
|
||||
} else {
|
||||
provider, scmError = scm_provider.NewBitbucketServerProviderNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||
provider, scmError = scm_provider.NewBitbucketServerProviderNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.AllBranches)
|
||||
}
|
||||
if scmError != nil {
|
||||
return nil, fmt.Errorf("error initializing Bitbucket Server service: %w", scmError)
|
||||
return nil, fmt.Errorf("error initializing Bitbucket Server service: %v", scmError)
|
||||
}
|
||||
} else if providerConfig.AzureDevOps != nil {
|
||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
if !ScmProviderAllowed(applicationSetInfo, providerConfig.AzureDevOps.API, g.allowedSCMProviders) {
|
||||
return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.AzureDevOps.API)
|
||||
}
|
||||
token, err := g.getSecretRef(ctx, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Azure Devops access token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Azure Devops access token: %v", err)
|
||||
}
|
||||
provider, err = scm_provider.NewAzureDevOpsProvider(ctx, token, providerConfig.AzureDevOps.Organization, providerConfig.AzureDevOps.API, providerConfig.AzureDevOps.TeamProject, providerConfig.AzureDevOps.AllBranches)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing Azure Devops service: %w", err)
|
||||
return nil, fmt.Errorf("error initializing Azure Devops service: %v", err)
|
||||
}
|
||||
} else if providerConfig.Bitbucket != nil {
|
||||
appPassword, err := utils.GetSecretRef(ctx, g.client, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
appPassword, err := g.getSecretRef(ctx, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %v", err)
|
||||
}
|
||||
provider, err = scm_provider.NewBitBucketCloudProvider(ctx, providerConfig.Bitbucket.Owner, providerConfig.Bitbucket.User, appPassword, providerConfig.Bitbucket.AllBranches)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing Bitbucket cloud service: %w", err)
|
||||
return nil, fmt.Errorf("error initializing Bitbucket cloud service: %v", err)
|
||||
}
|
||||
} else if providerConfig.AWSCodeCommit != nil {
|
||||
var awsErr error
|
||||
provider, awsErr = scm_provider.NewAWSCodeCommitProvider(ctx, providerConfig.AWSCodeCommit.TagFilters, providerConfig.AWSCodeCommit.Role, providerConfig.AWSCodeCommit.Region, providerConfig.AWSCodeCommit.AllBranches)
|
||||
if awsErr != nil {
|
||||
return nil, fmt.Errorf("error initializing AWS codecommit service: %w", awsErr)
|
||||
return nil, fmt.Errorf("error initializing AWS codecommit service: %v", awsErr)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("no SCM provider implementation configured")
|
||||
@@ -232,7 +186,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
// Find all the available repos.
|
||||
repos, err := scm_provider.ListRepos(ctx, provider, providerConfig.Filters, providerConfig.CloneProtocol)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing repos: %w", err)
|
||||
return nil, fmt.Errorf("error listing repos: %v", err)
|
||||
}
|
||||
paramsArray := make([]map[string]interface{}, 0, len(repos))
|
||||
var shortSHALength int
|
||||
@@ -270,11 +224,34 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
||||
return paramsArray, nil
|
||||
}
|
||||
|
||||
func (g *SCMProviderGenerator) getSecretRef(ctx context.Context, ref *argoprojiov1alpha1.SecretRef, namespace string) (string, error) {
|
||||
if ref == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
err := g.client.Get(
|
||||
ctx,
|
||||
client.ObjectKey{
|
||||
Name: ref.SecretName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
secret)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error fetching secret %s/%s: %v", namespace, ref.SecretName, err)
|
||||
}
|
||||
tokenBytes, ok := secret.Data[ref.Key]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("key %q in secret %s/%s not found", ref.Key, namespace, ref.SecretName)
|
||||
}
|
||||
return string(tokenBytes), nil
|
||||
}
|
||||
|
||||
func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argoprojiov1alpha1.SCMProviderGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (scm_provider.SCMProviderService, error) {
|
||||
if github.AppSecretName != "" {
|
||||
auth, err := g.GitHubApps.GetAuthSecret(ctx, github.AppSecretName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Github app secret: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Github app secret: %v", err)
|
||||
}
|
||||
|
||||
return scm_provider.NewGithubAppProviderFor(
|
||||
@@ -285,9 +262,9 @@ func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argop
|
||||
)
|
||||
}
|
||||
|
||||
token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||
token, err := g.getSecretRef(ctx, github.TokenRef, applicationSetInfo.Namespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching Github token: %w", err)
|
||||
return nil, fmt.Errorf("error fetching Github token: %v", err)
|
||||
}
|
||||
return scm_provider.NewGithubProvider(ctx, github.Organization, token, github.API, github.AllBranches)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,84 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider"
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestSCMProviderGetSecretRef(t *testing.T) {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test-secret", Namespace: "test"},
|
||||
Data: map[string][]byte{
|
||||
"my-token": []byte("secret"),
|
||||
},
|
||||
}
|
||||
gen := &SCMProviderGenerator{client: fake.NewClientBuilder().WithObjects(secret).Build()}
|
||||
ctx := context.Background()
|
||||
|
||||
cases := []struct {
|
||||
name, namespace, token string
|
||||
ref *argoprojiov1alpha1.SecretRef
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "valid ref",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"},
|
||||
namespace: "test",
|
||||
token: "secret",
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "nil ref",
|
||||
ref: nil,
|
||||
namespace: "test",
|
||||
token: "",
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "wrong name",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "other", Key: "my-token"},
|
||||
namespace: "test",
|
||||
token: "",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "wrong key",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "other-token"},
|
||||
namespace: "test",
|
||||
token: "",
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "wrong namespace",
|
||||
ref: &argoprojiov1alpha1.SecretRef{SecretName: "test-secret", Key: "my-token"},
|
||||
namespace: "other",
|
||||
token: "",
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
token, err := gen.getSecretRef(ctx, c.ref, c.namespace)
|
||||
if c.hasError {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
assert.Equal(t, c.token, token)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCMProviderGenerateParams(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
@@ -106,7 +174,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
||||
mockProvider := &scm_provider.MockProvider{
|
||||
Repos: testCaseCopy.repos,
|
||||
}
|
||||
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, SCMConfig: SCMConfig{enableSCMProviders: true}}
|
||||
scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider}
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
@@ -120,14 +188,15 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
got, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
|
||||
got, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
if testCaseCopy.expectedError != nil {
|
||||
assert.EqualError(t, err, testCaseCopy.expectedError.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCaseCopy.expected, got)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -136,6 +205,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Error Github",
|
||||
@@ -144,6 +214,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitlab",
|
||||
@@ -152,6 +223,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Gitea",
|
||||
@@ -160,6 +232,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error Bitbucket",
|
||||
@@ -168,6 +241,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
{
|
||||
name: "Error AzureDevops",
|
||||
@@ -176,6 +250,7 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -185,18 +260,13 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scmGenerator := &SCMProviderGenerator{
|
||||
SCMConfig: SCMConfig{
|
||||
allowedSCMProviders: []string{
|
||||
"github.myorg.com",
|
||||
"gitlab.myorg.com",
|
||||
"gitea.myorg.com",
|
||||
"bitbucket.myorg.com",
|
||||
"azuredevops.myorg.com",
|
||||
},
|
||||
enableSCMProviders: true,
|
||||
},
|
||||
}
|
||||
scmGenerator := &SCMProviderGenerator{allowedSCMProviders: []string{
|
||||
"github.myorg.com",
|
||||
"gitlab.myorg.com",
|
||||
"gitea.myorg.com",
|
||||
"bitbucket.myorg.com",
|
||||
"azuredevops.myorg.com",
|
||||
}}
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -209,33 +279,10 @@ func TestAllowedSCMProvider(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
_, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
|
||||
_, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo)
|
||||
|
||||
require.Error(t, err, "Must return an error")
|
||||
var expectedError ErrDisallowedSCMProvider
|
||||
assert.ErrorAs(t, err, &expectedError)
|
||||
assert.Error(t, err, "Must return an error")
|
||||
assert.Equal(t, testCaseCopy.expectedError, err.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSCMProviderDisabled_SCMGenerator(t *testing.T) {
|
||||
generator := &SCMProviderGenerator{SCMConfig: SCMConfig{enableSCMProviders: false}}
|
||||
|
||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "set",
|
||||
},
|
||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||
Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
|
||||
SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{
|
||||
Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{
|
||||
API: "https://myservice.mynamespace.svc.cluster.local",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
|
||||
assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package generators
|
||||
|
||||
type SCMGeneratorWithCustomApiUrl interface {
|
||||
CustomApiUrl() string
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services"
|
||||
)
|
||||
|
||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, namespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
||||
terminalGenerators := map[string]Generator{
|
||||
"List": NewListGenerator(),
|
||||
"Clusters": NewClusterGenerator(c, ctx, k8sClient, namespace),
|
||||
"Git": NewGitGenerator(argoCDService, namespace),
|
||||
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
||||
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
||||
"Plugin": NewPluginGenerator(c, ctx, k8sClient, namespace),
|
||||
}
|
||||
|
||||
nestedGenerators := map[string]Generator{
|
||||
"List": terminalGenerators["List"],
|
||||
"Clusters": terminalGenerators["Clusters"],
|
||||
"Git": terminalGenerators["Git"],
|
||||
"SCMProvider": terminalGenerators["SCMProvider"],
|
||||
"ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"],
|
||||
"PullRequest": terminalGenerators["PullRequest"],
|
||||
"Plugin": terminalGenerators["Plugin"],
|
||||
"Matrix": NewMatrixGenerator(terminalGenerators),
|
||||
"Merge": NewMergeGenerator(terminalGenerators),
|
||||
}
|
||||
|
||||
topLevelGenerators := map[string]Generator{
|
||||
"List": terminalGenerators["List"],
|
||||
"Clusters": terminalGenerators["Clusters"],
|
||||
"Git": terminalGenerators["Git"],
|
||||
"SCMProvider": terminalGenerators["SCMProvider"],
|
||||
"ClusterDecisionResource": terminalGenerators["ClusterDecisionResource"],
|
||||
"PullRequest": terminalGenerators["PullRequest"],
|
||||
"Plugin": terminalGenerators["Plugin"],
|
||||
"Matrix": NewMatrixGenerator(nestedGenerators),
|
||||
"Merge": NewMergeGenerator(nestedGenerators),
|
||||
}
|
||||
|
||||
return topLevelGenerators
|
||||
}
|
||||
@@ -12,6 +12,7 @@ func appendTemplatedValues(values map[string]string, params map[string]interface
|
||||
|
||||
for key, value := range values {
|
||||
result, err := replaceTemplatedString(value, params, useGoTemplate, goTemplateOptions)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to replace templated string: %w", err)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestValueInterpolation(t *testing.T) {
|
||||
@@ -54,9 +53,10 @@ func TestValueInterpolation(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
err := appendTemplatedValues(testCase.values, testCase.params, false, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testCase.expected, testCase.params)
|
||||
})
|
||||
}
|
||||
@@ -115,9 +115,10 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
err := appendTemplatedValues(testCase.values, testCase.params, true, nil)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, testCase.expected, testCase.params)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// Fake implementation for testing
|
||||
func NewFakeAppsetMetrics(client ctrlclient.WithWatch) *ApplicationsetMetrics {
|
||||
reconcileHistogram := prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "argocd_appset_reconcile",
|
||||
Help: "Application reconciliation performance in seconds.",
|
||||
// Buckets can be set later on after observing median time
|
||||
},
|
||||
[]string{"name", "namespace"},
|
||||
)
|
||||
|
||||
return &ApplicationsetMetrics{
|
||||
reconcileHistogram: reconcileHistogram,
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
||||
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
|
||||
)
|
||||
|
||||
var (
|
||||
descAppsetLabels *prometheus.Desc
|
||||
descAppsetDefaultLabels = []string{"namespace", "name"}
|
||||
descAppsetInfo = prometheus.NewDesc(
|
||||
"argocd_appset_info",
|
||||
"Information about applicationset",
|
||||
append(descAppsetDefaultLabels, "resource_update_status"),
|
||||
nil,
|
||||
)
|
||||
|
||||
descAppsetGeneratedApps = prometheus.NewDesc(
|
||||
"argocd_appset_owned_applications",
|
||||
"Number of applications owned by the applicationset",
|
||||
descAppsetDefaultLabels,
|
||||
nil,
|
||||
)
|
||||
)
|
||||
|
||||
type ApplicationsetMetrics struct {
|
||||
reconcileHistogram *prometheus.HistogramVec
|
||||
}
|
||||
|
||||
type appsetCollector struct {
|
||||
lister applisters.ApplicationSetLister
|
||||
// appsClientSet appclientset.Interface
|
||||
labels []string
|
||||
filter func(appset *argoappv1.ApplicationSet) bool
|
||||
}
|
||||
|
||||
func NewApplicationsetMetrics(appsetLister applisters.ApplicationSetLister, appsetLabels []string, appsetFilter func(appset *argoappv1.ApplicationSet) bool) ApplicationsetMetrics {
|
||||
reconcileHistogram := prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "argocd_appset_reconcile",
|
||||
Help: "Application reconciliation performance in seconds.",
|
||||
// Buckets can be set later on after observing median time
|
||||
},
|
||||
descAppsetDefaultLabels,
|
||||
)
|
||||
|
||||
appsetCollector := newAppsetCollector(appsetLister, appsetLabels, appsetFilter)
|
||||
|
||||
// Register collectors and metrics
|
||||
metrics.Registry.MustRegister(reconcileHistogram)
|
||||
metrics.Registry.MustRegister(appsetCollector)
|
||||
|
||||
return ApplicationsetMetrics{
|
||||
reconcileHistogram: reconcileHistogram,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ApplicationsetMetrics) ObserveReconcile(appset *argoappv1.ApplicationSet, duration time.Duration) {
|
||||
m.reconcileHistogram.WithLabelValues(appset.Namespace, appset.Name).Observe(duration.Seconds())
|
||||
}
|
||||
|
||||
func newAppsetCollector(lister applisters.ApplicationSetLister, labels []string, filter func(appset *argoappv1.ApplicationSet) bool) *appsetCollector {
|
||||
descAppsetDefaultLabels = []string{"namespace", "name"}
|
||||
|
||||
if len(labels) > 0 {
|
||||
descAppsetLabels = prometheus.NewDesc(
|
||||
"argocd_appset_labels",
|
||||
"Applicationset labels translated to Prometheus labels",
|
||||
append(descAppsetDefaultLabels, metricsutil.NormalizeLabels("label", labels)...),
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return &appsetCollector{
|
||||
lister: lister,
|
||||
labels: labels,
|
||||
filter: filter,
|
||||
}
|
||||
}
|
||||
|
||||
// Describe implements the prometheus.Collector interface
|
||||
func (c *appsetCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- descAppsetInfo
|
||||
ch <- descAppsetGeneratedApps
|
||||
|
||||
if len(c.labels) > 0 {
|
||||
ch <- descAppsetLabels
|
||||
}
|
||||
}
|
||||
|
||||
// Collect implements the prometheus.Collector interface
|
||||
func (c *appsetCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
appsets, _ := c.lister.List(labels.NewSelector())
|
||||
|
||||
for _, appset := range appsets {
|
||||
if c.filter(appset) {
|
||||
collectAppset(appset, c.labels, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectAppset(appset *argoappv1.ApplicationSet, labelsToCollect []string, ch chan<- prometheus.Metric) {
|
||||
labelValues := make([]string, 0)
|
||||
commonLabelValues := []string{appset.Namespace, appset.Name}
|
||||
|
||||
for _, label := range labelsToCollect {
|
||||
labelValues = append(labelValues, appset.GetLabels()[label])
|
||||
}
|
||||
|
||||
resourceUpdateStatus := "Unknown"
|
||||
|
||||
for _, condition := range appset.Status.Conditions {
|
||||
if condition.Type == argoappv1.ApplicationSetConditionResourcesUpToDate {
|
||||
resourceUpdateStatus = condition.Reason
|
||||
}
|
||||
}
|
||||
|
||||
if len(labelsToCollect) > 0 {
|
||||
ch <- prometheus.MustNewConstMetric(descAppsetLabels, prometheus.GaugeValue, 1, append(commonLabelValues, labelValues...)...)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(descAppsetInfo, prometheus.GaugeValue, 1, appset.Namespace, appset.Name, resourceUpdateStatus)
|
||||
ch <- prometheus.MustNewConstMetric(descAppsetGeneratedApps, prometheus.GaugeValue, float64(len(appset.Status.Resources)), appset.Namespace, appset.Name)
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
fake "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
prometheus "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
applicationsetNamespaces = []string{"argocd", "test-namespace1"}
|
||||
|
||||
filter = func(appset *argoappv1.ApplicationSet) bool {
|
||||
return utils.IsNamespaceAllowed(applicationsetNamespaces, appset.Namespace)
|
||||
}
|
||||
|
||||
collectedLabels = []string{"included/test"}
|
||||
)
|
||||
|
||||
const fakeAppsetList = `
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: test1
|
||||
namespace: argocd
|
||||
labels:
|
||||
included/test: test
|
||||
not-included.label/test: test
|
||||
spec:
|
||||
generators:
|
||||
- git:
|
||||
directories:
|
||||
- path: test/*
|
||||
repoURL: https://github.com/test/test.git
|
||||
revision: HEAD
|
||||
template:
|
||||
metadata:
|
||||
name: '{{.path.basename}}'
|
||||
spec:
|
||||
destination:
|
||||
namespace: '{{.path.basename}}'
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
path: '{{.path.path}}'
|
||||
repoURL: https://github.com/test/test.git
|
||||
targetRevision: HEAD
|
||||
status:
|
||||
resources:
|
||||
- group: argoproj.io
|
||||
health:
|
||||
status: Missing
|
||||
kind: Application
|
||||
name: test-app1
|
||||
namespace: argocd
|
||||
status: OutOfSync
|
||||
version: v1alpha1
|
||||
- group: argoproj.io
|
||||
health:
|
||||
status: Missing
|
||||
kind: Application
|
||||
name: test-app2
|
||||
namespace: argocd
|
||||
status: OutOfSync
|
||||
version: v1alpha1
|
||||
conditions:
|
||||
- lastTransitionTime: "2024-01-01T00:00:00Z"
|
||||
message: Successfully generated parameters for all Applications
|
||||
reason: ApplicationSetUpToDate
|
||||
status: "False"
|
||||
type: ErrorOccurred
|
||||
- lastTransitionTime: "2024-01-01T00:00:00Z"
|
||||
message: Successfully generated parameters for all Applications
|
||||
reason: ParametersGenerated
|
||||
status: "True"
|
||||
type: ParametersGenerated
|
||||
- lastTransitionTime: "2024-01-01T00:00:00Z"
|
||||
message: ApplicationSet up to date
|
||||
reason: ApplicationSetUpToDate
|
||||
status: "True"
|
||||
type: ResourcesUpToDate
|
||||
---
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: test2
|
||||
namespace: argocd
|
||||
labels:
|
||||
not-included.label/test: test
|
||||
spec:
|
||||
generators:
|
||||
- git:
|
||||
directories:
|
||||
- path: test/*
|
||||
repoURL: https://github.com/test/test.git
|
||||
revision: HEAD
|
||||
template:
|
||||
metadata:
|
||||
name: '{{.path.basename}}'
|
||||
spec:
|
||||
destination:
|
||||
namespace: '{{.path.basename}}'
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
path: '{{.path.path}}'
|
||||
repoURL: https://github.com/test/test.git
|
||||
targetRevision: HEAD
|
||||
---
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: should-be-filtered-out
|
||||
namespace: not-allowed
|
||||
spec:
|
||||
generators:
|
||||
- git:
|
||||
directories:
|
||||
- path: test/*
|
||||
repoURL: https://github.com/test/test.git
|
||||
revision: HEAD
|
||||
template:
|
||||
metadata:
|
||||
name: '{{.path.basename}}'
|
||||
spec:
|
||||
destination:
|
||||
namespace: '{{.path.basename}}'
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
path: '{{.path.path}}'
|
||||
repoURL: https://github.com/test/test.git
|
||||
targetRevision: HEAD
|
||||
`
|
||||
|
||||
func newFakeAppsets(fakeAppsetYAML string) []argoappv1.ApplicationSet {
|
||||
var results []argoappv1.ApplicationSet
|
||||
|
||||
appsetRawYamls := strings.Split(fakeAppsetYAML, "---")
|
||||
|
||||
for _, appsetRawYaml := range appsetRawYamls {
|
||||
var appset argoappv1.ApplicationSet
|
||||
err := yaml.Unmarshal([]byte(appsetRawYaml), &appset)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
results = append(results, appset)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func TestApplicationsetCollector(t *testing.T) {
|
||||
appsetList := newFakeAppsets(fakeAppsetList)
|
||||
client := initializeClient(appsetList)
|
||||
metrics.Registry = prometheus.NewRegistry()
|
||||
|
||||
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||
|
||||
metrics.Registry.MustRegister(appsetCollector)
|
||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
require.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
// Test correct appset_info and owned applications
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_info{name="test1",namespace="argocd",resource_update_status="ApplicationSetUpToDate"} 1
|
||||
`)
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_owned_applications{name="test1",namespace="argocd"} 2
|
||||
`)
|
||||
// Test labels collection - should not include labels not included in the list of collected labels and include the ones that do.
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_labels{label_included_test="test",name="test1",namespace="argocd"} 1
|
||||
`)
|
||||
assert.NotContains(t, rr.Body.String(), normalizeLabel("not-included.label/test"))
|
||||
// If collected label is not present on the applicationset the value should be empty
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_labels{label_included_test="",name="test2",namespace="argocd"} 1
|
||||
`)
|
||||
// If ResourcesUpToDate condition is not present on the applicationset the status should be reported as 'Unknown'
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_info{name="test2",namespace="argocd",resource_update_status="Unknown"} 1
|
||||
`)
|
||||
// If there are no resources on the applicationset the owned application gague should return 0
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_owned_applications{name="test2",namespace="argocd"} 0
|
||||
`)
|
||||
// Test that filter is working
|
||||
assert.NotContains(t, rr.Body.String(), `name="should-be-filtered-out"`)
|
||||
}
|
||||
|
||||
func TestObserveReconcile(t *testing.T) {
|
||||
appsetList := newFakeAppsets(fakeAppsetList)
|
||||
client := initializeClient(appsetList)
|
||||
metrics.Registry = prometheus.NewRegistry()
|
||||
|
||||
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
||||
require.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||
appsetMetrics.ObserveReconcile(&appsetList[0], 5*time.Second)
|
||||
handler.ServeHTTP(rr, req)
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_reconcile_sum{name="test1",namespace="argocd"} 5
|
||||
`)
|
||||
// If there are no resources on the applicationset the owned application gague should return 0
|
||||
assert.Contains(t, rr.Body.String(), `
|
||||
argocd_appset_reconcile_count{name="test1",namespace="argocd"} 1
|
||||
`)
|
||||
}
|
||||
|
||||
func initializeClient(appsets []argoappv1.ApplicationSet) ctrlclient.WithWatch {
|
||||
scheme := runtime.NewScheme()
|
||||
err := argoappv1.AddToScheme(scheme)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var clientObjects []ctrlclient.Object
|
||||
|
||||
for _, appset := range appsets {
|
||||
clientObjects = append(clientObjects, appset.DeepCopy())
|
||||
}
|
||||
|
||||
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(clientObjects...).Build()
|
||||
}
|
||||
|
||||
func normalizeLabel(label string) string {
|
||||
return metricsutil.NormalizeLabels("label", []string{label})[0]
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/bradleyfalzon/ghinstallation/v2"
|
||||
"github.com/google/go-github/v66/github"
|
||||
"github.com/google/go-github/v35/github"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/github_app_auth"
|
||||
)
|
||||
@@ -26,7 +26,7 @@ func Client(g github_app_auth.Authentication, url string) (*github.Client, error
|
||||
} else {
|
||||
rt.BaseURL = url
|
||||
httpClient := http.Client{Transport: rt}
|
||||
client, err = github.NewClient(&httpClient).WithEnterpriseURLs(url, url)
|
||||
client, err = github.NewEnterpriseClient(url, url, &httpClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create github enterprise client: %w", err)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ func newClient(baseURL string, options ...ClientOptionFunc) (*Client, error) {
|
||||
}
|
||||
|
||||
func (c *Client) NewRequest(method, path string, body interface{}, options []ClientOptionFunc) (*http.Request, error) {
|
||||
|
||||
// Make sure the given URL end with a slash
|
||||
if !strings.HasSuffix(c.baseURL, "/") {
|
||||
c.baseURL += "/"
|
||||
@@ -134,13 +135,14 @@ func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*htt
|
||||
|
||||
// CheckResponse checks the API response for errors, and returns them if present.
|
||||
func CheckResponse(resp *http.Response) error {
|
||||
if c := resp.StatusCode; http.StatusOK <= c && c < http.StatusMultipleChoices {
|
||||
|
||||
if c := resp.StatusCode; 200 <= c && c <= 299 {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("API error with status code %d: %w", resp.StatusCode, err)
|
||||
return fmt.Errorf("API error with status code %d: %v", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
var raw map[string]interface{}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
@@ -18,14 +17,17 @@ func TestClient(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, err := w.Write([]byte("Hello, World!"))
|
||||
if err != nil {
|
||||
assert.NoError(t, fmt.Errorf("Error Write %w", err))
|
||||
assert.NoError(t, fmt.Errorf("Error Write %v", err))
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
var clientOptionFns []ClientOptionFunc
|
||||
_, err := NewClient(server.URL, clientOptionFns...)
|
||||
require.NoError(t, err, "Failed to create client")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDo(t *testing.T) {
|
||||
@@ -60,7 +62,7 @@ func TestClientDo(t *testing.T) {
|
||||
"key3": 123
|
||||
}]`))
|
||||
if err != nil {
|
||||
assert.NoError(t, fmt.Errorf("Error Write %w", err))
|
||||
assert.NoError(t, fmt.Errorf("Error Write %v", err))
|
||||
}
|
||||
})),
|
||||
clientOptionFns: nil,
|
||||
@@ -76,7 +78,7 @@ func TestClientDo(t *testing.T) {
|
||||
"key3": float64(123),
|
||||
},
|
||||
},
|
||||
expectedCode: http.StatusOK,
|
||||
expectedCode: 200,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
@@ -103,12 +105,12 @@ func TestClientDo(t *testing.T) {
|
||||
"key3": 123
|
||||
}]`))
|
||||
if err != nil {
|
||||
assert.NoError(t, fmt.Errorf("Error Write %w", err))
|
||||
assert.NoError(t, fmt.Errorf("Error Write %v", err))
|
||||
}
|
||||
})),
|
||||
clientOptionFns: nil,
|
||||
expected: []map[string]interface{}(nil),
|
||||
expectedCode: http.StatusUnauthorized,
|
||||
expectedCode: 401,
|
||||
expectedError: fmt.Errorf("API error with status code 401: "),
|
||||
},
|
||||
} {
|
||||
@@ -117,10 +119,16 @@ func TestClientDo(t *testing.T) {
|
||||
defer cc.fakeServer.Close()
|
||||
|
||||
client, err := NewClient(cc.fakeServer.URL, cc.clientOptionFns...)
|
||||
require.NoError(t, err, "NewClient returned unexpected error")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("NewClient returned unexpected error: %v", err)
|
||||
}
|
||||
|
||||
req, err := client.NewRequest("POST", "", cc.params, nil)
|
||||
require.NoError(t, err, "NewRequest returned unexpected error")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest returned unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var data []map[string]interface{}
|
||||
|
||||
@@ -129,8 +137,8 @@ func TestClientDo(t *testing.T) {
|
||||
if cc.expectedError != nil {
|
||||
assert.EqualError(t, err, cc.expectedError.Error())
|
||||
} else {
|
||||
assert.Equal(t, cc.expectedCode, resp.StatusCode)
|
||||
assert.Equal(t, cc.expected, data)
|
||||
assert.Equal(t, resp.StatusCode, cc.expectedCode)
|
||||
assert.Equal(t, data, cc.expected)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
@@ -144,5 +152,12 @@ func TestCheckResponse(t *testing.T) {
|
||||
}
|
||||
|
||||
err := CheckResponse(resp)
|
||||
require.EqualError(t, err, "API error with status code 400: invalid_request")
|
||||
if err == nil {
|
||||
t.Error("Expected an error, got nil")
|
||||
}
|
||||
|
||||
expected := "API error with status code 400: invalid_request"
|
||||
if err.Error() != expected {
|
||||
t.Errorf("Expected error '%s', got '%s'", expected, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.25.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -13,29 +13,25 @@ type Repos struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache, verifyCommit
|
||||
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool, verifyCommit bool) ([]string, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetDirectories")
|
||||
}
|
||||
// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache
|
||||
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, noRevisionCache)
|
||||
|
||||
var r0 []string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool, bool) ([]string, error)); ok {
|
||||
return rf(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) ([]string, error)); ok {
|
||||
return rf(ctx, repoURL, revision, noRevisionCache)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool, bool) []string); ok {
|
||||
r0 = rf(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool) []string); ok {
|
||||
r0 = rf(ctx, repoURL, revision, noRevisionCache)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, bool, bool) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, bool) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, noRevisionCache)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -43,29 +39,25 @@ func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision st
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit
|
||||
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetFiles")
|
||||
}
|
||||
// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache
|
||||
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) {
|
||||
ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
|
||||
var r0 map[string][]byte
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) (map[string][]byte, error)); ok {
|
||||
return rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) (map[string][]byte, error)); ok {
|
||||
return rf(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) map[string][]byte); ok {
|
||||
r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool) map[string][]byte); ok {
|
||||
r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string][]byte)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool, bool) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool) error); ok {
|
||||
r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@@ -73,12 +65,13 @@ func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewRepos(t interface {
|
||||
type mockConstructorTestingTNewRepos interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Repos {
|
||||
}
|
||||
|
||||
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewRepos(t mockConstructorTestingTNewRepos) *Repos {
|
||||
mock := &Repos{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
||||
57
applicationset/services/mocks/RepositoryDB.go
Normal file
57
applicationset/services/mocks/RepositoryDB.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Code generated by mockery v2.21.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// RepositoryDB is an autogenerated mock type for the RepositoryDB type
|
||||
type RepositoryDB struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetRepository provides a mock function with given fields: ctx, url
|
||||
func (_m *RepositoryDB) GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error) {
|
||||
ret := _m.Called(ctx, url)
|
||||
|
||||
var r0 *v1alpha1.Repository
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (*v1alpha1.Repository, error)); ok {
|
||||
return rf(ctx, url)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) *v1alpha1.Repository); ok {
|
||||
r0 = rf(ctx, url)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*v1alpha1.Repository)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, url)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewRepositoryDB interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewRepositoryDB creates a new instance of RepositoryDB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewRepositoryDB(t mockConstructorTestingTNewRepositoryDB) *RepositoryDB {
|
||||
mock := &RepositoryDB{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func NewPluginService(ctx context.Context, appSetName string, baseURL string, to
|
||||
|
||||
client, err := internalhttp.NewClient(baseURL, clientOptionFns...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating plugin client: %w", err)
|
||||
return nil, fmt.Errorf("error creating plugin client: %v", err)
|
||||
}
|
||||
|
||||
return &Service{
|
||||
@@ -56,15 +56,17 @@ func NewPluginService(ctx context.Context, appSetName string, baseURL string, to
|
||||
|
||||
func (p *Service) List(ctx context.Context, parameters v1alpha1.PluginParameters) (*ServiceResponse, error) {
|
||||
req, err := p.client.NewRequest(http.MethodPost, "api/v1/getparams.execute", ServiceRequest{ApplicationSetName: p.appSetName, Input: v1alpha1.PluginInput{Parameters: parameters}}, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("NewRequest returned unexpected error: %w", err)
|
||||
return nil, fmt.Errorf("NewRequest returned unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var data ServiceResponse
|
||||
|
||||
_, err = p.client.Do(ctx, req, &data)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error get api '%s': %w", p.appSetName, err)
|
||||
return nil, fmt.Errorf("error get api '%s': %v", p.appSetName, err)
|
||||
}
|
||||
|
||||
return &data, err
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
@@ -24,21 +23,30 @@ func TestPlugin(t *testing.T) {
|
||||
return
|
||||
}
|
||||
_, err := w.Write([]byte(expectedJSON))
|
||||
|
||||
if err != nil {
|
||||
assert.NoError(t, fmt.Errorf("Error Write %w", err))
|
||||
assert.NoError(t, fmt.Errorf("Error Write %v", err))
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(handler)
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPluginService(context.Background(), "plugin-test", ts.URL, token, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
data, err := client.List(context.Background(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var expectedData ServiceResponse
|
||||
err = json.Unmarshal([]byte(expectedJSON), &expectedData)
|
||||
require.NoError(t, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, &expectedData, data)
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ type AzureDevOpsService struct {
|
||||
labels []string
|
||||
}
|
||||
|
||||
var (
|
||||
_ PullRequestService = (*AzureDevOpsService)(nil)
|
||||
_ AzureDevOpsClientFactory = &devopsFactoryImpl{}
|
||||
)
|
||||
var _ PullRequestService = (*AzureDevOpsService)(nil)
|
||||
var _ AzureDevOpsClientFactory = &devopsFactoryImpl{}
|
||||
|
||||
func NewAzureDevOpsService(ctx context.Context, token, url, organization, project, repo string, labels []string) (PullRequestService, error) {
|
||||
organizationUrl := buildURL(url, organization)
|
||||
@@ -82,7 +80,6 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
pr.Repository.Name == nil ||
|
||||
pr.PullRequestId == nil ||
|
||||
pr.SourceRefName == nil ||
|
||||
pr.TargetRefName == nil ||
|
||||
pr.LastMergeSourceCommit == nil ||
|
||||
pr.LastMergeSourceCommit.CommitId == nil {
|
||||
continue
|
||||
@@ -95,13 +92,10 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
|
||||
if *pr.Repository.Name == a.repo {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: *pr.PullRequestId,
|
||||
Title: *pr.Title,
|
||||
Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1),
|
||||
TargetBranch: strings.Replace(*pr.TargetRefName, "refs/heads/", "", 1),
|
||||
HeadSHA: *pr.LastMergeSourceCommit.CommitId,
|
||||
Labels: azureDevOpsLabels,
|
||||
Author: strings.Split(*pr.CreatedBy.UniqueName, "@")[0], // Get the part before the @ in the email-address
|
||||
Number: *pr.PullRequestId,
|
||||
Branch: strings.Replace(*pr.SourceRefName, "refs/heads/", "", 1),
|
||||
HeadSHA: *pr.LastMergeSourceCommit.CommitId,
|
||||
Labels: azureDevOpsLabels,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,10 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/webapi"
|
||||
|
||||
"github.com/microsoft/azure-devops-go-api/azuredevops/core"
|
||||
git "github.com/microsoft/azure-devops-go-api/azuredevops/git"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks"
|
||||
)
|
||||
@@ -31,10 +28,6 @@ func createLabelsPtr(x []core.WebApiTagDefinition) *[]core.WebApiTagDefinition {
|
||||
return &x
|
||||
}
|
||||
|
||||
func createUniqueNamePtr(x string) *string {
|
||||
return &x
|
||||
}
|
||||
|
||||
type AzureClientFactoryMock struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
@@ -62,17 +55,13 @@ func TestListPullRequest(t *testing.T) {
|
||||
teamProject := "myorg_project"
|
||||
repoName := "myorg_project_repo"
|
||||
pr_id := 123
|
||||
pr_title := "feat(123)"
|
||||
pr_head_sha := "cd4973d9d14a08ffe6b641a89a68891d6aac8056"
|
||||
ctx := context.Background()
|
||||
uniqueName := "testName"
|
||||
|
||||
pullRequestMock := []git.GitPullRequest{
|
||||
{
|
||||
PullRequestId: createIntPtr(pr_id),
|
||||
Title: createStringPtr(pr_title),
|
||||
SourceRefName: createStringPtr("refs/heads/feature-branch"),
|
||||
TargetRefName: createStringPtr("refs/heads/main"),
|
||||
LastMergeSourceCommit: &git.GitCommitRef{
|
||||
CommitId: createStringPtr(pr_head_sha),
|
||||
},
|
||||
@@ -80,9 +69,6 @@ func TestListPullRequest(t *testing.T) {
|
||||
Repository: &git.GitRepository{
|
||||
Name: createStringPtr(repoName),
|
||||
},
|
||||
CreatedBy: &webapi.IdentityRef{
|
||||
UniqueName: createUniqueNamePtr(uniqueName + "@example.com"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -104,14 +90,11 @@ func TestListPullRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
list, err := provider.List(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, list, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(list))
|
||||
assert.Equal(t, "feature-branch", list[0].Branch)
|
||||
assert.Equal(t, "main", list[0].TargetBranch)
|
||||
assert.Equal(t, pr_head_sha, list[0].HeadSHA)
|
||||
assert.Equal(t, "feat(123)", list[0].Title)
|
||||
assert.Equal(t, pr_id, list[0].Number)
|
||||
assert.Equal(t, uniqueName, list[0].Author)
|
||||
}
|
||||
|
||||
func TestConvertLabes(t *testing.T) {
|
||||
@@ -223,16 +206,16 @@ func TestBuildURL(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Provided custom URL and organization",
|
||||
url: "https://azuredevops.example.com/",
|
||||
url: "https://azuredevops.mycompany.com/",
|
||||
organization: "myorganization",
|
||||
expected: "https://azuredevops.example.com/myorganization",
|
||||
expected: "https://azuredevops.mycompany.com/myorganization",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := buildURL(tc.url, tc.organization)
|
||||
assert.Equal(t, tc.expected, result)
|
||||
assert.Equal(t, result, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@ type BitbucketCloudService struct {
|
||||
|
||||
type BitbucketCloudPullRequest struct {
|
||||
ID int `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Source BitbucketCloudPullRequestSource `json:"source"`
|
||||
Author BitbucketCloudPullRequestAuthor `json:"author"`
|
||||
}
|
||||
|
||||
type BitbucketCloudPullRequestSource struct {
|
||||
@@ -35,11 +33,6 @@ type BitbucketCloudPullRequestSourceCommit struct {
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
// Also have display_name and uuid, but don't plan to use them.
|
||||
type BitbucketCloudPullRequestAuthor struct {
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
type PullRequestResponse struct {
|
||||
Page int32 `json:"page"`
|
||||
Size int32 `json:"size"`
|
||||
@@ -67,7 +60,7 @@ func parseUrl(uri string) (*url.URL, error) {
|
||||
func NewBitbucketCloudServiceBasicAuth(baseUrl, username, password, owner, repositorySlug string) (PullRequestService, error) {
|
||||
url, err := parseUrl(baseUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseUrl, owner, repositorySlug, err)
|
||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %v", baseUrl, owner, repositorySlug, err)
|
||||
}
|
||||
|
||||
bitbucketClient := bitbucket.NewBasicAuth(username, password)
|
||||
@@ -83,7 +76,7 @@ func NewBitbucketCloudServiceBasicAuth(baseUrl, username, password, owner, repos
|
||||
func NewBitbucketCloudServiceBearerToken(baseUrl, bearerToken, owner, repositorySlug string) (PullRequestService, error) {
|
||||
url, err := parseUrl(baseUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseUrl, owner, repositorySlug, err)
|
||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %v", baseUrl, owner, repositorySlug, err)
|
||||
}
|
||||
|
||||
bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken)
|
||||
@@ -109,7 +102,7 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
||||
|
||||
response, err := b.client.Repositories.PullRequests.Gets(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err)
|
||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %v", b.owner, b.repositorySlug, err)
|
||||
}
|
||||
|
||||
resp, ok := response.(map[string]interface{})
|
||||
@@ -124,22 +117,20 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
||||
|
||||
jsonStr, err := json.Marshal(repoArray)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error marshalling response body to json: %w", err)
|
||||
return nil, fmt.Errorf("error marshalling response body to json: %v", err)
|
||||
}
|
||||
|
||||
var pulls []BitbucketCloudPullRequest
|
||||
if err := json.Unmarshal(jsonStr, &pulls); err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
|
||||
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %v", err)
|
||||
}
|
||||
|
||||
pullRequests := []*PullRequest{}
|
||||
for _, pull := range pulls {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: pull.ID,
|
||||
Title: pull.Title,
|
||||
Branch: pull.Source.Branch.Name,
|
||||
HeadSHA: pull.Source.Commit.Hash,
|
||||
Author: pull.Author.Nickname,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,13 +9,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
t.Helper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var err error
|
||||
@@ -28,7 +26,6 @@ func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request)
|
||||
"values": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "feat(foo-bar)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature/foo-bar"
|
||||
@@ -37,9 +34,6 @@ func defaultHandlerCloud(t *testing.T) func(http.ResponseWriter, *http.Request)
|
||||
"type": "commit",
|
||||
"hash": "1a8dd249c04a"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -57,26 +51,26 @@ func TestParseUrlEmptyUrl(t *testing.T) {
|
||||
url, err := parseUrl("")
|
||||
bitbucketUrl, _ := url.Parse("https://api.bitbucket.org/2.0")
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, bitbucketUrl, url)
|
||||
}
|
||||
|
||||
func TestInvalidBaseUrlBasicAuthCloud(t *testing.T) {
|
||||
_, err := NewBitbucketCloudServiceBasicAuth("http:// example.org", "user", "password", "OWNER", "REPO")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestInvalidBaseUrlBearerTokenCloud(t *testing.T) {
|
||||
_, err := NewBitbucketCloudServiceBearerToken("http:// example.org", "TOKEN", "OWNER", "REPO")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestInvalidBaseUrlNoAuthCloud(t *testing.T) {
|
||||
_, err := NewBitbucketCloudServiceNoAuth("http:// example.org", "OWNER", "REPO")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestListPullRequestBearerTokenCloud(t *testing.T) {
|
||||
@@ -86,15 +80,13 @@ func TestListPullRequestBearerTokenCloud(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketCloudServiceBearerToken(ts.URL, "TOKEN", "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, 101, pullRequests[0].Number)
|
||||
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
||||
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||
assert.Equal(t, "testName", pullRequests[0].Author)
|
||||
}
|
||||
|
||||
func TestListPullRequestNoAuthCloud(t *testing.T) {
|
||||
@@ -104,15 +96,13 @@ func TestListPullRequestNoAuthCloud(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, 101, pullRequests[0].Number)
|
||||
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
||||
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||
assert.Equal(t, "testName", pullRequests[0].Author)
|
||||
}
|
||||
|
||||
func TestListPullRequestBasicAuthCloud(t *testing.T) {
|
||||
@@ -122,15 +112,13 @@ func TestListPullRequestBasicAuthCloud(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketCloudServiceBasicAuth(ts.URL, "user", "password", "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, 101, pullRequests[0].Number)
|
||||
assert.Equal(t, "feat(foo-bar)", pullRequests[0].Title)
|
||||
assert.Equal(t, "feature/foo-bar", pullRequests[0].Branch)
|
||||
assert.Equal(t, "1a8dd249c04a", pullRequests[0].HeadSHA)
|
||||
assert.Equal(t, "testName", pullRequests[0].Author)
|
||||
}
|
||||
|
||||
func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
@@ -147,7 +135,6 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "feat(101)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature-101"
|
||||
@@ -156,14 +143,10 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
"type": "commit",
|
||||
"hash": "1a8dd249c04a"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"title": "feat(102)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature-102"
|
||||
@@ -172,9 +155,6 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
"type": "commit",
|
||||
"hash": "4cf807e67a6d"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -188,7 +168,6 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 103,
|
||||
"title": "feat(103)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature-103"
|
||||
@@ -197,9 +176,6 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
"type": "commit",
|
||||
"hash": "6344d9623e3b"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -213,41 +189,35 @@ func TestListPullRequestPaginationCloud(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(pullRequests))
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 101,
|
||||
Title: "feat(101)",
|
||||
Branch: "feature-101",
|
||||
HeadSHA: "1a8dd249c04a",
|
||||
Author: "testName",
|
||||
}, *pullRequests[0])
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 102,
|
||||
Title: "feat(102)",
|
||||
Branch: "feature-102",
|
||||
HeadSHA: "4cf807e67a6d",
|
||||
Author: "testName",
|
||||
}, *pullRequests[1])
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 103,
|
||||
Title: "feat(103)",
|
||||
Branch: "feature-103",
|
||||
HeadSHA: "6344d9623e3b",
|
||||
Author: "testName",
|
||||
}, *pullRequests[2])
|
||||
}
|
||||
|
||||
func TestListResponseErrorCloud(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.WriteHeader(500)
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
_, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestListResponseMalformedCloud(t *testing.T) {
|
||||
@@ -271,7 +241,7 @@ func TestListResponseMalformedCloud(t *testing.T) {
|
||||
defer ts.Close()
|
||||
svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
_, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestListResponseMalformedValuesCloud(t *testing.T) {
|
||||
@@ -295,7 +265,7 @@ func TestListResponseMalformedValuesCloud(t *testing.T) {
|
||||
defer ts.Close()
|
||||
svc, _ := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
_, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestListResponseEmptyCloud(t *testing.T) {
|
||||
@@ -318,9 +288,9 @@ func TestListResponseEmptyCloud(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, pullRequests)
|
||||
}
|
||||
|
||||
@@ -338,7 +308,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "feat(101)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature-101"
|
||||
@@ -347,14 +316,10 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
"type": "commit",
|
||||
"hash": "1a8dd249c04a"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 200,
|
||||
"title": "feat(200)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature-200"
|
||||
@@ -363,9 +328,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
"type": "commit",
|
||||
"hash": "4cf807e67a6d"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -379,7 +341,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 102,
|
||||
"title": "feat(102)",
|
||||
"source": {
|
||||
"branch": {
|
||||
"name": "feature-102"
|
||||
@@ -388,9 +349,6 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
"type": "commit",
|
||||
"hash": "6344d9623e3b"
|
||||
}
|
||||
},
|
||||
"author": {
|
||||
"nickname": "testName"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -405,54 +363,48 @@ func TestListPullRequestBranchMatchCloud(t *testing.T) {
|
||||
defer ts.Close()
|
||||
regexp := `feature-1[\d]{2}`
|
||||
svc, err := NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: ®exp,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(pullRequests))
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 101,
|
||||
Title: "feat(101)",
|
||||
Branch: "feature-101",
|
||||
HeadSHA: "1a8dd249c04a",
|
||||
Author: "testName",
|
||||
}, *pullRequests[0])
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 102,
|
||||
Title: "feat(102)",
|
||||
Branch: "feature-102",
|
||||
HeadSHA: "6344d9623e3b",
|
||||
Author: "testName",
|
||||
}, *pullRequests[1])
|
||||
|
||||
regexp = `.*2$`
|
||||
svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: ®exp,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 102,
|
||||
Title: "feat(102)",
|
||||
Branch: "feature-102",
|
||||
HeadSHA: "6344d9623e3b",
|
||||
Author: "testName",
|
||||
}, *pullRequests[0])
|
||||
|
||||
regexp = `[\d{2}`
|
||||
svc, err = NewBitbucketCloudServiceNoAuth(ts.URL, "OWNER", "REPO")
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
_, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: ®exp,
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package pull_request
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
bitbucketv1 "github.com/gfleury/go-bitbucket-v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type BitbucketService struct {
|
||||
@@ -21,7 +19,7 @@ type BitbucketService struct {
|
||||
|
||||
var _ PullRequestService = (*BitbucketService)(nil)
|
||||
|
||||
func NewBitbucketServiceBasicAuth(ctx context.Context, username, password, url, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
||||
func NewBitbucketServiceBasicAuth(ctx context.Context, username, password, url, projectKey, repositorySlug string) (PullRequestService, error) {
|
||||
bitbucketConfig := bitbucketv1.NewConfiguration(url)
|
||||
// Avoid the XSRF check
|
||||
bitbucketConfig.AddDefaultHeader("x-atlassian-token", "no-check")
|
||||
@@ -31,29 +29,15 @@ func NewBitbucketServiceBasicAuth(ctx context.Context, username, password, url,
|
||||
UserName: username,
|
||||
Password: password,
|
||||
})
|
||||
return newBitbucketService(ctx, bitbucketConfig, projectKey, repositorySlug, scmRootCAPath, insecure, caCerts)
|
||||
return newBitbucketService(ctx, bitbucketConfig, projectKey, repositorySlug)
|
||||
}
|
||||
|
||||
func NewBitbucketServiceBearerToken(ctx context.Context, bearerToken, url, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
||||
bitbucketConfig := bitbucketv1.NewConfiguration(url)
|
||||
// Avoid the XSRF check
|
||||
bitbucketConfig.AddDefaultHeader("x-atlassian-token", "no-check")
|
||||
bitbucketConfig.AddDefaultHeader("x-requested-with", "XMLHttpRequest")
|
||||
|
||||
ctx = context.WithValue(ctx, bitbucketv1.ContextAccessToken, bearerToken)
|
||||
return newBitbucketService(ctx, bitbucketConfig, projectKey, repositorySlug, scmRootCAPath, insecure, caCerts)
|
||||
func NewBitbucketServiceNoAuth(ctx context.Context, url, projectKey, repositorySlug string) (PullRequestService, error) {
|
||||
return newBitbucketService(ctx, bitbucketv1.NewConfiguration(url), projectKey, repositorySlug)
|
||||
}
|
||||
|
||||
func NewBitbucketServiceNoAuth(ctx context.Context, url, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
||||
return newBitbucketService(ctx, bitbucketv1.NewConfiguration(url), projectKey, repositorySlug, scmRootCAPath, insecure, caCerts)
|
||||
}
|
||||
|
||||
func newBitbucketService(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey, repositorySlug string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
||||
func newBitbucketService(ctx context.Context, bitbucketConfig *bitbucketv1.Configuration, projectKey, repositorySlug string) (PullRequestService, error) {
|
||||
bitbucketConfig.BasePath = utils.NormalizeBitbucketBasePath(bitbucketConfig.BasePath)
|
||||
tlsConfig := utils.GetTlsConfig(scmRootCAPath, insecure, caCerts)
|
||||
bitbucketConfig.HTTPClient = &http.Client{Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}}
|
||||
bitbucketClient := bitbucketv1.NewAPIClient(ctx, bitbucketConfig)
|
||||
|
||||
return &BitbucketService{
|
||||
@@ -72,23 +56,21 @@ func (b *BitbucketService) List(_ context.Context) ([]*PullRequest, error) {
|
||||
for {
|
||||
response, err := b.client.DefaultApi.GetPullRequestsPage(b.projectKey, b.repositorySlug, paged)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.projectKey, b.repositorySlug, err)
|
||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %v", b.projectKey, b.repositorySlug, err)
|
||||
}
|
||||
pulls, err := bitbucketv1.GetPullRequestsResponse(response)
|
||||
if err != nil {
|
||||
log.Errorf("error parsing pull request response '%v'", response.Values)
|
||||
return nil, fmt.Errorf("error parsing pull request response for %s/%s: %w", b.projectKey, b.repositorySlug, err)
|
||||
return nil, fmt.Errorf("error parsing pull request response for %s/%s: %v", b.projectKey, b.repositorySlug, err)
|
||||
}
|
||||
|
||||
for _, pull := range pulls {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: pull.ID,
|
||||
Title: pull.Title,
|
||||
Branch: pull.FromRef.DisplayID, // ID: refs/heads/main DisplayID: main
|
||||
TargetBranch: pull.ToRef.DisplayID,
|
||||
HeadSHA: pull.FromRef.LatestCommit, // This is not defined in the official docs, but works in practice
|
||||
Labels: []string{}, // Not supported by library
|
||||
Author: pull.Author.User.Name,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,16 @@ package pull_request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
t.Helper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var err error
|
||||
@@ -29,7 +24,6 @@ func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
"values": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "feat(ABC) : 123",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "master",
|
||||
@@ -39,11 +33,6 @@ func defaultHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
"id": "refs/heads/feature-ABC-123",
|
||||
"displayId": "feature-ABC-123",
|
||||
"latestCommit": "cb3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -64,17 +53,15 @@ func TestListPullRequestNoAuth(t *testing.T) {
|
||||
defaultHandler(t)(w, r)
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, 101, pullRequests[0].Number)
|
||||
assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title)
|
||||
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
||||
assert.Equal(t, "master", pullRequests[0].TargetBranch)
|
||||
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||
assert.Equal(t, "testName", pullRequests[0].Author)
|
||||
}
|
||||
|
||||
func TestListPullRequestPagination(t *testing.T) {
|
||||
@@ -90,7 +77,6 @@ func TestListPullRequestPagination(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "feat(101)",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "master",
|
||||
@@ -100,16 +86,10 @@ func TestListPullRequestPagination(t *testing.T) {
|
||||
"id": "refs/heads/feature-101",
|
||||
"displayId": "feature-101",
|
||||
"latestCommit": "ab3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"title": "feat(102)",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "branch",
|
||||
@@ -119,11 +99,6 @@ func TestListPullRequestPagination(t *testing.T) {
|
||||
"id": "refs/heads/feature-102",
|
||||
"displayId": "feature-102",
|
||||
"latestCommit": "bb3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -137,7 +112,6 @@ func TestListPullRequestPagination(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 200,
|
||||
"title": "feat(200)",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "master",
|
||||
@@ -147,11 +121,6 @@ func TestListPullRequestPagination(t *testing.T) {
|
||||
"id": "refs/heads/feature-200",
|
||||
"displayId": "feature-200",
|
||||
"latestCommit": "cb3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -165,37 +134,31 @@ func TestListPullRequestPagination(t *testing.T) {
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(pullRequests))
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 101,
|
||||
Title: "feat(101)",
|
||||
Branch: "feature-101",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992",
|
||||
Labels: []string{},
|
||||
Author: "testName",
|
||||
}, *pullRequests[0])
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 102,
|
||||
Title: "feat(102)",
|
||||
Branch: "feature-102",
|
||||
TargetBranch: "branch",
|
||||
HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992",
|
||||
Labels: []string{},
|
||||
Author: "testName",
|
||||
}, *pullRequests[1])
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 200,
|
||||
Title: "feat(200)",
|
||||
Branch: "feature-200",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "cb3cf2e4d1517c83e720d2585b9402dbef71f992",
|
||||
Labels: []string{},
|
||||
Author: "testName",
|
||||
}, *pullRequests[2])
|
||||
}
|
||||
|
||||
@@ -207,109 +170,24 @@ func TestListPullRequestBasicAuth(t *testing.T) {
|
||||
defaultHandler(t)(w, r)
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketServiceBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewBitbucketServiceBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, 101, pullRequests[0].Number)
|
||||
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
||||
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||
}
|
||||
|
||||
func TestListPullRequestBearerAuth(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "Bearer tolkien", r.Header.Get("Authorization"))
|
||||
assert.Equal(t, "no-check", r.Header.Get("X-Atlassian-Token"))
|
||||
defaultHandler(t)(w, r)
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketServiceBearerToken(context.Background(), "tolkien", ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.Equal(t, 101, pullRequests[0].Number)
|
||||
assert.Equal(t, "feat(ABC) : 123", pullRequests[0].Title)
|
||||
assert.Equal(t, "feature-ABC-123", pullRequests[0].Branch)
|
||||
assert.Equal(t, "cb3cf2e4d1517c83e720d2585b9402dbef71f992", pullRequests[0].HeadSHA)
|
||||
}
|
||||
|
||||
func TestListPullRequestTLS(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tlsInsecure bool
|
||||
passCerts bool
|
||||
requireErr bool
|
||||
}{
|
||||
{
|
||||
name: "TLS Insecure: true, No Certs",
|
||||
tlsInsecure: true,
|
||||
passCerts: false,
|
||||
requireErr: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Insecure: true, With Certs",
|
||||
tlsInsecure: true,
|
||||
passCerts: true,
|
||||
requireErr: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Insecure: false, With Certs",
|
||||
tlsInsecure: false,
|
||||
passCerts: true,
|
||||
requireErr: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Insecure: false, No Certs",
|
||||
tlsInsecure: false,
|
||||
passCerts: false,
|
||||
requireErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var certs []byte
|
||||
if test.passCerts == true {
|
||||
for _, cert := range ts.TLS.Certificates {
|
||||
for _, c := range cert.Certificate {
|
||||
parsedCert, err := x509.ParseCertificate(c)
|
||||
require.NoError(t, err, "Failed to parse certificate")
|
||||
certs = append(certs, pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: parsedCert.Raw,
|
||||
})...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svc, err := NewBitbucketServiceBasicAuth(context.Background(), "user", "password", ts.URL, "PROJECT", "REPO", "", test.tlsInsecure, certs)
|
||||
require.NoError(t, err)
|
||||
_, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
if test.requireErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListResponseError(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
_, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestListResponseMalformed(t *testing.T) {
|
||||
@@ -332,9 +210,9 @@ func TestListResponseMalformed(t *testing.T) {
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
svc, _ := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
_, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestListResponseEmpty(t *testing.T) {
|
||||
@@ -357,10 +235,10 @@ func TestListResponseEmpty(t *testing.T) {
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{})
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, pullRequests)
|
||||
}
|
||||
|
||||
@@ -377,7 +255,6 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 101,
|
||||
"title": "feat(101)",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "master",
|
||||
@@ -387,16 +264,10 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
"id": "refs/heads/feature-101",
|
||||
"displayId": "feature-101",
|
||||
"latestCommit": "ab3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"title": "feat(102)",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "branch",
|
||||
@@ -406,11 +277,6 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
"id": "refs/heads/feature-102",
|
||||
"displayId": "feature-102",
|
||||
"latestCommit": "bb3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -424,7 +290,6 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
"values": [
|
||||
{
|
||||
"id": 200,
|
||||
"title": "feat(200)",
|
||||
"toRef": {
|
||||
"latestCommit": "5b766e3564a3453808f3cd3dd3f2e5fad8ef0e7a",
|
||||
"displayId": "master",
|
||||
@@ -434,11 +299,6 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
"id": "refs/heads/feature-200",
|
||||
"displayId": "feature-200",
|
||||
"latestCommit": "cb3cf2e4d1517c83e720d2585b9402dbef71f992"
|
||||
},
|
||||
"author": {
|
||||
"user": {
|
||||
"name": "testName"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -453,61 +313,55 @@ func TestListPullRequestBranchMatch(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
regexp := `feature-1[\d]{2}`
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err := ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: ®exp,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(pullRequests))
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 101,
|
||||
Title: "feat(101)",
|
||||
Branch: "feature-101",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "ab3cf2e4d1517c83e720d2585b9402dbef71f992",
|
||||
Labels: []string{},
|
||||
Author: "testName",
|
||||
}, *pullRequests[0])
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 102,
|
||||
Title: "feat(102)",
|
||||
Branch: "feature-102",
|
||||
TargetBranch: "branch",
|
||||
HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992",
|
||||
Labels: []string{},
|
||||
Author: "testName",
|
||||
}, *pullRequests[1])
|
||||
|
||||
regexp = `.*2$`
|
||||
svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
pullRequests, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: ®exp,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(pullRequests))
|
||||
assert.Equal(t, PullRequest{
|
||||
Number: 102,
|
||||
Title: "feat(102)",
|
||||
Branch: "feature-102",
|
||||
TargetBranch: "branch",
|
||||
HeadSHA: "bb3cf2e4d1517c83e720d2585b9402dbef71f992",
|
||||
Labels: []string{},
|
||||
Author: "testName",
|
||||
}, *pullRequests[0])
|
||||
|
||||
regexp = `[\d{2}`
|
||||
svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err = NewBitbucketServiceNoAuth(context.Background(), ts.URL, "PROJECT", "REPO")
|
||||
assert.NoError(t, err)
|
||||
_, err = ListPullRequests(context.Background(), svc, []v1alpha1.PullRequestGeneratorFilter{
|
||||
{
|
||||
BranchMatch: ®exp,
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -57,12 +57,10 @@ func (g *GiteaService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
for _, pr := range prs {
|
||||
list = append(list, &PullRequest{
|
||||
Number: int(pr.Index),
|
||||
Title: pr.Title,
|
||||
Branch: pr.Head.Ref,
|
||||
TargetBranch: pr.Base.Ref,
|
||||
HeadSHA: pr.Head.Sha,
|
||||
Labels: getGiteaPRLabelNames(pr.Labels),
|
||||
Author: pr.Poster.UserName,
|
||||
})
|
||||
}
|
||||
return list, nil
|
||||
|
||||
@@ -10,11 +10,9 @@ import (
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func giteaMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
|
||||
t.Helper()
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Println(r.RequestURI)
|
||||
@@ -252,16 +250,14 @@ func TestGiteaList(t *testing.T) {
|
||||
giteaMockHandler(t)(w, r)
|
||||
}))
|
||||
host, err := NewGiteaService(context.Background(), "", ts.URL, "test-argocd", "pr-test", false)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, err)
|
||||
prs, err := host.List(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, prs, 1)
|
||||
assert.Equal(t, 1, prs[0].Number)
|
||||
assert.Equal(t, "add an empty file", prs[0].Title)
|
||||
assert.Equal(t, "test", prs[0].Branch)
|
||||
assert.Equal(t, "main", prs[0].TargetBranch)
|
||||
assert.Equal(t, "7bbaf62d92ddfafd9cc8b340c619abaec32bc09f", prs[0].HeadSHA)
|
||||
assert.Equal(t, "graytshirt", prs[0].Author)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, len(prs), 1)
|
||||
assert.Equal(t, prs[0].Number, 1)
|
||||
assert.Equal(t, prs[0].Branch, "test")
|
||||
assert.Equal(t, prs[0].TargetBranch, "main")
|
||||
assert.Equal(t, prs[0].HeadSHA, "7bbaf62d92ddfafd9cc8b340c619abaec32bc09f")
|
||||
}
|
||||
|
||||
func TestGetGiteaPRLabelNames(t *testing.T) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/google/go-github/v66/github"
|
||||
"github.com/google/go-github/v35/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ func NewGithubService(ctx context.Context, token, url, owner, repo string, label
|
||||
client = github.NewClient(httpClient)
|
||||
} else {
|
||||
var err error
|
||||
client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url)
|
||||
client, err = github.NewEnterpriseClient(url, url, httpClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -66,12 +66,10 @@ func (g *GithubService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
}
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: *pull.Number,
|
||||
Title: *pull.Title,
|
||||
Branch: *pull.Head.Ref,
|
||||
TargetBranch: *pull.Base.Ref,
|
||||
HeadSHA: *pull.Head.SHA,
|
||||
Labels: getGithubPRLabelNames(pull.Labels),
|
||||
Author: *pull.User.Login,
|
||||
})
|
||||
}
|
||||
if resp.NextPage == 0 {
|
||||
|
||||
@@ -3,8 +3,8 @@ package pull_request
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/v66/github"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/google/go-github/v35/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func toPtr(s string) *string {
|
||||
@@ -52,8 +52,9 @@ func TestContainLabels(t *testing.T) {
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.Name, func(t *testing.T) {
|
||||
got := containLabels(c.Labels, c.PullLabels)
|
||||
require.Equal(t, got, c.Expect)
|
||||
if got := containLabels(c.Labels, c.PullLabels); got != c.Expect {
|
||||
t.Errorf("expect: %v, got: %v", c.Expect, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func TestGetGitHubPRLabelNames(t *testing.T) {
|
||||
for _, test := range Tests {
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
labels := getGithubPRLabelNames(test.PullLabels)
|
||||
require.Equal(t, test.ExpectedResult, labels)
|
||||
assert.Equal(t, test.ExpectedResult, labels)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
gitlab "github.com/xanzy/go-gitlab"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
||||
)
|
||||
|
||||
type GitLabService struct {
|
||||
@@ -21,7 +20,7 @@ type GitLabService struct {
|
||||
|
||||
var _ PullRequestService = (*GitLabService)(nil)
|
||||
|
||||
func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string, scmRootCAPath string, insecure bool, caCerts []byte) (PullRequestService, error) {
|
||||
func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string, scmRootCAPath string, insecure bool) (PullRequestService, error) {
|
||||
var clientOptionFns []gitlab.ClientOptionFunc
|
||||
|
||||
// Set a custom Gitlab base URL if one is provided
|
||||
@@ -34,7 +33,7 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels []
|
||||
}
|
||||
|
||||
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||
tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure, caCerts)
|
||||
tr.TLSClientConfig = utils.GetTlsConfig(scmRootCAPath, insecure)
|
||||
|
||||
retryClient := retryablehttp.NewClient()
|
||||
retryClient.HTTPClient.Transport = tr
|
||||
@@ -43,7 +42,7 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels []
|
||||
|
||||
client, err := gitlab.NewClient(token, clientOptionFns...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating Gitlab client: %w", err)
|
||||
return nil, fmt.Errorf("error creating Gitlab client: %v", err)
|
||||
}
|
||||
|
||||
return &GitLabService{
|
||||
@@ -55,12 +54,13 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels []
|
||||
}
|
||||
|
||||
func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
|
||||
// Filter the merge requests on labels, if they are specified.
|
||||
var labels *gitlab.LabelOptions
|
||||
var labels *gitlab.Labels
|
||||
if len(g.labels) > 0 {
|
||||
var labelsList gitlab.LabelOptions = g.labels
|
||||
labels = &labelsList
|
||||
labels = (*gitlab.Labels)(&g.labels)
|
||||
}
|
||||
|
||||
opts := &gitlab.ListProjectMergeRequestsOptions{
|
||||
ListOptions: gitlab.ListOptions{
|
||||
PerPage: 100,
|
||||
@@ -76,17 +76,15 @@ func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {
|
||||
for {
|
||||
mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error listing merge requests for project '%s': %w", g.project, err)
|
||||
return nil, fmt.Errorf("error listing merge requests for project '%s': %v", g.project, err)
|
||||
}
|
||||
for _, mr := range mrs {
|
||||
pullRequests = append(pullRequests, &PullRequest{
|
||||
Number: mr.IID,
|
||||
Title: mr.Title,
|
||||
Branch: mr.SourceBranch,
|
||||
TargetBranch: mr.TargetBranch,
|
||||
HeadSHA: mr.SHA,
|
||||
Labels: mr.Labels,
|
||||
Author: mr.Author.Username,
|
||||
})
|
||||
}
|
||||
if resp.NextPage == 0 {
|
||||
|
||||
@@ -2,8 +2,6 @@ package pull_request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -11,16 +9,17 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func writeMRListResponse(t *testing.T, w io.Writer) {
|
||||
t.Helper()
|
||||
f, err := os.Open("fixtures/gitlab_mr_list_response.json")
|
||||
require.NoErrorf(t, err, "error opening fixture file: %v", err)
|
||||
if err != nil {
|
||||
t.Fatalf("error opening fixture file: %v", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, f)
|
||||
require.NoErrorf(t, err, "error writing response: %v", err)
|
||||
if _, err = io.Copy(w, f); err != nil {
|
||||
t.Fatalf("error writing response: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitLabServiceCustomBaseURL(t *testing.T) {
|
||||
@@ -35,11 +34,11 @@ func TestGitLabServiceCustomBaseURL(t *testing.T) {
|
||||
writeMRListResponse(t, w)
|
||||
})
|
||||
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = svc.List(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGitLabServiceToken(t *testing.T) {
|
||||
@@ -54,11 +53,11 @@ func TestGitLabServiceToken(t *testing.T) {
|
||||
writeMRListResponse(t, w)
|
||||
})
|
||||
|
||||
svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = svc.List(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
@@ -73,18 +72,16 @@ func TestList(t *testing.T) {
|
||||
writeMRListResponse(t, w)
|
||||
})
|
||||
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
prs, err := svc.List(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, prs, 1)
|
||||
assert.Equal(t, 15442, prs[0].Number)
|
||||
assert.Equal(t, "Draft: Use structured logging for DB load balancer", prs[0].Title)
|
||||
assert.Equal(t, "use-structured-logging-for-db-load-balancer", prs[0].Branch)
|
||||
assert.Equal(t, "master", prs[0].TargetBranch)
|
||||
assert.Equal(t, "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7", prs[0].HeadSHA)
|
||||
assert.Equal(t, "hfyngvason", prs[0].Author)
|
||||
assert.Equal(t, prs[0].Number, 15442)
|
||||
assert.Equal(t, prs[0].Branch, "use-structured-logging-for-db-load-balancer")
|
||||
assert.Equal(t, prs[0].TargetBranch, "master")
|
||||
assert.Equal(t, prs[0].HeadSHA, "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7")
|
||||
}
|
||||
|
||||
func TestListWithLabels(t *testing.T) {
|
||||
@@ -99,11 +96,11 @@ func TestListWithLabels(t *testing.T) {
|
||||
writeMRListResponse(t, w)
|
||||
})
|
||||
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "", "", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = svc.List(context.Background())
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestListWithState(t *testing.T) {
|
||||
@@ -118,77 +115,9 @@ func TestListWithState(t *testing.T) {
|
||||
writeMRListResponse(t, w)
|
||||
})
|
||||
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened", "", false, nil)
|
||||
require.NoError(t, err)
|
||||
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened", "", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = svc.List(context.Background())
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestListWithStateTLS(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
tlsInsecure bool
|
||||
passCerts bool
|
||||
requireErr bool
|
||||
}{
|
||||
{
|
||||
name: "TLS Insecure: true, No Certs",
|
||||
tlsInsecure: true,
|
||||
passCerts: false,
|
||||
requireErr: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Insecure: true, With Certs",
|
||||
tlsInsecure: true,
|
||||
passCerts: true,
|
||||
requireErr: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Insecure: false, With Certs",
|
||||
tlsInsecure: false,
|
||||
passCerts: true,
|
||||
requireErr: false,
|
||||
},
|
||||
{
|
||||
name: "TLS Insecure: false, No Certs",
|
||||
tlsInsecure: false,
|
||||
passCerts: false,
|
||||
requireErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
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) {
|
||||
writeMRListResponse(t, w)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var certs []byte
|
||||
if test.passCerts == true {
|
||||
for _, cert := range ts.TLS.Certificates {
|
||||
for _, c := range cert.Certificate {
|
||||
parsedCert, err := x509.ParseCertificate(c)
|
||||
require.NoError(t, err, "Failed to parse certificate")
|
||||
certs = append(certs, pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: parsedCert.Raw,
|
||||
})...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svc, err := NewGitLabService(context.Background(), "", ts.URL, "278964", []string{}, "opened", "", test.tlsInsecure, certs)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = svc.List(context.Background())
|
||||
if test.requireErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
type PullRequest struct {
|
||||
// Number is a number that will be the ID of the pull request.
|
||||
Number int
|
||||
// Title of the pull request.
|
||||
Title string
|
||||
// Branch is the name of the branch from which the pull request originated.
|
||||
Branch string
|
||||
// TargetBranch is the name of the target branch of the pull request.
|
||||
@@ -18,8 +16,6 @@ type PullRequest struct {
|
||||
HeadSHA string
|
||||
// Labels of the pull request.
|
||||
Labels []string
|
||||
// Author is the author of the pull request.
|
||||
Author string
|
||||
}
|
||||
|
||||
type PullRequestService interface {
|
||||
|
||||
@@ -16,13 +16,13 @@ func compileFilters(filters []argoprojiov1alpha1.PullRequestGeneratorFilter) ([]
|
||||
if filter.BranchMatch != nil {
|
||||
outFilter.BranchMatch, err = regexp.Compile(*filter.BranchMatch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %w", *filter.BranchMatch, err)
|
||||
return nil, fmt.Errorf("error compiling BranchMatch regexp %q: %v", *filter.BranchMatch, err)
|
||||
}
|
||||
}
|
||||
if filter.TargetBranchMatch != nil {
|
||||
outFilter.TargetBranchMatch, err = regexp.Compile(*filter.TargetBranchMatch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error compiling TargetBranchMatch regexp %q: %w", *filter.TargetBranchMatch, err)
|
||||
return nil, fmt.Errorf("error compiling TargetBranchMatch regexp %q: %v", *filter.TargetBranchMatch, err)
|
||||
}
|
||||
}
|
||||
outFilters = append(outFilters, outFilter)
|
||||
|
||||
@@ -4,27 +4,22 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func strp(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestFilterBranchMatchBadRegexp(t *testing.T) {
|
||||
provider, _ := NewFakeService(
|
||||
context.Background(),
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR branch1",
|
||||
Branch: "branch1",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "089d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -35,7 +30,7 @@ func TestFilterBranchMatchBadRegexp(t *testing.T) {
|
||||
},
|
||||
}
|
||||
_, err := ListPullRequests(context.Background(), provider, filters)
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestFilterBranchMatch(t *testing.T) {
|
||||
@@ -44,35 +39,27 @@ func TestFilterBranchMatch(t *testing.T) {
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two",
|
||||
Branch: "two",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
Title: "PR three",
|
||||
Branch: "three",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name3",
|
||||
},
|
||||
{
|
||||
Number: 4,
|
||||
Title: "PR four",
|
||||
Branch: "four",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -83,7 +70,7 @@ func TestFilterBranchMatch(t *testing.T) {
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(context.Background(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.Equal(t, "two", pullRequests[0].Branch)
|
||||
}
|
||||
@@ -94,35 +81,27 @@ func TestFilterTargetBranchMatch(t *testing.T) {
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two",
|
||||
Branch: "two",
|
||||
TargetBranch: "branch1",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
Title: "PR three",
|
||||
Branch: "three",
|
||||
TargetBranch: "branch2",
|
||||
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name3",
|
||||
},
|
||||
{
|
||||
Number: 4,
|
||||
Title: "PR four",
|
||||
Branch: "four",
|
||||
TargetBranch: "branch3",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -133,7 +112,7 @@ func TestFilterTargetBranchMatch(t *testing.T) {
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(context.Background(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 1)
|
||||
assert.Equal(t, "two", pullRequests[0].Branch)
|
||||
}
|
||||
@@ -144,35 +123,27 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two",
|
||||
Branch: "two",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
Title: "PR three",
|
||||
Branch: "three",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name3",
|
||||
},
|
||||
{
|
||||
Number: 4,
|
||||
Title: "PR four",
|
||||
Branch: "four",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -186,7 +157,7 @@ func TestMultiFilterOr(t *testing.T) {
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(context.Background(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 3)
|
||||
assert.Equal(t, "two", pullRequests[0].Branch)
|
||||
assert.Equal(t, "three", pullRequests[1].Branch)
|
||||
@@ -199,35 +170,27 @@ func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) {
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two",
|
||||
Branch: "two",
|
||||
TargetBranch: "branch1",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
{
|
||||
Number: 3,
|
||||
Title: "PR three",
|
||||
Branch: "three",
|
||||
TargetBranch: "branch2",
|
||||
HeadSHA: "389d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name3",
|
||||
},
|
||||
{
|
||||
Number: 4,
|
||||
Title: "PR four",
|
||||
Branch: "four",
|
||||
TargetBranch: "branch3",
|
||||
HeadSHA: "489d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name4",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
@@ -243,7 +206,7 @@ func TestMultiFilterOrWithTargetBranchFilter(t *testing.T) {
|
||||
},
|
||||
}
|
||||
pullRequests, err := ListPullRequests(context.Background(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, pullRequests, 2)
|
||||
assert.Equal(t, "two", pullRequests[0].Branch)
|
||||
assert.Equal(t, "four", pullRequests[1].Branch)
|
||||
@@ -255,26 +218,22 @@ func TestNoFilters(t *testing.T) {
|
||||
[]*PullRequest{
|
||||
{
|
||||
Number: 1,
|
||||
Title: "PR one",
|
||||
Branch: "one",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "189d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name1",
|
||||
},
|
||||
{
|
||||
Number: 2,
|
||||
Title: "PR two",
|
||||
Branch: "two",
|
||||
TargetBranch: "master",
|
||||
HeadSHA: "289d92cbf9ff857a39e6feccd32798ca700fb958",
|
||||
Author: "name2",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
filters := []argoprojiov1alpha1.PullRequestGeneratorFilter{}
|
||||
repos, err := ListPullRequests(context.Background(), provider, filters)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, repos, 2)
|
||||
assert.Equal(t, "one", repos[0].Branch)
|
||||
assert.Equal(t, "two", repos[1].Branch)
|
||||
|
||||
@@ -6,37 +6,49 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=RepositoryDB
|
||||
|
||||
// RepositoryDB Is a lean facade for ArgoDB,
|
||||
// Using a lean interface makes it easier to test the functionality of the git generator
|
||||
type RepositoryDB interface {
|
||||
GetRepository(ctx context.Context, url string) (*v1alpha1.Repository, error)
|
||||
}
|
||||
|
||||
type argoCDService struct {
|
||||
getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error)
|
||||
repositoriesDB RepositoryDB
|
||||
storecreds git.CredsStore
|
||||
submoduleEnabled bool
|
||||
repoServerClientSet apiclient.Clientset
|
||||
newFileGlobbingEnabled bool
|
||||
}
|
||||
|
||||
//go:generate go run github.com/vektra/mockery/v2@v2.25.1 --name=Repos
|
||||
|
||||
type Repos interface {
|
||||
|
||||
// GetFiles returns content of files (not directories) within the target repo
|
||||
GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache, verifyCommit bool) (map[string][]byte, error)
|
||||
GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error)
|
||||
|
||||
// GetDirectories returns a list of directories (not files) within the target repo
|
||||
GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache, verifyCommit bool) ([]string, error)
|
||||
GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error)
|
||||
}
|
||||
|
||||
func NewArgoCDService(getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error), submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) {
|
||||
func NewArgoCDService(db db.ArgoDB, submoduleEnabled bool, repoClientset apiclient.Clientset, newFileGlobbingEnabled bool) (Repos, error) {
|
||||
return &argoCDService{
|
||||
getRepository: getRepository,
|
||||
repositoriesDB: db.(RepositoryDB),
|
||||
submoduleEnabled: submoduleEnabled,
|
||||
repoServerClientSet: repoClientset,
|
||||
newFileGlobbingEnabled: newFileGlobbingEnabled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache, verifyCommit bool) (map[string][]byte, error) {
|
||||
repo, err := a.getRepository(ctx, repoURL, "")
|
||||
func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool) (map[string][]byte, error) {
|
||||
repo, err := a.repositoriesDB.GetRepository(ctx, repoURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in GetRepository: %w", err)
|
||||
}
|
||||
@@ -48,7 +60,6 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s
|
||||
Path: pattern,
|
||||
NewGitFileGlobbingEnabled: a.newFileGlobbingEnabled,
|
||||
NoRevisionCache: noRevisionCache,
|
||||
VerifyCommit: verifyCommit,
|
||||
}
|
||||
closer, client, err := a.repoServerClientSet.NewRepoServerClient()
|
||||
if err != nil {
|
||||
@@ -63,8 +74,8 @@ func (a *argoCDService) GetFiles(ctx context.Context, repoURL string, revision s
|
||||
return fileResponse.GetMap(), nil
|
||||
}
|
||||
|
||||
func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache, verifyCommit bool) ([]string, error) {
|
||||
repo, err := a.getRepository(ctx, repoURL, "")
|
||||
func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool) ([]string, error) {
|
||||
repo, err := a.repositoriesDB.GetRepository(ctx, repoURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in GetRepository: %w", err)
|
||||
}
|
||||
@@ -74,7 +85,6 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
SubmoduleEnabled: a.submoduleEnabled,
|
||||
Revision: revision,
|
||||
NoRevisionCache: noRevisionCache,
|
||||
VerifyCommit: verifyCommit,
|
||||
}
|
||||
|
||||
closer, client, err := a.repoServerClientSet.NewRepoServerClient()
|
||||
@@ -88,4 +98,5 @@ func (a *argoCDService) GetDirectories(ctx context.Context, repoURL string, revi
|
||||
return nil, fmt.Errorf("error retrieving Git Directories: %w", err)
|
||||
}
|
||||
return dirResponse.GetPaths(), nil
|
||||
|
||||
}
|
||||
|
||||
@@ -5,22 +5,23 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
repo_mocks "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
db_mocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestGetDirectories(t *testing.T) {
|
||||
|
||||
type fields struct {
|
||||
repositoriesDBFuncs []func(*mocks.RepositoryDB)
|
||||
storecreds git.CredsStore
|
||||
submoduleEnabled bool
|
||||
getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error)
|
||||
repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient)
|
||||
}
|
||||
type args struct {
|
||||
@@ -28,7 +29,6 @@ func TestGetDirectories(t *testing.T) {
|
||||
repoURL string
|
||||
revision string
|
||||
noRevisionCache bool
|
||||
verifyCommit bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -38,13 +38,17 @@ func TestGetDirectories(t *testing.T) {
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{name: "ErrorGettingRepos", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return nil, fmt.Errorf("unable to get repos")
|
||||
repositoriesDBFuncs: []func(*mocks.RepositoryDB){
|
||||
func(db *mocks.RepositoryDB) {
|
||||
db.On("GetRepository", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get repos"))
|
||||
},
|
||||
},
|
||||
}, args: args{}, want: nil, wantErr: assert.Error},
|
||||
{name: "ErrorGettingDirs", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
repositoriesDBFuncs: []func(*mocks.RepositoryDB){
|
||||
func(db *mocks.RepositoryDB) {
|
||||
db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil)
|
||||
},
|
||||
},
|
||||
repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){
|
||||
func(client *repo_mocks.RepoServerServiceClient) {
|
||||
@@ -53,8 +57,10 @@ func TestGetDirectories(t *testing.T) {
|
||||
},
|
||||
}, args: args{}, want: nil, wantErr: assert.Error},
|
||||
{name: "HappyCase", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
repositoriesDBFuncs: []func(*mocks.RepositoryDB){
|
||||
func(db *mocks.RepositoryDB) {
|
||||
db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil)
|
||||
},
|
||||
},
|
||||
repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){
|
||||
func(client *repo_mocks.RepoServerServiceClient) {
|
||||
@@ -64,32 +70,26 @@ func TestGetDirectories(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, args: args{}, want: []string{"foo", "foo/bar", "bar/foo"}, wantErr: assert.NoError},
|
||||
{name: "ErrorVerifyingCommit", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
},
|
||||
repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){
|
||||
func(client *repo_mocks.RepoServerServiceClient) {
|
||||
client.On("GetGitDirectories", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("revision HEAD is not signed"))
|
||||
},
|
||||
},
|
||||
}, args: args{}, want: nil, wantErr: assert.Error},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockDb := &mocks.RepositoryDB{}
|
||||
mockRepoClient := &repo_mocks.RepoServerServiceClient{}
|
||||
// decorate the mocks
|
||||
for i := range tt.fields.repositoriesDBFuncs {
|
||||
tt.fields.repositoriesDBFuncs[i](mockDb)
|
||||
}
|
||||
for i := range tt.fields.repoServerClientFuncs {
|
||||
tt.fields.repoServerClientFuncs[i](mockRepoClient)
|
||||
}
|
||||
|
||||
a := &argoCDService{
|
||||
getRepository: tt.fields.getRepository,
|
||||
repositoriesDB: mockDb,
|
||||
storecreds: tt.fields.storecreds,
|
||||
submoduleEnabled: tt.fields.submoduleEnabled,
|
||||
repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient},
|
||||
}
|
||||
got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache, tt.args.verifyCommit)
|
||||
got, err := a.GetDirectories(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetDirectories(%v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.noRevisionCache)) {
|
||||
return
|
||||
}
|
||||
@@ -100,10 +100,10 @@ func TestGetDirectories(t *testing.T) {
|
||||
|
||||
func TestGetFiles(t *testing.T) {
|
||||
type fields struct {
|
||||
repositoriesDBFuncs []func(*mocks.RepositoryDB)
|
||||
storecreds git.CredsStore
|
||||
submoduleEnabled bool
|
||||
repoServerClientFuncs []func(*repo_mocks.RepoServerServiceClient)
|
||||
getRepository func(ctx context.Context, url, project string) (*v1alpha1.Repository, error)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -111,7 +111,6 @@ func TestGetFiles(t *testing.T) {
|
||||
revision string
|
||||
pattern string
|
||||
noRevisionCache bool
|
||||
verifyCommit bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -121,13 +120,17 @@ func TestGetFiles(t *testing.T) {
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{name: "ErrorGettingRepos", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return nil, fmt.Errorf("unable to get repos")
|
||||
repositoriesDBFuncs: []func(*mocks.RepositoryDB){
|
||||
func(db *mocks.RepositoryDB) {
|
||||
db.On("GetRepository", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("unable to get repos"))
|
||||
},
|
||||
},
|
||||
}, args: args{}, want: nil, wantErr: assert.Error},
|
||||
{name: "ErrorGettingFiles", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
repositoriesDBFuncs: []func(*mocks.RepositoryDB){
|
||||
func(db *mocks.RepositoryDB) {
|
||||
db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil)
|
||||
},
|
||||
},
|
||||
repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){
|
||||
func(client *repo_mocks.RepoServerServiceClient) {
|
||||
@@ -136,8 +139,10 @@ func TestGetFiles(t *testing.T) {
|
||||
},
|
||||
}, args: args{}, want: nil, wantErr: assert.Error},
|
||||
{name: "HappyCase", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
repositoriesDBFuncs: []func(*mocks.RepositoryDB){
|
||||
func(db *mocks.RepositoryDB) {
|
||||
db.On("GetRepository", mock.Anything, mock.Anything).Return(&v1alpha1.Repository{}, nil)
|
||||
},
|
||||
},
|
||||
repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){
|
||||
func(client *repo_mocks.RepoServerServiceClient) {
|
||||
@@ -153,32 +158,26 @@ func TestGetFiles(t *testing.T) {
|
||||
"foo.json": []byte("hello: world!"),
|
||||
"bar.yaml": []byte("yay: appsets"),
|
||||
}, wantErr: assert.NoError},
|
||||
{name: "ErrorVerifyingCommit", fields: fields{
|
||||
getRepository: func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
},
|
||||
repoServerClientFuncs: []func(*repo_mocks.RepoServerServiceClient){
|
||||
func(client *repo_mocks.RepoServerServiceClient) {
|
||||
client.On("GetGitFiles", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("revision HEAD is not signed"))
|
||||
},
|
||||
},
|
||||
}, args: args{}, want: nil, wantErr: assert.Error},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockDb := &mocks.RepositoryDB{}
|
||||
mockRepoClient := &repo_mocks.RepoServerServiceClient{}
|
||||
// decorate the mocks
|
||||
for i := range tt.fields.repositoriesDBFuncs {
|
||||
tt.fields.repositoriesDBFuncs[i](mockDb)
|
||||
}
|
||||
for i := range tt.fields.repoServerClientFuncs {
|
||||
tt.fields.repoServerClientFuncs[i](mockRepoClient)
|
||||
}
|
||||
|
||||
a := &argoCDService{
|
||||
getRepository: tt.fields.getRepository,
|
||||
repositoriesDB: mockDb,
|
||||
storecreds: tt.fields.storecreds,
|
||||
submoduleEnabled: tt.fields.submoduleEnabled,
|
||||
repoServerClientSet: &repo_mocks.Clientset{RepoServerServiceClient: mockRepoClient},
|
||||
}
|
||||
got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache, tt.args.verifyCommit)
|
||||
got, err := a.GetFiles(tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetFiles(%v, %v, %v, %v, %v)", tt.args.ctx, tt.args.repoURL, tt.args.revision, tt.args.pattern, tt.args.noRevisionCache)) {
|
||||
return
|
||||
}
|
||||
@@ -188,9 +187,7 @@ func TestGetFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewArgoCDService(t *testing.T) {
|
||||
service, err := NewArgoCDService(func(ctx context.Context, url, project string) (*v1alpha1.Repository, error) {
|
||||
return &v1alpha1.Repository{}, nil
|
||||
}, false, &repo_mocks.Clientset{}, false)
|
||||
require.NoError(t, err)
|
||||
service, err := NewArgoCDService(&db_mocks.ArgoDB{}, false, &repo_mocks.Clientset{}, false)
|
||||
assert.NoError(t, err, err)
|
||||
assert.NotNil(t, service)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ package scm_provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
pathpkg "path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/arn"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
@@ -20,8 +19,6 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/maps"
|
||||
"k8s.io/utils/strings/slices"
|
||||
|
||||
application "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -328,8 +325,7 @@ func getCodeCommitFIPSEndpoint(repoUrl string) (string, error) {
|
||||
}
|
||||
|
||||
func hasAwsError(err error, codes ...string) bool {
|
||||
var awsErr awserr.Error
|
||||
if errors.As(err, &awsErr) {
|
||||
if awsErr, ok := err.(awserr.Error); ok {
|
||||
return slices.Contains(codes, awsErr.Code())
|
||||
}
|
||||
return false
|
||||
@@ -358,7 +354,7 @@ func createAWSDiscoveryClients(_ context.Context, role string, region string) (*
|
||||
Credentials: assumeRoleCreds,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error creating new AWS discovery session: %w", err)
|
||||
return nil, nil, fmt.Errorf("error creating new AWS discovery session: %s", err)
|
||||
}
|
||||
} else {
|
||||
log.Debugf("role is not provided for AWS CodeCommit discovery, using pod role")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.26.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -17,6 +17,14 @@ type AWSCodeCommitClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AWSCodeCommitClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AWSCodeCommitClient) EXPECT() *AWSCodeCommitClient_Expecter {
|
||||
return &AWSCodeCommitClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetFolderWithContext provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *AWSCodeCommitClient) GetFolderWithContext(_a0 context.Context, _a1 *codecommit.GetFolderInput, _a2 ...request.Option) (*codecommit.GetFolderOutput, error) {
|
||||
_va := make([]interface{}, len(_a2))
|
||||
@@ -28,10 +36,6 @@ func (_m *AWSCodeCommitClient) GetFolderWithContext(_a0 context.Context, _a1 *co
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetFolderWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetFolderOutput
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)); ok {
|
||||
@@ -54,6 +58,43 @@ func (_m *AWSCodeCommitClient) GetFolderWithContext(_a0 context.Context, _a1 *co
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetFolderWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFolderWithContext'
|
||||
type AWSCodeCommitClient_GetFolderWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetFolderWithContext is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *codecommit.GetFolderInput
|
||||
// - _a2 ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetFolderWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
return &AWSCodeCommitClient_GetFolderWithContext_Call{Call: _e.mock.On("GetFolderWithContext",
|
||||
append([]interface{}{_a0, _a1}, _a2...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.GetFolderInput, _a2 ...request.Option)) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*codecommit.GetFolderInput), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) Return(_a0 *codecommit.GetFolderOutput, _a1 error) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetFolderWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.GetFolderInput, ...request.Option) (*codecommit.GetFolderOutput, error)) *AWSCodeCommitClient_GetFolderWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetRepositoryWithContext provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *AWSCodeCommitClient) GetRepositoryWithContext(_a0 context.Context, _a1 *codecommit.GetRepositoryInput, _a2 ...request.Option) (*codecommit.GetRepositoryOutput, error) {
|
||||
_va := make([]interface{}, len(_a2))
|
||||
@@ -65,10 +106,6 @@ func (_m *AWSCodeCommitClient) GetRepositoryWithContext(_a0 context.Context, _a1
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetRepositoryWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.GetRepositoryOutput
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)); ok {
|
||||
@@ -91,6 +128,43 @@ func (_m *AWSCodeCommitClient) GetRepositoryWithContext(_a0 context.Context, _a1
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_GetRepositoryWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRepositoryWithContext'
|
||||
type AWSCodeCommitClient_GetRepositoryWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetRepositoryWithContext is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *codecommit.GetRepositoryInput
|
||||
// - _a2 ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) GetRepositoryWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
return &AWSCodeCommitClient_GetRepositoryWithContext_Call{Call: _e.mock.On("GetRepositoryWithContext",
|
||||
append([]interface{}{_a0, _a1}, _a2...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.GetRepositoryInput, _a2 ...request.Option)) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*codecommit.GetRepositoryInput), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) Return(_a0 *codecommit.GetRepositoryOutput, _a1 error) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_GetRepositoryWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.GetRepositoryInput, ...request.Option) (*codecommit.GetRepositoryOutput, error)) *AWSCodeCommitClient_GetRepositoryWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListBranchesWithContext provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *AWSCodeCommitClient) ListBranchesWithContext(_a0 context.Context, _a1 *codecommit.ListBranchesInput, _a2 ...request.Option) (*codecommit.ListBranchesOutput, error) {
|
||||
_va := make([]interface{}, len(_a2))
|
||||
@@ -102,10 +176,6 @@ func (_m *AWSCodeCommitClient) ListBranchesWithContext(_a0 context.Context, _a1
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListBranchesWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListBranchesOutput
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)); ok {
|
||||
@@ -128,6 +198,43 @@ func (_m *AWSCodeCommitClient) ListBranchesWithContext(_a0 context.Context, _a1
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// AWSCodeCommitClient_ListBranchesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBranchesWithContext'
|
||||
type AWSCodeCommitClient_ListBranchesWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListBranchesWithContext is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *codecommit.ListBranchesInput
|
||||
// - _a2 ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListBranchesWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
return &AWSCodeCommitClient_ListBranchesWithContext_Call{Call: _e.mock.On("ListBranchesWithContext",
|
||||
append([]interface{}{_a0, _a1}, _a2...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.ListBranchesInput, _a2 ...request.Option)) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*codecommit.ListBranchesInput), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) Return(_a0 *codecommit.ListBranchesOutput, _a1 error) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListBranchesWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.ListBranchesInput, ...request.Option) (*codecommit.ListBranchesOutput, error)) *AWSCodeCommitClient_ListBranchesWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// ListRepositoriesWithContext provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *AWSCodeCommitClient) ListRepositoriesWithContext(_a0 context.Context, _a1 *codecommit.ListRepositoriesInput, _a2 ...request.Option) (*codecommit.ListRepositoriesOutput, error) {
|
||||
_va := make([]interface{}, len(_a2))
|
||||
@@ -139,10 +246,6 @@ func (_m *AWSCodeCommitClient) ListRepositoriesWithContext(_a0 context.Context,
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListRepositoriesWithContext")
|
||||
}
|
||||
|
||||
var r0 *codecommit.ListRepositoriesOutput
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)); ok {
|
||||
@@ -165,12 +268,50 @@ func (_m *AWSCodeCommitClient) ListRepositoriesWithContext(_a0 context.Context,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewAWSCodeCommitClient creates a new instance of AWSCodeCommitClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAWSCodeCommitClient(t interface {
|
||||
// AWSCodeCommitClient_ListRepositoriesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListRepositoriesWithContext'
|
||||
type AWSCodeCommitClient_ListRepositoriesWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// ListRepositoriesWithContext is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *codecommit.ListRepositoriesInput
|
||||
// - _a2 ...request.Option
|
||||
func (_e *AWSCodeCommitClient_Expecter) ListRepositoriesWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
return &AWSCodeCommitClient_ListRepositoriesWithContext_Call{Call: _e.mock.On("ListRepositoriesWithContext",
|
||||
append([]interface{}{_a0, _a1}, _a2...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Run(run func(_a0 context.Context, _a1 *codecommit.ListRepositoriesInput, _a2 ...request.Option)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*codecommit.ListRepositoriesInput), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) Return(_a0 *codecommit.ListRepositoriesOutput, _a1 error) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSCodeCommitClient_ListRepositoriesWithContext_Call) RunAndReturn(run func(context.Context, *codecommit.ListRepositoriesInput, ...request.Option) (*codecommit.ListRepositoriesOutput, error)) *AWSCodeCommitClient_ListRepositoriesWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewAWSCodeCommitClient interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AWSCodeCommitClient {
|
||||
}
|
||||
|
||||
// NewAWSCodeCommitClient creates a new instance of AWSCodeCommitClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewAWSCodeCommitClient(t mockConstructorTestingTNewAWSCodeCommitClient) *AWSCodeCommitClient {
|
||||
mock := &AWSCodeCommitClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
// Code generated by mockery v2.26.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
@@ -16,6 +16,14 @@ type AWSTaggingClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type AWSTaggingClient_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *AWSTaggingClient) EXPECT() *AWSTaggingClient_Expecter {
|
||||
return &AWSTaggingClient_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetResourcesWithContext provides a mock function with given fields: _a0, _a1, _a2
|
||||
func (_m *AWSTaggingClient) GetResourcesWithContext(_a0 context.Context, _a1 *resourcegroupstaggingapi.GetResourcesInput, _a2 ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
|
||||
_va := make([]interface{}, len(_a2))
|
||||
@@ -27,10 +35,6 @@ func (_m *AWSTaggingClient) GetResourcesWithContext(_a0 context.Context, _a1 *re
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetResourcesWithContext")
|
||||
}
|
||||
|
||||
var r0 *resourcegroupstaggingapi.GetResourcesOutput
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)); ok {
|
||||
@@ -53,12 +57,50 @@ func (_m *AWSTaggingClient) GetResourcesWithContext(_a0 context.Context, _a1 *re
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewAWSTaggingClient creates a new instance of AWSTaggingClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewAWSTaggingClient(t interface {
|
||||
// AWSTaggingClient_GetResourcesWithContext_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetResourcesWithContext'
|
||||
type AWSTaggingClient_GetResourcesWithContext_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetResourcesWithContext is a helper method to define mock.On call
|
||||
// - _a0 context.Context
|
||||
// - _a1 *resourcegroupstaggingapi.GetResourcesInput
|
||||
// - _a2 ...request.Option
|
||||
func (_e *AWSTaggingClient_Expecter) GetResourcesWithContext(_a0 interface{}, _a1 interface{}, _a2 ...interface{}) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
return &AWSTaggingClient_GetResourcesWithContext_Call{Call: _e.mock.On("GetResourcesWithContext",
|
||||
append([]interface{}{_a0, _a1}, _a2...)...)}
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Run(run func(_a0 context.Context, _a1 *resourcegroupstaggingapi.GetResourcesInput, _a2 ...request.Option)) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
variadicArgs := make([]request.Option, len(args)-2)
|
||||
for i, a := range args[2:] {
|
||||
if a != nil {
|
||||
variadicArgs[i] = a.(request.Option)
|
||||
}
|
||||
}
|
||||
run(args[0].(context.Context), args[1].(*resourcegroupstaggingapi.GetResourcesInput), variadicArgs...)
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) Return(_a0 *resourcegroupstaggingapi.GetResourcesOutput, _a1 error) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *AWSTaggingClient_GetResourcesWithContext_Call) RunAndReturn(run func(context.Context, *resourcegroupstaggingapi.GetResourcesInput, ...request.Option) (*resourcegroupstaggingapi.GetResourcesOutput, error)) *AWSTaggingClient_GetResourcesWithContext_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewAWSTaggingClient interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *AWSTaggingClient {
|
||||
}
|
||||
|
||||
// NewAWSTaggingClient creates a new instance of AWSTaggingClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewAWSTaggingClient(t mockConstructorTestingTNewAWSTaggingClient) *AWSTaggingClient {
|
||||
mock := &AWSTaggingClient{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
||||
@@ -6,15 +6,14 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/aws_codecommit/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/codecommit"
|
||||
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/aws_codecommit/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
type awsCodeCommitTestRepository struct {
|
||||
@@ -178,8 +177,8 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
if repo.getRepositoryNilMetadata {
|
||||
repoMetadata = nil
|
||||
}
|
||||
codeCommitClient.
|
||||
On("GetRepositoryWithContext", ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}).
|
||||
codeCommitClient.EXPECT().
|
||||
GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(repo.name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: repoMetadata}, repo.getRepositoryError)
|
||||
codecommitRepoNameIdPairs = append(codecommitRepoNameIdPairs, &codecommit.RepositoryNameIdPair{
|
||||
RepositoryId: aws.String(repo.id),
|
||||
@@ -194,14 +193,14 @@ func TestAWSCodeCommitListRepos(t *testing.T) {
|
||||
}
|
||||
|
||||
if testCase.expectListAtCodeCommit {
|
||||
codeCommitClient.
|
||||
On("ListRepositoriesWithContext", ctx, &codecommit.ListRepositoriesInput{}).
|
||||
codeCommitClient.EXPECT().
|
||||
ListRepositoriesWithContext(ctx, &codecommit.ListRepositoriesInput{}).
|
||||
Return(&codecommit.ListRepositoriesOutput{
|
||||
Repositories: codecommitRepoNameIdPairs,
|
||||
}, testCase.listRepositoryError)
|
||||
} else {
|
||||
taggingClient.
|
||||
On("GetResourcesWithContext", ctx, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{
|
||||
taggingClient.EXPECT().
|
||||
GetResourcesWithContext(ctx, mock.MatchedBy(equalIgnoringTagFilterOrder(&resourcegroupstaggingapi.GetResourcesInput{
|
||||
TagFilters: testCase.expectTagFilters,
|
||||
ResourceTypeFilters: aws.StringSlice([]string{resourceTypeCodeCommitRepository}),
|
||||
}))).
|
||||
@@ -351,8 +350,8 @@ func TestAWSCodeCommitRepoHasPath(t *testing.T) {
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := context.Background()
|
||||
if testCase.expectedGetFolderPath != "" {
|
||||
codeCommitClient.
|
||||
On("GetFolderWithContext", ctx, &codecommit.GetFolderInput{
|
||||
codeCommitClient.EXPECT().
|
||||
GetFolderWithContext(ctx, &codecommit.GetFolderInput{
|
||||
CommitSpecifier: aws.String(branch),
|
||||
FolderPath: aws.String(testCase.expectedGetFolderPath),
|
||||
RepositoryName: aws.String(repoName),
|
||||
@@ -424,14 +423,14 @@ func TestAWSCodeCommitGetBranches(t *testing.T) {
|
||||
taggingClient := mocks.NewAWSTaggingClient(t)
|
||||
ctx := context.Background()
|
||||
if testCase.allBranches {
|
||||
codeCommitClient.
|
||||
On("ListBranchesWithContext", ctx, &codecommit.ListBranchesInput{
|
||||
codeCommitClient.EXPECT().
|
||||
ListBranchesWithContext(ctx, &codecommit.ListBranchesInput{
|
||||
RepositoryName: aws.String(name),
|
||||
}).
|
||||
Return(&codecommit.ListBranchesOutput{Branches: aws.StringSlice(testCase.branches)}, testCase.apiError)
|
||||
} else {
|
||||
codeCommitClient.
|
||||
On("GetRepositoryWithContext", ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}).
|
||||
codeCommitClient.EXPECT().
|
||||
GetRepositoryWithContext(ctx, &codecommit.GetRepositoryInput{RepositoryName: aws.String(name)}).
|
||||
Return(&codecommit.GetRepositoryOutput{RepositoryMetadata: &codecommit.RepositoryMetadata{
|
||||
AccountId: aws.String(organization),
|
||||
DefaultBranch: aws.String(defaultBranch),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user