Compare commits

..

16 Commits

Author SHA1 Message Date
Ryan
69d23e105e docs: Clarifying override RBAC needs (#16964)
Signed-off-by: Ryan Shatford <ryan@shatford.com>
Signed-off-by: Dan Garfield <dan@codefresh.io>
Co-authored-by: Ryan Shatford <ryan@shatford.com>
Co-authored-by: Dan Garfield <dan@codefresh.io>
2025-06-13 16:17:40 -04:00
Kostis (Codefresh)
2d963ab8d0 docs: Cluster overview page (#11637)
* docs: Cluster overview page

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: finished with text

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: pictures for cluster section

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: Apply suggestions from code review

Co-authored-by: Dan Garfield <dan@codefresh.io>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
Signed-off-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com>

* docs: optimized images

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>
Signed-off-by: Kostis (Codefresh) <39800303+kostis-codefresh@users.noreply.github.com>
Co-authored-by: Dan Garfield <dan@codefresh.io>
Co-authored-by: Blake Pettersson <blake.pettersson@gmail.com>
2022-12-22 13:30:45 -05:00
Kostis (Codefresh)
7a3cd61060 docs: refreshed page for sync windows (#9770)
Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>
2022-06-28 10:50:33 -04:00
Kostis (Codefresh)
5cbb397c93 docs: sync hooks and waves (#9552)
* docs: sync hooks and waves

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: review comments

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: spelling mistakes

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>
2022-06-12 21:31:46 +02:00
Kostis (Codefresh)
5257a902fb docs: first steps (#8732)
* docs: first steps

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: Apply suggestions from code review

Signed-off-by: Kostis Kapelonis kostis@codefresh.io

Co-authored-by: jannfis <jann@mistrust.net>
Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: extra comments from code review

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: minor spelling mistake

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

Co-authored-by: jannfis <jann@mistrust.net>
2022-03-11 11:06:15 +01:00
Kostis (Codefresh)
4c6a74642f docs: Cli windows installation (#8589)
* docs: more installation options

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: cli windows install

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: fixed broken link

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: Apply suggestions from code review

Co-authored-by: jannfis <jann@mistrust.net>

Co-authored-by: jannfis <jann@mistrust.net>
2022-02-22 17:35:11 +01:00
Kostis (Codefresh)
ab715efd2b docs: First page for new documentation (#8507)
* docs: first page

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: first page with relative links

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* applied first round of suggestions

Co-authored-by: jannfis <jann@mistrust.net>

* docs: first page - second round of comments

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

Co-authored-by: jannfis <jann@mistrust.net>
2022-02-16 13:11:47 +01:00
Kostis (Codefresh)
50a693e554 docs: Terminology page in the new documentation structure (#8456)
* docs: basic terminology

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: finished terminology

Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

* docs: Apply suggestions from code review

Co-authored-by: jannfis <jann@mistrust.net>
Signed-off-by: Kostis Kapelonis <kostis@codefresh.io>

Co-authored-by: jannfis <jann@mistrust.net>
2022-02-10 19:20:13 +01:00
Fischer Jemison
8409da4df8 docs: add scaling docs (#6038)
Signed-off-by: Fischer Jemison <fjemison@newrelic.com>
2021-04-15 20:22:50 +02:00
J. Mark Pim
857ea0edfe docs: Two small typos/grammar fix (#5857)
Part of #5635

Signed-off-by: Mark Pim <j.mark.pim@gmail.com>
2021-03-25 08:19:07 +01:00
Fischer Jemison
068a7a6abc docs: fill in docs page for managing clusters (#5822)
Signed-off-by: Fischer Jemison <fjemison@newrelic.com>
2021-03-19 19:40:24 +01:00
jannfis
6c019b037b More moving things around
Signed-off-by: jannfis <jann@mistrust.net>
2021-02-28 20:25:43 +01:00
jannfis
b37bb53f5e Another fix of mkdocs configuration
Signed-off-by: jannfis <jann@mistrust.net>
2021-02-28 17:22:11 +01:00
jannfis
4251200f78 Fix overrides path
Signed-off-by: jannfis <jann@mistrust.net>
2021-02-28 17:20:05 +01:00
jannfis
b407d59fa5 Add docs/requirements.txt
Signed-off-by: jannfis <jann@mistrust.net>
2021-02-28 17:15:58 +01:00
jannfis
4f7aaf0d71 docs: Initial replacement of docs
Signed-off-by: jannfis <jann@mistrust.net>
2021-02-28 16:49:18 +01:00
1043 changed files with 26135 additions and 59456 deletions

View File

@@ -10,10 +10,6 @@ on:
branches:
- 'master'
env:
# Golang version to use across CI steps
GOLANG_VERSION: '1.16.5'
jobs:
build-docker:
name: Build Docker image
@@ -34,7 +30,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- name: Download all Go modules
run: |
go mod download
@@ -52,7 +48,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- name: Restore go build cache
uses: actions/cache@v1
with:
@@ -73,8 +69,8 @@ jobs:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.38.0
args: --timeout 10m --exclude SA5011
version: v1.29
args: --timeout 5m --exclude SA5011
test-go:
name: Run unit tests for Go packages
@@ -91,7 +87,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- name: Install required packages
run: |
sudo apt-get install git -y
@@ -151,7 +147,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- name: Install required packages
run: |
sudo apt-get install git -y
@@ -200,7 +196,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- name: Create symlink in GOPATH
run: |
mkdir -p ~/go/src/github.com/argoproj
@@ -263,7 +259,6 @@ jobs:
yarn build
env:
NODE_ENV: production
NODE_ONLINE_ENV: online
working-directory: ui/
- name: Run ESLint
run: yarn lint
@@ -340,7 +335,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
k3s-version: [v1.21.2, v1.20.2, v1.19.2, v1.18.9, v1.17.11]
k3s-version: [v1.20.2, v1.19.2, v1.18.9, v1.17.11, v1.16.15]
needs:
- build-go
env:
@@ -359,10 +354,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
- name: GH actions workaround - Kill XSP4 process
run: |
sudo pkill mono || true
go-version: '1.14.12'
- name: Install K3S
env:
INSTALL_K3S_VERSION: ${{ matrix.k3s-version }}+k3s1
@@ -400,7 +392,7 @@ jobs:
run: |
docker pull quay.io/dexidp/dex:v2.25.0
docker pull argoproj/argo-cd-ci-builder:v1.0.0
docker pull redis:6.2.4-alpine
docker pull redis:5.0.10-alpine
- name: Create target directory for binaries in the build-process
run: |
mkdir -p dist

View File

@@ -20,4 +20,11 @@ jobs:
- name: build
run: |
pip install -r docs/requirements.txt
mkdocs build
mkdocs build
- name: deploy
if: ${{ github.event_name == 'push' }}
uses: peaceiris/actions-gh-pages@v2.5.0
env:
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
PUBLISH_BRANCH: gh-pages
PUBLISH_DIR: ./site

View File

@@ -5,9 +5,6 @@ on:
branches:
- master
env:
GOLANG_VERSION: '1.16.5'
jobs:
publish:
runs-on: ubuntu-latest
@@ -16,7 +13,7 @@ jobs:
steps:
- uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- uses: actions/checkout@master
with:
path: src/github.com/argoproj/argo-cd
@@ -29,31 +26,25 @@ jobs:
# build
- run: |
docker images -a --format "{{.ID}}" | xargs -I {} docker rmi {}
make image DEV_IMAGE=true DOCKER_PUSH=false IMAGE_NAMESPACE=ghcr.io/argoproj IMAGE_TAG=${{ steps.image.outputs.tag }}
make image DEV_IMAGE=true DOCKER_PUSH=false IMAGE_NAMESPACE=docker.pkg.github.com/argoproj/argo-cd IMAGE_TAG=${{ steps.image.outputs.tag }}
working-directory: ./src/github.com/argoproj/argo-cd
# publish
- run: |
docker login ghcr.io --username $USERNAME --password $PASSWORD
docker push ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }}
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
docker tag ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} argoproj/argocd:latest
docker push argoproj/argocd:latest
docker login docker.pkg.github.com --username $USERNAME --password $PASSWORD
docker push docker.pkg.github.com/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }}
env:
USERNAME: ${{ secrets.USERNAME }}
PASSWORD: ${{ secrets.TOKEN }}
DOCKER_USERNAME: ${{ secrets.RELEASE_DOCKERHUB_USERNAME }}
DOCKER_TOKEN: ${{ secrets.RELEASE_DOCKERHUB_TOKEN }}
# deploy
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
env:
TOKEN: ${{ secrets.TOKEN }}
- run: |
docker run -v $(pwd):/src -w /src --rm -t lyft/kustomizer:v3.3.0 kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }}
docker run -v $(pwd):/src -w /src --rm -t lyft/kustomizer:v3.3.0 kustomize edit set image quay.io/argoproj/argocd=docker.pkg.github.com/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }}
git config --global user.email 'ci@argoproj.com'
git config --global user.name 'CI'
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push)
working-directory: argoproj-deployments/argocd
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-GitHub-Package-Registry/m-p/41202/thread-id/9811
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-Github-Package-Registry/m-p/41202/thread-id/9811

View File

@@ -10,10 +10,6 @@ on:
- '!release-v1.1*'
- '!release-v1.0*'
- '!release-v0*'
env:
GOLANG_VERSION: '1.16.5'
jobs:
prepare-release:
name: Perform automatic release on trigger ${{ github.ref }}
@@ -22,7 +18,7 @@ jobs:
# The name of the tag as supplied by the GitHub event
SOURCE_TAG: ${{ github.ref }}
# The image namespace where Docker image will be published to
IMAGE_NAMESPACE: quay.io/argoproj
IMAGE_NAMESPACE: argoproj
# Whether to create & push image and release assets
DRY_RUN: false
# Whether a draft release should be created, instead of public one
@@ -143,7 +139,7 @@ jobs:
- name: Setup Golang
uses: actions/setup-go@v1
with:
go-version: ${{ env.GOLANG_VERSION }}
go-version: '1.14.12'
- name: Setup Git author information
run: |
@@ -201,12 +197,11 @@ jobs:
QUAY_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
run: |
set -ue
docker login quay.io --username "${QUAY_USERNAME}" --password "${QUAY_TOKEN}"
docker push ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
# Remove the following when Docker Hub is gone
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
docker tag ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} argoproj/argocd:v${TARGET_VERSION}
docker push argoproj/argocd:v${TARGET_VERSION}
docker push ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
docker login quay.io --username "${QUAY_USERNAME}" --password "${QUAY_TOKEN}"
docker tag ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} quay.io/${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
docker push quay.io/${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
if: ${{ env.DRY_RUN != 'true' }}
- name: Read release notes file
@@ -266,6 +261,40 @@ jobs:
asset_content_type: application/octet-stream
if: ${{ env.DRY_RUN != 'true' }}
# include argocd-util as part of release artifacts (argoproj/argo-cd#5174)
- name: Upload argocd-util-linux-amd64 binary to release assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist/argocd-linux-amd64
asset_name: argocd-util-linux-amd64
asset_content_type: application/octet-stream
if: ${{ env.DRY_RUN != 'true' }}
- name: Upload argocd-util-darwin-amd64 binary to release assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist/argocd-darwin-amd64
asset_name: argocd-util-darwin-amd64
asset_content_type: application/octet-stream
if: ${{ env.DRY_RUN != 'true' }}
- name: Upload argocd-util-windows-amd64 binary to release assets
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist/argocd-windows-amd64.exe
asset_name: argocd-util-windows-amd64.exe
asset_content_type: application/octet-stream
if: ${{ env.DRY_RUN != 'true' }}
- name: Update homebrew formula
env:
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}

8
.gitignore vendored
View File

@@ -2,9 +2,7 @@
.idea/
.DS_Store
vendor/
dist/*
ui/dist/app/*
!ui/dist/app/gitkeep
dist/
site/
*.iml
# delve debug binaries
@@ -15,10 +13,10 @@ test-results
.scannerwork
.scratch
node_modules/
.kube/
# ignore built binaries
cmd/argocd/argocd
cmd/argocd-application-controller/argocd-application-controller
cmd/argocd-repo-server/argocd-repo-server
cmd/argocd-server/argocd-server
cmd/argocd-server/argocd-server
cmd/argocd-util/argocd-util

17
.gitpod.Dockerfile vendored
View File

@@ -1,17 +0,0 @@
FROM gitpod/workspace-full
USER root
RUN curl -o /usr/local/bin/kubectl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
chmod +x /usr/local/bin/kubectl
RUN curl -L https://go.kubebuilder.io/dl/2.3.1/$(go env GOOS)/$(go env GOARCH) | \
tar -xz -C /tmp/ && mv /tmp/kubebuilder_2.3.1_$(go env GOOS)_$(go env GOARCH) /usr/local/kubebuilder
RUN apt-get install redis-server -y
RUN go get github.com/mattn/goreman
USER gitpod
ENV ARGOCD_REDIS_LOCAL=true
ENV KUBECONFIG=/tmp/kubeconfig

View File

@@ -1,6 +0,0 @@
image:
file: .gitpod.Dockerfile
tasks:
- init: make mod-download-local dep-ui-local && GO111MODULE=off go get github.com/mattn/goreman
command: make start-test-k8s

View File

@@ -1,205 +1,6 @@
# Changelog
## v2.1.0 (Unreleased)
> [Upgrade instructions](./docs/operator-manual/upgrading/2.0-2.1.md)
### Argo CD Core
Argo CD Core - lightweight Argo CD distribution that packages only core GitOps features and relies
on Kubernetes API/RBAC to power UI and CLI.
### Core Features
* The synchronization process became much much faster and requires significantly less memory.
* An additional caching that ensures that each repository's target revisions are queried only once per
reconciliation cycle. This dramatically reduces the number of Git requests.
* Improved Diffing Customizations: use JQ path expressions to exclude required fields from the diffing.
* Health assessment support for new CRDs: introduced health assessment of CRDs from trident.netapp.io,
elasticsearch.k8s.elastic.co, cluster.x-k8s.io, and minio.min.io API groups.
### Improved Settings
A set of changes had been implemented to simplify configuring Argo CD.
* Simplified Repository Registration: you no longer need to modify the argocd-cm ConfigMap to register a
new Git or Helm repository.
* Enhanced Resource Customizations: the resource.customizations key has been deprecated in favor of
a separate ConfigMap key per resource.
* Reference secret values from any Kubernetes secret: starting v2.1 you can use sensitive data stored in
any Kubernetes secret to configure Argo CD.
* Simplify parametrization of Argo CD server processes: an additional optional ConfigMap argocd-cmd-params-cm
has been introduced.
### Refreshed User Interface
* Enhanced and more consistent filters on Applications List and Applications Details pages.
* Status bar on the Application List page.
* The redesigned search box on the Application List page and more.
### The argocd-util CLI deprecation
The argocd CLI and now available under argocd admin subcommand.
## v2.0.5 (2021-07-22)
* fix: allow argocd-notification ingress to repo-server (#6746)
* fix: argocd-server crashes due to nil pointer dereference (#6757)
* fix: WebUI failure when loading pod view 't.parentRefs is undefined' (#6490) (#6535)
* fix: prevent 'cannot read property "filter" of undefined' during nodes filtering (#6453)
* fix: download Pod Logs button not honouring argocd-server rootpath (#6548) (#6627)
* fix: Version warning banner in docs (#6682)
* fix: upgrade gitops engine to fix workflow health check
## v2.0.4 (2021-06-22)
* fix: typo in networkPolicy definition in manifests (#6532)
* fix: Update redis to 6.2.4 (#6475)
* fix: allows access to dex metrics from any pod (#6420)
* fix: add client side retry to prevent 'transport is closing' errors (#6402)
* fix: Update documentation Argocd app CRD health with app of apps (#6281)
* fix(ui): Crash on application pod view (#6384)
* chore: pin mkdocs version to fix docs build (#6421)
* chore: regenerate manifests using codegen (#6422)
* refactor: use RLock and RUnlock for project to improve performance (#6225)
* chore: Update Golang to v1.16.4 (#6358)
## v2.0.3 (2021-05-27)
### Bug Fixes
* fix: add missing --container flag to 'argocd app logs' command (#6320)
* fix: grpc web proxy must ensure to read full header (#6319)
* fix: controller should refresh app before running sync operation (#6294)
## v2.0.2 (2021-05-20)
### Bug Fixes
* fix: enable access to metrics port in embedded network policies (#6277)
* fix: display log streaming error in logs viewer (#6100) (#6273)
* fix: Don't count errored or completed neighbor pods toward resource consumption (#6259)
* fix: Enable kex algo diffie-hellman-group-exchange-sha256 for go-git ssh (#6256)
* fix: copy github app key from repocreds (#6140, #6197)
* fix(ui): UI crashes after reinstalling ArgoCD (#6218)
* fix: add network policies to restrict traffic flow between argocd components (#6156)
* fix: Revert "feat: Add health checks for kubernetes-external-secrets (#5435)"
* chore: Allow ingress traffic to argocd-server by default (#6179)
## v2.0.1 (2021-04-15)
### Bug Fixes
* fix: spark application check fails on missing section (#6036)
* fix: Adding explicit bind to redis and sentinel for IPv4 clusters #5957 (#6005)
* fix: fix: use correct field for evaluating whether or not GitHub Enterprise is selected (#5987)
## v2.0.0 (2021-04-07)
> [Upgrade instructions](./docs/operator-manual/upgrading/1.8-2.0.md)
### Pods View
Pods View is particularly useful for applications that have hundreds of pods. Instead of visualizing all Kubernetes
resources for the application, it only shows Kubernetes pods and closely related resources. The Pods View supports
grouping related resources by Parent Resource, Top Level Parent, or by Node. Each way of grouping solves a particular
use case. For example grouping by Top Level Parent allows you to quickly find how many pods your application is running
and which resources created them. Grouping by Node allows to see how Pods are spread across the nodes and how many
resources they requested.
### Logs Viewer
Argo CD provides a way to see live logs of pods, which is very useful for debugging and troubleshooting. In the v2.0
release, the log visualization has been rewritten to support pagination, filtering, the ability to disable/enable log
streaming, and even a dark mode for terminal lovers. Do you want to see aggregated logs of multiple deployment pods?
Not a problem! Just click on the parent resource such as Deployment, ReplicaSet, or StatefulSet and navigate
to the Logs tab.
### Banner Feature
Want to notify your Argo CD users of upcoming changes? Just specify the notification message and optional URL using the
`ui.bannercontent` and `ui.bannerurl` attributes in the `argocd-cm` ConfigMap.
### Core Features
* The new sync option `PrunePropagationPolicy=background` allows using background deletion during syncing
* New application finalizer `resources-finalizer.argocd.argoproj.io:background` allows using background deletion when the application is deleted
* The new sync option `ApplyOutOfSyncOnly=true` allows skipping syncing resources that are already in the desired state.
* The new sync option `PruneLast=true` allows deferring resource pruning until the last synchronization phase after all other resources are synced and healthy.
### The argocd-util CLI
Argo CD Util is a CLI tool that contains useful commands for operators who manage Argo CD. Starting from this release
the Argo CD Utility is published with every Argo CD release as a Homebrew installation.
## v1.8.7 (2021-02-26)
### Important note
This release fixed a regression regarding which cluster resources are permitted on the AppProject level.
Previous to this fix, after #3960 has been merged, all cluster resources were allowed on project level when neither of
the allow or deny lists was defined. However, the correct behavior is to block all resources in this case.
If you have Projects with empty allow and deny lists, but want the associated applications be able to sync cluster
resources, you will have to adapt your cluster resources allow lists to explicitly allow the resources.
- fix: redact sensitive data in logs (#5662)
- fix: Properly escape HTML for error message from CLI SSO (#5563)
- fix: Empty resource whitelist allowed all resources (#5540) (#5551)
## v1.8.6 (2021-02-26)
- fix: Properly escape HTML for error message from CLI SSO (#5563)
- fix: API server should not print resource body when resource update fails (#5617)
- fix: fix memory leak in application controller (#5604)
## v1.8.5 (2021-02-19)
- fix: 'argocd app wait --suspended' stuck if operation is in progress (#5511)
- fix: Presync hooks stop working after namespace resource is added in a Helm chart #5522
- docs: add the missing rbac resources to the documentation (#5476)
- refactor: optimize argocd-application-controller redis usage (#5345)
## v1.8.4 (2021-02-05)
- feat: set X-XSS-Protection while serving static content (#5412)
- fix: version info should be avaialble if anonymous access is enabled (#5422)
- fix: disable jwt claim audience validation #5381 (#5413)
- fix: /api/version should not return tools version for unauthenticated requests (#5415)
- fix: account tokens should be rejected if required capability is disabled (#5414)
- fix: tokens keep working after account is deactivated (#5402)
- fix: a request which was using a revoked project token, would still be allowed to perform requests allowed by default policy (#5378)
## v1.8.3 (2021-01-21)
- fix: make sure JWT token time fields contain only integer values (#5228)
## v1.8.2 (2021-01-09)
### Bug Fixes
- fix: updating cluster drops secret (#5220)
- fix: remove invalid assumption about OCI helm chart path (#5179)
- fix: Possible nil pointer dereference in repository API (#5128)
- fix: Possible nil pointer dereference in repocreds API (#5130)
- fix: use json serialization to store cache instead of github.com/vmihailenco/msgpack (#4965)
- fix: add liveness probe to restart repo server if it fails to server tls requests (#5110) (#5119)
- fix: Allow correct SSO redirect URL for CLI static client (#5098)
- fix: add grpc health check (#5060)
- fix: setting 'revision history limit' errors in UI (#5035)
- fix: add api-server liveness probe that catches bad data in informer (#5026)
### Refactoring
- chore: Update Dex to v2.27.0 (#5058)
- chore: Upgrade gorilla/handlers and gorilla/websocket (#5186)
- chore: Upgrade jwt-go to 4.0.0-preview1 (#5184)
## v1.8.1 (2020-12-09)
- fix: sync retry is broken for multi-phase syncs (#5017)
## v1.8.0 (2020-12-09)
## v1.8.0 (Unreleased)
### Mono-Repository Improvements
@@ -292,7 +93,7 @@ In addition to new features and enhancements, weve fixed more than 50 bugs an
## v1.7.5 (2020-09-15)
- fix: app create with -f should not ignore other options (#4322)
- fix: limit concurrent list requests across all clusters (#4328)
- fix: limit concurrent list requests accross all clusters (#4328)
- fix: fix possible deadlock in /v1/api/stream/applications and /v1/api/application APIs (#4315)
- fix: WatchResourceTree does not enforce RBAC (#4311)
- fix: app refresh API should use app resource version (#4303)
@@ -426,7 +227,7 @@ use cases, such as bootstrapping a Kubernetes cluster, or decentralized manageme
#### Other
- refactoring: GitOps engine (#3066)
- refactoring: Gitops engine (#3066)
## v1.5.8 (2020-06-16)
@@ -489,7 +290,7 @@ customizations, custom resource health checks, and more.
### Other
* New Project and Application CRD settings ([#2900](https://github.com/argoproj/argo-cd/issues/2900), [#2873](https://github.com/argoproj/argo-cd/issues/2873)) that allows customizing Argo CD behavior.
* Upgraded Dex (v2.22.0) enables seamless [SSO integration](https://www.openshift.com/blog/openshift-authentication-integration-with-argocd) with OpenShift.
* Upgraded Dex (v2.22.0) enables seamless [SSO integration](https://www.openshift.com/blog/openshift-authentication-integration-with-argocd) with Openshift.
#### Enhancements
@@ -521,7 +322,7 @@ customizations, custom resource health checks, and more.
* fix for helm repo add with flag --insecure-skip-server-verification (#3420)
* fix: app diff --local support for helm repo. #3151 (#3407)
* fix: Syncing apps incorrectly states "app synced", but this is not true (#3286)
* fix: for jsonnet when it is located in nested subdirectory and uses import (#3372)
* fix: for jsonnet when it is localed in nested subdirectory and uses import (#3372)
* fix: Update 4.5.3 redis-ha helm manifest (#3370)
* fix: return 401 error code if username does not exist (#3369)
* fix: Do not panic while running hooks with short revision (#3368)
@@ -637,7 +438,7 @@ Last-minute bugs that will be addressed in 1.5.1 shortly:
- fix: argocd-util backup produced truncated backups. import app status (#3096)
- fix: upgrade redis-ha chart and enable haproxy (#3147)
- fix: make dex server deployment init container resilient to restarts (#3136)
- fix: redact secret values of manifests stored in git (#3088)
- fix: reduct secret values of manifests stored in git (#3088)
- fix: labels not being deleted via UI (#3081)
- fix: HTTP|HTTPS|NO_PROXY env variable reading #3055 (#3063)
- fix: Correct usage text for repo add command regarding insecure repos (#3068)
@@ -747,7 +548,7 @@ an in-flight state for all Kubernetes resources including `Deployment`, `PVC`, `
[Sync Waves](https://argoproj.github.io/argo-cd/user-guide/sync-waves/) instead.
#### Enhancements
* feat: Add custom health checks for cert-manager v0.11.0 (#2689)
* feat: Add custom healthchecks for cert-manager v0.11.0 (#2689)
* feat: add git submodule support (#2495)
* feat: Add repository credential management API and CLI (addresses #2136) (#2207)
* feat: add support for --additional-headers cli flag (#2467)
@@ -932,7 +733,7 @@ There may be instances when you want to control the times during which an Argo C
#### Bug Fixes
- failed parsing on parameters with comma (#1660)
- StatefulSet with OnDelete Update Strategy stuck progressing (#1881)
- Statefulset with OnDelete Update Strategy stuck progressing (#1881)
- Warning during secret diffing (#1923)
- Error message "Unable to load data: key is missing" is confusing (#1944)
- OIDC group bindings are truncated (#2006)
@@ -1014,7 +815,7 @@ There may be instances when you want to control the times during which an Argo C
## v1.2.3 (2019-10-1)
* Make argo-cd docker images openshift friendly (#2362) (@duboisf)
* Add dest-server and dest-namespace field to reconciliation logs (#2354)
- Stop logging /repository.RepositoryService/ValidateAccess parameters (#2386)
- Stop loggin /repository.RepositoryService/ValidateAccess parameters (#2386)
## v1.2.2 (2019-09-26)
+ Resource action equivalent to `kubectl rollout restart` (#2177)
@@ -1099,7 +900,7 @@ Support for Git LFS enabled repositories - now you can store Helm charts as tar
- Wait for CRD creation during sync process (#1940)
- Added a button to select out of sync items in the sync panel (#1902)
- Proper handling of an excluded resource in an application (#1621)
- Stop repeating logs on stopped container (#1614)
- Stop repeating logs on stoped container (#1614)
- Fix git repo url parsing on application list view (#2174)
- Fix nil pointer dereference error during app reconciliation (#2146)
- Fix history api fallback implementation to support app names with dots (#2114)
@@ -1155,7 +956,7 @@ optimized which significantly reduced the number of Git requests. With v1.1 rele
#### User Defined Application Metadata
User-defined Application metadata enables the user to define a list of useful URLs for their specific application and expose those links on the UI
(e.g. reference to a CI pipeline or an application-specific management tool). These links should provide helpful shortcuts that make easier to integrate Argo CD into existing
(e.g. reference tp a CI pipeline or an application-specific management tool). These links should provide helpful shortcuts that make easier to integrate Argo CD into existing
systems by making it easier to find other components inside and outside Argo CD.
### Deprecation Notice
@@ -1519,7 +1320,7 @@ has a minimum client version of v0.12.0. Older CLI clients will be rejected.
* Deprecate componentParameterOverrides in favor of source specific config (#1207)
* Support talking to Dex using local cluster address instead of public address (#1211)
* Use Recreate deployment strategy for controller (#1315)
* Honor OS environment variables for helm commands (#1306) (@1337andre)
* Honor os environment variables for helm commands (#1306) (@1337andre)
* Disable CGO_ENABLED for server/controller binaries (#1286)
* Documentation fixes and improvements (@twz123, @yann-soubeyrand, @OmerKahani, @dulltz)
- Fix CRD creation/deletion handling (#1249)
@@ -1609,7 +1410,7 @@ running Dex (e.g. Okta, OneLogin, Auth0, Microsoft, etc...)
The optional, [Dex IDP OIDC provider](https://github.com/dexidp/dex) is still bundled as part of the
default installation, in order to provide a seamless out-of-box experience, enabling Argo CD to
integrate with non-OIDC providers, and to benefit from Dex's full range of
[connectors](https://dexidp.io/docs/connectors/).
[connectors](https://github.com/dexidp/dex/tree/master/Documentation/connectors).
#### OIDC group bindings to Project Roles
OIDC group claims from an OAuth2 provider can now be bound to a Argo CD project roles. Previously,
@@ -2011,8 +1812,8 @@ RBAC policy rules, need to be rewritten to include one extra column with the eff
+ Override parameters
## v0.1.0 (2018-03-12)
+ Define app in GitHub with dev and preprod environment using KSonnet
+ Define app in Github with dev and preprod environment using KSonnet
+ Add cluster Diff App with a cluster Deploy app in a cluster
+ Deploy a new version of the app in the cluster
+ App sync based on GitHub app config change - polling only
+ App sync based on Github app config change - polling only
+ Basic UI: App diff between Git and k8s cluster for all environments Basic GUI

View File

@@ -1,10 +1,10 @@
ARG BASE_IMAGE=docker.io/library/ubuntu:21.04
ARG BASE_IMAGE=ubuntu:20.10
####################################################################################################
# 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.16.5 as builder
FROM golang:1.14.12 as builder
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
@@ -17,7 +17,6 @@ RUN apt-get update && apt-get install -y \
make \
wget \
gcc \
sudo \
zip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@@ -28,6 +27,7 @@ ADD hack/install.sh .
ADD hack/installers installers
ADD hack/tool-versions.sh .
RUN ./install.sh packr-linux
RUN ./install.sh ksonnet-linux
RUN ./install.sh helm2-linux
RUN ./install.sh helm-linux
@@ -47,9 +47,10 @@ RUN groupadd -g 999 argocd && \
mkdir -p /home/argocd && \
chown argocd:0 /home/argocd && \
chmod g=u /home/argocd && \
chmod g=u /etc/passwd && \
apt-get update && \
apt-get dist-upgrade -y && \
apt-get install -y git git-lfs python3-pip tini gpg tzdata && \
apt-get install -y git git-lfs python3-pip tini gpg && \
apt-get clean && \
pip3 install awscli==1.18.80 && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@@ -61,9 +62,9 @@ COPY --from=builder /usr/local/bin/ks /usr/local/bin/ks
COPY --from=builder /usr/local/bin/helm2 /usr/local/bin/helm2
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
# keep uid_entrypoint.sh for backward compatibility
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
# script to add current (possibly arbitrary) user to /etc/passwd at runtime
# (if it's not already there, to be openshift friendly)
COPY uid_entrypoint.sh /usr/local/bin/uid_entrypoint.sh
# support for mounting configuration from a configmap
RUN mkdir -p /app/config/ssh && \
@@ -85,7 +86,7 @@ WORKDIR /home/argocd
####################################################################################################
# Argo CD UI stage
####################################################################################################
FROM docker.io/library/node:12.18.4 as argocd-ui
FROM node:12.18.4 as argocd-ui
WORKDIR /src
ADD ["ui/package.json", "ui/yarn.lock", "./"]
@@ -96,12 +97,14 @@ ADD ["ui/", "."]
ARG ARGO_VERSION=latest
ENV ARGO_VERSION=$ARGO_VERSION
RUN NODE_ENV='production' NODE_ONLINE_ENV='online' yarn build
RUN NODE_ENV='production' yarn build
####################################################################################################
# Argo CD Build stage which performs the actual build of Argo CD binaries
####################################################################################################
FROM golang:1.16.5 as argocd-build
FROM golang:1.14.12 as argocd-build
COPY --from=builder /usr/local/bin/packr /usr/local/bin/packr
WORKDIR /go/src/github.com/argoproj/argo-cd
@@ -112,7 +115,6 @@ RUN go mod download
# Perform the build
COPY . .
COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app
RUN make argocd-all
ARG BUILD_ALL_CLIS=true
@@ -126,11 +128,13 @@ RUN if [ "$BUILD_ALL_CLIS" = "true" ] ; then \
####################################################################################################
FROM argocd-base
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
COPY --from=argocd-ui ./src/dist/app /shared/app
USER root
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex
USER 999
USER 999

View File

@@ -11,4 +11,9 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util
RUN ln -s /usr/local/bin/argocd-darwin-amd64 /usr/local/bin/argocd-util-darwin-amd64
RUN ln -s /usr/local/bin/argocd-windows-amd64.exe /usr/local/bin/argocd-util-windows-amd64.exe
USER 999
COPY --from=argocd-ui ./src/dist/app /shared/app

View File

@@ -1,7 +1,8 @@
PACKAGE=github.com/argoproj/argo-cd/v2/common
PACKAGE=github.com/argoproj/argo-cd/common
CURRENT_DIR=$(shell pwd)
DIST_DIR=${CURRENT_DIR}/dist
CLI_NAME=argocd
UTIL_CLI_NAME=argocd-util
BIN_NAME=argocd
HOST_OS:=$(shell go env GOOS)
@@ -12,6 +13,7 @@ BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
GIT_COMMIT=$(shell git rev-parse HEAD)
GIT_TAG=$(shell if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)
GIT_TREE_STATE=$(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)
PACKR_CMD=$(shell if [ "`which packr`" ]; then echo "packr"; else echo "go run github.com/gobuffalo/packr/packr"; fi)
VOLUME_MOUNT=$(shell if test "$(go env GOOS)" = "darwin"; then echo ":delegated"; elif test selinuxenabled; then echo ":delegated"; else echo ""; fi)
KUBECTL_VERSION=$(shell go list -m all | grep k8s.io/client-go | cut -d ' ' -f5)
@@ -43,9 +45,6 @@ ARGOCD_E2E_REPOSERVER_PORT?=8081
ARGOCD_E2E_REDIS_PORT?=6379
ARGOCD_E2E_DEX_PORT?=5556
ARGOCD_E2E_YARN_HOST?=localhost
ARGOCD_E2E_DISABLE_AUTH?=
ARGOCD_E2E_TEST_TIMEOUT?=20m
ARGOCD_IN_CI?=false
ARGOCD_TEST_E2E?=true
@@ -74,10 +73,6 @@ define run-in-test-server
-e ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
-e ARGOCD_E2E_TEST=$(ARGOCD_E2E_TEST) \
-e ARGOCD_E2E_YARN_HOST=$(ARGOCD_E2E_YARN_HOST) \
-e ARGOCD_E2E_DISABLE_AUTH=$(ARGOCD_E2E_DISABLE_AUTH) \
-e ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} \
-e ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} \
-e ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} \
-v ${DOCKER_SRC_MOUNT} \
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
@@ -86,7 +81,6 @@ define run-in-test-server
-w ${DOCKER_WORKDIR} \
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
-p 4000:4000 \
-p 5000:5000 \
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
bash -c "$(1)"
endef
@@ -158,7 +152,7 @@ IMAGE_PREFIX=${IMAGE_NAMESPACE}/
endif
.PHONY: all
all: cli image
all: cli image argocd-util
# 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
@@ -209,7 +203,11 @@ cli: test-tools-image
.PHONY: cli-local
cli-local: clean-debug
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
.PHONY: cli-argocd
cli-argocd:
go build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
.PHONY: release-cli
release-cli: clean-debug image
@@ -219,6 +217,16 @@ release-cli: clean-debug image
docker cp tmp-argocd-linux:/usr/local/bin/argocd-windows-amd64.exe ${DIST_DIR}/argocd-windows-amd64.exe
docker rm tmp-argocd-linux
.PHONY: argocd-util
argocd-util: clean-debug
# Build argocd-util as a statically linked binary, so it could run within the alpine-based dex container (argoproj/argo-cd#844)
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${UTIL_CLI_NAME} ./cmd
# .PHONY: dev-tools-image
# dev-tools-image:
# docker build -t $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) . -f hack/Dockerfile.dev-tools
# docker tag $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE):$(DEV_TOOLS_VERSION)
.PHONY: test-tools-image
test-tools-image:
docker build --build-arg UID=$(shell id -u) -t $(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE) -f test/container/Dockerfile .
@@ -235,19 +243,25 @@ manifests: test-tools-image
# consolidated binary for cli, util, server, repo-server, controller
.PHONY: argocd-all
argocd-all: clean-debug
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
# NOTE: we use packr to do the build instead of go, since we embed swagger files and policy.csv
# files into the go binary
.PHONY: server
server: clean-debug
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
.PHONY: repo-server
repo-server:
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
.PHONY: controller
controller:
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
.PHONY: packr
packr:
go build -o ${DIST_DIR}/packr github.com/gobuffalo/packr/packr/
.PHONY: image
ifeq ($(DEV_IMAGE), true)
@@ -255,18 +269,19 @@ ifeq ($(DEV_IMAGE), true)
# which speeds up builds. Dockerfile.dev needs to be copied into dist to perform the build, since
# the dist directory is under .dockerignore.
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
image:
image: packr
docker build -t argocd-base --target argocd-base .
docker build -t argocd-ui --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/'
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-darwin-amd64 ./cmd
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-windows-amd64.exe ./cmd
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 dist/packr build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 dist/packr build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-darwin-amd64 ./cmd
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 dist/packr build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-windows-amd64.exe ./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-dex
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-util
ln -sfn ${DIST_DIR}/argocd-darwin-amd64 ${DIST_DIR}/argocd-util-darwin-amd64
ln -sfn ${DIST_DIR}/argocd-windows-amd64.exe ${DIST_DIR}/argocd-util-windows-amd64.exe
cp Dockerfile.dev dist
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
else
@@ -292,7 +307,7 @@ mod-download: test-tools-image
.PHONY: mod-download-local
mod-download-local:
go mod download && go mod tidy # go mod download changes go.sum https://github.com/golang/go/issues/42970
go mod download
.PHONY: mod-vendor
mod-vendor: test-tools-image
@@ -382,7 +397,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
ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e
ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout 20m -v ./test/e2e
# Spawns a shell in the test server container for debugging purposes
debug-test-server: test-tools-image
@@ -414,14 +429,14 @@ start-e2e-local:
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
ARGOCD_GPG_DATA_PATH=/tmp/argo-e2e/app/config/gpg/source \
ARGOCD_GNUPGHOME=/tmp/argo-e2e/app/config/gpg/keys \
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
ARGOCD_GPG_ENABLED=true \
ARGOCD_E2E_DISABLE_AUTH=false \
ARGOCD_ZJWT_FEATURE_FLAG=always \
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
ARGOCD_E2E_TEST=true \
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
# Cleans VSCode debug.test files from sub-dirs to prevent them from being included in by golang embed
# Cleans VSCode debug.test files from sub-dirs to prevent them from being included in packr boxes
.PHONY: clean-debug
clean-debug:
-find ${CURRENT_DIR} -name debug.test | xargs rm -f
@@ -447,7 +462,7 @@ start-local: mod-vendor-local dep-ui-local
mkdir -p /tmp/argocd-local/gpg/source
ARGOCD_ZJWT_FEATURE_FLAG=always \
ARGOCD_IN_CI=false \
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
ARGOCD_GPG_ENABLED=true \
ARGOCD_E2E_TEST=false \
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
@@ -509,15 +524,16 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal
# Installs all tools required for running unit & end-to-end tests (Linux packages)
.PHONY: install-test-tools-local
install-test-tools-local:
./hack/install.sh kustomize-linux
./hack/install.sh ksonnet-linux
./hack/install.sh helm2-linux
./hack/install.sh helm-linux
sudo ./hack/install.sh packr-linux
sudo ./hack/install.sh kustomize-linux
sudo ./hack/install.sh ksonnet-linux
sudo ./hack/install.sh helm2-linux
sudo ./hack/install.sh helm-linux
# Installs all tools required for running codegen (Linux packages)
.PHONY: install-codegen-tools-local
install-codegen-tools-local:
./hack/install.sh codegen-tools
sudo ./hack/install.sh codegen-tools
# Installs all tools required for running codegen (Go packages)
.PHONY: install-go-tools-local
@@ -530,6 +546,3 @@ dep-ui: test-tools-image
dep-ui-local:
cd ui && yarn install
start-test-k8s:
go run ./hack/k8s

11
OWNERS
View File

@@ -5,16 +5,13 @@ owners:
approvers:
- alexec
- alexmt
- dthomson25
- jannfis
- jessesuen
- jgwest
- mayzhang2000
- rachelwang20
reviewers:
- dthomson25
- tetchel
- jgwest
- wtam2018
- ishitasequeira
- reginapizza
- hblixt
- chetan-rns
- tetchel

View File

@@ -1,9 +1,8 @@
controller: 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 go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
api-server: 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 go run ./cmd/main.go --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} "
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && 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:v2.27.0 serve /dex.yaml"
redis: bash -c "if [ $ARGOCD_REDIS_LOCAL == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.4-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
api-server: 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 go run ./cmd/main.go --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} --staticassets ui/dist/app"
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && 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:v2.27.0 serve /dex.yaml"
redis: docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:5.0.10-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} 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 go run ./cmd/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
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}

View File

@@ -28,44 +28,26 @@ Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.
To learn more about Argo CD [go to the complete documentation](https://argo-cd.readthedocs.io/).
Check live demo at https://cd.apps.argoproj.io/.
## Community
## Community Blogs and Presentations
### Contribution, Discussion and Support
You can reach the Argo CD community and developers via the following channels:
* Q & A : [Github Discussions](https://github.com/argoproj/argo-cd/discussions)
* Chat : [The #argo-cd Slack channel](https://argoproj.github.io/community/join-slack)
* Contributors Office Hours: [Every Thursday](https://calendar.google.com/calendar/u/0/embed?src=argoproj@gmail.com) | [Agenda](https://docs.google.com/document/d/1ttgw98MO45Dq7ZUHpIiOIEfbyeitKHNfMjbY5dLLMKQ)
* User Community meeting: [Every other Wednesday](https://calendar.google.com/calendar/u/0/embed?src=argoproj@gmail.com) | [Agenda](https://docs.google.com/document/d/1xkoFkVviB70YBzSEa4bDnu-rUZ1sIFtwKKG1Uw8XsY8)
Participation in the Argo CD project is governed by the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md)
### Blogs and Presentations
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)
1. [Couchbase - How To Run a Database Cluster in Kubernetes Using Argo CD](https://youtu.be/nkPoPaVzExY)
1. [Automation of Everything - How To Combine Argo Events, Workflows & Pipelines, CD, and Rollouts](https://youtu.be/XNXJtxkUKeY)
1. [Environments Based On Pull Requests (PRs): Using Argo CD To Apply GitOps Principles On Previews](https://youtu.be/cpAaI8p4R60)
1. [Argo CD: Applying GitOps Principles To Manage Production Environment In Kubernetes](https://youtu.be/vpWQeoaiRM4)
1. [Creating Temporary Preview Environments Based On Pull Requests With Argo CD And Codefresh](https://codefresh.io/continuous-deployment/creating-temporary-preview-environments-based-pull-requests-argo-cd-codefresh/)
1. [Tutorial: Everything You Need To Become A GitOps Ninja](https://www.youtube.com/watch?v=r50tRQjisxw) 90m tutorial on GitOps and Argo CD.
1. [Comparison of Argo CD, Spinnaker, Jenkins X, and Tekton](https://www.inovex.de/blog/spinnaker-vs-argo-cd-vs-tekton-vs-jenkins-x/)
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://www.ibm.com/cloud/blog/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2)
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://medium.com/ibm-cloud/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2-4395af317359)
1. [GitOps for Kubeflow using Argo CD](https://v0-6.kubeflow.org/docs/use-cases/gitops-for-kubeflow/)
1. [GitOps Toolsets on Kubernetes with CircleCI and Argo CD](https://www.digitalocean.com/community/tutorials/webinar-series-gitops-tool-sets-on-kubernetes-with-circleci-and-argo-cd)
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager](https://www.ibm.com/blogs/bluemix/2019/02/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2/)
1. [CI/CD in Light Speed with K8s and Argo CD](https://www.youtube.com/watch?v=OdzH82VpMwI&feature=youtu.be)
1. [Machine Learning as Code](https://www.youtube.com/watch?v=VXrGp5er1ZE&t=0s&index=135&list=PLj6h78yzYM2PZf9eA7bhWnIh_mK1vyOfU). Among other things, describes how Kubeflow uses Argo CD to implement GitOPs for ML
1. [Argo CD - GitOps Continuous Delivery for Kubernetes](https://www.youtube.com/watch?v=aWDIQMbp1cc&feature=youtu.be&t=1m4s)
1. [Introduction to Argo CD : Kubernetes DevOps CI/CD](https://www.youtube.com/watch?v=2WSJF7d8dUg&feature=youtu.be)
1. [GitOps Deployment and Kubernetes - using Argo CD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
1. [GitOps Deployment and Kubernetes - using ArgoCD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
1. [Deploy Argo CD with Ingress and TLS in Three Steps: No YAML Yak Shaving Required](https://itnext.io/deploy-argo-cd-with-ingress-and-tls-in-three-steps-no-yaml-yak-shaving-required-bc536d401491)
1. [GitOps Continuous Delivery with Argo and Codefresh](https://codefresh.io/events/cncf-member-webinar-gitops-continuous-delivery-argo-codefresh/)
1. [Stay up to date with Argo CD and Renovate](https://mjpitz.com/blog/2020/12/03/renovate-your-gitops/)
1. [Stay up to date with ArgoCD and Renovate](https://mjpitz.com/blog/2020/12/03/renovate-your-gitops/)
1. [Setting up Argo CD with Helm](https://www.arthurkoziel.com/setting-up-argocd-with-helm/)
1. [Applied GitOps with Argo CD](https://thenewstack.io/applied-gitops-with-argocd/)
1. [Applied GitOps with ArgoCD](https://thenewstack.io/applied-gitops-with-argocd/)
1. [Solving configuration drift using GitOps with Argo CD](https://www.cncf.io/blog/2020/12/17/solving-configuration-drift-using-gitops-with-argo-cd/)
1. [Decentralized GitOps over environments](https://blogs.sap.com/2021/05/06/decentralized-gitops-over-environments/)

View File

@@ -1,6 +1,6 @@
# Security Policy for Argo CD
Version: **v1.1 (2020-06-29)**
Version: **v1.0 (2020-02-26)**
## Preface
@@ -8,27 +8,6 @@ As a deployment tool, Argo CD needs to have production access which makes
security a very important topic. The Argoproj team takes security very
seriously and is continuously working on improving it.
## A word about security scanners
Many organisations these days employ security scanners to validate their
container images before letting them on their clusters, and that is a good
thing. However, the quality and results of these scanners vary greatly,
many of them produce false positives and require people to look at the
issues reported and validate them for correctness. A great example of that
is, that some scanners report kernel vulnerabilities for container images
just because they are derived from some distribution.
We kindly ask you to not raise issues or contact us regarding any issues
that are found by your security scanner. Many of those produce a lot of false
positives, and many of these issues don't affect Argo CD. We do have scanners
in place for our code, dependencies and container images that we publish. We
are well aware of the issues that may affect Argo CD and are constantly
working on the remediation of those that affect Argo CD and our users.
If you believe that we might have missed an issue that we should take a look
at (that can happen), then please discuss it with us. But please, do validate
that assumption before at least roughly.
## Supported Versions
We currently support the most recent release (`N`, e.g. `1.8`) and the release

View File

@@ -8,33 +8,24 @@ Currently, the following organizations are **officially** using Argo CD:
1. [3Rein](https://www.3rein.com/)
1. [7shifts](https://www.7shifts.com/)
1. [Adevinta](https://www.adevinta.com/)
1. [Adventure](https://jp.adventurekk.com/)
1. [Alibaba Group](https://www.alibabagroup.com/)
1. [Ambassador Labs](https://www.getambassador.io/)
1. [Ant Group](https://www.antgroup.com/)
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
1. [AppDirect](https://www.appdirect.com)
1. [Arctiq Inc.](https://www.arctiq.ca)
1. [ARZ Allgemeines Rechenzentrum GmbH ](https://www.arz.at/)
1. [Axual B.V.](https://axual.com)
1. [Baloise](https://www.baloise.com)
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
1. [Beat](https://thebeat.co/en/)
1. [Beez Innovation Labs](https://www.beezlabs.com/)
1. [BioBox Analytics](https://biobox.io)
1. [BMW Group](https://www.bmwgroup.com/)
1. [Camptocamp](https://camptocamp.com)
1. [CARFAX](https://www.carfax.com)
1. [Celonis](https://www.celonis.com/)
1. [Chime](https://www.chime.com)
1. [Codefresh](https://www.codefresh.io/)
1. [Codility](https://www.codility.com/)
1. [Commonbond](https://commonbond.co/)
1. [Crédit Agricole](https://www.ca-cib.com)
1. [CROZ d.o.o.](https://croz.net/)
1. [CyberAgent](https://www.cyberagent.co.jp/en/)
1. [Cybozu](https://cybozu-global.com)
1. [Chargetrip](https://chargetrip.com)
1. [D2iQ](https://www.d2iq.com)
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
1. [EDF Renewables](https://www.edf-re.com/)
@@ -46,24 +37,15 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Fave](https://myfave.com)
1. [Future PLC](https://www.futureplc.com/)
1. [Garner](https://www.garnercorp.com)
1. [G DATA CyberDefense AG](https://www.gdata-software.com/)
1. [Generali Deutschland AG](https://www.generali.de/)
1. [Glovo](https://www.glovoapp.com)
1. [GMETRI](https://gmetri.com/)
1. [Gojek](https://www.gojek.io/)
1. [Greenpass](https://www.greenpass.com.br/)
1. [Handelsbanken](https://www.handelsbanken.se)
1. [Healy](https://www.healyworld.net)
1. [hipages](https://hipages.com.au/)
1. [Hiya](https://hiya.com)
1. [Honestbank](https://honestbank.com)
1. [IBM](https://www.ibm.com/)
1. [Index Exchange](https://www.indexexchange.com/)
1. [InsideBoard](https://www.insideboard.com)
1. [Intuit](https://www.intuit.com/)
1. [Joblift](https://joblift.com/)
1. [JovianX](https://www.jovianx.com/)
1. [Karrot](https://www.daangn.com/)
1. [Kasa](https://kasa.co.kr/)
1. [Keptn](https://keptn.sh)
1. [Kinguin](https://www.kinguin.net/)
@@ -81,7 +63,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Money Forward](https://corp.moneyforward.com/en/)
1. [MOO Print](https://www.moo.com/)
1. [MTN Group](https://www.mtn.com/)
1. [Natura &Co](https://naturaeco.com/)
1. [New Relic](https://newrelic.com/)
1. [Nextdoor](https://nextdoor.com/)
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
@@ -96,7 +77,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [PayPay](https://paypay.ne.jp/)
1. [Peloton Interactive](https://www.onepeloton.com/)
1. [Pipefy](https://www.pipefy.com/)
1. [Polarpoint.io](https://polarpoint.io)
1. [Preferred Networks](https://preferred.jp/en/)
1. [Prudential](https://prudential.com.sg)
1. [PUBG](https://www.pubg.com)
@@ -109,7 +89,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [Robotinfra](https://www.robotinfra.com)
1. [Saildrone](https://www.saildrone.com/)
1. [Saloodo! GmbH](https://www.saloodo.com)
1. [Schwarz IT](https://jobs.schwarz/it-mission)
1. [Speee](https://speee.jp/)
1. [Spendesk](https://spendesk.com/)
1. [Sumo Logic](https://sumologic.com/)
@@ -136,22 +115,6 @@ Currently, the following organizations are **officially** using Argo CD:
1. [VSHN - The DevOps Company](https://vshn.ch/)
1. [Walkbase](https://www.walkbase.com/)
1. [WeMo Scooter](https://www.wemoscooter.com/)
1. [Webstores](https://www.webstores.nl)
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
1. [Witick](https://witick.io/)
1. [WooliesX](https://wooliesx.com.au/)
1. [Woolworths Group](https://www.woolworthsgroup.com.au/)
1. [WSpot](https://www.wspot.com.br/)
1. [Yieldlab](https://www.yieldlab.de/)
1. [Zimpler](https://www.zimpler.com/)
1. [Sap Labs](http://sap.com)
1. [Smilee.io](https://smilee.io)
1. [Metanet](http://www.metanet.co.kr/en/)
1. [Unifonic Inc](https://www.unifonic.com/)
1. [Tamkeen Technologies](https://tamkeentech.sa/)
1. [Kaltura](https://corp.kaltura.com/)
1. [Boticario](https://www.boticario.com.br/)
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
1. [MariaDB](https://mariadb.com)
1. [Lightricks](https://www.lightricks.com/)
1. [Snapp](https://snapp.ir/)
1. [Sap Labs] (http://sap.com)

View File

@@ -1 +1 @@
2.1.6
1.9.0

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -1,7 +0,0 @@
package assets
import "embed"
// Embedded contains embedded assets
//go:embed *
var Embedded embed.FS

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@ package commands
import (
"context"
"fmt"
"math"
"time"
@@ -13,21 +12,20 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/controller"
"github.com/argoproj/argo-cd/v2/controller/sharding"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/env"
"github.com/argoproj/argo-cd/v2/util/errors"
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/v2/util/tls"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller"
"github.com/argoproj/argo-cd/controller/sharding"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/reposerver/apiclient"
cacheutil "github.com/argoproj/argo-cd/util/cache"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/env"
"github.com/argoproj/argo-cd/util/errors"
kubeutil "github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/settings"
)
const (
@@ -52,8 +50,6 @@ func NewCommand() *cobra.Command {
kubectlParallelismLimit int64
cacheSrc func() (*appstatecache.Cache, error)
redisClient *redis.Client
repoServerPlaintext bool
repoServerStrictTLS bool
)
var command = cobra.Command{
Use: cliName,
@@ -75,34 +71,8 @@ func NewCommand() *cobra.Command {
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
var resyncDuration time.Duration
if appResyncPeriod == 0 {
// Re-sync should be disabled if period is 0. Set duration to a very long duration
resyncDuration = time.Hour * 24 * 365 * 100
} else {
resyncDuration = time.Duration(appResyncPeriod) * time.Second
}
tlsConfig := apiclient.TLSConfiguration{
DisableTLS: repoServerPlaintext,
StrictValidation: repoServerStrictTLS,
}
// Load CA information to use for validating connections to the
// repository server, if strict TLS validation was requested.
if !repoServerPlaintext && repoServerStrictTLS {
pool, err := tls.LoadX509CertPool(
fmt.Sprintf("%s/controller/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
fmt.Sprintf("%s/controller/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
)
if err != nil {
log.Fatalf("%v", err)
}
tlsConfig.Certificates = pool
}
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig)
resyncDuration := time.Duration(appResyncPeriod) * time.Second
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -144,20 +114,18 @@ func NewCommand() *cobra.Command {
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().Int64Var(&appResyncPeriod, "app-resync", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application resync.")
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address.")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
command.Flags().IntVar(&statusProcessors, "status-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS", 20, 0, math.MaxInt32), "Number of application status processors")
command.Flags().IntVar(&operationProcessors, "operation-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS", 10, 0, math.MaxInt32), "Number of application operation processors")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address.")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt64), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)")
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 5, 0, math.MaxInt32), "Specifies timeout between application self heal attempts")
command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", 0*time.Second, "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)")
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", 5, "Specifies timeout between application self heal attempts")
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.")
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server")
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
redisClient = client
})

View File

@@ -14,11 +14,11 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/dex"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/settings"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/dex"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/settings"
)
const (

View File

@@ -14,21 +14,21 @@ import (
"github.com/spf13/cobra"
"google.golang.org/grpc/health/grpc_health_v1"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/reposerver"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache"
"github.com/argoproj/argo-cd/v2/reposerver/metrics"
"github.com/argoproj/argo-cd/v2/reposerver/repository"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/env"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/gpg"
"github.com/argoproj/argo-cd/v2/util/healthz"
ioutil "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/tls"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/reposerver"
"github.com/argoproj/argo-cd/reposerver/apiclient"
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
"github.com/argoproj/argo-cd/reposerver/metrics"
"github.com/argoproj/argo-cd/reposerver/repository"
cacheutil "github.com/argoproj/argo-cd/util/cache"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/env"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/gpg"
"github.com/argoproj/argo-cd/util/healthz"
ioutil "github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/tls"
)
const (
@@ -67,10 +67,8 @@ func NewCommand() *cobra.Command {
listenPort int
metricsPort int
cacheSrc func() (*reposervercache.Cache, error)
tlsConfigCustomizer tls.ConfigCustomizer
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
redisClient *redis.Client
disableTLS bool
)
var command = cobra.Command{
Use: cliName,
@@ -81,11 +79,8 @@ func NewCommand() *cobra.Command {
cli.SetLogFormat(cmdutil.LogFormat)
cli.SetLogLevel(cmdutil.LogLevel)
if !disableTLS {
var err error
tlsConfigCustomizer, err = tlsConfigCustomizerSrc()
errors.CheckError(err)
}
tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
errors.CheckError(err)
cache, err := cacheSrc()
errors.CheckError(err)
@@ -109,7 +104,7 @@ func NewCommand() *cobra.Command {
// connect to itself to make sure repo server is able to serve connection
// used by liveness probe to auto restart repo server
// see https://github.com/argoproj/argo-cd/issues/5110 for more information
conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60, &apiclient.TLSConfiguration{DisableTLS: disableTLS})
conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60)
if err != nil {
return err
}
@@ -151,15 +146,12 @@ func NewCommand() *cobra.Command {
return nil
},
}
if cmdutil.LogFormat == "" {
cmdutil.LogFormat = os.Getenv("ARGOCD_REPO_SERVER_LOGLEVEL")
}
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().Int64Var(&parallelismLimit, "parallelismlimit", int64(env.ParseNumFromEnv("ARGOCD_REPO_SERVER_PARALLELISM_LIMIT", 0, 0, math.MaxInt32)), "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().Int64Var(&parallelismLimit, "parallelismlimit", 0, "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
command.Flags().IntVar(&listenPort, "port", common.DefaultPortRepoServer, "Listen on given port for incoming connections")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port")
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {

View File

@@ -2,8 +2,6 @@ package commands
import (
"context"
"fmt"
"math"
"time"
"github.com/argoproj/pkg/stats"
@@ -13,18 +11,18 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/server"
servercache "github.com/argoproj/argo-cd/v2/server/cache"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/env"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/tls"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/server"
servercache "github.com/argoproj/argo-cd/server/cache"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/env"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/tls"
)
const (
@@ -52,6 +50,7 @@ func NewCommand() *cobra.Command {
glogLevel int
clientConfig clientcmd.ClientConfig
repoServerTimeoutSeconds int
staticAssetsDir string
baseHRef string
rootPath string
repoServerAddress string
@@ -61,9 +60,6 @@ func NewCommand() *cobra.Command {
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
cacheSrc func() (*servercache.Cache, error)
frameOptions string
repoServerPlaintext bool
repoServerStrictTLS bool
staticAssetsDir string
)
var command = &cobra.Command{
Use: cliName,
@@ -97,25 +93,8 @@ func NewCommand() *cobra.Command {
appclientsetConfig = kube.AddFailureRetryWrapper(appclientsetConfig, failureRetryCount, failureRetryPeriodMilliSeconds)
}
appclientset := appclientset.NewForConfigOrDie(appclientsetConfig)
tlsConfig := apiclient.TLSConfiguration{
DisableTLS: repoServerPlaintext,
StrictValidation: repoServerStrictTLS,
}
repoclientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
// Load CA information to use for validating connections to the
// repository server, if strict TLS validation was requested.
if !repoServerPlaintext && repoServerStrictTLS {
pool, err := tls.LoadX509CertPool(
fmt.Sprintf("%s/server/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
fmt.Sprintf("%s/server/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
)
if err != nil {
log.Fatalf("%v", err)
}
tlsConfig.Certificates = pool
}
repoclientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig)
if rootPath != "" {
if baseHRef != "" && baseHRef != rootPath {
log.Warnf("--basehref and --rootpath had conflict: basehref: %s rootpath: %s", baseHRef, rootPath)
@@ -128,6 +107,7 @@ func NewCommand() *cobra.Command {
ListenPort: listenPort,
MetricsPort: metricsPort,
Namespace: namespace,
StaticAssetsDir: staticAssetsDir,
BaseHRef: baseHRef,
RootPath: rootPath,
KubeClientset: kubeclientset,
@@ -140,7 +120,6 @@ func NewCommand() *cobra.Command {
Cache: cache,
XFrameOptions: frameOptions,
RedisClient: redisClient,
StaticAssetsDir: staticAssetsDir,
}
stats.RegisterStackDumper()
@@ -158,24 +137,22 @@ func NewCommand() *cobra.Command {
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
command.Flags().BoolVar(&insecure, "insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_INSECURE", false), "Run server without TLS")
command.Flags().StringVar(&staticAssetsDir, "staticassets", env.StringFromEnv("ARGOCD_SERVER_STATIC_ASSETS", "/shared/app"), "Directory path that contains additional static assets")
command.Flags().StringVar(&baseHRef, "basehref", env.StringFromEnv("ARGOCD_SERVER_BASEHREF", "/"), "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
command.Flags().StringVar(&rootPath, "rootpath", env.StringFromEnv("ARGOCD_SERVER_ROOTPATH", ""), "Used if Argo CD is running behind reverse proxy under subpath different from /")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
command.Flags().BoolVar(&insecure, "insecure", false, "Run server without TLS")
command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
command.Flags().StringVar(&baseHRef, "basehref", "/", "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
command.Flags().StringVar(&rootPath, "rootpath", "", "Used if Argo CD is running behind reverse proxy under subpath different from /")
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address")
command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address")
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", false), "Enable GZIP compression")
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address")
command.Flags().StringVar(&dexServerAddress, "dex-server", common.DefaultDexServerAddr, "Dex server address")
command.Flags().BoolVar(&disableAuth, "disable-auth", false, "Disable client authentication")
command.Flags().BoolVar(&enableGZip, "enable-gzip", false, "Enable GZIP compression")
command.AddCommand(cli.NewVersionCmd(cliName))
command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port")
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server")
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to repo server")
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
command.Flags().StringVar(&frameOptions, "x-frame-options", "sameorigin", "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) {
redisClient = client

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"context"
@@ -10,6 +10,8 @@ import (
"sort"
"time"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
@@ -20,104 +22,37 @@ import (
kubecache "k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/controller"
"github.com/argoproj/argo-cd/v2/controller/cache"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/config"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller"
"github.com/argoproj/argo-cd/controller/cache"
"github.com/argoproj/argo-cd/controller/metrics"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
appinformers "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
"github.com/argoproj/argo-cd/reposerver/apiclient"
cacheutil "github.com/argoproj/argo-cd/util/cache"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/errors"
kubeutil "github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/settings"
)
func NewAppCommand() *cobra.Command {
func NewAppsCommand() *cobra.Command {
var command = &cobra.Command{
Use: "app",
Short: "Manage applications configuration",
Use: "apps",
Short: "Utility commands operate on ArgoCD applications",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewGenAppSpecCommand())
command.AddCommand(NewReconcileCommand())
command.AddCommand(NewDiffReconcileResults())
return command
}
// NewGenAppSpecCommand generates declarative configuration file for given application
func NewGenAppSpecCommand() *cobra.Command {
var (
appOpts cmdutil.AppOptions
fileURL string
appName string
labels []string
outputFormat string
annotations []string
inline bool
)
var command = &cobra.Command{
Use: "generate-spec APPNAME",
Short: "Generate declarative config for an application",
Example: `
# Generate declarative config for a directory app
argocd admin app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse
# Generate declarative config for a Jsonnet app
argocd admin app generate-spec jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2
# Generate declarative config for a Helm app
argocd admin app generate-spec helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2
# Generate declarative config for a Helm app from a Helm repo
argocd admin app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Generate declarative config for a Kustomize app
argocd admin app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
# Generate declarative config for a app using a custom tool:
argocd admin app generate-spec ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane
`,
Run: func(c *cobra.Command, args []string) {
app, err := cmdutil.ConstructApp(fileURL, appName, labels, annotations, args, appOpts, c.Flags())
errors.CheckError(err)
if app.Name == "" {
c.HelpFunc()(c, args)
os.Exit(1)
}
out, closer, err := getOutWriter(inline, fileURL)
errors.CheckError(err)
defer io.Close(closer)
errors.CheckError(PrintResources(outputFormat, out, app))
},
}
command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set (DEPRECATED)")
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app")
command.Flags().StringArrayVarP(&annotations, "annotations", "", []string{}, "Set metadata annotations (e.g. example=value)")
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
command.Flags().BoolVarP(&inline, "inline", "i", false, "If set then generated resource is written back to the file specified in --file flag")
// Only complete files with appropriate extension.
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
errors.CheckError(err)
cmdutil.AddAppFlags(command, &appOpts)
return command
}
type appReconcileResult struct {
Name string `json:"name"`
Health *v1alpha1.HealthStatus `json:"health"`
@@ -243,7 +178,7 @@ func NewReconcileCommand() *cobra.Command {
}
outputPath := args[0]
errors.CheckError(os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true"))
errors.CheckError(os.Setenv(common.EnvVarFakeInClusterConfig, "true"))
cfg, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
@@ -253,12 +188,11 @@ func NewReconcileCommand() *cobra.Command {
if refresh {
if repoServerAddress == "" {
printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.")
overrides := clientcmd.ConfigOverrides{}
repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
repoServerPort, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-repo-server", 8081, namespace)
errors.CheckError(err)
repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort)
}
repoServerClient := apiclient.NewRepoServerClientset(repoServerAddress, 60, apiclient.TLSConfiguration{DisableTLS: false, StrictValidation: false})
repoServerClient := apiclient.NewRepoServerClientset(repoServerAddress, 60)
appClientset := appclientset.NewForConfigOrDie(cfg)
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
@@ -396,7 +330,7 @@ func reconcileApplications(
return nil, err
}
res := appStateManager.CompareAppState(&app, proj, app.Spec.Source.TargetRevision, app.Spec.Source, false, false, nil)
res := appStateManager.CompareAppState(&app, proj, app.Spec.Source.TargetRevision, app.Spec.Source, false, nil)
items = append(items, appReconcileResult{
Name: app.Name,
Conditions: app.Status.Conditions,

View File

@@ -1,9 +1,9 @@
package admin
package commands
import (
"testing"
"github.com/argoproj/argo-cd/v2/test"
"github.com/argoproj/argo-cd/test"
clustermocks "github.com/argoproj/gitops-engine/pkg/cache/mocks"
"github.com/argoproj/gitops-engine/pkg/health"
@@ -16,15 +16,16 @@ import (
kubefake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
cachemocks "github.com/argoproj/argo-cd/v2/controller/cache/mocks"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appfake "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
statecache "github.com/argoproj/argo-cd/controller/cache"
cachemocks "github.com/argoproj/argo-cd/controller/cache/mocks"
"github.com/argoproj/argo-cd/controller/metrics"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appfake "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/reposerver/apiclient/mocks"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/settings"
)
func TestGetReconcileResults(t *testing.T) {
@@ -78,7 +79,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
Spec: v1alpha1.ApplicationSpec{
Project: "default",
Destination: v1alpha1.ApplicationDestination{
Server: v1alpha1.KubernetesInternalAPIServerAddr,
Server: common.KubernetesInternalAPIServerAddr,
Namespace: "default",
},
},

View File

@@ -0,0 +1,547 @@
package commands
import (
"bufio"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/settings"
)
const (
// CLIName is the name of the CLI
cliName = "argocd-util"
// YamlSeparator separates sections of a YAML file
yamlSeparator = "---\n"
)
var (
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
)
// NewCommand returns a new instance of an argocd command
func NewCommand() *cobra.Command {
var (
pathOpts = clientcmd.NewDefaultPathOptions()
)
var command = &cobra.Command{
Use: cliName,
Short: "argocd-util tools used by Argo CD",
Long: "argocd-util has internal utility tools used by Argo CD",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(cli.NewVersionCmd(cliName))
command.AddCommand(NewImportCommand())
command.AddCommand(NewExportCommand())
command.AddCommand(NewClusterConfig())
command.AddCommand(NewProjectsCommand())
command.AddCommand(NewSettingsCommand())
command.AddCommand(NewAppsCommand())
command.AddCommand(NewRBACCommand())
command.AddCommand(NewGenerateConfigCommand(pathOpts))
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
return command
}
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewImportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
prune bool
dryRun bool
)
var command = cobra.Command{
Use: "import SOURCE",
Short: "Import Argo CD data from stdin (specify `-') or a file",
Run: func(c *cobra.Command, args []string) {
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
config.QPS = 100
config.Burst = 50
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
acdClients := newArgoCDClientsets(config, namespace)
var input []byte
if in := args[0]; in == "-" {
input, err = ioutil.ReadAll(os.Stdin)
} else {
input, err = ioutil.ReadFile(in)
}
errors.CheckError(err)
var dryRunMsg string
if dryRun {
dryRunMsg = " (dry run)"
}
// pruneObjects tracks live objects and it's current resource version. any remaining
// items in this map indicates the resource should be pruned since it no longer appears
// in the backup
pruneObjects := make(map[kube.ResourceKey]unstructured.Unstructured)
configMaps, err := acdClients.configMaps.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
// referencedSecrets holds any secrets referenced in the argocd-cm configmap. These
// secrets need to be imported too
var referencedSecrets map[string]bool
for _, cm := range configMaps.Items {
if isArgoCDConfigMap(cm.GetName()) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
}
if cm.GetName() == common.ArgoCDConfigMapName {
referencedSecrets = getReferencedSecrets(cm)
}
}
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
}
}
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
}
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
}
// Create or replace existing object
backupObjects, err := kube.SplitYAML(input)
errors.CheckError(err)
for _, bakObj := range backupObjects {
gvk := bakObj.GroupVersionKind()
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
liveObj, exists := pruneObjects[key]
delete(pruneObjects, key)
var dynClient dynamic.ResourceInterface
switch bakObj.GetKind() {
case "Secret":
dynClient = acdClients.secrets
case "ConfigMap":
dynClient = acdClients.configMaps
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
}
if !exists {
if !dryRun {
_, err = dynClient.Create(context.Background(), bakObj, metav1.CreateOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
} else if specsEqual(*bakObj, liveObj) {
fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
} else {
if !dryRun {
newLive := updateLive(bakObj, &liveObj)
_, err = dynClient.Update(context.Background(), newLive, metav1.UpdateOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
}
// Delete objects not in backup
for key := range pruneObjects {
if prune {
var dynClient dynamic.ResourceInterface
switch key.Kind {
case "Secret":
dynClient = acdClients.secrets
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
default:
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
}
if !dryRun {
err = dynClient.Delete(context.Background(), key.Name, metav1.DeleteOptions{})
errors.CheckError(err)
}
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
} else {
fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name)
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed")
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
return &command
}
type argoCDClientsets struct {
configMaps dynamic.ResourceInterface
secrets dynamic.ResourceInterface
applications dynamic.ResourceInterface
projects dynamic.ResourceInterface
}
func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets {
dynamicIf, err := dynamic.NewForConfig(config)
errors.CheckError(err)
return &argoCDClientsets{
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
}
}
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewExportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = cobra.Command{
Use: "export",
Short: "Export all Argo CD data to stdout (default) or a file",
Run: func(c *cobra.Command, args []string) {
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
var writer io.Writer
if out == "-" {
writer = os.Stdout
} else {
f, err := os.Create(out)
errors.CheckError(err)
bw := bufio.NewWriter(f)
writer = bw
defer func() {
err = bw.Flush()
errors.CheckError(err)
err = f.Close()
errors.CheckError(err)
}()
}
acdClients := newArgoCDClientsets(config, namespace)
acdConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdConfigMap)
acdRBACConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDRBACConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdRBACConfigMap)
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDKnownHostsConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdKnownHostsConfigMap)
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDTLSCertsConfigMapName, metav1.GetOptions{})
errors.CheckError(err)
export(writer, *acdTLSCertsConfigMap)
referencedSecrets := getReferencedSecrets(*acdConfigMap)
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
export(writer, secret)
}
}
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
export(writer, proj)
}
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
export(writer, app)
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
return &command
}
// getReferencedSecrets examines the argocd-cm config for any referenced repo secrets and returns a
// map of all referenced secrets.
func getReferencedSecrets(un unstructured.Unstructured) map[string]bool {
var cm apiv1.ConfigMap
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
errors.CheckError(err)
referencedSecrets := make(map[string]bool)
// Referenced repository secrets
if reposRAW, ok := cm.Data["repositories"]; ok {
repos := make([]settings.Repository, 0)
err := yaml.Unmarshal([]byte(reposRAW), &repos)
errors.CheckError(err)
for _, cred := range repos {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
// Referenced repository credentials secrets
if reposRAW, ok := cm.Data["repository.credentials"]; ok {
creds := make([]settings.RepositoryCredentials, 0)
err := yaml.Unmarshal([]byte(reposRAW), &creds)
errors.CheckError(err)
for _, cred := range creds {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
return referencedSecrets
}
// isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration
// (e.g. argocd-secret, repo credentials, or cluster credentials)
func isArgoCDSecret(repoSecretRefs map[string]bool, un unstructured.Unstructured) bool {
secretName := un.GetName()
if secretName == common.ArgoCDSecretName {
return true
}
if repoSecretRefs != nil {
if _, ok := repoSecretRefs[secretName]; ok {
return true
}
}
if labels := un.GetLabels(); labels != nil {
if _, ok := labels[common.LabelKeySecretType]; ok {
return true
}
}
if annotations := un.GetAnnotations(); annotations != nil {
if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD {
return true
}
}
return false
}
// isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps
func isArgoCDConfigMap(name string) bool {
switch name {
case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName:
return true
}
return false
}
// specsEqual returns if the spec, data, labels, annotations, and finalizers of the two
// supplied objects are equal, indicating that no update is necessary during importing
func specsEqual(left, right unstructured.Unstructured) bool {
if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) {
return false
}
if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) {
return false
}
if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) {
return false
}
switch left.GetKind() {
case "Secret", "ConfigMap":
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
return reflect.DeepEqual(leftData, rightData)
case "AppProject":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
return reflect.DeepEqual(leftSpec, rightSpec)
case "Application":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")
rightStatus, _, _ := unstructured.NestedMap(right.Object, "status")
// reconciledAt and observedAt are constantly changing and we ignore any diff there
delete(leftStatus, "reconciledAt")
delete(rightStatus, "reconciledAt")
delete(leftStatus, "observedAt")
delete(rightStatus, "observedAt")
return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus)
}
return false
}
// updateLive replaces the live object's finalizers, spec, annotations, labels, and data from the
// backup object but leaves all other fields intact (status, other metadata, etc...)
func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured {
newLive := live.DeepCopy()
newLive.SetAnnotations(bak.GetAnnotations())
newLive.SetLabels(bak.GetLabels())
newLive.SetFinalizers(bak.GetFinalizers())
switch live.GetKind() {
case "Secret", "ConfigMap":
newLive.Object["data"] = bak.Object["data"]
case "AppProject":
newLive.Object["spec"] = bak.Object["spec"]
case "Application":
newLive.Object["spec"] = bak.Object["spec"]
if _, ok := bak.Object["status"]; ok {
newLive.Object["status"] = bak.Object["status"]
}
}
return newLive
}
// export writes the unstructured object and removes extraneous cruft from output before writing
func export(w io.Writer, un unstructured.Unstructured) {
name := un.GetName()
finalizers := un.GetFinalizers()
apiVersion := un.GetAPIVersion()
kind := un.GetKind()
labels := un.GetLabels()
annotations := un.GetAnnotations()
unstructured.RemoveNestedField(un.Object, "metadata")
un.SetName(name)
un.SetFinalizers(finalizers)
un.SetAPIVersion(apiVersion)
un.SetKind(kind)
un.SetLabels(labels)
un.SetAnnotations(annotations)
data, err := yaml.Marshal(un.Object)
errors.CheckError(err)
_, err = w.Write(data)
errors.CheckError(err)
_, err = w.Write([]byte(yamlSeparator))
errors.CheckError(err)
}
// NewClusterConfig returns a new instance of `argocd-util kubeconfig` command
func NewClusterConfig() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = &cobra.Command{
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
Short: "Generates kubeconfig for the specified cluster",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
serverUrl := args[0]
output := args[1]
conf, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeclientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
cluster, err := db.NewDB(namespace, settings.NewSettingsManager(context.Background(), kubeclientset, namespace), kubeclientset).GetCluster(context.Background(), serverUrl)
errors.CheckError(err)
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
errors.CheckError(err)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
return command
}
func iterateStringFields(obj interface{}, callback func(name string, val string) string) {
if mapField, ok := obj.(map[string]interface{}); ok {
for field, val := range mapField {
if strVal, ok := val.(string); ok {
mapField[field] = callback(field, strVal)
} else {
iterateStringFields(val, callback)
}
}
} else if arrayField, ok := obj.([]interface{}); ok {
for i := range arrayField {
iterateStringFields(arrayField[i], callback)
}
}
}
func redactor(dirtyString string) string {
config := make(map[string]interface{})
err := yaml.Unmarshal([]byte(dirtyString), &config)
errors.CheckError(err)
iterateStringFields(config, func(name string, val string) string {
if name == "clientSecret" || name == "secret" || name == "bindPW" {
return "********"
} else {
return val
}
})
data, err := yaml.Marshal(config)
errors.CheckError(err)
return string(data)
}

View File

@@ -0,0 +1,359 @@
package commands
import (
"context"
"fmt"
"io/ioutil"
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
apierr "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/settings"
)
const (
ArgoCDNamespace = "argocd"
repoSecretPrefix = "repo"
)
func NewGenerateConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
var command = &cobra.Command{
Use: "config",
Short: "Generate declarative configuration files",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewGenAppConfigCommand())
command.AddCommand(NewGenProjectConfigCommand())
command.AddCommand(NewGenClusterConfigCommand(pathOpts))
command.AddCommand(NewGenRepoConfigCommand())
return command
}
// NewGenAppConfigCommand generates declarative configuration file for given application
func NewGenAppConfigCommand() *cobra.Command {
var (
appOpts cmdutil.AppOptions
fileURL string
appName string
labels []string
outputFormat string
)
var command = &cobra.Command{
Use: "app APPNAME",
Short: "Generate declarative config for an application",
Example: `
# Generate declarative config for a directory app
argocd-util config app guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse
# Generate declarative config for a Jsonnet app
argocd-util config app jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2
# Generate declarative config for a Helm app
argocd-util config app helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2
# Generate declarative config for a Helm app from a Helm repo
argocd-util config app nginx-ingress --repo https://kubernetes-charts.storage.googleapis.com --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Generate declarative config for a Kustomize app
argocd-util config app kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
# Generate declarative config for a app using a custom tool:
argocd-util config app ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane
`,
Run: func(c *cobra.Command, args []string) {
app, err := cmdutil.ConstructApp(fileURL, appName, labels, args, appOpts, c.Flags())
errors.CheckError(err)
if app.Name == "" {
c.HelpFunc()(c, args)
os.Exit(1)
}
var printResources []interface{}
printResources = append(printResources, app)
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
},
}
command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set (DEPRECATED)")
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app")
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
// Only complete files with appropriate extension.
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
errors.CheckError(err)
cmdutil.AddAppFlags(command, &appOpts)
return command
}
// NewGenProjectConfigCommand generates declarative configuration file for given project
func NewGenProjectConfigCommand() *cobra.Command {
var (
opts cmdutil.ProjectOpts
fileURL string
outputFormat string
)
var command = &cobra.Command{
Use: "proj PROJECT",
Short: "Generate declarative config for a project",
Run: func(c *cobra.Command, args []string) {
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
errors.CheckError(err)
var printResources []interface{}
printResources = append(printResources, proj)
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
},
}
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the project")
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
if err != nil {
log.Fatal(err)
}
cmdutil.AddProjFlags(command, &opts)
return command
}
func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
var (
clusterOpts cmdutil.ClusterOptions
bearerToken string
outputFormat string
)
var command = &cobra.Command{
Use: "cluster CONTEXT",
Short: "Generate declarative config for a cluster",
Run: func(c *cobra.Command, args []string) {
var configAccess clientcmd.ConfigAccess = pathOpts
if len(args) == 0 {
log.Error("Choose a context name from:")
cmdutil.PrintKubeContexts(configAccess)
os.Exit(1)
}
cfgAccess, err := configAccess.GetStartingConfig()
errors.CheckError(err)
contextName := args[0]
clstContext := cfgAccess.Contexts[contextName]
if clstContext == nil {
log.Fatalf("Context %s does not exist in kubeconfig", contextName)
}
overrides := clientcmd.ConfigOverrides{
Context: *clstContext,
}
clientConfig := clientcmd.NewDefaultClientConfig(*cfgAccess, &overrides)
conf, err := clientConfig.ClientConfig()
errors.CheckError(err)
kubeClientset := fake.NewSimpleClientset()
var awsAuthConf *argoappv1.AWSAuthConfig
var execProviderConf *argoappv1.ExecProviderConfig
if clusterOpts.AwsClusterName != "" {
awsAuthConf = &argoappv1.AWSAuthConfig{
ClusterName: clusterOpts.AwsClusterName,
RoleARN: clusterOpts.AwsRoleArn,
}
} else if clusterOpts.ExecProviderCommand != "" {
execProviderConf = &argoappv1.ExecProviderConfig{
Command: clusterOpts.ExecProviderCommand,
Args: clusterOpts.ExecProviderArgs,
Env: clusterOpts.ExecProviderEnv,
APIVersion: clusterOpts.ExecProviderAPIVersion,
InstallHint: clusterOpts.ExecProviderInstallHint,
}
} else if bearerToken == "" {
bearerToken = "bearer-token"
}
if clusterOpts.Name != "" {
contextName = clusterOpts.Name
}
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, conf, bearerToken, awsAuthConf, execProviderConf)
if clusterOpts.InCluster {
clst.Server = common.KubernetesInternalAPIServerAddr
}
if clusterOpts.Shard >= 0 {
clst.Shard = &clusterOpts.Shard
}
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
_, err = argoDB.CreateCluster(context.Background(), clst)
errors.CheckError(err)
secName, err := db.ServerToSecretName(clst.Server)
errors.CheckError(err)
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), secName, v1.GetOptions{})
errors.CheckError(err)
cmdutil.ConvertSecretData(secret)
var printResources []interface{}
printResources = append(printResources, secret)
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
},
}
command.PersistentFlags().StringVar(&pathOpts.LoadingRules.ExplicitPath, pathOpts.ExplicitFileFlag, pathOpts.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
command.Flags().StringVar(&bearerToken, "bearer-token", "", "Authentication token that should be used to access K8S API server")
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
cmdutil.AddClusterFlags(command, &clusterOpts)
return command
}
func NewGenRepoConfigCommand() *cobra.Command {
var (
repoOpts cmdutil.RepoOptions
outputFormat string
)
// For better readability and easier formatting
var repoAddExamples = `
# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
argocd-util config repo git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
argocd-util config repo ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
argocd-util config repo https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
# Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate
argocd-util config repo https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
# Add a public Helm repository named 'stable' via HTTPS
argocd-util config repo https://kubernetes-charts.storage.googleapis.com --type helm --name stable
# Add a private Helm repository named 'stable' via HTTPS
argocd-util config repo https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
# Add a private Helm OCI-based repository named 'stable' via HTTPS
argocd-util config repo helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
`
var command = &cobra.Command{
Use: "repo REPOURL",
Short: "Generate declarative config for a repo",
Example: repoAddExamples,
Run: func(c *cobra.Command, args []string) {
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
// Repository URL
repoOpts.Repo.Repo = args[0]
// Specifying ssh-private-key-path is only valid for SSH repositories
if repoOpts.SshPrivateKeyPath != "" {
if ok, _ := git.IsSSHURL(repoOpts.Repo.Repo); ok {
keyData, err := ioutil.ReadFile(repoOpts.SshPrivateKeyPath)
if err != nil {
log.Fatal(err)
}
repoOpts.Repo.SSHPrivateKey = string(keyData)
} else {
err := fmt.Errorf("--ssh-private-key-path is only supported for SSH repositories.")
errors.CheckError(err)
}
}
// tls-client-cert-path and tls-client-cert-key-key-path must always be
// specified together
if (repoOpts.TlsClientCertPath != "" && repoOpts.TlsClientCertKeyPath == "") || (repoOpts.TlsClientCertPath == "" && repoOpts.TlsClientCertKeyPath != "") {
err := fmt.Errorf("--tls-client-cert-path and --tls-client-cert-key-path must be specified together")
errors.CheckError(err)
}
// Specifying tls-client-cert-path is only valid for HTTPS repositories
if repoOpts.TlsClientCertPath != "" {
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
tlsCertData, err := ioutil.ReadFile(repoOpts.TlsClientCertPath)
errors.CheckError(err)
tlsCertKey, err := ioutil.ReadFile(repoOpts.TlsClientCertKeyPath)
errors.CheckError(err)
repoOpts.Repo.TLSClientCertData = string(tlsCertData)
repoOpts.Repo.TLSClientCertKey = string(tlsCertKey)
} else {
err := fmt.Errorf("--tls-client-cert-path is only supported for HTTPS repositories")
errors.CheckError(err)
}
}
// Set repository connection properties only when creating repository, not
// when creating repository credentials.
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
repoOpts.Repo.InsecureIgnoreHostKey = repoOpts.InsecureIgnoreHostKey
repoOpts.Repo.Insecure = repoOpts.InsecureSkipServerVerification
repoOpts.Repo.EnableLFS = repoOpts.EnableLfs
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
errors.CheckError(fmt.Errorf("must specify --name for repos of type 'helm'"))
}
// If the user set a username, but didn't supply password via --password,
// then we prompt for it
if repoOpts.Repo.Username != "" && repoOpts.Repo.Password == "" {
repoOpts.Repo.Password = cli.PromptPassword(repoOpts.Repo.Password)
}
argoCDCM := &apiv1.ConfigMap{
TypeMeta: v1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: v1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: ArgoCDNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
}
kubeClientset := fake.NewSimpleClientset(argoCDCM)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
var printResources []interface{}
_, err := argoDB.CreateRepository(context.Background(), &repoOpts.Repo)
errors.CheckError(err)
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo), v1.GetOptions{})
if err != nil {
if !apierr.IsNotFound(err) {
errors.CheckError(err)
}
} else {
cmdutil.ConvertSecretData(secret)
printResources = append(printResources, secret)
}
cm, err := kubeClientset.CoreV1().ConfigMaps(ArgoCDNamespace).Get(context.Background(), common.ArgoCDConfigMapName, v1.GetOptions{})
errors.CheckError(err)
printResources = append(printResources, cm)
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
},
}
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
cmdutil.AddRepoFlags(command, &repoOpts)
return command
}

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"bufio"
@@ -17,10 +17,10 @@ import (
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
// load the gcp plugin (required to authenticate against GKE clusters).
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"reflect"
@@ -30,9 +30,9 @@ func TestProjectAllowListGen(t *testing.T) {
})
assert.NoError(t, err)
var patchSeverPreferredResources *mpatch.Patch
var patchSeverPreferedResources *mpatch.Patch
discoClient := &discovery.DiscoveryClient{}
patchSeverPreferredResources, err = mpatch.PatchInstanceMethodByName(reflect.TypeOf(discoClient), "ServerPreferredResources", func(*discovery.DiscoveryClient) ([]*metav1.APIResourceList, error) {
patchSeverPreferedResources, err = mpatch.PatchInstanceMethodByName(reflect.TypeOf(discoClient), "ServerPreferredResources", func(*discovery.DiscoveryClient) ([]*metav1.APIResourceList, error) {
res := metav1.APIResource{
Name: "services",
Kind: "Service",
@@ -47,7 +47,7 @@ func TestProjectAllowListGen(t *testing.T) {
assert.NoError(t, err)
err = patch.Unpatch()
assert.NoError(t, err)
err = patchSeverPreferredResources.Unpatch()
err = patchSeverPreferedResources.Unpatch()
err = patch.Unpatch()
}()
}

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"context"
@@ -7,13 +7,11 @@ import (
"path/filepath"
"strings"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
appclient "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/typed/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/spf13/cobra"
@@ -23,53 +21,18 @@ import (
func NewProjectsCommand() *cobra.Command {
var command = &cobra.Command{
Use: "proj",
Short: "Manage projects configuration",
Use: "projects",
Short: "Utility commands operate on ArgoCD Projects",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewGenProjectSpecCommand())
command.AddCommand(NewUpdatePolicyRuleCommand())
command.AddCommand(NewProjectAllowListGenCommand())
return command
}
// NewGenProjectSpecCommand generates declarative configuration file for given project
func NewGenProjectSpecCommand() *cobra.Command {
var (
opts cmdutil.ProjectOpts
fileURL string
outputFormat string
inline bool
)
var command = &cobra.Command{
Use: "generate-spec PROJECT",
Short: "Generate declarative config for a project",
Run: func(c *cobra.Command, args []string) {
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
errors.CheckError(err)
out, closer, err := getOutWriter(inline, fileURL)
errors.CheckError(err)
defer io.Close(closer)
errors.CheckError(PrintResources(outputFormat, out, proj))
},
}
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the project")
command.Flags().BoolVarP(&inline, "inline", "i", false, "If set then generated resource is written back to the file specified in --file flag")
// Only complete files with appropriate extension.
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
errors.CheckError(err)
cmdutil.AddProjFlags(command, &opts)
return command
}
func globMatch(pattern string, val string) bool {
if pattern == "*" {
return true
@@ -143,10 +106,10 @@ func NewUpdatePolicyRuleCommand() *cobra.Command {
Use: "update-role-policy PROJECT_GLOB MODIFICATION ACTION",
Short: "Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions.",
Example: ` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects
argocd admin projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow
argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow
# Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects
argocd admin projects update-role-policy '*' remove override --role '*deployer*'
argocd-util projects update-role-policy '*' remove override --role '*deployer*'
`,
Run: func(c *cobra.Command, args []string) {
if len(args) != 3 {

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"context"
@@ -7,8 +7,8 @@ import (
"github.com/stretchr/testify/assert"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
)
const (

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"context"
@@ -14,11 +14,11 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/util/assets"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/rbac"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/server/rbacpolicy"
"github.com/argoproj/argo-cd/util/assets"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/rbac"
)
// Provide a mapping of short-hand resource names to their RBAC counterparts
@@ -101,19 +101,19 @@ something.
Example: `
# Check whether role some:role has permissions to create an application in the
# 'default' project, using a local policy.csv file
argocd admin settings rbac can some:role create application 'default/app' --policy-file policy.csv
argocd-util rbac can some:role create application 'default/app' --policy-file policy.csv
# Policy file can also be K8s config map with data keys like argocd-rbac-cm,
# i.e. 'policy.csv' and (optionally) 'policy.default'
argocd admin settings rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml
argocd-util rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml
# If --policy-file is not given, the ConfigMap 'argocd-rbac-cm' from K8s is
# used. You need to specify the argocd namespace, and make sure that your
# current Kubernetes context is pointing to the cluster Argo CD is running in
argocd admin settings rbac can some:role create application 'default/app' --namespace argocd
argocd-util rbac can some:role create application 'default/app' --namespace argocd
# You can override a possibly configured default role
argocd admin settings rbac can someuser create application 'default/app' --default-role role:readonly
argocd-util rbac can someuser create application 'default/app' --default-role role:readonly
`,
Run: func(c *cobra.Command, args []string) {

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"io/ioutil"
@@ -10,7 +10,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
"github.com/argoproj/argo-cd/v2/util/assets"
"github.com/argoproj/argo-cd/util/assets"
)
func Test_isValidRBACAction(t *testing.T) {

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"testing"

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"bytes"
@@ -23,13 +23,13 @@ import (
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/lua"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo/normalizers"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/lua"
"github.com/argoproj/argo-cd/util/settings"
)
type settingsOpts struct {
@@ -162,7 +162,6 @@ func NewSettingsCommand() *cobra.Command {
command.AddCommand(NewValidateSettingsCommand(&opts))
command.AddCommand(NewResourceOverridesCommand(&opts))
command.AddCommand(NewRBACCommand())
opts.clientConfig = cli.AddKubectlFlagsToCmd(command)
command.PersistentFlags().StringVar(&opts.argocdCMPath, "argocd-cm-path", "", "Path to local argocd-cm.yaml file")
@@ -303,10 +302,10 @@ func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command {
Long: "Validates settings specified in 'argocd-cm' ConfigMap and 'argocd-secret' Secret",
Example: `
#Validates all settings in the specified YAML file
argocd admin settings validate --argocd-cm-path ./argocd-cm.yaml
argocd-util settings validate --argocd-cm-path ./argocd-cm.yaml
#Validates accounts and plugins settings in Kubernetes cluster of current kubeconfig context
argocd admin settings validate --group accounts --group plugins --load-cluster-settings`,
argocd-util settings validate --group accounts --group plugins --load-cluster-settings`,
Run: func(c *cobra.Command, args []string) {
settingsManager, err := cmdCtx.createSettingsManager()
errors.CheckError(err)
@@ -392,7 +391,7 @@ func NewResourceIgnoreDifferencesCommand(cmdCtx commandContext) *cobra.Command {
Short: "Renders fields excluded from diffing",
Long: "Renders ignored fields using the 'ignoreDifferences' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap",
Example: `
argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
Run: func(c *cobra.Command, args []string) {
if len(args) < 1 {
c.HelpFunc()(c, args)
@@ -436,7 +435,7 @@ func NewResourceHealthCommand(cmdCtx commandContext) *cobra.Command {
Short: "Assess resource health",
Long: "Assess resource health using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap",
Example: `
argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
Run: func(c *cobra.Command, args []string) {
if len(args) < 1 {
c.HelpFunc()(c, args)
@@ -467,7 +466,7 @@ func NewResourceActionListCommand(cmdCtx commandContext) *cobra.Command {
Short: "List available resource actions",
Long: "List actions available for given resource action using the lua scripts configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
Example: `
argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
argocd-util settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
Run: func(c *cobra.Command, args []string) {
if len(args) < 1 {
c.HelpFunc()(c, args)
@@ -510,7 +509,7 @@ func NewResourceActionRunCommand(cmdCtx commandContext) *cobra.Command {
Short: "Executes resource action",
Long: "Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
Example: `
argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`,
argocd-util settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`,
Run: func(c *cobra.Command, args []string) {
if len(args) < 2 {
c.HelpFunc()(c, args)

View File

@@ -1,4 +1,4 @@
package admin
package commands
import (
"bytes"
@@ -9,9 +9,9 @@ import (
"os"
"testing"
"github.com/argoproj/argo-cd/v2/common"
utils "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
utils "github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/settings"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
@@ -240,7 +240,6 @@ func tempFile(content string) (string, io.Closer, error) {
_ = os.Remove(f.Name())
return "", nil, err
}
defer f.Close()
return f.Name(), utils.NewCloser(func() error {
return os.Remove(f.Name())
}), nil

View File

@@ -14,17 +14,17 @@ import (
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/term"
"golang.org/x/crypto/ssh/terminal"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/session"
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/localconfig"
sessionutil "github.com/argoproj/argo-cd/v2/util/session"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
accountpkg "github.com/argoproj/argo-cd/pkg/apiclient/account"
"github.com/argoproj/argo-cd/pkg/apiclient/session"
"github.com/argoproj/argo-cd/server/rbacpolicy"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/localconfig"
sessionutil "github.com/argoproj/argo-cd/util/session"
)
func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
@@ -68,7 +68,7 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
if userInfo.Iss == sessionutil.SessionManagerClaimsIssuer && currentPassword == "" {
fmt.Print("*** Enter current password: ")
password, err := term.ReadPassword(int(os.Stdin.Fd()))
password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
errors.CheckError(err)
currentPassword = string(password)
fmt.Print("\n")
@@ -99,7 +99,7 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
errors.CheckError(err)
claims, err := configCtx.User.Claims()
errors.CheckError(err)
tokenString := passwordLogin(acdClient, localconfig.GetUsername(claims.Subject), newPassword)
tokenString := passwordLogin(acdClient, claims.Subject, newPassword)
localCfg.UpsertUser(localconfig.User{
Name: localCfg.CurrentContext,
AuthToken: tokenString,
@@ -362,7 +362,7 @@ argocd account generate-token --account <account-name>`,
}
cmd.Flags().StringVarP(&account, "account", "a", "", "Account name. Defaults to the current account.")
cmd.Flags().StringVarP(&expiresIn, "expires-in", "e", "0s", "Duration before the token will expire. (Default: No expiration)")
cmd.Flags().StringVar(&id, "id", "", "Optional token id. Fall back to uuid if not value specified.")
cmd.Flags().StringVar(&id, "id", "", "Optional token id. Fallback to uuid if not value specified.")
return cmd
}
@@ -377,7 +377,7 @@ func NewAccountDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra
argocd account delete-token ID
# Delete token of the account with the specified name
argocd account delete-token --account <account-name> ID`,
argocd account generate-token --account <account-name>`,
Run: func(c *cobra.Command, args []string) {
if len(args) != 1 {
c.HelpFunc()(c, args)

View File

@@ -1,243 +0,0 @@
package admin
import (
"reflect"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/settings"
)
const (
// YamlSeparator separates sections of a YAML file
yamlSeparator = "---\n"
)
var (
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
appplicationSetResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applicationsets"}
)
// NewAdminCommand returns a new instance of an argocd command
func NewAdminCommand() *cobra.Command {
var (
pathOpts = clientcmd.NewDefaultPathOptions()
)
var command = &cobra.Command{
Use: "admin",
Short: "Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewClusterCommand(pathOpts))
command.AddCommand(NewProjectsCommand())
command.AddCommand(NewSettingsCommand())
command.AddCommand(NewAppCommand())
command.AddCommand(NewRepoCommand())
command.AddCommand(NewImportCommand())
command.AddCommand(NewExportCommand())
command.AddCommand(NewDashboardCommand())
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
return command
}
type argoCDClientsets struct {
configMaps dynamic.ResourceInterface
secrets dynamic.ResourceInterface
applications dynamic.ResourceInterface
projects dynamic.ResourceInterface
applicationSets dynamic.ResourceInterface
}
func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets {
dynamicIf, err := dynamic.NewForConfig(config)
errors.CheckError(err)
return &argoCDClientsets{
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
applicationSets: dynamicIf.Resource(appplicationSetResource).Namespace(namespace),
}
}
// getReferencedSecrets examines the argocd-cm config for any referenced repo secrets and returns a
// map of all referenced secrets.
func getReferencedSecrets(un unstructured.Unstructured) map[string]bool {
var cm apiv1.ConfigMap
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
errors.CheckError(err)
referencedSecrets := make(map[string]bool)
// Referenced repository secrets
if reposRAW, ok := cm.Data["repositories"]; ok {
repos := make([]settings.Repository, 0)
err := yaml.Unmarshal([]byte(reposRAW), &repos)
errors.CheckError(err)
for _, cred := range repos {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
// Referenced repository credentials secrets
if reposRAW, ok := cm.Data["repository.credentials"]; ok {
creds := make([]settings.RepositoryCredentials, 0)
err := yaml.Unmarshal([]byte(reposRAW), &creds)
errors.CheckError(err)
for _, cred := range creds {
if cred.PasswordSecret != nil {
referencedSecrets[cred.PasswordSecret.Name] = true
}
if cred.SSHPrivateKeySecret != nil {
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
}
if cred.UsernameSecret != nil {
referencedSecrets[cred.UsernameSecret.Name] = true
}
if cred.TLSClientCertDataSecret != nil {
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
}
if cred.TLSClientCertKeySecret != nil {
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
}
}
}
return referencedSecrets
}
// isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration
// (e.g. argocd-secret, repo credentials, or cluster credentials)
func isArgoCDSecret(repoSecretRefs map[string]bool, un unstructured.Unstructured) bool {
secretName := un.GetName()
if secretName == common.ArgoCDSecretName {
return true
}
if repoSecretRefs != nil {
if _, ok := repoSecretRefs[secretName]; ok {
return true
}
}
if labels := un.GetLabels(); labels != nil {
if _, ok := labels[common.LabelKeySecretType]; ok {
return true
}
}
if annotations := un.GetAnnotations(); annotations != nil {
if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD {
return true
}
}
return false
}
// isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps
func isArgoCDConfigMap(name string) bool {
switch name {
case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName:
return true
}
return false
}
// specsEqual returns if the spec, data, labels, annotations, and finalizers of the two
// supplied objects are equal, indicating that no update is necessary during importing
func specsEqual(left, right unstructured.Unstructured) bool {
if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) {
return false
}
if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) {
return false
}
if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) {
return false
}
switch left.GetKind() {
case "Secret", "ConfigMap":
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
return reflect.DeepEqual(leftData, rightData)
case "AppProject":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
return reflect.DeepEqual(leftSpec, rightSpec)
case "Application":
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")
rightStatus, _, _ := unstructured.NestedMap(right.Object, "status")
// reconciledAt and observedAt are constantly changing and we ignore any diff there
delete(leftStatus, "reconciledAt")
delete(rightStatus, "reconciledAt")
delete(leftStatus, "observedAt")
delete(rightStatus, "observedAt")
return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus)
}
return false
}
func iterateStringFields(obj interface{}, callback func(name string, val string) string) {
if mapField, ok := obj.(map[string]interface{}); ok {
for field, val := range mapField {
if strVal, ok := val.(string); ok {
mapField[field] = callback(field, strVal)
} else {
iterateStringFields(val, callback)
}
}
} else if arrayField, ok := obj.([]interface{}); ok {
for i := range arrayField {
iterateStringFields(arrayField[i], callback)
}
}
}
func redactor(dirtyString string) string {
config := make(map[string]interface{})
err := yaml.Unmarshal([]byte(dirtyString), &config)
errors.CheckError(err)
iterateStringFields(config, func(name string, val string) string {
if name == "clientSecret" || name == "secret" || name == "bindPW" {
return "********"
} else {
return val
}
})
data, err := yaml.Marshal(config)
errors.CheckError(err)
return string(data)
}

View File

@@ -1,351 +0,0 @@
package admin
import (
"bufio"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apierr "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
)
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewExportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
out string
)
var command = cobra.Command{
Use: "export",
Short: "Export all Argo CD data to stdout (default) or a file",
Run: func(c *cobra.Command, args []string) {
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
var writer io.Writer
if out == "-" {
writer = os.Stdout
} else {
f, err := os.Create(out)
errors.CheckError(err)
bw := bufio.NewWriter(f)
writer = bw
defer func() {
err = bw.Flush()
errors.CheckError(err)
err = f.Close()
errors.CheckError(err)
}()
}
acdClients := newArgoCDClientsets(config, namespace)
acdConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdConfigMap)
acdRBACConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDRBACConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdRBACConfigMap)
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDKnownHostsConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdKnownHostsConfigMap)
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDTLSCertsConfigMapName, v1.GetOptions{})
errors.CheckError(err)
export(writer, *acdTLSCertsConfigMap)
referencedSecrets := getReferencedSecrets(*acdConfigMap)
secrets, err := acdClients.secrets.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
export(writer, secret)
}
}
projects, err := acdClients.projects.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
export(writer, proj)
}
applications, err := acdClients.applications.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
export(writer, app)
}
applicationSets, err := acdClients.applicationSets.List(context.Background(), v1.ListOptions{})
if err != nil && !apierr.IsNotFound(err) {
if apierr.IsForbidden(err) {
log.Warn(err)
} else {
errors.CheckError(err)
}
}
if applicationSets != nil {
for _, appSet := range applicationSets.Items {
export(writer, appSet)
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
return &command
}
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
func NewImportCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
prune bool
dryRun bool
verbose bool
)
var command = cobra.Command{
Use: "import SOURCE",
Short: "Import Argo CD data from stdin (specify `-') or a file",
Run: func(c *cobra.Command, args []string) {
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
config, err := clientConfig.ClientConfig()
errors.CheckError(err)
config.QPS = 100
config.Burst = 50
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
acdClients := newArgoCDClientsets(config, namespace)
var input []byte
if in := args[0]; in == "-" {
input, err = ioutil.ReadAll(os.Stdin)
} else {
input, err = ioutil.ReadFile(in)
}
errors.CheckError(err)
var dryRunMsg string
if dryRun {
dryRunMsg = " (dry run)"
}
// pruneObjects tracks live objects and it's current resource version. any remaining
// items in this map indicates the resource should be pruned since it no longer appears
// in the backup
pruneObjects := make(map[kube.ResourceKey]unstructured.Unstructured)
configMaps, err := acdClients.configMaps.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
// referencedSecrets holds any secrets referenced in the argocd-cm configmap. These
// secrets need to be imported too
var referencedSecrets map[string]bool
for _, cm := range configMaps.Items {
if isArgoCDConfigMap(cm.GetName()) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
}
if cm.GetName() == common.ArgoCDConfigMapName {
referencedSecrets = getReferencedSecrets(cm)
}
}
secrets, err := acdClients.secrets.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
for _, secret := range secrets.Items {
if isArgoCDSecret(referencedSecrets, secret) {
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
}
}
applications, err := acdClients.applications.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
for _, app := range applications.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
}
projects, err := acdClients.projects.List(context.Background(), v1.ListOptions{})
errors.CheckError(err)
for _, proj := range projects.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
}
applicationSets, err := acdClients.applicationSets.List(context.Background(), v1.ListOptions{})
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
log.Warnf("argoproj.io/ApplicationSet: %v\n", err)
} else {
errors.CheckError(err)
}
if applicationSets != nil {
for _, appSet := range applicationSets.Items {
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "ApplicationSet", Name: appSet.GetName()}] = appSet
}
}
// Create or replace existing object
backupObjects, err := kube.SplitYAML(input)
errors.CheckError(err)
for _, bakObj := range backupObjects {
gvk := bakObj.GroupVersionKind()
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
liveObj, exists := pruneObjects[key]
delete(pruneObjects, key)
var dynClient dynamic.ResourceInterface
switch bakObj.GetKind() {
case "Secret":
dynClient = acdClients.secrets
case "ConfigMap":
dynClient = acdClients.configMaps
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
case "ApplicationSet":
dynClient = acdClients.applicationSets
}
if !exists {
isForbidden := false
if !dryRun {
_, err = dynClient.Create(context.Background(), bakObj, v1.CreateOptions{})
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
isForbidden = true
log.Warnf("%s/%s %s: %v", gvk.Group, gvk.Kind, bakObj.GetName(), err)
} else {
errors.CheckError(err)
}
}
if !isForbidden {
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
} else if specsEqual(*bakObj, liveObj) {
if verbose {
fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
} else {
isForbidden := false
if !dryRun {
newLive := updateLive(bakObj, &liveObj)
_, err = dynClient.Update(context.Background(), newLive, v1.UpdateOptions{})
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
isForbidden = true
log.Warnf("%s/%s %s: %v", gvk.Group, gvk.Kind, bakObj.GetName(), err)
} else {
errors.CheckError(err)
}
}
if !isForbidden {
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
}
}
}
// Delete objects not in backup
for key, liveObj := range pruneObjects {
if prune {
var dynClient dynamic.ResourceInterface
switch key.Kind {
case "Secret":
dynClient = acdClients.secrets
case "AppProject":
dynClient = acdClients.projects
case "Application":
dynClient = acdClients.applications
if !dryRun {
if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 {
newLive := liveObj.DeepCopy()
newLive.SetFinalizers(nil)
_, err = dynClient.Update(context.Background(), newLive, v1.UpdateOptions{})
if err != nil && !apierr.IsNotFound(err) {
errors.CheckError(err)
}
}
}
case "ApplicationSet":
dynClient = acdClients.applicationSets
default:
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
}
isForbidden := false
if !dryRun {
err = dynClient.Delete(context.Background(), key.Name, v1.DeleteOptions{})
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
isForbidden = true
log.Warnf("%s/%s %s: %v\n", key.Group, key.Kind, key.Name, err)
} else {
errors.CheckError(err)
}
}
if !isForbidden {
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
}
} else {
fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name)
}
}
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed")
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
command.Flags().BoolVar(&verbose, "verbose", false, "Verbose output (versus only changed output)")
return &command
}
// export writes the unstructured object and removes extraneous cruft from output before writing
func export(w io.Writer, un unstructured.Unstructured) {
name := un.GetName()
finalizers := un.GetFinalizers()
apiVersion := un.GetAPIVersion()
kind := un.GetKind()
labels := un.GetLabels()
annotations := un.GetAnnotations()
unstructured.RemoveNestedField(un.Object, "metadata")
un.SetName(name)
un.SetFinalizers(finalizers)
un.SetAPIVersion(apiVersion)
un.SetKind(kind)
un.SetLabels(labels)
un.SetAnnotations(annotations)
data, err := yaml.Marshal(un.Object)
errors.CheckError(err)
_, err = w.Write(data)
errors.CheckError(err)
_, err = w.Write([]byte(yamlSeparator))
errors.CheckError(err)
}
// updateLive replaces the live object's finalizers, spec, annotations, labels, and data from the
// backup object but leaves all other fields intact (status, other metadata, etc...)
func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured {
newLive := live.DeepCopy()
newLive.SetAnnotations(bak.GetAnnotations())
newLive.SetLabels(bak.GetLabels())
newLive.SetFinalizers(bak.GetFinalizers())
switch live.GetKind() {
case "Secret", "ConfigMap":
newLive.Object["data"] = bak.Object["data"]
case "AppProject":
newLive.Object["spec"] = bak.Object["spec"]
case "Application":
newLive.Object["spec"] = bak.Object["spec"]
if _, ok := bak.Object["status"]; ok {
newLive.Object["status"] = bak.Object["status"]
}
case "ApplicationSet":
newLive.Object["spec"] = bak.Object["spec"]
}
return newLive
}

View File

@@ -1,606 +0,0 @@
package admin
import (
"context"
"fmt"
"math"
"os"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/go-redis/redis/v8"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/controller/sharding"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/util/argo"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/clusterauth"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/glob"
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/settings"
)
func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
var command = &cobra.Command{
Use: "cluster",
Short: "Manage clusters configuration",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewClusterConfig())
command.AddCommand(NewGenClusterConfigCommand(pathOpts))
command.AddCommand(NewClusterStatsCommand())
command.AddCommand(NewClusterShardsCommand())
namespacesCommand := NewClusterNamespacesCommand()
namespacesCommand.AddCommand(NewClusterEnableNamespacedMode())
namespacesCommand.AddCommand(NewClusterDisableNamespacedMode())
command.AddCommand(namespacesCommand)
return command
}
type ClusterWithInfo struct {
argoappv1.Cluster
// Shard holds controller shard number that handles the cluster
Shard int
// Namespaces holds list of namespaces managed by Argo CD in the cluster
Namespaces []string
}
func loadClusters(kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int) ([]ClusterWithInfo, error) {
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace)
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
clustersList, err := argoDB.ListClusters(context.Background())
if err != nil {
return nil, err
}
var cache *appstatecache.Cache
if portForwardRedis {
overrides := clientcmd.ConfigOverrides{}
port, err := kubeutil.PortForward(6379, namespace, &overrides,
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
if err != nil {
return nil, err
}
client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)})
cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour)), time.Hour)
} else {
cache, err = cacheSrc()
if err != nil {
return nil, err
}
}
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{})
if err != nil {
return nil, err
}
apps := appItems.Items
for i, app := range apps {
err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, argoDB)
if err != nil {
return nil, err
}
apps[i] = app
}
clusters := make([]ClusterWithInfo, len(clustersList.Items))
batchSize := 10
batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize)))
for batchNum := 0; batchNum < batchesCount; batchNum++ {
batchStart := batchSize * batchNum
batchEnd := batchSize * (batchNum + 1)
if batchEnd > len(clustersList.Items) {
batchEnd = len(clustersList.Items)
}
batch := clustersList.Items[batchStart:batchEnd]
_ = kube.RunAllAsync(len(batch), func(i int) error {
cluster := batch[i]
clusterShard := 0
if replicas > 0 {
clusterShard = sharding.GetShardByID(cluster.ID, replicas)
}
if shard != -1 && clusterShard != shard {
return nil
}
nsSet := map[string]bool{}
for _, app := range apps {
if app.Spec.Destination.Server == cluster.Server {
nsSet[app.Spec.Destination.Namespace] = true
}
}
var namespaces []string
for ns := range nsSet {
namespaces = append(namespaces, ns)
}
_ = cache.GetClusterInfo(cluster.Server, &cluster.Info)
clusters[batchStart+i] = ClusterWithInfo{cluster, clusterShard, namespaces}
return nil
})
}
return clusters, nil
}
func getControllerReplicas(kubeClient *kubernetes.Clientset, namespace string) (int, error) {
controllerPods, err := kubeClient.CoreV1().Pods(namespace).List(context.Background(), v1.ListOptions{
LabelSelector: "app.kubernetes.io/name=argocd-application-controller"})
if err != nil {
return 0, err
}
return len(controllerPods.Items), nil
}
func NewClusterShardsCommand() *cobra.Command {
var (
shard int
replicas int
clientConfig clientcmd.ClientConfig
cacheSrc func() (*appstatecache.Cache, error)
portForwardRedis bool
)
var command = cobra.Command{
Use: "shards",
Short: "Print information about each controller shard and portion of Kubernetes resources it is responsible for.",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
clientCfg, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
appClient := versioned.NewForConfigOrDie(clientCfg)
if replicas == 0 {
replicas, err = getControllerReplicas(kubeClient, namespace)
errors.CheckError(err)
}
if replicas == 0 {
return
}
clusters, err := loadClusters(kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard)
errors.CheckError(err)
if len(clusters) == 0 {
return
}
printStatsSummary(clusters)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter")
command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified")
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
return &command
}
func printStatsSummary(clusters []ClusterWithInfo) {
totalResourcesCount := int64(0)
resourcesCountByShard := map[int]int64{}
for _, c := range clusters {
totalResourcesCount += c.Info.CacheInfo.ResourcesCount
resourcesCountByShard[c.Shard] += c.Info.CacheInfo.ResourcesCount
}
avgResourcesByShard := totalResourcesCount / int64(len(resourcesCountByShard))
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
_, _ = fmt.Fprintf(w, "SHARD\tRESOURCES COUNT\n")
for shard := 0; shard < len(resourcesCountByShard); shard++ {
cnt := resourcesCountByShard[shard]
percent := (float64(cnt) / float64(avgResourcesByShard)) * 100.0
_, _ = fmt.Fprintf(w, "%d\t%s\n", shard, fmt.Sprintf("%d (%.0f%%)", cnt, percent))
}
_ = w.Flush()
}
func runClusterNamespacesCommand(clientConfig clientcmd.ClientConfig, action func(appClient *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error) error {
clientCfg, err := clientConfig.ClientConfig()
if err != nil {
return err
}
namespace, _, err := clientConfig.Namespace()
if err != nil {
return err
}
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
appClient := versioned.NewForConfigOrDie(clientCfg)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace)
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
clustersList, err := argoDB.ListClusters(context.Background())
if err != nil {
return err
}
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{})
if err != nil {
return err
}
apps := appItems.Items
for i, app := range apps {
err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, argoDB)
if err != nil {
return err
}
apps[i] = app
}
clusters := map[string][]string{}
for _, cluster := range clustersList.Items {
nsSet := map[string]bool{}
for _, app := range apps {
if app.Spec.Destination.Server != cluster.Server {
continue
}
// Use namespaces of actually deployed resources, since some application use dummy target namespace
// If resources list is empty then use target namespace
if len(app.Status.Resources) != 0 {
for _, res := range app.Status.Resources {
if res.Namespace != "" {
nsSet[res.Namespace] = true
}
}
} else {
if app.Spec.Destination.Server == cluster.Server {
nsSet[app.Spec.Destination.Namespace] = true
}
}
}
var namespaces []string
for ns := range nsSet {
namespaces = append(namespaces, ns)
}
clusters[cluster.Server] = namespaces
}
return action(appClient, argoDB, clusters)
}
func NewClusterNamespacesCommand() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = cobra.Command{
Use: "namespaces",
Short: "Print information namespaces which Argo CD manages in each cluster.",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
err := runClusterNamespacesCommand(clientConfig, func(appClient *versioned.Clientset, _ db.ArgoDB, clusters map[string][]string) error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
_, _ = fmt.Fprintf(w, "CLUSTER\tNAMESPACES\n")
for cluster, namespaces := range clusters {
// print shortest namespace names first
sort.Slice(namespaces, func(i, j int) bool {
return len(namespaces[j]) > len(namespaces[i])
})
namespacesStr := ""
if len(namespaces) > 4 {
namespacesStr = fmt.Sprintf("%s (total %d)", strings.Join(namespaces[:4], ","), len(namespaces))
} else {
namespacesStr = strings.Join(namespaces, ",")
}
_, _ = fmt.Fprintf(w, "%s\t%s\n", cluster, namespacesStr)
}
_ = w.Flush()
return nil
})
errors.CheckError(err)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
return &command
}
func NewClusterEnableNamespacedMode() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
dryRun bool
clusterResources bool
namespacesCount int
)
var command = cobra.Command{
Use: "enable-namespaced-mode PATTERN",
Short: "Enable namespaced mode for clusters which name matches to the specified pattern.",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
if len(args) == 0 {
cmd.HelpFunc()(cmd, args)
os.Exit(1)
}
pattern := args[0]
errors.CheckError(runClusterNamespacesCommand(clientConfig, func(_ *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error {
for server, namespaces := range clusters {
if len(namespaces) == 0 || len(namespaces) > namespacesCount || !glob.Match(pattern, server) {
continue
}
cluster, err := argoDB.GetCluster(context.Background(), server)
if err != nil {
return err
}
cluster.Namespaces = namespaces
cluster.ClusterResources = clusterResources
fmt.Printf("Setting cluster %s namespaces to %v...", server, namespaces)
if !dryRun {
_, err = argoDB.UpdateCluster(context.Background(), cluster)
if err != nil {
return err
}
fmt.Println("done")
} else {
fmt.Println("done (dry run)")
}
}
return nil
}))
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().BoolVar(&dryRun, "dry-run", true, "Print what will be performed")
command.Flags().BoolVar(&clusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed.")
command.Flags().IntVar(&namespacesCount, "max-namespace-count", 0, "Max number of namespaces that cluster should managed managed namespaces is less or equal to specified count")
return &command
}
func NewClusterDisableNamespacedMode() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
dryRun bool
)
var command = cobra.Command{
Use: "disable-namespaced-mode PATTERN",
Short: "Disable namespaced mode for clusters which name matches to the specified pattern.",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
if len(args) == 0 {
cmd.HelpFunc()(cmd, args)
os.Exit(1)
}
pattern := args[0]
errors.CheckError(runClusterNamespacesCommand(clientConfig, func(_ *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error {
for server := range clusters {
if !glob.Match(pattern, server) {
continue
}
cluster, err := argoDB.GetCluster(context.Background(), server)
if err != nil {
return err
}
if len(cluster.Namespaces) == 0 {
continue
}
cluster.Namespaces = nil
fmt.Printf("Disabling namespaced mode for cluster %s...", server)
if !dryRun {
_, err = argoDB.UpdateCluster(context.Background(), cluster)
if err != nil {
return err
}
fmt.Println("done")
} else {
fmt.Println("done (dry run)")
}
}
return nil
}))
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().BoolVar(&dryRun, "dry-run", true, "Print what will be performed")
return &command
}
func NewClusterStatsCommand() *cobra.Command {
var (
shard int
replicas int
clientConfig clientcmd.ClientConfig
cacheSrc func() (*appstatecache.Cache, error)
portForwardRedis bool
)
var command = cobra.Command{
Use: "stats",
Short: "Prints information cluster statistics and inferred shard number",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
clientCfg, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
appClient := versioned.NewForConfigOrDie(clientCfg)
if replicas == 0 {
replicas, err = getControllerReplicas(kubeClient, namespace)
errors.CheckError(err)
}
clusters, err := loadClusters(kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard)
errors.CheckError(err)
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
_, _ = fmt.Fprintf(w, "SERVER\tSHARD\tCONNECTION\tNAMESPACES COUNT\tAPPS COUNT\tRESOURCES COUNT\n")
for _, cluster := range clusters {
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%d\t%d\n", cluster.Server, cluster.Shard, cluster.Info.ConnectionState.Status, len(cluster.Namespaces), cluster.Info.ApplicationsCount, cluster.Info.CacheInfo.ResourcesCount)
}
_ = w.Flush()
},
}
clientConfig = cli.AddKubectlFlagsToCmd(&command)
command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter")
command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified")
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
return &command
}
// NewClusterConfig returns a new instance of `argocd admin kubeconfig` command
func NewClusterConfig() *cobra.Command {
var (
clientConfig clientcmd.ClientConfig
)
var command = &cobra.Command{
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
Short: "Generates kubeconfig for the specified cluster",
DisableAutoGenTag: true,
Run: func(c *cobra.Command, args []string) {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
serverUrl := args[0]
output := args[1]
conf, err := clientConfig.ClientConfig()
errors.CheckError(err)
namespace, _, err := clientConfig.Namespace()
errors.CheckError(err)
kubeclientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
cluster, err := db.NewDB(namespace, settings.NewSettingsManager(context.Background(), kubeclientset, namespace), kubeclientset).GetCluster(context.Background(), serverUrl)
errors.CheckError(err)
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
errors.CheckError(err)
},
}
clientConfig = cli.AddKubectlFlagsToCmd(command)
return command
}
func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
var (
clusterOpts cmdutil.ClusterOptions
bearerToken string
generateToken bool
outputFormat string
)
var command = &cobra.Command{
Use: "generate-spec CONTEXT",
Short: "Generate declarative config for a cluster",
Run: func(c *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
var configAccess clientcmd.ConfigAccess = pathOpts
if len(args) == 0 {
log.Error("Choose a context name from:")
cmdutil.PrintKubeContexts(configAccess)
os.Exit(1)
}
cfgAccess, err := configAccess.GetStartingConfig()
errors.CheckError(err)
contextName := args[0]
clstContext := cfgAccess.Contexts[contextName]
if clstContext == nil {
log.Fatalf("Context %s does not exist in kubeconfig", contextName)
return
}
overrides := clientcmd.ConfigOverrides{
Context: *clstContext,
}
clientConfig := clientcmd.NewDefaultClientConfig(*cfgAccess, &overrides)
conf, err := clientConfig.ClientConfig()
errors.CheckError(err)
kubeClientset := fake.NewSimpleClientset()
var awsAuthConf *argoappv1.AWSAuthConfig
var execProviderConf *argoappv1.ExecProviderConfig
if clusterOpts.AwsClusterName != "" {
awsAuthConf = &argoappv1.AWSAuthConfig{
ClusterName: clusterOpts.AwsClusterName,
RoleARN: clusterOpts.AwsRoleArn,
}
} else if clusterOpts.ExecProviderCommand != "" {
execProviderConf = &argoappv1.ExecProviderConfig{
Command: clusterOpts.ExecProviderCommand,
Args: clusterOpts.ExecProviderArgs,
Env: clusterOpts.ExecProviderEnv,
APIVersion: clusterOpts.ExecProviderAPIVersion,
InstallHint: clusterOpts.ExecProviderInstallHint,
}
} else if generateToken {
bearerToken, err = GenerateToken(clusterOpts, conf)
errors.CheckError(err)
} else if bearerToken == "" {
bearerToken = "bearer-token"
}
if clusterOpts.Name != "" {
contextName = clusterOpts.Name
}
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf)
if clusterOpts.InCluster {
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
}
if clusterOpts.Shard >= 0 {
clst.Shard = &clusterOpts.Shard
}
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
_, err = argoDB.CreateCluster(context.Background(), clst)
errors.CheckError(err)
secName, err := db.URIToSecretName("cluster", clst.Server)
errors.CheckError(err)
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), secName, v1.GetOptions{})
errors.CheckError(err)
errors.CheckError(PrintResources(outputFormat, os.Stdout, secret))
},
}
command.PersistentFlags().StringVar(&pathOpts.LoadingRules.ExplicitPath, pathOpts.ExplicitFileFlag, pathOpts.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
command.Flags().StringVar(&bearerToken, "bearer-token", "", "Authentication token that should be used to access K8S API server")
command.Flags().BoolVar(&generateToken, "generate-bearer-token", false, "Generate authentication token that should be used to access K8S API server")
command.Flags().StringVar(&clusterOpts.ServiceAccount, "service-account", "argocd-manager", fmt.Sprintf("System namespace service account to use for kubernetes resource management. If not set then default \"%s\" SA will be used", clusterauth.ArgoCDManagerServiceAccount))
command.Flags().StringVar(&clusterOpts.SystemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace")
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
cmdutil.AddClusterFlags(command, &clusterOpts)
return command
}
func GenerateToken(clusterOpts cmdutil.ClusterOptions, conf *rest.Config) (string, error) {
clientset, err := kubernetes.NewForConfig(conf)
errors.CheckError(err)
bearerToken, err := clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount)
if err != nil {
return "", err
}
return bearerToken, nil
}

View File

@@ -1,30 +0,0 @@
package admin
import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
)
func NewDashboardCommand() *cobra.Command {
var (
port int
)
cmd := &cobra.Command{
Use: "dashboard",
Short: "Starts Argo CD Web UI locally",
Run: func(cmd *cobra.Command, args []string) {
println(fmt.Sprintf("Argo CD UI is available at http://localhost:%d", port))
<-context.Background().Done()
},
}
clientOpts := &apiclient.ClientOptions{Core: true}
headless.InitCommand(cmd, clientOpts, &port)
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
return cmd
}

View File

@@ -1,108 +0,0 @@
package admin
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
v1 "k8s.io/api/core/v1"
ioutil "github.com/argoproj/argo-cd/v2/util/io"
)
func getOutWriter(inline bool, filePath string) (io.Writer, io.Closer, error) {
if !inline {
return os.Stdout, ioutil.NopCloser, nil
}
if filePath == "" {
return nil, nil, errors.New("The file path must be specified using flag '--file'")
}
err := os.Rename(filePath, fmt.Sprintf("%s.back", filePath))
if err != nil {
return nil, nil, err
}
fileOut, err := os.Create(filePath)
if err != nil {
return nil, nil, err
}
return fileOut, fileOut, nil
}
// PrintResources prints a single resource in YAML or JSON format to stdout according to the output format
func PrintResources(output string, out io.Writer, resources ...interface{}) error {
for i, resource := range resources {
if secret, ok := resource.(*v1.Secret); ok {
convertSecretData(secret)
}
filteredResource, err := omitFields(resource)
if err != nil {
return err
}
resources[i] = filteredResource
}
var obj interface{} = resources
if len(resources) == 1 {
obj = resources[0]
}
switch output {
case "json":
jsonBytes, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
_, _ = fmt.Fprintln(out, string(jsonBytes))
case "yaml":
yamlBytes, err := yaml.Marshal(obj)
if err != nil {
return err
}
// marshaled YAML already ends with the new line character
_, _ = fmt.Fprint(out, string(yamlBytes))
default:
return fmt.Errorf("unknown output format: %s", output)
}
return nil
}
// omit fields such as status, creationTimestamp and metadata.namespace in k8s objects
func omitFields(resource interface{}) (interface{}, error) {
jsonBytes, err := json.Marshal(resource)
if err != nil {
return nil, err
}
toMap := make(map[string]interface{})
err = json.Unmarshal(jsonBytes, &toMap)
if err != nil {
return nil, err
}
delete(toMap, "status")
if v, ok := toMap["metadata"]; ok {
if metadata, ok := v.(map[string]interface{}); ok {
delete(metadata, "creationTimestamp")
delete(metadata, "namespace")
}
}
return toMap, nil
}
// convertSecretData converts kubernetes secret's data to stringData
func convertSecretData(secret *v1.Secret) {
secret.Kind = kube.SecretKind
secret.APIVersion = "v1"
secret.StringData = map[string]string{}
for k, v := range secret.Data {
secret.StringData[k] = string(v)
}
secret.Data = map[string][]byte{}
}

View File

@@ -1,58 +0,0 @@
package admin
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestGetOutWriter_InlineOff(t *testing.T) {
out, closer, err := getOutWriter(false, "")
require.NoError(t, err)
defer io.Close(closer)
assert.Equal(t, os.Stdout, out)
}
func TestGetOutWriter_InlineOn(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "")
require.NoError(t, err)
defer func() {
_ = os.Remove(tmpFile.Name())
_ = os.Remove(fmt.Sprintf("%s.back", tmpFile.Name()))
}()
out, closer, err := getOutWriter(true, tmpFile.Name())
require.NoError(t, err)
defer io.Close(closer)
assert.Equal(t, tmpFile.Name(), out.(*os.File).Name())
_, err = os.Stat(fmt.Sprintf("%s.back", tmpFile.Name()))
assert.NoError(t, err, "Back file must be created")
}
func TestPrintResources_Secret_YAML(t *testing.T) {
out := bytes.Buffer{}
err := PrintResources("yaml", &out, &v1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: "my-secret"},
Data: map[string][]byte{"my-secret-key": []byte("my-secret-data")},
})
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret
metadata:
name: my-secret
stringData:
my-secret-key: my-secret-data
`, out.String())
}

View File

@@ -1,169 +0,0 @@
package admin
import (
"context"
"fmt"
"io/ioutil"
"os"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
apiv1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/settings"
)
const (
ArgoCDNamespace = "argocd"
repoSecretPrefix = "repo"
)
func NewRepoCommand() *cobra.Command {
var command = &cobra.Command{
Use: "repo",
Short: "Manage repositories configuration",
Run: func(c *cobra.Command, args []string) {
c.HelpFunc()(c, args)
},
}
command.AddCommand(NewGenRepoSpecCommand())
return command
}
func NewGenRepoSpecCommand() *cobra.Command {
var (
repoOpts cmdutil.RepoOptions
outputFormat string
)
// For better readability and easier formatting
var repoAddExamples = `
# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
argocd admin repo generate-spec git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
argocd admin repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
# Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate
argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
# Add a public Helm repository named 'stable' via HTTPS
argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable
# Add a private Helm repository named 'stable' via HTTPS
argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test
# Add a private Helm OCI-based repository named 'stable' via HTTPS
argocd admin repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
`
var command = &cobra.Command{
Use: "generate-spec REPOURL",
Short: "Generate declarative config for a repo",
Example: repoAddExamples,
Run: func(c *cobra.Command, args []string) {
log.SetLevel(log.WarnLevel)
if len(args) != 1 {
c.HelpFunc()(c, args)
os.Exit(1)
}
// Repository URL
repoOpts.Repo.Repo = args[0]
// Specifying ssh-private-key-path is only valid for SSH repositories
if repoOpts.SshPrivateKeyPath != "" {
if ok, _ := git.IsSSHURL(repoOpts.Repo.Repo); ok {
keyData, err := ioutil.ReadFile(repoOpts.SshPrivateKeyPath)
if err != nil {
log.Fatal(err)
}
repoOpts.Repo.SSHPrivateKey = string(keyData)
} else {
err := fmt.Errorf("--ssh-private-key-path is only supported for SSH repositories.")
errors.CheckError(err)
}
}
// tls-client-cert-path and tls-client-cert-key-key-path must always be
// specified together
if (repoOpts.TlsClientCertPath != "" && repoOpts.TlsClientCertKeyPath == "") || (repoOpts.TlsClientCertPath == "" && repoOpts.TlsClientCertKeyPath != "") {
err := fmt.Errorf("--tls-client-cert-path and --tls-client-cert-key-path must be specified together")
errors.CheckError(err)
}
// Specifying tls-client-cert-path is only valid for HTTPS repositories
if repoOpts.TlsClientCertPath != "" {
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
tlsCertData, err := ioutil.ReadFile(repoOpts.TlsClientCertPath)
errors.CheckError(err)
tlsCertKey, err := ioutil.ReadFile(repoOpts.TlsClientCertKeyPath)
errors.CheckError(err)
repoOpts.Repo.TLSClientCertData = string(tlsCertData)
repoOpts.Repo.TLSClientCertKey = string(tlsCertKey)
} else {
err := fmt.Errorf("--tls-client-cert-path is only supported for HTTPS repositories")
errors.CheckError(err)
}
}
// Set repository connection properties only when creating repository, not
// when creating repository credentials.
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
repoOpts.Repo.InsecureIgnoreHostKey = repoOpts.InsecureIgnoreHostKey
repoOpts.Repo.Insecure = repoOpts.InsecureSkipServerVerification
repoOpts.Repo.EnableLFS = repoOpts.EnableLfs
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
errors.CheckError(fmt.Errorf("must specify --name for repos of type 'helm'"))
}
// If the user set a username, but didn't supply password via --password,
// then we prompt for it
if repoOpts.Repo.Username != "" && repoOpts.Repo.Password == "" {
repoOpts.Repo.Password = cli.PromptPassword(repoOpts.Repo.Password)
}
argoCDCM := &apiv1.ConfigMap{
TypeMeta: v1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: v1.ObjectMeta{
Name: common.ArgoCDConfigMapName,
Namespace: ArgoCDNamespace,
Labels: map[string]string{
"app.kubernetes.io/part-of": "argocd",
},
},
}
kubeClientset := fake.NewSimpleClientset(argoCDCM)
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
_, err := argoDB.CreateRepository(context.Background(), &repoOpts.Repo)
errors.CheckError(err)
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo), v1.GetOptions{})
errors.CheckError(err)
errors.CheckError(PrintResources(outputFormat, os.Stdout, secret))
},
}
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
cmdutil.AddRepoFlags(command, &repoOpts)
return command
}

View File

@@ -30,27 +30,28 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/pointer"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/controller"
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/reposerver/repository"
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
argoio "github.com/argoproj/argo-cd/v2/util/io"
argokube "github.com/argoproj/argo-cd/v2/util/kube"
"github.com/argoproj/argo-cd/v2/util/templates"
"github.com/argoproj/argo-cd/v2/util/text/label"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller"
"github.com/argoproj/argo-cd/pkg/apiclient"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient/application"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apiclient/settings"
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
repoapiclient "github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/reposerver/repository"
"github.com/argoproj/argo-cd/util/argo"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
argoio "github.com/argoproj/argo-cd/util/io"
argokube "github.com/argoproj/argo-cd/util/kube"
"github.com/argoproj/argo-cd/util/templates"
"github.com/argoproj/argo-cd/util/text/label"
)
var (
@@ -101,12 +102,11 @@ func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
// NewApplicationCreateCommand returns a new instance of an `argocd app create` command
func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
appOpts cmdutil.AppOptions
fileURL string
appName string
upsert bool
labels []string
annotations []string
appOpts cmdutil.AppOptions
fileURL string
appName string
upsert bool
labels []string
)
var command = &cobra.Command{
Use: "create APPNAME",
@@ -122,7 +122,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
argocd app create helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2
# Create a Helm app from a Helm repo
argocd app create nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
argocd app create nginx-ingress --repo https://kubernetes-charts.storage.googleapis.com --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
# Create a Kustomize app
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
@@ -133,7 +133,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
Run: func(c *cobra.Command, args []string) {
argocdClient := argocdclient.NewClientOrDie(clientOpts)
app, err := cmdutil.ConstructApp(fileURL, appName, labels, annotations, args, appOpts, c.Flags())
app, err := cmdutil.ConstructApp(fileURL, appName, labels, args, appOpts, c.Flags())
errors.CheckError(err)
if app.Name == "" {
@@ -157,7 +157,6 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
command.Flags().BoolVar(&upsert, "upsert", false, "Allows to override application with the same name even if supplied application spec is different from existing spec")
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app")
command.Flags().StringArrayVarP(&annotations, "annotations", "", []string{}, "Set metadata annotations (e.g. example=value)")
// Only complete files with appropriate extension.
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
if err != nil {
@@ -277,7 +276,6 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
sinceSeconds int64
untilTime string
filter string
container string
)
var command = &cobra.Command{
Use: "logs APPNAME",
@@ -306,7 +304,6 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
SinceSeconds: sinceSeconds,
UntilTime: &untilTime,
Filter: &filter,
Container: container,
})
if err != nil {
log.Fatalf("failed to get pod logs: %v", err)
@@ -347,7 +344,6 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().Int64Var(&sinceSeconds, "since-seconds", 0, "A relative time in seconds before the current time from which to show logs")
command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time")
command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string")
command.Flags().StringVar(&container, "container", "", "Optional container name")
return command
}
@@ -560,7 +556,6 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
namePrefix bool
kustomizeVersion bool
kustomizeImages []string
pluginEnvs []string
appOpts cmdutil.AppOptions
)
var command = &cobra.Command{
@@ -666,23 +661,9 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
}
}
}
}
if app.Spec.Source.Plugin != nil {
if len(pluginEnvs) == 0 {
c.HelpFunc()(c, args)
os.Exit(1)
if !updated {
return
}
for _, env := range pluginEnvs {
err = app.Spec.Source.Plugin.RemoveEnvEntry(env)
if err == nil {
updated = true
}
}
}
if !updated {
return
}
cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
@@ -701,7 +682,6 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
command.Flags().BoolVar(&namePrefix, "nameprefix", false, "Kustomize nameprefix")
command.Flags().BoolVar(&kustomizeVersion, "kustomize-version", false, "Kustomize version")
command.Flags().StringArrayVar(&kustomizeImages, "kustomize-image", []string{}, "Kustomize images name (e.g. --kustomize-image node --kustomize-image mysql)")
command.Flags().StringArrayVar(&pluginEnvs, "plugin-env", []string{}, "Unset plugin env variables (e.g --plugin-env name)")
return command
}
@@ -803,7 +783,6 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var (
refresh bool
hardRefresh bool
exitCode bool
local string
revision string
localRepoRoot string
@@ -907,7 +886,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
_ = cli.PrintDiff(item.key.Name, live, target)
}
}
if foundDiffs && exitCode {
if foundDiffs {
os.Exit(1)
}
@@ -915,7 +894,6 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
}
command.Flags().BoolVar(&refresh, "refresh", false, "Refresh application data when retrieving")
command.Flags().BoolVar(&hardRefresh, "hard-refresh", false, "Refresh application data as well as target manifests cache")
command.Flags().BoolVar(&exitCode, "exit-code", true, "Return non-zero exit code when there is a diff")
command.Flags().StringVar(&local, "local", "", "Compare live app to a local manifests")
command.Flags().StringVar(&revision, "revision", "", "Compare live app to a particular revision")
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
@@ -958,9 +936,8 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[
// NewApplicationDeleteCommand returns a new instance of an `argocd app delete` command
func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
cascade bool
noPrompt bool
propagationPolicy string
cascade bool
noPrompt bool
)
var command = &cobra.Command{
Use: "delete APPNAME",
@@ -986,9 +963,6 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
if c.Flag("cascade").Changed {
appDeleteReq.Cascade = &cascade
}
if c.Flag("propagation-policy").Changed {
appDeleteReq.PropagationPolicy = &propagationPolicy
}
if cascade && isTerminal && !noPrompt {
var confirmAnswer string = "n"
var lowercaseAnswer string
@@ -1023,7 +997,6 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
},
}
command.Flags().BoolVar(&cascade, "cascade", true, "Perform a cascaded deletion of all application resources")
command.Flags().StringVarP(&propagationPolicy, "propagation-policy", "p", "foreground", "Specify propagation policy for deletion of application's resources. One of: foreground|background")
command.Flags().BoolVarP(&noPrompt, "yes", "y", false, "Turn off prompting to confirm cascaded deletion of application resources")
return command
}
@@ -1072,7 +1045,6 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
output string
selector string
projects []string
repo string
)
var command = &cobra.Command{
Use: "list",
@@ -1091,9 +1063,6 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
if len(projects) != 0 {
appList = argo.FilterByProjects(appList, projects)
}
if repo != "" {
appList = argo.FilterByRepo(appList, repo)
}
switch output {
case "yaml", "json":
err := PrintResourceList(appList, output, false)
@@ -1110,7 +1079,6 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: wide|name|json|yaml")
command.Flags().StringVarP(&selector, "selector", "l", "", "List apps by label")
command.Flags().StringArrayVarP(&projects, "project", "p", []string{}, "Filter by project name")
command.Flags().StringVarP(&repo, "repo", "r", "", "List apps by source repo URL")
return command
}
@@ -1439,9 +1407,9 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
command.Flags().StringArrayVar(&labels, "label", []string{}, "Sync only specific resources with a label. This option may be specified repeatedly.")
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
command.Flags().Int64Var(&retryLimit, "retry-limit", 0, "Max number of allowed sync retries")
command.Flags().DurationVar(&retryBackoffDuration, "retry-backoff-duration", argoappv1.DefaultSyncRetryDuration, "Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
command.Flags().DurationVar(&retryBackoffMaxDuration, "retry-backoff-max-duration", argoappv1.DefaultSyncRetryMaxDuration, "Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
command.Flags().Int64Var(&retryBackoffFactor, "retry-backoff-factor", argoappv1.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed retry")
command.Flags().DurationVar(&retryBackoffDuration, "retry-backoff-duration", common.DefaultSyncRetryDuration, "Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
command.Flags().DurationVar(&retryBackoffMaxDuration, "retry-backoff-max-duration", common.DefaultSyncRetryMaxDuration, "Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
command.Flags().Int64Var(&retryBackoffFactor, "retry-backoff-factor", common.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed retry")
command.Flags().StringVar(&strategy, "strategy", "", "Sync strategy (one of: apply|hook)")
command.Flags().BoolVar(&force, "force", false, "Use a force apply")
command.Flags().BoolVar(&async, "async", false, "Do not wait for application to sync before continuing")
@@ -1814,23 +1782,6 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra
return command
}
func findRevisionHistory(application *argoappv1.Application, historyId int64) (*argoappv1.RevisionHistory, error) {
// in case if history id not passed and need fetch previous history revision
if historyId == -1 {
l := len(application.Status.History)
if l < 2 {
return nil, fmt.Errorf("Application '%s' should have at least two successful deployments", application.ObjectMeta.Name)
}
return &application.Status.History[l-2], nil
}
for _, di := range application.Status.History {
if di.ID == historyId {
return &di, nil
}
}
return nil, fmt.Errorf("Application '%s' does not have deployment id '%d' in history\n", application.ObjectMeta.Name, historyId)
}
// NewApplicationRollbackCommand returns a new instance of an `argocd app rollback` command
func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
@@ -1838,33 +1789,36 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
timeout uint
)
var command = &cobra.Command{
Use: "rollback APPNAME [ID]",
Short: "Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version",
Use: "rollback APPNAME ID",
Short: "Rollback application to a previous deployed version by History ID",
Run: func(c *cobra.Command, args []string) {
if len(args) == 0 {
if len(args) != 2 {
c.HelpFunc()(c, args)
os.Exit(1)
}
appName := args[0]
var err error
depID := -1
if len(args) > 1 {
depID, err = strconv.Atoi(args[1])
errors.CheckError(err)
}
depID, err := strconv.Atoi(args[1])
errors.CheckError(err)
acdClient := argocdclient.NewClientOrDie(clientOpts)
conn, appIf := acdClient.NewApplicationClientOrDie()
defer argoio.Close(conn)
ctx := context.Background()
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName})
errors.CheckError(err)
depInfo, err := findRevisionHistory(app, int64(depID))
errors.CheckError(err)
var depInfo *argoappv1.RevisionHistory
for _, di := range app.Status.History {
if di.ID == int64(depID) {
depInfo = &di
break
}
}
if depInfo == nil {
log.Fatalf("Application '%s' does not have deployment id '%d' in history\n", app.ObjectMeta.Name, depID)
}
_, err = appIf.Rollback(ctx, &applicationpkg.ApplicationRollbackRequest{
Name: &appName,
ID: depInfo.ID,
ID: int64(depID),
Prune: prune,
})
errors.CheckError(err)

View File

@@ -12,10 +12,10 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
type DisplayedAction struct {

View File

@@ -1,163 +0,0 @@
package commands
import (
"testing"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)
func TestFindRevisionHistoryWithoutPassedId(t *testing.T) {
histories := v1alpha1.RevisionHistories{}
histories = append(histories, v1alpha1.RevisionHistory{ID: 1})
histories = append(histories, v1alpha1.RevisionHistory{ID: 2})
histories = append(histories, v1alpha1.RevisionHistory{ID: 3})
status := v1alpha1.ApplicationStatus{
Resources: nil,
Sync: v1alpha1.SyncStatus{},
Health: v1alpha1.HealthStatus{},
History: histories,
Conditions: nil,
ReconciledAt: nil,
OperationState: nil,
ObservedAt: nil,
SourceType: "",
Summary: v1alpha1.ApplicationSummary{},
}
application := v1alpha1.Application{
Status: status,
}
history, err := findRevisionHistory(&application, -1)
if err != nil {
t.Fatal("Find revision history should fail without errors")
}
if history == nil {
t.Fatal("History should be found")
}
}
func TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) {
histories := v1alpha1.RevisionHistories{}
status := v1alpha1.ApplicationStatus{
Resources: nil,
Sync: v1alpha1.SyncStatus{},
Health: v1alpha1.HealthStatus{},
History: histories,
Conditions: nil,
ReconciledAt: nil,
OperationState: nil,
ObservedAt: nil,
SourceType: "",
Summary: v1alpha1.ApplicationSummary{},
}
application := v1alpha1.Application{
Status: status,
}
history, err := findRevisionHistory(&application, -1)
if err == nil {
t.Fatal("Find revision history should fail with errors")
}
if history != nil {
t.Fatal("History should be empty")
}
if err.Error() != "Application '' should have at least two successful deployments" {
t.Fatal("Find revision history should fail with correct error message")
}
}
func TestFindRevisionHistoryWithPassedId(t *testing.T) {
histories := v1alpha1.RevisionHistories{}
histories = append(histories, v1alpha1.RevisionHistory{ID: 1})
histories = append(histories, v1alpha1.RevisionHistory{ID: 2})
histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"})
status := v1alpha1.ApplicationStatus{
Resources: nil,
Sync: v1alpha1.SyncStatus{},
Health: v1alpha1.HealthStatus{},
History: histories,
Conditions: nil,
ReconciledAt: nil,
OperationState: nil,
ObservedAt: nil,
SourceType: "",
Summary: v1alpha1.ApplicationSummary{},
}
application := v1alpha1.Application{
Status: status,
}
history, err := findRevisionHistory(&application, 3)
if err != nil {
t.Fatal("Find revision history should fail without errors")
}
if history == nil {
t.Fatal("History should be found")
}
if history.Revision != "123" {
t.Fatal("Failed to find correct history with correct revision")
}
}
func TestFindRevisionHistoryWithPassedIdThatNotExist(t *testing.T) {
histories := v1alpha1.RevisionHistories{}
histories = append(histories, v1alpha1.RevisionHistory{ID: 1})
histories = append(histories, v1alpha1.RevisionHistory{ID: 2})
histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"})
status := v1alpha1.ApplicationStatus{
Resources: nil,
Sync: v1alpha1.SyncStatus{},
Health: v1alpha1.HealthStatus{},
History: histories,
Conditions: nil,
ReconciledAt: nil,
OperationState: nil,
ObservedAt: nil,
SourceType: "",
Summary: v1alpha1.ApplicationSummary{},
}
application := v1alpha1.Application{
Status: status,
}
history, err := findRevisionHistory(&application, 4)
if err == nil {
t.Fatal("Find revision history should fail with errors")
}
if history != nil {
t.Fatal("History should be not found")
}
if err.Error() != "Application '' does not have deployment id '4' in history\n" {
t.Fatal("Find revision history should fail with correct error message")
}
}

View File

@@ -11,12 +11,12 @@ import (
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
certificatepkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/certificate"
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
certutil "github.com/argoproj/argo-cd/v2/util/cert"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
certutil "github.com/argoproj/argo-cd/util/cert"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
// NewCertCommand returns a new instance of an `argocd repo` command

View File

@@ -7,21 +7,19 @@ import (
"strings"
"text/tabwriter"
"github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
"github.com/argoproj/argo-cd/v2/common"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/clusterauth"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
"github.com/argoproj/argo-cd/common"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/clusterauth"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
// NewClusterCommand returns a new instance of an `argocd cluster` command
@@ -58,8 +56,7 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
// NewClusterAddCommand returns a new instance of an `argocd cluster add` command
func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command {
var (
clusterOpts cmdutil.ClusterOptions
skipConfirmation bool
clusterOpts cmdutil.ClusterOptions
)
var command = &cobra.Command{
Use: "add CONTEXT",
@@ -79,15 +76,6 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
log.Fatalf("Context %s does not exist in kubeconfig", contextName)
}
isTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
if isTerminal && !skipConfirmation {
message := fmt.Sprintf("WARNING: This will create a service account `argocd-manager` on the cluster referenced by context `%s` with full cluster level admin privileges. Do you want to continue [y/N]? ", contextName)
if !cli.AskToProceed(message) {
os.Exit(1)
}
}
overrides := clientcmd.ConfigOverrides{
Context: *clstContext,
}
@@ -127,9 +115,9 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
if clusterOpts.Name != "" {
contextName = clusterOpts.Name
}
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, managerBearerToken, awsAuthConf, execProviderConf)
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, conf, managerBearerToken, awsAuthConf, execProviderConf)
if clusterOpts.InCluster {
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
clst.Server = common.KubernetesInternalAPIServerAddr
}
if clusterOpts.Shard >= 0 {
clst.Shard = &clusterOpts.Shard
@@ -147,7 +135,6 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
command.Flags().BoolVar(&clusterOpts.Upsert, "upsert", false, "Override an existing cluster with the same name even if the spec differs")
command.Flags().StringVar(&clusterOpts.ServiceAccount, "service-account", "", fmt.Sprintf("System namespace service account to use for kubernetes resource management. If not set then default \"%s\" SA will be created", clusterauth.ArgoCDManagerServiceAccount))
command.Flags().StringVar(&clusterOpts.SystemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace")
command.Flags().BoolVarP(&skipConfirmation, "yes", "y", false, "Skip explicit confirmation")
cmdutil.AddClusterFlags(command, &clusterOpts)
return command
}

View File

@@ -5,7 +5,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func Test_printClusterTable(t *testing.T) {

View File

@@ -11,9 +11,9 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/localconfig"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/localconfig"
)
// NewContextCommand returns a new instance of an `argocd ctx` command

View File

@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/argoproj/argo-cd/v2/util/localconfig"
"github.com/argoproj/argo-cd/util/localconfig"
)
const testConfig = `contexts:

View File

@@ -10,11 +10,11 @@ import (
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
gpgkeypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/gpgkey"
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/errors"
argoio "github.com/argoproj/argo-cd/v2/util/io"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
gpgkeypkg "github.com/argoproj/argo-cd/pkg/apiclient/gpgkey"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
argoio "github.com/argoproj/argo-cd/util/io"
)
// NewGPGCommand returns a new instance of an `argocd repo` command

View File

@@ -1,97 +0,0 @@
package headless
import (
"context"
"fmt"
"sync"
"time"
"github.com/go-redis/redis/v8"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/util/cache"
"github.com/argoproj/argo-cd/v2/util/io"
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
)
type forwardCacheClient struct {
namespace string
init sync.Once
client cache.CacheClient
err error
}
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
c.init.Do(func() {
overrides := clientcmd.ConfigOverrides{}
redisPort, err := kubeutil.PortForward(6379, c.namespace, &overrides,
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
if err != nil {
c.err = err
return
}
redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)})
c.client = cache.NewRedisCache(redisClient, time.Hour)
})
if c.err != nil {
return c.err
}
return action(c.client)
}
func (c *forwardCacheClient) Set(item *cache.Item) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.Set(item)
})
}
func (c *forwardCacheClient) Get(key string, obj interface{}) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.Get(key, obj)
})
}
func (c *forwardCacheClient) Delete(key string) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.Delete(key)
})
}
func (c *forwardCacheClient) OnUpdated(ctx context.Context, key string, callback func() error) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.OnUpdated(ctx, key, callback)
})
}
func (c *forwardCacheClient) NotifyUpdated(key string) error {
return c.doLazy(func(client cache.CacheClient) error {
return client.NotifyUpdated(key)
})
}
type forwardRepoClientset struct {
namespace string
init sync.Once
repoClientset repoapiclient.Clientset
err error
}
func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) {
c.init.Do(func() {
overrides := clientcmd.ConfigOverrides{}
repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
if err != nil {
c.err = err
return
}
c.repoClientset = apiclient.NewRepoServerClientset(fmt.Sprintf("localhost:%d", repoServerPort), 60, apiclient.TLSConfiguration{
DisableTLS: false, StrictValidation: false})
})
if c.err != nil {
return nil, nil, c.err
}
return c.repoClientset.NewRepoServerClient()
}

View File

@@ -1,152 +0,0 @@
package headless
import (
"context"
"fmt"
"net"
"os"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/go-redis/redis/v8"
"github.com/golang/protobuf/ptypes/empty"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
argoapi "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/server"
servercache "github.com/argoproj/argo-cd/v2/server/cache"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/localconfig"
)
func testAPI(clientOpts *argoapi.ClientOptions) error {
apiClient, err := argoapi.NewClient(clientOpts)
if err != nil {
return err
}
closer, versionClient, err := apiClient.NewVersionClient()
if err != nil {
return err
}
defer io.Close(closer)
_, err = versionClient.Version(context.Background(), &empty.Empty{})
return err
}
func addKubectlFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
overrides := clientcmd.ConfigOverrides{}
kflags := clientcmd.RecommendedConfigOverrideFlags("")
cmd.Flags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
clientcmd.BindOverrideFlags(&overrides, cmd.Flags(), kflags)
return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
}
// InitCommand allows executing command in a headless mode: on the fly starts Argo CD API server and
// changes provided client options to use started API server port
func InitCommand(cmd *cobra.Command, clientOpts *argoapi.ClientOptions, port *int) *cobra.Command {
ctx, cancel := context.WithCancel(context.Background())
clientConfig := addKubectlFlagsToCmd(cmd)
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
startInProcessAPI := clientOpts.Core
if !startInProcessAPI {
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
if err != nil {
return err
}
if localCfg != nil {
configCtx, err := localCfg.ResolveContext(clientOpts.Context)
if err != nil {
return err
}
startInProcessAPI = configCtx.Server.Core
}
}
if !startInProcessAPI {
return nil
}
// get rid of logging error handler
runtime.ErrorHandlers = runtime.ErrorHandlers[1:]
cli.SetLogLevel(log.ErrorLevel.String())
log.SetLevel(log.ErrorLevel)
os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true")
if port == nil || *port == 0 {
ln, err := net.Listen("tcp", "localhost:0")
if err != nil {
return err
}
port = &ln.Addr().(*net.TCPAddr).Port
io.Close(ln)
}
restConfig, err := clientConfig.ClientConfig()
if err != nil {
return err
}
appClientset, err := appclientset.NewForConfig(restConfig)
if err != nil {
return err
}
kubeClientset, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return err
}
namespace, _, err := clientConfig.Namespace()
if err != nil {
return err
}
mr, err := miniredis.Run()
if err != nil {
return err
}
appstateCache := appstatecache.NewCache(cacheutil.NewCache(&forwardCacheClient{namespace: namespace}), time.Hour)
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
EnableGZip: false,
Namespace: namespace,
ListenPort: *port,
AppClientset: appClientset,
DisableAuth: true,
RedisClient: redis.NewClient(&redis.Options{Addr: mr.Addr()}),
Cache: servercache.NewCache(appstateCache, 0, 0, 0),
KubeClientset: kubeClientset,
Insecure: true,
ListenHost: "localhost",
RepoClientset: &forwardRepoClientset{namespace: namespace},
})
go srv.Run(ctx, *port, 0)
clientOpts.ServerAddr = fmt.Sprintf("localhost:%d", *port)
clientOpts.PlainText = true
if !cache.WaitForCacheSync(ctx.Done(), srv.Initialized) {
log.Fatal("Timed out waiting for project cache to sync")
}
tries := 5
for i := 0; i < tries; i++ {
err = testAPI(clientOpts)
if err == nil {
break
}
time.Sleep(time.Second)
}
return err
}
cmd.PostRun = func(cmd *cobra.Command, args []string) {
cancel()
}
return cmd
}

View File

@@ -19,17 +19,17 @@ import (
"github.com/spf13/cobra"
"golang.org/x/oauth2"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
sessionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/session"
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
grpc_util "github.com/argoproj/argo-cd/v2/util/grpc"
"github.com/argoproj/argo-cd/v2/util/io"
jwtutil "github.com/argoproj/argo-cd/v2/util/jwt"
"github.com/argoproj/argo-cd/v2/util/localconfig"
oidcutil "github.com/argoproj/argo-cd/v2/util/oidc"
"github.com/argoproj/argo-cd/v2/util/rand"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
sessionpkg "github.com/argoproj/argo-cd/pkg/apiclient/session"
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
grpc_util "github.com/argoproj/argo-cd/util/grpc"
"github.com/argoproj/argo-cd/util/io"
jwtutil "github.com/argoproj/argo-cd/util/jwt"
"github.com/argoproj/argo-cd/util/localconfig"
oidcutil "github.com/argoproj/argo-cd/util/oidc"
"github.com/argoproj/argo-cd/util/rand"
)
// NewLoginCommand returns a new instance of `argocd login` command
@@ -45,26 +45,16 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
Use: "login SERVER",
Short: "Log in to Argo CD",
Long: "Log in to Argo CD",
Example: `# Login to Argo CD using a username and password
argocd login cd.argoproj.io
# Login to Argo CD using SSO
argocd login cd.argoproj.io --sso
# Configure direct access using Kubernetes API server
argocd login cd.argoproj.io --core`,
Run: func(c *cobra.Command, args []string) {
var server string
if len(args) != 1 && !globalClientOpts.PortForward && !globalClientOpts.Core {
if len(args) != 1 && !globalClientOpts.PortForward {
c.HelpFunc()(c, args)
os.Exit(1)
}
if globalClientOpts.PortForward {
server = "port-forward"
} else if globalClientOpts.Core {
server = "kubernetes"
} else {
server = args[0]
tlsTestResult, err := grpc_util.TestTLS(server)
@@ -96,6 +86,9 @@ argocd login cd.argoproj.io --core`,
PortForwardNamespace: globalClientOpts.PortForwardNamespace,
Headers: globalClientOpts.Headers,
}
acdClient := argocdclient.NewClientOrDie(&clientOpts)
setConn, setIf := acdClient.NewSettingsClientOrDie()
defer io.Close(setConn)
if ctxName == "" {
ctxName = server
@@ -108,32 +101,28 @@ argocd login cd.argoproj.io --core`,
// Perform the login
var tokenString string
var refreshToken string
if !globalClientOpts.Core {
acdClient := argocdclient.NewClientOrDie(&clientOpts)
setConn, setIf := acdClient.NewSettingsClientOrDie()
defer io.Close(setConn)
if !sso {
tokenString = passwordLogin(acdClient, username, password)
} else {
ctx := context.Background()
httpClient, err := acdClient.HTTPClient()
errors.CheckError(err)
ctx = oidc.ClientContext(ctx, httpClient)
acdSet, err := setIf.Get(ctx, &settingspkg.SettingsQuery{})
errors.CheckError(err)
oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet)
errors.CheckError(err)
tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider)
}
parser := &jwt.Parser{
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
}
claims := jwt.MapClaims{}
_, _, err := parser.ParseUnverified(tokenString, &claims)
if !sso {
tokenString = passwordLogin(acdClient, username, password)
} else {
ctx := context.Background()
httpClient, err := acdClient.HTTPClient()
errors.CheckError(err)
fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims))
ctx = oidc.ClientContext(ctx, httpClient)
acdSet, err := setIf.Get(ctx, &settingspkg.SettingsQuery{})
errors.CheckError(err)
oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet)
errors.CheckError(err)
tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider)
}
parser := &jwt.Parser{
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
}
claims := jwt.MapClaims{}
_, _, err := parser.ParseUnverified(tokenString, &claims)
errors.CheckError(err)
fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims))
// login successful. Persist the config
localCfg, err := localconfig.ReadLocalConfig(globalClientOpts.ConfigPath)
errors.CheckError(err)
@@ -146,7 +135,6 @@ argocd login cd.argoproj.io --core`,
Insecure: globalClientOpts.Insecure,
GRPCWeb: globalClientOpts.GRPCWeb,
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
Core: globalClientOpts.Core,
})
localCfg.UpsertUser(localconfig.User{
Name: ctxName,

View File

@@ -7,9 +7,9 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/localconfig"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/localconfig"
)
// NewLogoutCommand returns a new instance of `argocd logout` command

View File

@@ -5,11 +5,11 @@ import (
"os"
"testing"
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/stretchr/testify/assert"
"github.com/argoproj/argo-cd/v2/util/localconfig"
"github.com/argoproj/argo-cd/util/localconfig"
)
func TestLogout(t *testing.T) {

View File

@@ -14,18 +14,19 @@ import (
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/gpg"
argoio "github.com/argoproj/argo-cd/v2/util/io"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/gpg"
argoio "github.com/argoproj/argo-cd/util/io"
)
type policyOpts struct {
@@ -128,7 +129,23 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
errors.CheckError(err)
if visited := cmdutil.SetProjSpecOptions(c.Flags(), &proj.Spec, &opts); visited == 0 {
visited := 0
c.Flags().Visit(func(f *pflag.Flag) {
visited++
switch f.Name {
case "description":
proj.Spec.Description = opts.Description
case "dest":
proj.Spec.Destinations = opts.GetDestinations()
case "src":
proj.Spec.SourceRepos = opts.Sources
case "signature-keys":
proj.Spec.SignatureKeys = opts.GetSignatureKeys()
case "orphaned-resources", "orphaned-resources-warn":
proj.Spec.OrphanedResources = cmdutil.GetOrphanedResourcesSettings(c, opts)
}
})
if visited == 0 {
log.Error("Please set at least one option to update")
c.HelpFunc()(c, args)
os.Exit(1)

View File

@@ -12,12 +12,12 @@ import (
jwtgo "github.com/dgrijalva/jwt-go/v4"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/jwt"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/jwt"
)
const (
@@ -265,7 +265,7 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
},
}
command.Flags().StringVarP(&expiresIn, "expires-in", "e", "",
"Duration before the token will expire, e.g. \"12h\", \"7d\". (Default: No expiration)",
"Duration before the token will expire, eg \"12h\", \"7d\". (Default: No expiration)",
)
command.Flags().StringVarP(&tokenID, "id", "i", "", "Token unique identifier. (Default: Random UUID)")
command.Flags().BoolVarP(&outputTokenOnly, "token-only", "t", false, "Output token only - for use in scripts.")

View File

@@ -10,11 +10,11 @@ import (
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/io"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/io"
)
// NewProjectWindowsCommand returns a new instance of the `argocd proj windows` command

View File

@@ -9,12 +9,12 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
"github.com/argoproj/argo-cd/v2/util/errors"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/localconfig"
"github.com/argoproj/argo-cd/v2/util/session"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
"github.com/argoproj/argo-cd/util/errors"
argoio "github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/localconfig"
"github.com/argoproj/argo-cd/util/session"
)
// NewReloginCommand returns a new instance of `argocd relogin` command
@@ -55,8 +55,8 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
claims, err := configCtx.User.Claims()
errors.CheckError(err)
if claims.Issuer == session.SessionManagerClaimsIssuer {
fmt.Printf("Relogging in as '%s'\n", localconfig.GetUsername(claims.Subject))
tokenString = passwordLogin(acdClient, localconfig.GetUsername(claims.Subject), password)
fmt.Printf("Relogging in as '%s'\n", claims.Subject)
tokenString = passwordLogin(acdClient, claims.Subject, password)
} else {
fmt.Println("Reinitiating SSO login")
setConn, setIf := acdClient.NewSettingsClientOrDie()

View File

@@ -10,14 +10,14 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/io"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/io"
)
// NewRepoCommand returns a new instance of an `argocd repo` command
@@ -58,10 +58,10 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
argocd repo add https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
# Add a public Helm repository named 'stable' via HTTPS
argocd repo add https://charts.helm.sh/stable --type helm --name stable
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable
# Add a private Helm repository named 'stable' via HTTPS
argocd repo add https://charts.helm.sh/stable --type helm --name stable --username test --password test
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
# Add a private Helm OCI-based repository named 'stable' via HTTPS
argocd repo add helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
@@ -144,7 +144,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
repoOpts.Repo.GithubAppId = repoOpts.GithubAppId
repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId
repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL
repoOpts.Repo.Proxy = repoOpts.Proxy
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'"))
@@ -181,7 +180,6 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
GithubAppID: repoOpts.Repo.GithubAppId,
GithubAppInstallationID: repoOpts.Repo.GithubAppInstallationId,
GithubAppEnterpriseBaseUrl: repoOpts.Repo.GitHubAppEnterpriseBaseURL,
Proxy: repoOpts.Proxy,
}
_, err := repoIf.ValidateAccess(context.Background(), &repoAccessReq)
errors.CheckError(err)

View File

@@ -10,14 +10,13 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/argoproj/argo-cd/v2/common"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
repocredspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repocreds"
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/io"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
repocredspkg "github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/io"
)
// NewRepoCredsCommand returns a new instance of an `argocd repocreds` command
@@ -60,9 +59,6 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
# Add credentials with GitHub App authentication to use for all repositories under https://ghe.example.com/repos
argocd repocreds add https://ghe.example.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3
# Add credentials with helm oci registry so that these oci registry urls do not need to be added as repos individually.
argocd repocreds add localhost:5000/myrepo --enable-oci --type helm
`
var command = &cobra.Command{
@@ -155,8 +151,6 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
command.Flags().StringVar(&githubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
command.Flags().StringVar(&repo.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
command.Flags().BoolVar(&repo.EnableOCI, "enable-oci", false, "Specifies whether helm-oci support should be enabled for this repo")
command.Flags().StringVar(&repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"")
return command
}

View File

@@ -4,14 +4,12 @@ import (
"github.com/spf13/cobra"
"k8s.io/client-go/tools/clientcmd"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin"
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/config"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/localconfig"
cmdutil "github.com/argoproj/argo-cd/cmd/util"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/util/cli"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/localconfig"
)
func init() {
@@ -40,20 +38,19 @@ func NewCommand() *cobra.Command {
}
command.AddCommand(NewCompletionCommand())
command.AddCommand(headless.InitCommand(NewVersionCmd(&clientOpts), &clientOpts, nil))
command.AddCommand(headless.InitCommand(NewClusterCommand(&clientOpts, pathOpts), &clientOpts, nil))
command.AddCommand(headless.InitCommand(NewApplicationCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(NewVersionCmd(&clientOpts))
command.AddCommand(NewClusterCommand(&clientOpts, pathOpts))
command.AddCommand(NewApplicationCommand(&clientOpts))
command.AddCommand(NewLoginCommand(&clientOpts))
command.AddCommand(NewReloginCommand(&clientOpts))
command.AddCommand(headless.InitCommand(NewRepoCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(headless.InitCommand(NewRepoCredsCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(NewRepoCommand(&clientOpts))
command.AddCommand(NewRepoCredsCommand(&clientOpts))
command.AddCommand(NewContextCommand(&clientOpts))
command.AddCommand(headless.InitCommand(NewProjectCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(headless.InitCommand(NewAccountCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(NewProjectCommand(&clientOpts))
command.AddCommand(NewAccountCommand(&clientOpts))
command.AddCommand(NewLogoutCommand(&clientOpts))
command.AddCommand(headless.InitCommand(NewCertCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(headless.InitCommand(NewGPGCommand(&clientOpts), &clientOpts, nil))
command.AddCommand(admin.NewAdminCommand())
command.AddCommand(NewCertCommand(&clientOpts))
command.AddCommand(NewGPGCommand(&clientOpts))
defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath()
errors.CheckError(err)
@@ -72,7 +69,5 @@ func NewCommand() *cobra.Command {
command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", []string{}, "Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)")
command.PersistentFlags().BoolVar(&clientOpts.PortForward, "port-forward", config.GetBoolFlag("port-forward"), "Connect to a random argocd-server port using port forwarding")
command.PersistentFlags().StringVar(&clientOpts.PortForwardNamespace, "port-forward-namespace", config.GetFlag("port-forward-namespace", ""), "Namespace name which should be used for port forwarding")
command.PersistentFlags().IntVar(&clientOpts.HttpRetryMax, "http-retry-max", 0, "Maximum number of retries to establish http connection to Argo CD server")
command.PersistentFlags().BoolVar(&clientOpts.Core, "core", false, "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server")
return command
}

View File

@@ -8,11 +8,11 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/argoproj/argo-cd/v2/common"
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
"github.com/argoproj/argo-cd/v2/pkg/apiclient/version"
"github.com/argoproj/argo-cd/v2/util/errors"
argoio "github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/common"
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient/version"
"github.com/argoproj/argo-cd/util/errors"
argoio "github.com/argoproj/argo-cd/util/io"
)
// NewVersionCmd returns a new `version` command to be used as a sub-command to root

View File

@@ -7,11 +7,12 @@ import (
"github.com/spf13/cobra"
appcontroller "github.com/argoproj/argo-cd/v2/cmd/argocd-application-controller/commands"
dex "github.com/argoproj/argo-cd/v2/cmd/argocd-dex/commands"
reposerver "github.com/argoproj/argo-cd/v2/cmd/argocd-repo-server/commands"
apiserver "github.com/argoproj/argo-cd/v2/cmd/argocd-server/commands"
cli "github.com/argoproj/argo-cd/v2/cmd/argocd/commands"
appcontroller "github.com/argoproj/argo-cd/cmd/argocd-application-controller/commands"
dex "github.com/argoproj/argo-cd/cmd/argocd-dex/commands"
reposerver "github.com/argoproj/argo-cd/cmd/argocd-repo-server/commands"
apiserver "github.com/argoproj/argo-cd/cmd/argocd-server/commands"
util "github.com/argoproj/argo-cd/cmd/argocd-util/commands"
cli "github.com/argoproj/argo-cd/cmd/argocd/commands"
)
const (
@@ -28,6 +29,8 @@ func main() {
switch binaryName {
case "argocd", "argocd-linux-amd64", "argocd-darwin-amd64", "argocd-windows-amd64.exe":
command = cli.NewCommand()
case "argocd-util", "argocd-util-linux-amd64", "argocd-util-darwin-amd64", "argocd-util-windows-amd64.exe":
command = util.NewCommand()
case "argocd-server":
command = apiserver.NewCommand()
case "argocd-application-controller":
@@ -37,7 +40,21 @@ func main() {
case "argocd-dex":
command = dex.NewCommand()
default:
command = cli.NewCommand()
if len(os.Args[1:]) > 0 {
// trying to guess between argocd and argocd-util by matching sub command
for _, cmd := range []*cobra.Command{cli.NewCommand(), util.NewCommand()} {
if _, _, err := cmd.Find(os.Args[1:]); err == nil {
command = cmd
break
}
}
}
if command == nil {
fmt.Printf("Unknown binary name '%s'.Use '%s' environment variable to specify required binary name "+
"(possible values 'argocd' or 'argocd-util').\n", binaryName, binaryNameEnv)
os.Exit(1)
}
}
if err := command.Execute(); err != nil {

View File

@@ -7,68 +7,61 @@ import (
"net/url"
"os"
"strings"
"time"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/config"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/text/label"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/pkg/apis/application"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/text/label"
)
type AppOptions struct {
repoURL string
appPath string
chart string
env string
revision string
revisionHistoryLimit int
destName string
destServer string
destNamespace string
Parameters []string
valuesFiles []string
values string
releaseName string
helmSets []string
helmSetStrings []string
helmSetFiles []string
helmVersion string
project string
syncPolicy string
syncOptions []string
autoPrune bool
selfHeal bool
allowEmpty bool
namePrefix string
nameSuffix string
directoryRecurse bool
configManagementPlugin string
jsonnetTlaStr []string
jsonnetTlaCode []string
jsonnetExtVarStr []string
jsonnetExtVarCode []string
jsonnetLibs []string
kustomizeImages []string
kustomizeVersion string
kustomizeCommonLabels []string
kustomizeCommonAnnotations []string
kustomizeForceCommonLabels bool
kustomizeForceCommonAnnotations bool
pluginEnvs []string
Validate bool
directoryExclude string
directoryInclude string
retryLimit int64
retryBackoffDuration time.Duration
retryBackoffMaxDuration time.Duration
retryBackoffFactor int64
repoURL string
appPath string
chart string
env string
revision string
revisionHistoryLimit int
destName string
destServer string
destNamespace string
Parameters []string
valuesFiles []string
values string
releaseName string
helmSets []string
helmSetStrings []string
helmSetFiles []string
helmVersion string
project string
syncPolicy string
syncOptions []string
autoPrune bool
selfHeal bool
allowEmpty bool
namePrefix string
nameSuffix string
directoryRecurse bool
configManagementPlugin string
jsonnetTlaStr []string
jsonnetTlaCode []string
jsonnetExtVarStr []string
jsonnetExtVarCode []string
jsonnetLibs []string
kustomizeImages []string
kustomizeVersion string
kustomizeCommonLabels []string
kustomizeCommonAnnotations []string
pluginEnvs []string
Validate bool
directoryExclude string
directoryInclude string
}
func AddAppFlags(command *cobra.Command, opts *AppOptions) {
@@ -77,7 +70,7 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
command.Flags().StringVar(&opts.chart, "helm-chart", "", "Helm Chart name")
command.Flags().StringVar(&opts.env, "env", "", "Application environment to monitor")
command.Flags().StringVar(&opts.revision, "revision", "", "The tracking source branch, tag, commit or Helm chart version the application will sync to")
command.Flags().IntVar(&opts.revisionHistoryLimit, "revision-history-limit", argoappv1.RevisionHistoryLimit, "How many items to keep in revision history")
command.Flags().IntVar(&opts.revisionHistoryLimit, "revision-history-limit", common.RevisionHistoryLimit, "How many items to keep in revision history")
command.Flags().StringVar(&opts.destServer, "dest-server", "", "K8s cluster URL (e.g. https://kubernetes.default.svc)")
command.Flags().StringVar(&opts.destName, "dest-name", "", "K8s cluster Name (e.g. minikube)")
command.Flags().StringVar(&opts.destNamespace, "dest-namespace", "", "K8s target namespace (overrides the namespace specified in the ksonnet app.yaml)")
@@ -110,14 +103,8 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
command.Flags().BoolVar(&opts.Validate, "validate", true, "Validation of repo and cluster")
command.Flags().StringArrayVar(&opts.kustomizeCommonLabels, "kustomize-common-label", []string{}, "Set common labels in Kustomize")
command.Flags().StringArrayVar(&opts.kustomizeCommonAnnotations, "kustomize-common-annotation", []string{}, "Set common labels in Kustomize")
command.Flags().BoolVar(&opts.kustomizeForceCommonLabels, "kustomize-force-common-label", false, "Force common labels in Kustomize")
command.Flags().BoolVar(&opts.kustomizeForceCommonAnnotations, "kustomize-force-common-annotation", false, "Force common annotations in Kustomize")
command.Flags().StringVar(&opts.directoryExclude, "directory-exclude", "", "Set glob expression used to exclude files from application source path")
command.Flags().StringVar(&opts.directoryInclude, "directory-include", "", "Set glob expression used to include files from application source path")
command.Flags().Int64Var(&opts.retryLimit, "sync-retry-limit", 0, "Max number of allowed sync retries")
command.Flags().DurationVar(&opts.retryBackoffDuration, "sync-retry-backoff-duration", argoappv1.DefaultSyncRetryDuration, "Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
command.Flags().DurationVar(&opts.retryBackoffMaxDuration, "sync-retry-backoff-max-duration", argoappv1.DefaultSyncRetryMaxDuration, "Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
command.Flags().Int64Var(&opts.retryBackoffFactor, "sync-retry-backoff-factor", argoappv1.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed sync retry")
}
func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions) int {
@@ -206,10 +193,6 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations)
errors.CheckError(err)
setKustomizeOpt(&spec.Source, kustomizeOpts{commonAnnotations: parsedAnnotations})
case "kustomize-force-common-label":
setKustomizeOpt(&spec.Source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels})
case "kustomize-force-common-annotation":
setKustomizeOpt(&spec.Source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations})
case "jsonnet-tla-str":
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaStr, false)
case "jsonnet-tla-code":
@@ -255,28 +238,6 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
if spec.SyncPolicy.IsZero() {
spec.SyncPolicy = nil
}
case "sync-retry-limit":
if appOpts.retryLimit > 0 {
if spec.SyncPolicy == nil {
spec.SyncPolicy = &argoappv1.SyncPolicy{}
}
spec.SyncPolicy.Retry = &argoappv1.RetryStrategy{
Limit: appOpts.retryLimit,
Backoff: &argoappv1.Backoff{
Duration: appOpts.retryBackoffDuration.String(),
MaxDuration: appOpts.retryBackoffMaxDuration.String(),
Factor: pointer.Int64Ptr(appOpts.retryBackoffFactor),
},
}
} else if appOpts.retryLimit == 0 {
if spec.SyncPolicy.IsZero() {
spec.SyncPolicy = nil
} else {
spec.SyncPolicy.Retry = nil
}
} else {
log.Fatalf("Invalid sync-retry-limit [%d]", appOpts.retryLimit)
}
}
})
if flags.Changed("auto-prune") {
@@ -314,14 +275,12 @@ func setKsonnetOpt(src *argoappv1.ApplicationSource, env *string) {
}
type kustomizeOpts struct {
namePrefix string
nameSuffix string
images []string
version string
commonLabels map[string]string
commonAnnotations map[string]string
forceCommonLabels bool
forceCommonAnnotations bool
namePrefix string
nameSuffix string
images []string
version string
commonLabels map[string]string
commonAnnotations map[string]string
}
func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
@@ -343,12 +302,6 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
if opts.commonAnnotations != nil {
src.Kustomize.CommonAnnotations = opts.commonAnnotations
}
if opts.forceCommonLabels {
src.Kustomize.ForceCommonLabels = opts.forceCommonLabels
}
if opts.forceCommonAnnotations {
src.Kustomize.ForceCommonAnnotations = opts.forceCommonAnnotations
}
for _, image := range opts.images {
src.Kustomize.MergeImage(argoappv1.KustomizeImage(image))
}
@@ -537,7 +490,7 @@ func readAppFromURI(fileURL string, app *argoappv1.Application) error {
return err
}
func ConstructApp(fileURL, appName string, labels, annotations, args []string, appOpts AppOptions, flags *pflag.FlagSet) (*argoappv1.Application, error) {
func ConstructApp(fileURL, appName string, labels, args []string, appOpts AppOptions, flags *pflag.FlagSet) (*argoappv1.Application, error) {
var app argoappv1.Application
if fileURL == "-" {
// read stdin
@@ -563,7 +516,6 @@ func ConstructApp(fileURL, appName string, labels, annotations, args []string, a
SetAppSpecOptions(flags, &app.Spec, &appOpts)
SetParameterOverrides(&app, appOpts.Parameters)
mergeLabels(&app, labels)
setAnnotations(&app, annotations)
} else {
// read arguments
if len(args) == 1 {
@@ -584,7 +536,6 @@ func ConstructApp(fileURL, appName string, labels, annotations, args []string, a
SetAppSpecOptions(flags, &app.Spec, &appOpts)
SetParameterOverrides(&app, appOpts.Parameters)
mergeLabels(&app, labels)
setAnnotations(&app, annotations)
}
return &app, nil
}
@@ -605,17 +556,3 @@ func mergeLabels(app *argoappv1.Application, labels []string) {
app.SetLabels(mergedLabels)
}
func setAnnotations(app *argoappv1.Application, annotations []string) {
if len(annotations) > 0 && app.Annotations == nil {
app.Annotations = map[string]string{}
}
for _, a := range annotations {
annotation := strings.SplitN(a, "=", 2)
if len(annotation) == 2 {
app.Annotations[annotation[0]] = annotation[1]
} else {
app.Annotations[annotation[0]] = ""
}
}
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func Test_setHelmOpt(t *testing.T) {
@@ -164,29 +164,4 @@ func Test_setAppSpecOptions(t *testing.T) {
assert.NoError(t, f.SetFlag("sync-option", "!a=1"))
assert.Nil(t, f.spec.SyncPolicy)
})
t.Run("RetryLimit", func(t *testing.T) {
assert.NoError(t, f.SetFlag("sync-retry-limit", "5"))
assert.True(t, f.spec.SyncPolicy.Retry.Limit == 5)
assert.NoError(t, f.SetFlag("sync-retry-limit", "0"))
assert.Nil(t, f.spec.SyncPolicy.Retry)
})
}
func Test_setAnnotations(t *testing.T) {
t.Run("Annotations", func(t *testing.T) {
app := v1alpha1.Application{}
setAnnotations(&app, []string{"hoge=foo", "huga=bar"})
assert.Equal(t, map[string]string{"hoge": "foo", "huga": "bar"}, app.Annotations)
})
t.Run("Annotations value contains equal", func(t *testing.T) {
app := v1alpha1.Application{}
setAnnotations(&app, []string{"hoge=foo=bar"})
assert.Equal(t, map[string]string{"hoge": "foo=bar"}, app.Annotations)
})
t.Run("Annotations empty value", func(t *testing.T) {
app := v1alpha1.Application{}
setAnnotations(&app, []string{"hoge"})
assert.Equal(t, map[string]string{"hoge": ""}, app.Annotations)
})
}

View File

@@ -12,8 +12,8 @@ import (
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/errors"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/errors"
)
func PrintKubeContexts(ca clientcmd.ConfigAccess) {
@@ -55,7 +55,7 @@ func PrintKubeContexts(ca clientcmd.ConfigAccess) {
}
}
func NewCluster(name string, namespaces []string, clusterResources bool, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig) *argoappv1.Cluster {
func NewCluster(name string, namespaces []string, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig) *argoappv1.Cluster {
tlsClientConfig := argoappv1.TLSClientConfig{
Insecure: conf.TLSClientConfig.Insecure,
ServerName: conf.TLSClientConfig.ServerName,
@@ -80,10 +80,9 @@ func NewCluster(name string, namespaces []string, clusterResources bool, conf *r
}
clst := argoappv1.Cluster{
Server: conf.Host,
Name: name,
Namespaces: namespaces,
ClusterResources: clusterResources,
Server: conf.Host,
Name: name,
Namespaces: namespaces,
Config: argoappv1.ClusterConfig{
TLSClientConfig: tlsClientConfig,
AWSAuthConfig: awsAuthConf,
@@ -109,7 +108,6 @@ type ClusterOptions struct {
AwsClusterName string
SystemNamespace string
Namespaces []string
ClusterResources bool
Name string
Shard int64
ExecProviderCommand string
@@ -124,7 +122,6 @@ func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) {
command.Flags().StringVar(&opts.AwsClusterName, "aws-cluster-name", "", "AWS Cluster name if set then aws cli eks token command will be used to access cluster")
command.Flags().StringVar(&opts.AwsRoleArn, "aws-role-arn", "", "Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain.")
command.Flags().StringArrayVar(&opts.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage")
command.Flags().BoolVar(&opts.ClusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty.")
command.Flags().StringVar(&opts.Name, "name", "", "Overwrite the cluster name")
command.Flags().Int64Var(&opts.Shard, "shard", -1, "Cluster shard number; inferred from hostname if not set")
command.Flags().StringVar(&opts.ExecProviderCommand, "exec-command", "", "Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime.")

View File

@@ -7,11 +7,11 @@ import (
"github.com/stretchr/testify/assert"
"k8s.io/client-go/rest"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func Test_newCluster(t *testing.T) {
clusterWithData := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{
clusterWithData := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
TLSClientConfig: rest.TLSClientConfig{
Insecure: false,
ServerName: "test-endpoint.example.com",
@@ -29,7 +29,7 @@ func Test_newCluster(t *testing.T) {
assert.Equal(t, "test-key-data", string(clusterWithData.Config.KeyData))
assert.Equal(t, "", clusterWithData.Config.BearerToken)
clusterWithFiles := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{
clusterWithFiles := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
TLSClientConfig: rest.TLSClientConfig{
Insecure: false,
ServerName: "test-endpoint.example.com",
@@ -47,7 +47,7 @@ func Test_newCluster(t *testing.T) {
assert.True(t, strings.Contains(string(clusterWithFiles.Config.KeyData), "test-key-data"))
assert.Equal(t, "", clusterWithFiles.Config.BearerToken)
clusterWithBearerToken := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{
clusterWithBearerToken := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
TLSClientConfig: rest.TLSClientConfig{
Insecure: false,
ServerName: "test-endpoint.example.com",

View File

@@ -1,6 +1,80 @@
package util
import (
"encoding/json"
"fmt"
"github.com/ghodss/yaml"
v1 "k8s.io/api/core/v1"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
)
var (
LogFormat string
LogLevel string
)
// PrintResource prints a single resource in YAML or JSON format to stdout according to the output format
func PrintResources(resources []interface{}, output string) error {
for i, resource := range resources {
filteredResource, err := omitFields(resource)
if err != nil {
return err
}
resources[i] = filteredResource
}
switch output {
case "json":
jsonBytes, err := json.MarshalIndent(resources, "", " ")
if err != nil {
return err
}
fmt.Println(string(jsonBytes))
case "yaml":
yamlBytes, err := yaml.Marshal(resources)
if err != nil {
return err
}
fmt.Println(string(yamlBytes))
default:
return fmt.Errorf("unknown output format: %s", output)
}
return nil
}
// omit fields such as status, creationTimestamp and metadata.namespace in k8s objects
func omitFields(resource interface{}) (interface{}, error) {
jsonBytes, err := json.Marshal(resource)
if err != nil {
return nil, err
}
toMap := make(map[string]interface{})
err = json.Unmarshal([]byte(string(jsonBytes)), &toMap)
if err != nil {
return nil, err
}
delete(toMap, "status")
if v, ok := toMap["metadata"]; ok {
if metadata, ok := v.(map[string]interface{}); ok {
delete(metadata, "creationTimestamp")
delete(metadata, "namespace")
}
}
return toMap, nil
}
// ConvertSecretData converts kubernetes secret's data to stringData
func ConvertSecretData(secret *v1.Secret) {
secret.Kind = kube.SecretKind
secret.APIVersion = "v1"
secret.StringData = map[string]string{}
for k, v := range secret.Data {
secret.StringData[k] = string(v)
}
secret.Data = map[string][]byte{}
}

View File

@@ -9,28 +9,22 @@ import (
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/config"
"github.com/argoproj/argo-cd/v2/util/gpg"
"github.com/argoproj/argo-cd/pkg/apis/application"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/config"
"github.com/argoproj/argo-cd/util/gpg"
)
type ProjectOpts struct {
Description string
destinations []string
Sources []string
SignatureKeys []string
orphanedResourcesEnabled bool
orphanedResourcesWarn bool
allowedClusterResources []string
deniedClusterResources []string
allowedNamespacedResources []string
deniedNamespacedResources []string
Description string
destinations []string
Sources []string
SignatureKeys []string
orphanedResourcesEnabled bool
orphanedResourcesWarn bool
}
func AddProjFlags(command *cobra.Command, opts *ProjectOpts) {
@@ -41,39 +35,6 @@ func AddProjFlags(command *cobra.Command, opts *ProjectOpts) {
command.Flags().StringSliceVar(&opts.SignatureKeys, "signature-keys", []string{}, "GnuPG public key IDs for commit signature verification")
command.Flags().BoolVar(&opts.orphanedResourcesEnabled, "orphaned-resources", false, "Enables orphaned resources monitoring")
command.Flags().BoolVar(&opts.orphanedResourcesWarn, "orphaned-resources-warn", false, "Specifies if applications should have a warning condition when orphaned resources detected")
command.Flags().StringArrayVar(&opts.allowedClusterResources, "allow-cluster-resource", []string{}, "List of allowed cluster level resources")
command.Flags().StringArrayVar(&opts.deniedClusterResources, "deny-cluster-resource", []string{}, "List of denied cluster level resources")
command.Flags().StringArrayVar(&opts.allowedNamespacedResources, "allow-namespaced-resource", []string{}, "List of allowed namespaced resources")
command.Flags().StringArrayVar(&opts.deniedNamespacedResources, "deny-namespaced-resource", []string{}, "List of denied namespaced resources")
}
func getGroupKindList(values []string) []v1.GroupKind {
var res []v1.GroupKind
for _, val := range values {
if parts := strings.Split(val, "/"); len(parts) == 2 {
res = append(res, v1.GroupKind{Group: parts[0], Kind: parts[1]})
} else if len(parts) == 1 {
res = append(res, v1.GroupKind{Kind: parts[0]})
}
}
return res
}
func (opts *ProjectOpts) GetAllowedClusterResources() []v1.GroupKind {
return getGroupKindList(opts.allowedClusterResources)
}
func (opts *ProjectOpts) GetDeniedClusterResources() []v1.GroupKind {
return getGroupKindList(opts.deniedClusterResources)
}
func (opts *ProjectOpts) GetAllowedNamespacedResources() []v1.GroupKind {
return getGroupKindList(opts.allowedNamespacedResources)
}
func (opts *ProjectOpts) GetDeniedNamespacedResources() []v1.GroupKind {
return getGroupKindList(opts.deniedNamespacedResources)
}
func (opts *ProjectOpts) GetDestinations() []v1alpha1.ApplicationDestination {
@@ -104,8 +65,8 @@ func (opts *ProjectOpts) GetSignatureKeys() []v1alpha1.SignatureKey {
return signatureKeys
}
func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1alpha1.OrphanedResourcesMonitorSettings {
warnChanged := flagSet.Changed("orphaned-resources-warn")
func GetOrphanedResourcesSettings(c *cobra.Command, opts ProjectOpts) *v1alpha1.OrphanedResourcesMonitorSettings {
warnChanged := c.Flag("orphaned-resources-warn").Changed
if opts.orphanedResourcesEnabled || warnChanged {
settings := v1alpha1.OrphanedResourcesMonitorSettings{}
if warnChanged {
@@ -135,43 +96,8 @@ func readProjFromURI(fileURL string, proj *v1alpha1.AppProject) error {
return err
}
func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, projOpts *ProjectOpts) int {
visited := 0
flags.Visit(func(f *pflag.Flag) {
visited++
switch f.Name {
case "description":
spec.Description = projOpts.Description
case "dest":
spec.Destinations = projOpts.GetDestinations()
case "src":
spec.SourceRepos = projOpts.Sources
case "signature-keys":
spec.SignatureKeys = projOpts.GetSignatureKeys()
case "allow-cluster-resource":
spec.ClusterResourceWhitelist = projOpts.GetAllowedClusterResources()
case "deny-cluster-resource":
spec.ClusterResourceBlacklist = projOpts.GetDeniedClusterResources()
case "allow-namespaced-resource":
spec.NamespaceResourceWhitelist = projOpts.GetAllowedNamespacedResources()
case "deny-namespaced-resource":
spec.NamespaceResourceBlacklist = projOpts.GetDeniedNamespacedResources()
}
})
if flags.Changed("orphaned-resources") || flags.Changed("orphaned-resources-warn") {
spec.OrphanedResources = GetOrphanedResourcesSettings(flags, *projOpts)
visited++
}
return visited
}
func ConstructAppProj(fileURL string, args []string, opts ProjectOpts, c *cobra.Command) (*v1alpha1.AppProject, error) {
var proj = v1alpha1.AppProject{
TypeMeta: v1.TypeMeta{
Kind: application.AppProjectKind,
APIVersion: application.Group + "/v1alpha1",
},
}
var proj v1alpha1.AppProject
if fileURL == "-" {
// read stdin
err := readProjFromStdin(&proj)
@@ -194,9 +120,22 @@ func ConstructAppProj(fileURL string, args []string, opts ProjectOpts, c *cobra.
c.HelpFunc()(c, args)
os.Exit(1)
}
proj.Name = args[0]
projName := args[0]
proj = v1alpha1.AppProject{
TypeMeta: v1.TypeMeta{
Kind: application.AppProjectKind,
APIVersion: application.Group + "/v1alpha1",
},
ObjectMeta: v1.ObjectMeta{Name: projName},
Spec: v1alpha1.AppProjectSpec{
Description: opts.Description,
Destinations: opts.GetDestinations(),
SourceRepos: opts.Sources,
SignatureKeys: opts.GetSignatureKeys(),
OrphanedResources: GetOrphanedResourcesSettings(c, opts),
},
}
}
SetProjSpecOptions(c.Flags(), &proj.Spec, &opts)
return &proj, nil
}

View File

@@ -1,24 +0,0 @@
package util
import (
"testing"
"github.com/stretchr/testify/assert"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestProjectOpts_ResourceLists(t *testing.T) {
opts := ProjectOpts{
allowedNamespacedResources: []string{"ConfigMap"},
deniedNamespacedResources: []string{"apps/DaemonSet"},
allowedClusterResources: []string{"apiextensions.k8s.io/CustomResourceDefinition"},
deniedClusterResources: []string{"rbac.authorization.k8s.io/ClusterRole"},
}
assert.ElementsMatch(t,
[]v1.GroupKind{{Kind: "ConfigMap"}}, opts.GetAllowedNamespacedResources(),
[]v1.GroupKind{{Group: "apps", Kind: "DaemonSet"}}, opts.GetDeniedNamespacedResources(),
[]v1.GroupKind{{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"}}, opts.GetAllowedClusterResources(),
[]v1.GroupKind{{Group: "rbac.authorization.k8s.io", Kind: "ClusterRole"}}, opts.GetDeniedClusterResources(),
)
}

View File

@@ -3,8 +3,8 @@ package util
import (
"github.com/spf13/cobra"
"github.com/argoproj/argo-cd/v2/common"
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/common"
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
type RepoOptions struct {
@@ -21,7 +21,6 @@ type RepoOptions struct {
GithubAppInstallationId int64
GithubAppPrivateKeyPath string
GitHubAppEnterpriseBaseURL string
Proxy string
}
func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
@@ -40,5 +39,4 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
command.Flags().Int64Var(&opts.GithubAppInstallationId, "github-app-installation-id", 0, "installation id of the GitHub Application")
command.Flags().StringVar(&opts.GithubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
command.Flags().StringVar(&opts.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
command.Flags().StringVar(&opts.Proxy, "proxy", "", "use proxy to access repository")
}

View File

@@ -2,6 +2,7 @@ package common
import (
"os"
"strconv"
"time"
)
@@ -52,27 +53,32 @@ const (
DefaultSSHKnownHostsName = "ssh_known_hosts"
// Default path to GnuPG home directory
DefaultGnuPgHomePath = "/app/config/gpg/keys"
// Default path to repo server TLS endpoint config
DefaultAppConfigPath = "/app/config"
)
const (
DefaultSyncRetryDuration = 5 * time.Second
DefaultSyncRetryMaxDuration = 3 * time.Minute
DefaultSyncRetryFactor = int64(2)
)
// Argo CD application related constants
const (
// KubernetesInternalAPIServerAddr is address of the k8s API server when accessing internal to the cluster
KubernetesInternalAPIServerAddr = "https://kubernetes.default.svc"
// DefaultAppProjectName contains name of 'default' app project, which is available in every Argo CD installation
DefaultAppProjectName = "default"
// ArgoCDAdminUsername is the username of the 'admin' user
ArgoCDAdminUsername = "admin"
// ArgoCDUserAgentName is the default user-agent name used by the gRPC API client library and grpc-gateway
ArgoCDUserAgentName = "argocd-client"
// AuthCookieName is the HTTP cookie name where we store our auth token
AuthCookieName = "argocd.token"
// RevisionHistoryLimit is the max number of successful sync to keep in history
RevisionHistoryLimit = 10
// ChangePasswordSSOTokenMaxAge is the max token age for password change operation
ChangePasswordSSOTokenMaxAge = time.Minute * 5
// GithubAppCredsExpirationDuration is the default time used to cache the GitHub app credentials
GithubAppCredsExpirationDuration = time.Minute * 60
// PasswordPatten is the default password patten
PasswordPatten = `^.{8,32}$`
)
// Dex related constants
@@ -102,24 +108,31 @@ const (
// LabelKeyAppInstance is the label key to use to uniquely identify the instance of an application
// The Argo CD application name is used as the instance name
LabelKeyAppInstance = "app.kubernetes.io/instance"
// LabelKeyLegacyApplicationName is the legacy label (v0.10 and below) and is superseded by 'app.kubernetes.io/instance'
// LegacyLabelApplicationName is the legacy label (v0.10 and below) and is superceded by 'app.kubernetes.io/instance'
LabelKeyLegacyApplicationName = "applications.argoproj.io/app-name"
// LabelKeySecretType contains the type of argocd secret (currently: 'cluster', 'repository', 'repo-config' or 'repo-creds')
// LabelKeySecretType contains the type of argocd secret (currently: 'cluster')
LabelKeySecretType = "argocd.argoproj.io/secret-type"
// LabelValueSecretTypeCluster indicates a secret type of cluster
LabelValueSecretTypeCluster = "cluster"
// LabelValueSecretTypeRepository indicates a secret type of repository
LabelValueSecretTypeRepository = "repository"
// LabelValueSecretTypeRepoCreds indicates a secret type of repository credentials
LabelValueSecretTypeRepoCreds = "repo-creds"
// AnnotationCompareOptions is a comma-separated list of options for comparison
AnnotationCompareOptions = "argocd.argoproj.io/compare-options"
// AnnotationKeyRefresh is the annotation key which indicates that app needs to be refreshed. Removed by application controller after app is refreshed.
// Might take values 'normal'/'hard'. Value 'hard' means manifest cache and target cluster state cache should be invalidated before refresh.
AnnotationKeyRefresh = "argocd.argoproj.io/refresh"
// AnnotationKeyManagedBy is annotation name which indicates that k8s resource is managed by an application.
AnnotationKeyManagedBy = "managed-by"
// AnnotationValueManagedByArgoCD is a 'managed-by' annotation value for resources managed by Argo CD
AnnotationValueManagedByArgoCD = "argocd.argoproj.io"
// ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application
ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io"
// AnnotationKeyManifestGeneratePaths is an annotation that contains a list of semicolon-separated paths in the
// manifests repository that affects the manifest generation. Paths might be either relative or absolute. The
// absolute path means an absolute path within the repository and the relative path is relative to the application
// source path within the repository.
AnnotationKeyManifestGeneratePaths = "argocd.argoproj.io/manifest-generate-paths"
// AnnotationKeyLinkPrefix tells the UI to add an external link icon to the application node
// that links to the value given in the annotation.
@@ -138,6 +151,9 @@ const (
EnvVarSSODebug = "ARGOCD_SSO_DEBUG"
// EnvVarRBACDebug is an environment variable to enable additional RBAC debugging in the API server
EnvVarRBACDebug = "ARGOCD_RBAC_DEBUG"
// EnvVarFakeInClusterConfig is an environment variable to fake an in-cluster RESTConfig using
// the current kubectl context (for development purposes)
EnvVarFakeInClusterConfig = "ARGOCD_FAKE_IN_CLUSTER"
// Overrides the location where SSH known hosts for repo access data is stored
EnvVarSSHDataPath = "ARGOCD_SSH_DATA_PATH"
// Overrides the location where TLS certificate for repo access data is stored
@@ -146,6 +162,14 @@ const (
EnvGitAttemptsCount = "ARGOCD_GIT_ATTEMPTS_COUNT"
// Overrides git submodule support, true by default
EnvGitSubmoduleEnabled = "ARGOCD_GIT_MODULES_ENABLED"
// EnvK8sClientQPS is the QPS value used for the kubernetes client (default: 50)
EnvK8sClientQPS = "ARGOCD_K8S_CLIENT_QPS"
// EnvK8sClientBurst is the burst value used for the kubernetes client (default: twice the client QPS)
EnvK8sClientBurst = "ARGOCD_K8S_CLIENT_BURST"
// EnvClusterCacheResyncDuration is the env variable that holds cluster cache re-sync duration
EnvClusterCacheResyncDuration = "ARGOCD_CLUSTER_CACHE_RESYNC_DURATION"
// EnvK8sClientMaxIdleConnections is the number of max idle connections in K8s REST client HTTP transport (default: 500)
EnvK8sClientMaxIdleConnections = "ARGOCD_K8S_CLIENT_MAX_IDLE_CONNECTIONS"
// EnvGnuPGHome is the path to ArgoCD's GnuPG keyring for signature verification
EnvGnuPGHome = "ARGOCD_GNUPGHOME"
// EnvWatchAPIBufferSize is the buffer size used to transfer K8S watch events to watch API consumer
@@ -164,14 +188,6 @@ const (
EnvEnableGRPCTimeHistogramEnv = "ARGOCD_ENABLE_GRPC_TIME_HISTOGRAM"
// EnvGithubAppCredsExpirationDuration controls the caching of Github app credentials. This value is in minutes (default: 60)
EnvGithubAppCredsExpirationDuration = "ARGOCD_GITHUB_APP_CREDS_EXPIRATION_DURATION"
// EnvHelmIndexCacheDuration controls how the helm repository index file is cached for (default: 0)
EnvHelmIndexCacheDuration = "ARGOCD_HELM_INDEX_CACHE_DURATION"
// EnvRepoServerConfigPath allows to override the configuration path for repo server
EnvAppConfigPath = "ARGOCD_APP_CONF_PATH"
// EnvLogFormat log format that is defined by `--logformat` option
EnvLogFormat = "ARGOCD_LOG_FORMAT"
// EnvLogLevel log level that is defined by `--loglevel` option
EnvLogLevel = "ARGOCD_LOG_LEVEL"
)
const (
@@ -192,3 +208,40 @@ func GetGnuPGHomePath() string {
return gnuPgHome
}
}
var (
// K8sClientConfigQPS controls the QPS to be used in K8s REST client configs
K8sClientConfigQPS float32 = 50
// K8sClientConfigBurst controls the burst to be used in K8s REST client configs
K8sClientConfigBurst int = 100
// K8sMaxIdleConnections controls the number of max idle connections in K8s REST client HTTP transport
K8sMaxIdleConnections = 500
// K8sMaxIdleConnections controls the duration of cluster cache refresh
K8SClusterResyncDuration = 12 * time.Hour
)
func init() {
if envQPS := os.Getenv(EnvK8sClientQPS); envQPS != "" {
if qps, err := strconv.ParseFloat(envQPS, 32); err != nil {
K8sClientConfigQPS = float32(qps)
}
}
if envBurst := os.Getenv(EnvK8sClientBurst); envBurst != "" {
if burst, err := strconv.Atoi(envBurst); err != nil {
K8sClientConfigBurst = burst
}
} else {
K8sClientConfigBurst = 2 * int(K8sClientConfigQPS)
}
if envMaxConn := os.Getenv(EnvK8sClientMaxIdleConnections); envMaxConn != "" {
if maxConn, err := strconv.Atoi(envMaxConn); err != nil {
K8sMaxIdleConnections = maxConn
}
}
if clusterResyncDurationStr := os.Getenv(EnvClusterCacheResyncDuration); clusterResyncDurationStr != "" {
if duration, err := time.ParseDuration(clusterResyncDurationStr); err == nil {
K8SClusterResyncDuration = duration
}
}
}

View File

@@ -1,2 +0,0 @@
owners:
- alexmt

View File

@@ -39,21 +39,22 @@ import (
// make sure to register workqueue prometheus metrics
_ "k8s.io/component-base/metrics/prometheus/workqueue"
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/util/argo"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/glob"
logutils "github.com/argoproj/argo-cd/v2/util/log"
settings_util "github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
statecache "github.com/argoproj/argo-cd/controller/cache"
"github.com/argoproj/argo-cd/controller/metrics"
"github.com/argoproj/argo-cd/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/pkg/client/informers/externalversions/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/util/argo"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/errors"
"github.com/argoproj/argo-cd/util/glob"
logutils "github.com/argoproj/argo-cd/util/log"
settings_util "github.com/argoproj/argo-cd/util/settings"
)
const (
@@ -65,8 +66,6 @@ const (
type CompareWith int
const (
// Compare live application state against state defined in latest git revision with no resolved revision caching.
CompareWithLatestForceResolve CompareWith = 3
// Compare live application state against state defined in latest git revision.
CompareWithLatest CompareWith = 2
// Compare live application state against state defined using revision of most recent comparison.
@@ -414,13 +413,11 @@ func (ctrl *ApplicationController) getAppHosts(a *appv1.Application, appNodes []
appPods[kube.NewResourceKey(node.Group, node.Kind, node.Namespace, node.Name)] = true
}
}
allNodesInfo := map[string]statecache.NodeInfo{}
allPodsByNode := map[string][]statecache.PodInfo{}
appPodsByNode := map[string][]statecache.PodInfo{}
err := ctrl.stateCache.IterateResources(a.Spec.Destination.Server, func(res *clustercache.Resource, info *statecache.ResourceInfo) {
key := res.ResourceKey()
switch {
case info.NodeInfo != nil && key.Group == "" && key.Kind == "Node":
allNodesInfo[key.Name] = *info.NodeInfo
@@ -467,7 +464,7 @@ func (ctrl *ApplicationController) getAppHosts(a *appv1.Application, appNodes []
for _, pod := range neighbors {
for name, resource := range pod.ResourceRequests {
if !supportedResourceNames[name] || pod.Phase == v1.PodSucceeded || pod.Phase == v1.PodFailed {
if !supportedResourceNames[name] {
continue
}
info := resources[name]
@@ -518,7 +515,7 @@ func (ctrl *ApplicationController) managedResources(comparisonResult *comparison
}
resDiffPtr, err := diff.Diff(target, live,
diff.WithNormalizer(comparisonResult.diffNormalizer),
diff.WithLogr(logutils.NewLogrusLogger(logutils.NewWithCurrentConfig())),
diff.WithLogr(logutils.NewLogrusLogger(log.New())),
diff.IgnoreAggregatedRoles(compareOptions.IgnoreAggregatedRoles))
if err != nil {
return nil, err
@@ -665,18 +662,6 @@ func (ctrl *ApplicationController) processAppOperationQueueItem() (processNext b
}
app := origApp.DeepCopy()
if app.Operation != nil {
// If we get here, we are about process an operation but we cannot rely on informer since it might has stale data.
// So always retrieve the latest version to ensure it is not stale to avoid unnecessary syncing.
// We cannot rely on informer since applications might be updated by both application controller and api server.
freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
log.Errorf("Failed to retrieve latest application state: %v", err)
return
}
app = freshApp
}
if app.Operation != nil {
ctrl.processRequestedAppOperation(app)
} else if app.DeletionTimestamp != nil && app.CascadedDeletion() {
@@ -767,6 +752,7 @@ func (ctrl *ApplicationController) finalizeProjectDeletion(proj *appv1.AppProjec
for i := range apps {
if apps[i].Spec.GetProject() == proj.Name {
appsCount++
break
}
}
if appsCount == 0 {
@@ -824,115 +810,80 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
return nil, err
}
// validDestination is true if the Application destination points to a cluster that is managed by Argo CD
// (and thus either a cluster secret exists for it, or it's local); validDestination is false otherwise.
validDestination := true
err = argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db)
if err != nil {
return nil, err
}
// Validate the cluster using the Application destination's `name` field, if applicable,
// and set the Server field, if needed.
if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil {
log.Warnf("Unable to validate destination of the Application being deleted: %v", err)
validDestination = false
objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj)
if err != nil {
return nil, err
}
objs := make([]*unstructured.Unstructured, 0)
var cluster *appv1.Cluster
// Attempt to validate the destination via its URL
if validDestination {
if cluster, err = ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server); err != nil {
log.Warnf("Unable to locate cluster URL for Application being deleted: %v", err)
validDestination = false
}
}
if validDestination {
// ApplicationDestination points to a valid cluster, so we may clean up the live objects
objsMap, err := ctrl.getPermittedAppLiveObjects(app, proj)
if err != nil {
return nil, err
}
for k := range objsMap {
// Wait for objects pending deletion to complete before proceeding with next sync wave
if objsMap[k].GetDeletionTimestamp() != nil {
logCtx.Infof("%d objects remaining for deletion", len(objsMap))
return objs, nil
}
if ctrl.shouldBeDeleted(app, objsMap[k]) {
objs = append(objs, objsMap[k])
}
}
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig())
filteredObjs := FilterObjectsForDeletion(objs)
propagationPolicy := metav1.DeletePropagationForeground
if app.GetPropagationPolicy() == appv1.BackgroundPropagationPolicyFinalizer {
propagationPolicy = metav1.DeletePropagationBackground
}
logCtx.Infof("Deleting application's resources with %s propagation policy", propagationPolicy)
err = kube.RunAllAsync(len(filteredObjs), func(i int) error {
obj := filteredObjs[i]
return ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), metav1.DeleteOptions{PropagationPolicy: &propagationPolicy})
})
if err != nil {
return objs, err
}
objsMap, err = ctrl.getPermittedAppLiveObjects(app, proj)
if err != nil {
return nil, err
}
for k, obj := range objsMap {
if !ctrl.shouldBeDeleted(app, obj) {
delete(objsMap, k)
}
}
if len(objsMap) > 0 {
for k := range objsMap {
// Wait for objects pending deletion to complete before proceeding with next sync wave
if objsMap[k].GetDeletionTimestamp() != nil {
logCtx.Infof("%d objects remaining for deletion", len(objsMap))
return objs, nil
}
if ctrl.shouldBeDeleted(app, objsMap[k]) {
objs = append(objs, objsMap[k])
}
}
if err := ctrl.cache.SetAppManagedResources(app.Name, nil); err != nil {
cluster, err := ctrl.db.GetCluster(context.Background(), app.Spec.Destination.Server)
if err != nil {
return nil, err
}
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig())
filteredObjs := FilterObjectsForDeletion(objs)
err = kube.RunAllAsync(len(filteredObjs), func(i int) error {
obj := filteredObjs[i]
return ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), false)
})
if err != nil {
return objs, err
}
if err := ctrl.cache.SetAppResourcesTree(app.Name, nil); err != nil {
objsMap, err = ctrl.getPermittedAppLiveObjects(app, proj)
if err != nil {
return nil, err
}
for k, obj := range objsMap {
if !ctrl.shouldBeDeleted(app, obj) {
delete(objsMap, k)
}
}
if len(objsMap) > 0 {
logCtx.Infof("%d objects remaining for deletion", len(objsMap))
return objs, nil
}
err = ctrl.cache.SetAppManagedResources(app.Name, nil)
if err != nil {
return objs, err
}
if err := ctrl.removeCascadeFinalizer(app); err != nil {
err = ctrl.cache.SetAppResourcesTree(app.Name, nil)
if err != nil {
return objs, err
}
if validDestination {
logCtx.Infof("Successfully deleted %d resources", len(objs))
} else {
logCtx.Infof("Resource entries removed from undefined cluster")
}
ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", app.Namespace, app.Spec.GetProject()))
return objs, nil
}
func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application) error {
app.UnSetCascadedDeletion()
app.SetCascadedDeletion(false)
var patch []byte
patch, _ = json.Marshal(map[string]interface{}{
"metadata": map[string]interface{}{
"finalizers": app.Finalizers,
},
})
_, err = ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
return objs, err
}
_, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(app.Namespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{})
return err
logCtx.Infof("Successfully deleted %d resources", len(objs))
ctrl.projectRefreshQueue.Add(fmt.Sprintf("%s/%s", app.Namespace, app.Spec.GetProject()))
return objs, nil
}
func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condition appv1.ApplicationCondition) {
@@ -977,6 +928,20 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
}()
terminating := false
if isOperationInProgress(app) {
// If we get here, we are about process an operation but we notice it is already in progress.
// We need to detect if the app object we pulled off the informer is stale and doesn't
// reflect the fact that the operation is completed. We don't want to perform the operation
// again. To detect this, always retrieve the latest version to ensure it is not stale.
freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(context.Background(), app.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
logCtx.Errorf("Failed to retrieve latest application state: %v", err)
return
}
if !isOperationInProgress(freshApp) {
logCtx.Infof("Skipping operation on stale application state")
return
}
app = freshApp
state = app.Status.OperationState.DeepCopy()
terminating = state.Phase == synccommon.OperationTerminating
// Failed operation with retry strategy might have be in-progress and has completion time
@@ -1062,7 +1027,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
}
func (ctrl *ApplicationController) setOperationState(app *appv1.Application, state *appv1.OperationState) {
kube.RetryUntilSucceed(context.Background(), updateOperationStateTimeout, "Update application operation state", logutils.NewLogrusLogger(logutils.NewWithCurrentConfig()), func() error {
kube.RetryUntilSucceed(context.Background(), updateOperationStateTimeout, "Update application operation state", logutils.NewLogrusLogger(log.New()), func() error {
if state.Phase == "" {
// expose any bugs where we neglect to set phase
panic("no phase was set")
@@ -1184,7 +1149,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
if comparisonLevel == ComparisonWithNothing {
managedResources := make([]*appv1.ResourceDiff, 0)
if err := ctrl.cache.GetAppManagedResources(app.Name, &managedResources); err != nil {
logCtx.Warnf("Failed to get cached managed resources for tree reconciliation, fall back to full reconciliation")
logCtx.Warnf("Failed to get cached managed resources for tree reconciliation, fallback to full reconciliation")
} else {
var tree *appv1.ApplicationTree
if tree, err = ctrl.getResourceTree(app, managedResources); err == nil {
@@ -1219,9 +1184,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
}
now := metav1.Now()
compareResult := ctrl.appStateManager.CompareAppState(app, project, revision, app.Spec.Source,
refreshType == appv1.RefreshTypeHard,
comparisonLevel == CompareWithLatestForceResolve, localManifests)
compareResult := ctrl.appStateManager.CompareAppState(app, project, revision, app.Spec.Source, refreshType == appv1.RefreshTypeHard, localManifests)
for k, v := range compareResult.timings {
logCtx = logCtx.WithField(k, v.Milliseconds())
}
@@ -1252,7 +1215,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
logCtx.Info("Sync prevented by sync window")
}
if app.Status.ReconciledAt == nil || comparisonLevel >= CompareWithLatest {
if app.Status.ReconciledAt == nil || comparisonLevel == CompareWithLatest {
app.Status.ReconciledAt = &now
}
app.Status.Sync = *compareResult.syncStatus
@@ -1282,13 +1245,9 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
expired := app.Status.ReconciledAt == nil || app.Status.ReconciledAt.Add(statusRefreshTimeout).Before(time.Now().UTC())
if requestedType, ok := app.IsRefreshRequested(); ok {
compareWith = CompareWithLatestForceResolve
// user requested app refresh.
refreshType = requestedType
reason = fmt.Sprintf("%s refresh requested", refreshType)
} else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) {
reason = "spec.source differs"
compareWith = CompareWithLatestForceResolve
} else if expired {
// The commented line below mysteriously crashes if app.Status.ReconciledAt is nil
// reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
@@ -1298,6 +1257,8 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
reconciledAtStr = app.Status.ReconciledAt.String()
}
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", reconciledAtStr, statusRefreshTimeout)
} else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) {
reason = "spec.source differs"
} else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) {
reason = "spec.destination differs"
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
@@ -1380,7 +1341,7 @@ func (ctrl *ApplicationController) persistAppStatus(orig *appv1.Application, new
for k, v := range orig.GetAnnotations() {
newAnnotations[k] = v
}
delete(newAnnotations, appv1.AnnotationKeyRefresh)
delete(newAnnotations, common.AnnotationKeyRefresh)
}
patch, modified, err := diff.CreateTwoWayMergePatch(
&appv1.Application{ObjectMeta: metav1.ObjectMeta{Annotations: orig.GetAnnotations()}, Status: orig.Status},
@@ -1575,12 +1536,6 @@ func (ctrl *ApplicationController) newApplicationInformerAndLister() (cache.Shar
cache.NamespaceIndex: func(obj interface{}) ([]string, error) {
app, ok := obj.(*appv1.Application)
if ok {
// This call to 'ValidateDestination' ensures that the .spec.destination field of all Applications
// returned by the informer/lister will have server field set (if not already set) based on the name.
// (or, if not found, an error app condition)
// If the server field is not set, set it based on the cluster name; if the cluster name can't be found,
// log an error as an App Condition.
if err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, ctrl.db); err != nil {
ctrl.setAppCondition(app, appv1.ApplicationCondition{Type: appv1.ApplicationConditionInvalidSpecError, Message: err.Error()})
}

View File

@@ -10,8 +10,7 @@ import (
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/argo-cd/v2/common"
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
statecache "github.com/argoproj/argo-cd/controller/cache"
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
@@ -30,16 +29,16 @@ import (
kubetesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache"
mockstatecache "github.com/argoproj/argo-cd/v2/controller/cache/mocks"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
mockrepoclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
"github.com/argoproj/argo-cd/v2/test"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
mockstatecache "github.com/argoproj/argo-cd/controller/cache/mocks"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
"github.com/argoproj/argo-cd/reposerver/apiclient"
mockrepoclient "github.com/argoproj/argo-cd/reposerver/apiclient/mocks"
"github.com/argoproj/argo-cd/test"
cacheutil "github.com/argoproj/argo-cd/util/cache"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/settings"
)
type namespacedResource struct {
@@ -118,7 +117,6 @@ func newFakeController(data *fakeData) *ApplicationController {
defer cancelApp()
clusterCacheMock := mocks.ClusterCache{}
clusterCacheMock.On("IsNamespaced", mock.Anything).Return(true, nil)
clusterCacheMock.On("GetOpenAPISchema").Return(nil, nil)
mockStateCache := mockstatecache.LiveStateCache{}
ctrl.appStateManager.(*appStateManager).liveStateCache = &mockStateCache
@@ -634,43 +632,26 @@ func TestFinalizeAppDeletion(t *testing.T) {
assert.True(t, patched)
})
// Create an Application with a cluster that doesn't exist
// Ensure it can be deleted.
t.Run("DeleteWithInvalidClusterName", func(t *testing.T) {
t.Run("ErrorOnBothDestNameAndServer", func(t *testing.T) {
app := newFakeAppWithDestMismatch()
appObj := kube.MustToUnstructured(&app)
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
kube.GetResourceKey(appObj): appObj,
}})
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
func() {
fakeAppCs.Lock()
defer fakeAppCs.Unlock()
appTemplate := newFakeAppWithDestName()
testShouldDelete := func(app *argoappv1.Application) {
appObj := kube.MustToUnstructured(&app)
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app, &defaultProj}, managedLiveObjs: map[kube.ResourceKey]*unstructured.Unstructured{
kube.GetResourceKey(appObj): appObj,
}})
fakeAppCs := ctrl.applicationClientset.(*appclientset.Clientset)
defaultReactor := fakeAppCs.ReactionChain[0]
fakeAppCs.ReactionChain = nil
fakeAppCs.AddReactor("get", "*", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
return defaultReactor.React(action)
})
_, err := ctrl.finalizeApplicationDeletion(app)
assert.NoError(t, err)
}
app1 := appTemplate.DeepCopy()
app1.Spec.Destination.Server = "https://invalid"
testShouldDelete(app1)
app2 := appTemplate.DeepCopy()
app2.Spec.Destination.Name = "invalid"
testShouldDelete(app2)
app3 := appTemplate.DeepCopy()
app3.Spec.Destination.Name = "invalid"
app3.Spec.Destination.Server = "https://invalid"
testShouldDelete(app3)
}()
_, err := ctrl.finalizeApplicationDeletion(app)
assert.EqualError(t, err, "application destination can't have both name and server defined: another-cluster https://localhost:6443")
})
}
// TestNormalizeApplication verifies we normalize an application during reconciliation
@@ -750,7 +731,7 @@ func TestNormalizeApplication(t *testing.T) {
func TestHandleAppUpdated(t *testing.T) {
app := newFakeApp()
app.Spec.Destination.Namespace = test.FakeArgoCDNamespace
app.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr
app.Spec.Destination.Server = common.KubernetesInternalAPIServerAddr
ctrl := newFakeController(&fakeData{apps: []runtime.Object{app}})
ctrl.handleObjectUpdated(map[string]bool{app.Name: true}, kube.GetObjectRef(kube.MustToUnstructured(app)))
@@ -768,12 +749,12 @@ func TestHandleOrphanedResourceUpdated(t *testing.T) {
app1 := newFakeApp()
app1.Name = "app1"
app1.Spec.Destination.Namespace = test.FakeArgoCDNamespace
app1.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr
app1.Spec.Destination.Server = common.KubernetesInternalAPIServerAddr
app2 := newFakeApp()
app2.Name = "app2"
app2.Spec.Destination.Namespace = test.FakeArgoCDNamespace
app2.Spec.Destination.Server = argoappv1.KubernetesInternalAPIServerAddr
app2.Spec.Destination.Server = common.KubernetesInternalAPIServerAddr
proj := defaultProj.DeepCopy()
proj.Spec.OrphanedResources = &argoappv1.OrphanedResourcesMonitorSettings{}
@@ -874,7 +855,7 @@ func TestNeedRefreshAppStatus(t *testing.T) {
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
assert.Equal(t, CompareWithLatestForceResolve, compareWith)
assert.Equal(t, CompareWithLatest, compareWith)
{
// refresh app using the 'latest' level if comparison expired
@@ -885,7 +866,7 @@ func TestNeedRefreshAppStatus(t *testing.T) {
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Minute)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
assert.Equal(t, CompareWithLatestForceResolve, compareWith)
assert.Equal(t, CompareWithLatest, compareWith)
}
{
@@ -894,12 +875,12 @@ func TestNeedRefreshAppStatus(t *testing.T) {
reconciledAt := metav1.NewTime(time.Now().UTC().Add(-1 * time.Hour))
app.Status.ReconciledAt = &reconciledAt
app.Annotations = map[string]string{
v1alpha1.AnnotationKeyRefresh: string(argoappv1.RefreshTypeHard),
common.AnnotationKeyRefresh: string(argoappv1.RefreshTypeHard),
}
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeHard, refreshType)
assert.Equal(t, CompareWithLatestForceResolve, compareWith)
assert.Equal(t, CompareWithLatest, compareWith)
}
{
@@ -917,7 +898,7 @@ func TestNeedRefreshAppStatus(t *testing.T) {
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
assert.Equal(t, CompareWithLatestForceResolve, compareWith)
assert.Equal(t, CompareWithLatest, compareWith)
}
}

View File

@@ -3,10 +3,8 @@ package cache
import (
"context"
"fmt"
"os"
"reflect"
"sync"
"time"
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
"github.com/argoproj/gitops-engine/pkg/health"
@@ -20,34 +18,16 @@ import (
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
"github.com/argoproj/argo-cd/v2/controller/metrics"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo"
"github.com/argoproj/argo-cd/v2/util/db"
logutils "github.com/argoproj/argo-cd/v2/util/log"
"github.com/argoproj/argo-cd/v2/util/lua"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller/metrics"
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo"
"github.com/argoproj/argo-cd/util/db"
logutils "github.com/argoproj/argo-cd/util/log"
"github.com/argoproj/argo-cd/util/lua"
"github.com/argoproj/argo-cd/util/settings"
)
const (
// EnvClusterCacheResyncDuration is the env variable that holds cluster cache re-sync duration
EnvClusterCacheResyncDuration = "ARGOCD_CLUSTER_CACHE_RESYNC_DURATION"
)
var (
// K8SClusterResyncDuration controls the duration of cluster cache refresh
K8SClusterResyncDuration = 12 * time.Hour
)
func init() {
if clusterResyncDurationStr := os.Getenv(EnvClusterCacheResyncDuration); clusterResyncDurationStr != "" {
if duration, err := time.ParseDuration(clusterResyncDurationStr); err == nil {
K8SClusterResyncDuration = duration
}
}
}
type LiveStateCache interface {
// Returns k8s server version
GetVersionsInfo(serverURL string) (string, []metav1.APIGroup, error)
@@ -76,7 +56,6 @@ type ObjectUpdatedHandler = func(managedByApp map[string]bool, ref v1.ObjectRefe
type PodInfo struct {
NodeName string
ResourceRequests v1.ResourceList
Phase v1.PodPhase
}
type NodeInfo struct {
@@ -287,10 +266,9 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
clusterCache = clustercache.NewClusterCache(cluster.RESTConfig(),
clustercache.SetListSemaphore(c.listSemaphore),
clustercache.SetResyncTimeout(K8SClusterResyncDuration),
clustercache.SetResyncTimeout(common.K8SClusterResyncDuration),
clustercache.SetSettings(cacheSettings.clusterSettings),
clustercache.SetNamespaces(cluster.Namespaces),
clustercache.SetClusterResources(cluster.ClusterResources),
clustercache.SetPopulateResourceInfoHandler(func(un *unstructured.Unstructured, isRoot bool) (interface{}, bool) {
res := &ResourceInfo{}
populateNodeInfo(un, res)
@@ -490,7 +468,7 @@ func (c *liveStateCache) Init() error {
func (c *liveStateCache) Run(ctx context.Context) error {
go c.watchSettings(ctx)
kube.RetryUntilSucceed(ctx, clustercache.ClusterRetryTimeout, "watch clusters", logutils.NewLogrusLogger(logutils.NewWithCurrentConfig()), func() error {
kube.RetryUntilSucceed(ctx, clustercache.ClusterRetryTimeout, "watch clusters", logutils.NewLogrusLogger(log.New()), func() error {
return c.db.WatchClusters(ctx, c.handleAddEvent, c.handleModEvent, c.handleDeleteEvent)
})
@@ -545,9 +523,6 @@ func (c *liveStateCache) handleModEvent(oldCluster *appv1.Cluster, newCluster *a
if !reflect.DeepEqual(oldCluster.Namespaces, newCluster.Namespaces) {
updateSettings = append(updateSettings, clustercache.SetNamespaces(newCluster.Namespaces))
}
if !reflect.DeepEqual(oldCluster.ClusterResources, newCluster.ClusterResources) {
updateSettings = append(updateSettings, clustercache.SetClusterResources(newCluster.ClusterResources))
}
forceInvalidate := false
if newCluster.RefreshRequestedAt != nil &&
cluster.GetClusterInfo().LastCacheSyncTime != nil &&

View File

@@ -9,7 +9,7 @@ import (
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
"github.com/stretchr/testify/mock"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func TestHandleModEvent_HasChanges(t *testing.T) {

View File

@@ -11,9 +11,9 @@ import (
"k8s.io/apimachinery/pkg/runtime"
resourcehelper "k8s.io/kubectl/pkg/util/resource"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/resource"
"github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/util/resource"
)
func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {
@@ -146,17 +146,6 @@ func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) {
tlshost := tlsline["host"]
if tlshost == host {
stringPort = "https"
continue
}
if hosts := tlsline["hosts"]; hosts != nil {
tlshosts, ok := tlsline["hosts"].(map[string]interface{})
if ok {
for j := range tlshosts {
if tlshosts[j] == host {
stringPort = "https"
}
}
}
}
}
}
@@ -335,7 +324,7 @@ func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
}
req, _ := resourcehelper.PodRequestsAndLimits(&pod)
res.PodInfo = &PodInfo{NodeName: pod.Spec.NodeName, ResourceRequests: req, Phase: pod.Status.Phase}
res.PodInfo = &PodInfo{NodeName: pod.Spec.NodeName, ResourceRequests: req}
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Node", Value: pod.Spec.NodeName})
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Containers", Value: fmt.Sprintf("%d/%d", readyContainers, totalContainers)})

View File

@@ -15,7 +15,7 @@ import (
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func strToUnstructured(jsonStr string) *unstructured.Unstructured {

View File

@@ -7,7 +7,7 @@ import (
cache "github.com/argoproj/gitops-engine/pkg/cache"
controllercache "github.com/argoproj/argo-cd/v2/controller/cache"
controllercache "github.com/argoproj/argo-cd/controller/cache"
kube "github.com/argoproj/gitops-engine/pkg/utils/kube"
@@ -19,7 +19,7 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
v1alpha1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
// LiveStateCache is an autogenerated mock type for the LiveStateCache type

View File

@@ -10,12 +10,12 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"github.com/argoproj/argo-cd/v2/controller/metrics"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/controller/metrics"
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/db"
)
const (
@@ -106,7 +106,6 @@ func (c *clusterInfoUpdater) updateClusterInfo(cluster appv1.Cluster, info *cach
}
if info != nil {
clusterInfo.ServerVersion = info.K8SVersion
clusterInfo.APIVersions = argo.APIGroupsToVersions(info.APIGroups)
if info.LastCacheSyncTime == nil {
clusterInfo.ConnectionState.Status = appv1.ConnectionStatusUnknown
} else if info.SyncError == nil {

View File

@@ -6,14 +6,14 @@ import (
"testing"
"time"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appsfake "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
"github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appsfake "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
appinformers "github.com/argoproj/argo-cd/pkg/client/informers/externalversions/application/v1alpha1"
applisters "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
cacheutil "github.com/argoproj/argo-cd/util/cache"
"github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/settings"
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
"github.com/stretchr/testify/assert"

View File

@@ -1,64 +0,0 @@
package controller
import (
"github.com/argoproj/gitops-engine/pkg/health"
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/lua"
)
// setApplicationHealth updates the health statuses of all resources performed in the comparison
func setApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus, resourceOverrides map[string]appv1.ResourceOverride, app *appv1.Application) (*appv1.HealthStatus, error) {
var savedErr error
appHealth := appv1.HealthStatus{Status: health.HealthStatusHealthy}
for i, res := range resources {
if res.Target != nil && hookutil.Skip(res.Target) {
continue
}
if res.Live != nil && (hookutil.IsHook(res.Live) || ignore.Ignore(res.Live)) {
continue
}
var healthStatus *health.HealthStatus
var err error
healthOverrides := lua.ResourceHealthOverrides(resourceOverrides)
gvk := schema.GroupVersionKind{Group: res.Group, Version: res.Version, Kind: res.Kind}
if res.Live == nil {
healthStatus = &health.HealthStatus{Status: health.HealthStatusMissing}
} else {
// App the manages itself should not affect own health
if isSelfReferencedApp(app, kubeutil.GetObjectRef(res.Live)) {
continue
}
healthStatus, err = health.GetResourceHealth(res.Live, healthOverrides)
if err != nil && savedErr == nil {
savedErr = err
}
}
if healthStatus != nil {
resHealth := appv1.HealthStatus{Status: healthStatus.Status, Message: healthStatus.Message}
statuses[i].Health = &resHealth
// Is health status is missing but resource has not built-in/custom health check then it should not affect parent app health
if _, hasOverride := healthOverrides[lua.GetConfigMapKey(gvk)]; healthStatus.Status == health.HealthStatusMissing && !hasOverride && health.GetHealthCheckFunc(gvk) == nil {
continue
}
// Missing or Unknown health status of child Argo CD app should not affect parent
if res.Kind == application.ApplicationKind && res.Group == application.Group && (healthStatus.Status == health.HealthStatusMissing || healthStatus.Status == health.HealthStatusUnknown) {
continue
}
if health.IsWorse(appHealth.Status, healthStatus.Status) {
appHealth.Status = healthStatus.Status
}
}
}
return &appHealth, savedErr
}

View File

@@ -1,161 +0,0 @@
package controller
import (
"io/ioutil"
"testing"
"github.com/argoproj/gitops-engine/pkg/health"
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
"github.com/argoproj/gitops-engine/pkg/utils/kube"
"github.com/ghodss/yaml"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/lua"
)
var app = &appv1.Application{}
func initStatuses(resources []managedResource) []appv1.ResourceStatus {
statuses := make([]appv1.ResourceStatus, len(resources))
for i := range resources {
statuses[i] = appv1.ResourceStatus{Group: resources[i].Group, Kind: resources[i].Kind, Version: resources[i].Version}
}
return statuses
}
func resourceFromFile(filePath string) unstructured.Unstructured {
yamlBytes, err := ioutil.ReadFile(filePath)
if err != nil {
panic(err)
}
var res unstructured.Unstructured
err = yaml.Unmarshal(yamlBytes, &res)
if err != nil {
panic(err)
}
return res
}
func TestSetApplicationHealth(t *testing.T) {
failedJob := resourceFromFile("./testdata/job-failed.yaml")
runningPod := resourceFromFile("./testdata/pod-running-restart-always.yaml")
resources := []managedResource{{
Group: "", Version: "v1", Kind: "Pod", Live: &runningPod}, {
Group: "batch", Version: "v1", Kind: "Job", Live: &failedJob,
}}
resourceStatuses := initStatuses(resources)
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status)
// now mark the job as a hook and retry. it should ignore the hook and consider the app healthy
failedJob.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
healthStatus, err = setApplicationHealth(resources, resourceStatuses, nil, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
}
func TestSetApplicationHealth_MissingResource(t *testing.T) {
pod := resourceFromFile("./testdata/pod-running-restart-always.yaml")
resources := []managedResource{{
Group: "", Version: "v1", Kind: "Pod", Target: &pod}, {}}
resourceStatuses := initStatuses(resources)
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusMissing, healthStatus.Status)
}
func TestSetApplicationHealth_MissingResourceNoBuiltHealthCheck(t *testing.T) {
cm := resourceFromFile("./testdata/configmap.yaml")
resources := []managedResource{{
Group: "", Version: "v1", Kind: "ConfigMap", Target: &cm}}
resourceStatuses := initStatuses(resources)
t.Run("NoOverride", func(t *testing.T) {
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{}, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
assert.Equal(t, resourceStatuses[0].Health.Status, health.HealthStatusMissing)
})
t.Run("HasOverride", func(t *testing.T) {
healthStatus, err := setApplicationHealth(resources, resourceStatuses, lua.ResourceHealthOverrides{
lua.GetConfigMapKey(schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}): appv1.ResourceOverride{
HealthLua: "some health check",
},
}, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusMissing, healthStatus.Status)
})
}
func newAppLiveObj(status health.HealthStatusCode) *unstructured.Unstructured {
app := appv1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
TypeMeta: metav1.TypeMeta{
APIVersion: "argoproj.io/v1alpha1",
Kind: "Application",
},
Status: appv1.ApplicationStatus{
Health: appv1.HealthStatus{
Status: status,
},
},
}
return kube.MustToUnstructured(&app)
}
func TestChildAppHealth(t *testing.T) {
overrides := lua.ResourceHealthOverrides{
lua.GetConfigMapKey(appv1.ApplicationSchemaGroupVersionKind): appv1.ResourceOverride{
HealthLua: `
hs = {}
hs.status = "Progressing"
hs.message = ""
if obj.status ~= nil then
if obj.status.health ~= nil then
hs.status = obj.status.health.status
if obj.status.health.message ~= nil then
hs.message = obj.status.health.message
end
end
end
return hs`,
},
}
t.Run("ChildAppDegraded", func(t *testing.T) {
degradedApp := newAppLiveObj(health.HealthStatusDegraded)
resources := []managedResource{{
Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp}, {}}
resourceStatuses := initStatuses(resources)
healthStatus, err := setApplicationHealth(resources, resourceStatuses, overrides, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusDegraded, healthStatus.Status)
})
t.Run("ChildAppMissing", func(t *testing.T) {
degradedApp := newAppLiveObj(health.HealthStatusMissing)
resources := []managedResource{{
Group: application.Group, Version: "v1alpha1", Kind: application.ApplicationKind, Live: degradedApp}, {}}
resourceStatuses := initStatuses(resources)
healthStatus, err := setApplicationHealth(resources, resourceStatuses, overrides, app)
assert.NoError(t, err)
assert.Equal(t, health.HealthStatusHealthy, healthStatus.Status)
})
}

View File

@@ -16,10 +16,10 @@ import (
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/labels"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/healthz"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
applister "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/util/git"
"github.com/argoproj/argo-cd/util/healthz"
)
type MetricsServer struct {
@@ -62,14 +62,14 @@ var (
descAppDefaultLabels,
nil,
)
// DEPRECATED: superseded by sync_status label in argocd_app_info
// DEPRECATED: superceded by sync_status label in argocd_app_info
descAppSyncStatusCode = prometheus.NewDesc(
"argocd_app_sync_status",
"The application current sync status.",
append(descAppDefaultLabels, "sync_status"),
nil,
)
// DEPRECATED: superseded by health_status label in argocd_app_info
// DEPRECATED: superceded by health_status label in argocd_app_info
descAppHealthStatus = prometheus.NewDesc(
"argocd_app_health_status",
"The application current health status.",

View File

@@ -17,10 +17,10 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
applister "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
appinformer "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
applister "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
)
const fakeApp = `

View File

@@ -6,7 +6,7 @@ import (
"github.com/argoproj/pkg/kubeclientmetrics"
"k8s.io/client-go/rest"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
// AddMetricsTransportWrapper adds a transport wrapper which increments 'argocd_app_k8s_request_total' counter on each kubernetes request

View File

@@ -7,7 +7,7 @@ import (
"strconv"
"strings"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func InferShard() (int, error) {
@@ -26,8 +26,8 @@ func InferShard() (int, error) {
return shard, nil
}
// GetShardByID calculates cluster shard as `clusterSecret.UID % replicas count`
func GetShardByID(id string, replicas int) int {
// getShardByID calculates cluster shard as `clusterSecret.UID % replicas count`
func getShardByID(id string, replicas int) int {
if id == "" {
return 0
} else {
@@ -45,7 +45,7 @@ func GetClusterFilter(replicas int, shard int) func(c *v1alpha1.Cluster) bool {
if c.Shard != nil {
clusterShard = int(*c.Shard)
} else {
clusterShard = GetShardByID(c.ID, replicas)
clusterShard = getShardByID(c.ID, replicas)
}
}
return clusterShard == shard

View File

@@ -3,20 +3,20 @@ package sharding
import (
"testing"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/assert"
)
func TestGetShardByID_NotEmptyID(t *testing.T) {
assert.Equal(t, 0, GetShardByID("1", 2))
assert.Equal(t, 1, GetShardByID("2", 2))
assert.Equal(t, 0, GetShardByID("3", 2))
assert.Equal(t, 1, GetShardByID("4", 2))
assert.Equal(t, 0, getShardByID("1", 2))
assert.Equal(t, 1, getShardByID("2", 2))
assert.Equal(t, 0, getShardByID("3", 2))
assert.Equal(t, 1, getShardByID("4", 2))
}
func TestGetShardByID_EmptyID(t *testing.T) {
shard := GetShardByID("", 10)
shard := getShardByID("", 10)
assert.Equal(t, 0, shard)
}

View File

@@ -22,20 +22,21 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/cache"
"github.com/argoproj/argo-cd/v2/common"
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/util/argo"
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
"github.com/argoproj/argo-cd/v2/util/db"
"github.com/argoproj/argo-cd/v2/util/gpg"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/settings"
"github.com/argoproj/argo-cd/v2/util/stats"
"github.com/argoproj/argo-cd/common"
statecache "github.com/argoproj/argo-cd/controller/cache"
"github.com/argoproj/argo-cd/controller/metrics"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/util/argo"
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
"github.com/argoproj/argo-cd/util/db"
"github.com/argoproj/argo-cd/util/gpg"
argohealth "github.com/argoproj/argo-cd/util/health"
"github.com/argoproj/argo-cd/util/io"
"github.com/argoproj/argo-cd/util/settings"
"github.com/argoproj/argo-cd/util/stats"
)
type resourceInfoProviderStub struct {
@@ -58,9 +59,23 @@ type managedResource struct {
ResourceVersion string
}
func GetLiveObjsForApplicationHealth(resources []managedResource, statuses []appv1.ResourceStatus) ([]*appv1.ResourceStatus, []*unstructured.Unstructured) {
liveObjs := make([]*unstructured.Unstructured, 0)
resStatuses := make([]*appv1.ResourceStatus, 0)
for i, resource := range resources {
if resource.Target != nil && hookutil.Skip(resource.Target) {
continue
}
liveObjs = append(liveObjs, resource.Live)
resStatuses = append(resStatuses, &statuses[i])
}
return resStatuses, liveObjs
}
// AppStateManager defines methods which allow to compare application spec and actual application state.
type AppStateManager interface {
CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localObjects []string) *comparisonResult
CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, localObjects []string) *comparisonResult
SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState)
}
@@ -100,30 +115,18 @@ type appStateManager struct {
statusRefreshTimeout time.Duration
}
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, appLabelKey, revision string, noCache, noRevisionCache, verifySignature bool, proj *v1alpha1.AppProject) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) {
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, appLabelKey, revision string, noCache, verifySignature bool) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) {
ts := stats.NewTimingStats()
helmRepos, err := m.db.ListHelmRepositories(context.Background())
if err != nil {
return nil, nil, err
}
permittedHelmRepos, err := argo.GetPermittedRepos(proj, helmRepos)
if err != nil {
return nil, nil, err
}
ts.AddCheckpoint("helm_ms")
repo, err := m.db.GetRepository(context.Background(), source.RepoURL)
if err != nil {
return nil, nil, err
}
ts.AddCheckpoint("repo_ms")
helmRepositoryCredentials, err := m.db.GetAllHelmRepositoryCredentials(context.Background())
if err != nil {
return nil, nil, err
}
permittedHelmCredentials, err := argo.GetPermittedReposCredentials(proj, helmRepositoryCredentials)
if err != nil {
return nil, nil, err
}
conn, repoClient, err := m.repoClientset.NewRepoServerClient()
if err != nil {
return nil, nil, err
@@ -160,10 +163,9 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
ts.AddCheckpoint("version_ms")
manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
Repo: repo,
Repos: permittedHelmRepos,
Repos: helmRepos,
Revision: revision,
NoCache: noCache,
NoRevisionCache: noRevisionCache,
AppLabelKey: appLabelKey,
AppName: app.Name,
Namespace: app.Spec.Destination.Namespace,
@@ -173,7 +175,6 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
KubeVersion: serverVersion,
ApiVersions: argo.APIGroupsToVersions(apiGroups),
VerifySignature: verifySignature,
HelmRepoCreds: permittedHelmCredentials,
})
if err != nil {
return nil, nil, err
@@ -274,29 +275,34 @@ func verifyGnuPGSignature(revision string, project *appv1.AppProject, manifestIn
conditions := make([]appv1.ApplicationCondition, 0)
// We need to have some data in the verification result to parse, otherwise there was no signature
if manifestInfo.VerifyResult != "" {
verifyResult := gpg.ParseGitCommitVerification(manifestInfo.VerifyResult)
switch verifyResult.Result {
case gpg.VerifyResultGood:
// This is the only case we allow to sync to, but we need to make sure signing key is allowed
validKey := false
for _, k := range project.Spec.SignatureKeys {
if gpg.KeyID(k.KeyID) == gpg.KeyID(verifyResult.KeyID) && gpg.KeyID(k.KeyID) != "" {
validKey = true
break
verifyResult, err := gpg.ParseGitCommitVerification(manifestInfo.VerifyResult)
if err != nil {
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
log.Errorf("Error while verifying git commit for revision %s: %s", revision, err.Error())
} else {
switch verifyResult.Result {
case gpg.VerifyResultGood:
// This is the only case we allow to sync to, but we need to make sure signing key is allowed
validKey := false
for _, k := range project.Spec.SignatureKeys {
if gpg.KeyID(k.KeyID) == gpg.KeyID(verifyResult.KeyID) && gpg.KeyID(k.KeyID) != "" {
validKey = true
break
}
}
}
if !validKey {
msg := fmt.Sprintf("Found good signature made with %s key %s, but this key is not allowed in AppProject",
verifyResult.Cipher, verifyResult.KeyID)
if !validKey {
msg := fmt.Sprintf("Found good signature made with %s key %s, but this key is not allowed in AppProject",
verifyResult.Cipher, verifyResult.KeyID)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now})
}
case gpg.VerifyResultInvalid:
msg := fmt.Sprintf("Found signature made with %s key %s, but verification result was invalid: '%s'",
verifyResult.Cipher, verifyResult.KeyID, verifyResult.Message)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now})
default:
msg := fmt.Sprintf("Could not verify commit signature on revision '%s', check logs for more information.", revision)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now})
}
case gpg.VerifyResultInvalid:
msg := fmt.Sprintf("Found signature made with %s key %s, but verification result was invalid: '%s'",
verifyResult.Cipher, verifyResult.KeyID, verifyResult.Message)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now})
default:
msg := fmt.Sprintf("Could not verify commit signature on revision '%s', check logs for more information.", revision)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: msg, LastTransitionTime: &now})
}
} else {
msg := fmt.Sprintf("Target revision %s in Git is not signed, but a signature is required", revision)
@@ -347,11 +353,9 @@ func (m *appStateManager) diffArrayCached(configArray []*unstructured.Unstructur
}
dr = res
}
if dr != nil {
diffResultList.Diffs[i] = *dr
if dr.Modified {
diffResultList.Modified = true
}
diffResultList.Diffs[i] = *dr
if dr != nil && dr.Modified {
diffResultList.Modified = true
}
}
@@ -361,7 +365,7 @@ func (m *appStateManager) diffArrayCached(configArray []*unstructured.Unstructur
// CompareAppState compares application git state to the live app state, using the specified
// revision and supplied source. If revision or overrides are empty, then compares against
// revision and overrides in the app spec.
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, noRevisionCache bool, localManifests []string) *comparisonResult {
func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, localManifests []string) *comparisonResult {
ts := stats.NewTimingStats()
appLabelKey, resourceOverrides, diffNormalizer, resFilter, err := m.getComparisonSettings(app)
ts.AddCheckpoint("settings_ms")
@@ -395,7 +399,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
now := metav1.Now()
if len(localManifests) == 0 {
targetObjs, manifestInfo, err = m.getRepoObjs(app, source, appLabelKey, revision, noCache, noRevisionCache, verifySignature, project)
targetObjs, manifestInfo, err = m.getRepoObjs(app, source, appLabelKey, revision, noCache, verifySignature)
if err != nil {
targetObjs = make([]*unstructured.Unstructured, 0)
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
@@ -606,7 +610,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
}
ts.AddCheckpoint("sync_ms")
healthStatus, err := setApplicationHealth(managedResources, resourceSummaries, resourceOverrides, app)
resSumForAppHealth, liveObjsForAppHealth := GetLiveObjsForApplicationHealth(managedResources, resourceSummaries)
healthStatus, err := argohealth.SetApplicationHealth(resSumForAppHealth, liveObjsForAppHealth, resourceOverrides, func(obj *unstructured.Unstructured) bool {
return !isSelfReferencedApp(app, kubeutil.GetObjectRef(obj))
})
if err != nil {
conditions = append(conditions, appv1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
}

View File

@@ -17,10 +17,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"github.com/argoproj/argo-cd/v2/common"
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
"github.com/argoproj/argo-cd/v2/test"
"github.com/argoproj/argo-cd/common"
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/reposerver/apiclient"
"github.com/argoproj/argo-cd/test"
)
// TestCompareAppStateEmpty tests comparison when both git and live have no objects
@@ -36,7 +36,7 @@ func TestCompareAppStateEmpty(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -59,7 +59,7 @@ func TestCompareAppStateMissing(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
@@ -86,7 +86,7 @@ func TestCompareAppStateExtra(t *testing.T) {
},
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeOutOfSync, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -112,7 +112,7 @@ func TestCompareAppStateHook(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 0, len(compRes.resources))
@@ -139,7 +139,7 @@ func TestCompareAppStateSkipHook(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
assert.Equal(t, 1, len(compRes.resources))
@@ -165,7 +165,7 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -193,7 +193,7 @@ func TestCompareAppStateExtraHook(t *testing.T) {
},
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -236,7 +236,7 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
},
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.Equal(t, 1, len(app.Status.Conditions))
@@ -287,7 +287,7 @@ func TestSetHealth(t *testing.T) {
},
})
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy)
}
@@ -319,7 +319,7 @@ func TestSetHealthSelfReferencedApp(t *testing.T) {
},
})
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy)
}
@@ -389,7 +389,7 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
},
})
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status)
assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
@@ -525,7 +525,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -547,7 +547,7 @@ func TestSignedResponseNoSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -576,7 +576,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -598,7 +598,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -620,7 +620,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -642,7 +642,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -667,7 +667,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
ctrl := newFakeController(&data)
testProj := signedProj
testProj.Spec.SignatureKeys[0].KeyID = "4AEE18F83AFDEB24"
compRes := ctrl.appStateManager.CompareAppState(app, &testProj, "abc123", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &testProj, "abc123", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -692,7 +692,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
// it doesn't matter for our test whether local manifests are valid
localManifests := []string{"foobar"}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, localManifests)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, localManifests)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
@@ -717,7 +717,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured),
}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, nil)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, nil)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
@@ -742,7 +742,7 @@ func TestSignedResponseSignatureRequired(t *testing.T) {
// it doesn't matter for our test whether local manifests are valid
localManifests := []string{""}
ctrl := newFakeController(&data)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, false, localManifests)
compRes := ctrl.appStateManager.CompareAppState(app, &signedProj, "abc123", app.Spec.Source, false, localManifests)
assert.NotNil(t, compRes)
assert.NotNil(t, compRes.syncStatus)
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)

View File

@@ -15,16 +15,15 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubectl/pkg/util/openapi"
cdcommon "github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/controller/metrics"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
listersv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/argo"
logutils "github.com/argoproj/argo-cd/v2/util/log"
"github.com/argoproj/argo-cd/v2/util/lua"
"github.com/argoproj/argo-cd/v2/util/rand"
cdcommon "github.com/argoproj/argo-cd/common"
"github.com/argoproj/argo-cd/controller/metrics"
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
listersv1alpha1 "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
"github.com/argoproj/argo-cd/util/argo"
logutils "github.com/argoproj/argo-cd/util/log"
"github.com/argoproj/argo-cd/util/lua"
"github.com/argoproj/argo-cd/util/rand"
)
var syncIdPrefix uint64 = 0
@@ -35,14 +34,6 @@ const (
EnvVarSyncWaveDelay = "ARGOCD_SYNC_WAVE_DELAY"
)
func (m *appStateManager) getOpenAPISchema(server string) (openapi.Resources, error) {
cluster, err := m.liveStateCache.GetClusterCache(server)
if err != nil {
return nil, err
}
return cluster.GetOpenAPISchema(), nil
}
func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) {
// Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3).
// This can change meaning when resuming operations (e.g a hook sync). After calculating a
@@ -94,7 +85,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
return
}
compareResult := m.CompareAppState(app, proj, revision, source, false, true, syncOp.Manifests)
compareResult := m.CompareAppState(app, proj, revision, source, false, syncOp.Manifests)
// We now have a concrete commit SHA. Save this in the sync result revision so that we remember
// what we should be syncing to when resuming operations.
syncRes.Revision = compareResult.syncStatus.Revision
@@ -145,31 +136,13 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
})
}
prunePropagationPolicy := v1.DeletePropagationForeground
switch {
case syncOp.SyncOptions.HasOption("PrunePropagationPolicy=background"):
prunePropagationPolicy = v1.DeletePropagationBackground
case syncOp.SyncOptions.HasOption("PrunePropagationPolicy=foreground"):
prunePropagationPolicy = v1.DeletePropagationForeground
case syncOp.SyncOptions.HasOption("PrunePropagationPolicy=orphan"):
prunePropagationPolicy = v1.DeletePropagationOrphan
}
openAPISchema, err := m.getOpenAPISchema(clst.Server)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("failed to load openAPISchema: %v", err)
return
}
syncCtx, cleanup, err := sync.NewSyncContext(
syncCtx, err := sync.NewSyncContext(
compareResult.syncStatus.Revision,
compareResult.reconciliationResult,
restConfig,
rawConfig,
m.kubectl,
app.Spec.Destination.Namespace,
openAPISchema,
sync.WithLogr(logutils.NewLogrusLogger(logEntry)),
sync.WithHealthOverride(lua.ResourceHealthOverrides(resourceOverrides)),
sync.WithPermissionValidator(func(un *unstructured.Unstructured, res *v1.APIResource) error {
@@ -186,7 +159,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
sync.WithResourcesFilter(func(key kube.ResourceKey, target *unstructured.Unstructured, live *unstructured.Unstructured) bool {
return len(syncOp.Resources) == 0 || argo.ContainsSyncResource(key.Name, key.Namespace, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)
}),
sync.WithManifestValidation(!syncOp.SyncOptions.HasOption(common.SyncOptionsDisableValidation)),
sync.WithManifestValidation(!syncOp.SyncOptions.HasOption("Validate=false")),
sync.WithNamespaceCreation(syncOp.SyncOptions.HasOption("CreateNamespace=true"), func(un *unstructured.Unstructured) bool {
if un != nil && kube.GetAppInstanceLabel(un, cdcommon.LabelKeyAppInstance) != "" {
kube.UnsetLabel(un, cdcommon.LabelKeyAppInstance)
@@ -195,20 +168,15 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
return false
}),
sync.WithSyncWaveHook(delayBetweenSyncWaves),
sync.WithPruneLast(syncOp.SyncOptions.HasOption(common.SyncOptionPruneLast)),
sync.WithPruneLast(syncOp.SyncOptions.HasOption("PruneLast=true")),
sync.WithResourceModificationChecker(syncOp.SyncOptions.HasOption("ApplyOutOfSyncOnly=true"), compareResult.diffResultList),
sync.WithPrunePropagationPolicy(&prunePropagationPolicy),
sync.WithReplace(syncOp.SyncOptions.HasOption(common.SyncOptionReplace)),
)
if err != nil {
state.Phase = common.OperationError
state.Message = fmt.Sprintf("failed to initialize sync context: %v", err)
return
state.Message = fmt.Sprintf("failed to record sync to history: %v", err)
}
defer cleanup()
start := time.Now()
if state.Phase == common.OperationTerminating {

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