Compare commits
16 Commits
v2.0.0-rc2
...
refresh-do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69d23e105e | ||
|
|
2d963ab8d0 | ||
|
|
7a3cd61060 | ||
|
|
5cbb397c93 | ||
|
|
5257a902fb | ||
|
|
4c6a74642f | ||
|
|
ab715efd2b | ||
|
|
50a693e554 | ||
|
|
8409da4df8 | ||
|
|
857ea0edfe | ||
|
|
068a7a6abc | ||
|
|
6c019b037b | ||
|
|
b37bb53f5e | ||
|
|
4251200f78 | ||
|
|
b407d59fa5 | ||
|
|
4f7aaf0d71 |
20
.github/workflows/ci-build.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
- name: Download all Go modules
|
||||
run: |
|
||||
go mod download
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.38.0
|
||||
version: v1.29
|
||||
args: --timeout 5m --exclude SA5011
|
||||
|
||||
test-go:
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt-get install git -y
|
||||
@@ -147,7 +147,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt-get install git -y
|
||||
@@ -196,7 +196,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
- name: Create symlink in GOPATH
|
||||
run: |
|
||||
mkdir -p ~/go/src/github.com/argoproj
|
||||
@@ -259,7 +259,6 @@ jobs:
|
||||
yarn build
|
||||
env:
|
||||
NODE_ENV: production
|
||||
NODE_ONLINE_ENV: online
|
||||
working-directory: ui/
|
||||
- name: Run ESLint
|
||||
run: yarn lint
|
||||
@@ -355,10 +354,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
- 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
|
||||
@@ -396,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.1-alpine
|
||||
docker pull redis:5.0.10-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
2
.github/workflows/image.yaml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
path: src/github.com/argoproj/argo-cd
|
||||
|
||||
13
.github/workflows/release.yaml
vendored
@@ -18,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
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: '1.14.12'
|
||||
|
||||
- name: Setup Git author information
|
||||
run: |
|
||||
@@ -197,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
|
||||
|
||||
109
CHANGELOG.md
@@ -1,111 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## v2.0.0 (Unreleased)
|
||||
|
||||
> [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
|
||||
|
||||
@@ -1515,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,
|
||||
|
||||
@@ -4,7 +4,7 @@ ARG BASE_IMAGE=ubuntu:20.10
|
||||
# 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 golang:1.16.2 as builder
|
||||
FROM golang:1.14.12 as builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -97,12 +97,12 @@ 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.0 as argocd-build
|
||||
FROM golang:1.14.12 as argocd-build
|
||||
|
||||
COPY --from=builder /usr/local/bin/packr /usr/local/bin/packr
|
||||
|
||||
|
||||
4
Makefile
@@ -45,7 +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_IN_CI?=false
|
||||
ARGOCD_TEST_E2E?=true
|
||||
@@ -74,7 +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) \
|
||||
-v ${DOCKER_SRC_MOUNT} \
|
||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||
@@ -309,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
|
||||
|
||||
7
OWNERS
@@ -5,12 +5,13 @@ owners:
|
||||
approvers:
|
||||
- alexec
|
||||
- alexmt
|
||||
- dthomson25
|
||||
- jannfis
|
||||
- jessesuen
|
||||
- jgwest
|
||||
- mayzhang2000
|
||||
- rachelwang20
|
||||
|
||||
reviewers:
|
||||
- dthomson25
|
||||
- tetchel
|
||||
- jgwest
|
||||
- wtam2018
|
||||
- tetchel
|
||||
|
||||
2
Procfile
@@ -1,7 +1,7 @@
|
||||
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} --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:6.2.1-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
|
||||
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
|
||||
|
||||
24
README.md
@@ -28,24 +28,8 @@ 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. [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)
|
||||
@@ -60,10 +44,10 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
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/)
|
||||
|
||||
6
USERS.md
@@ -8,7 +8,6 @@ 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. [Ambassador Labs](https://www.getambassador.io/)
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
@@ -39,7 +38,6 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Future PLC](https://www.futureplc.com/)
|
||||
1. [Garner](https://www.garnercorp.com)
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Gojek](https://www.gojek.io/)
|
||||
1. [Greenpass](https://www.greenpass.com.br/)
|
||||
1. [Healy](https://www.healyworld.net)
|
||||
1. [hipages](https://hipages.com.au/)
|
||||
@@ -91,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/)
|
||||
@@ -120,5 +117,4 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [WeMo Scooter](https://www.wemoscooter.com/)
|
||||
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
|
||||
1. [Yieldlab](https://www.yieldlab.de/)
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [Smilee.io](https://smilee.io)
|
||||
1. [Sap Labs] (http://sap.com)
|
||||
|
||||
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 5.6 KiB |
@@ -2,7 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -27,7 +26,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"github.com/argoproj/argo-cd/util/tls"
|
||||
)
|
||||
|
||||
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,
|
||||
@@ -76,26 +72,7 @@ func NewCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
|
||||
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)
|
||||
|
||||
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -149,8 +126,6 @@ func NewCommand() *cobra.Command {
|
||||
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", false, "Disable TLS on connections to repo server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "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
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -157,7 +152,6 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().Int64Var(¶llelismLimit, "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", false, "Disable TLS on the gRPC endpoint")
|
||||
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
|
||||
@@ -2,7 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
@@ -61,8 +60,6 @@ func NewCommand() *cobra.Command {
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
cacheSrc func() (*servercache.Cache, error)
|
||||
frameOptions string
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -96,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)
|
||||
@@ -173,8 +153,6 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
|
||||
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 \"\".")
|
||||
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
|
||||
cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
|
||||
@@ -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,7 +22,6 @@ import (
|
||||
kubecache "k8s.io/client-go/tools/cache"
|
||||
"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/controller"
|
||||
"github.com/argoproj/argo-cd/controller/cache"
|
||||
@@ -30,7 +31,6 @@ import (
|
||||
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"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
@@ -39,79 +39,20 @@ import (
|
||||
"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
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-spec APPNAME",
|
||||
Short: "Generate declarative config for an application",
|
||||
Example: `
|
||||
# Generate declarative config for a directory app
|
||||
argocd-util 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-util 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-util 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-util app generate-spec 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 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-util 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, 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
|
||||
}
|
||||
|
||||
type appReconcileResult struct {
|
||||
Name string `json:"name"`
|
||||
Health *v1alpha1.HealthStatus `json:"health"`
|
||||
@@ -251,7 +192,7 @@ func NewReconcileCommand() *cobra.Command {
|
||||
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)
|
||||
@@ -1,21 +1,32 @@
|
||||
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"
|
||||
)
|
||||
@@ -51,19 +62,162 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.AddCommand(NewClusterCommand(pathOpts))
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
command.AddCommand(NewSettingsCommand())
|
||||
command.AddCommand(NewAppCommand())
|
||||
command.AddCommand(NewRepoCommand())
|
||||
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
|
||||
@@ -82,6 +236,78 @@ func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientset
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -211,6 +437,83 @@ func specsEqual(left, right unstructured.Unstructured) bool {
|
||||
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 {
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
"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/common"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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, v1.CreateOptions{})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
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 {
|
||||
if !dryRun {
|
||||
newLive := updateLive(bakObj, &liveObj)
|
||||
_, err = dynClient.Update(context.Background(), newLive, v1.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, 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
logrus.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
|
||||
}
|
||||
if !dryRun {
|
||||
err = dynClient.Delete(context.Background(), key.Name, v1.DeleteOptions{})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
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")
|
||||
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"]
|
||||
}
|
||||
}
|
||||
return newLive
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"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/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller/sharding"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
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/db"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/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())
|
||||
|
||||
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)
|
||||
if replicas == 0 {
|
||||
controllerPods, err := kubeClient.CoreV1().Pods(namespace).List(context.Background(), v1.ListOptions{
|
||||
LabelSelector: "app.kubernetes.io/name=argocd-application-controller"})
|
||||
errors.CheckError(err)
|
||||
replicas = len(controllerPods.Items)
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace)
|
||||
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
|
||||
clusters, err := argoDB.ListClusters(context.Background())
|
||||
errors.CheckError(err)
|
||||
var cache *appstatecache.Cache
|
||||
if portForwardRedis {
|
||||
port, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-redis-ha-haproxy", 6379, namespace)
|
||||
errors.CheckError(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()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "SERVER\tSHARD\tCONNECTION\tAPPS COUNT\tRESOURCES COUNT\n")
|
||||
|
||||
for _, cluster := range clusters.Items {
|
||||
clusterShard := 0
|
||||
if replicas > 0 {
|
||||
clusterShard = sharding.GetShardByID(cluster.ID, replicas)
|
||||
}
|
||||
|
||||
if shard != -1 && clusterShard != shard {
|
||||
continue
|
||||
}
|
||||
|
||||
var info argoappv1.ClusterInfo
|
||||
_ = cache.GetClusterInfo(cluster.Server, &info)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%d\n", cluster.Server, clusterShard, info.ConnectionState.Status, info.ApplicationsCount, 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-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 NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var (
|
||||
clusterOpts cmdutil.ClusterOptions
|
||||
bearerToken string
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
359
cmd/argocd-util/commands/config.go
Normal 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
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"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"
|
||||
@@ -22,49 +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
|
||||
}
|
||||
|
||||
// NewGenProjectConfigCommand generates declarative configuration file for given project
|
||||
func NewGenProjectSpecCommand() *cobra.Command {
|
||||
var (
|
||||
opts cmdutil.ProjectOpts
|
||||
fileURL string
|
||||
outputFormat string
|
||||
)
|
||||
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)
|
||||
|
||||
var printResources []interface{}
|
||||
printResources = append(printResources, proj)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
},
|
||||
}
|
||||
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")
|
||||
|
||||
// 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
|
||||
@@ -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-util 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-util 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-util 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-util 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) {
|
||||
@@ -1,182 +0,0 @@
|
||||
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"
|
||||
|
||||
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/git"
|
||||
"github.com/argoproj/argo-cd/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-util 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-util 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-util 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-util 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-util repo generate-spec https://kubernetes-charts.storage.googleapis.com --type helm --name stable
|
||||
|
||||
# Add a private Helm repository named 'stable' via HTTPS
|
||||
argocd-util repo generate-spec 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 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)
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -556,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{
|
||||
@@ -662,22 +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)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
|
||||
if !updated {
|
||||
return
|
||||
}
|
||||
|
||||
cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
|
||||
@@ -696,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
|
||||
}
|
||||
|
||||
@@ -798,7 +783,6 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var (
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
@@ -902,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)
|
||||
}
|
||||
|
||||
@@ -910,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")
|
||||
@@ -953,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",
|
||||
@@ -981,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
|
||||
@@ -1018,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
|
||||
}
|
||||
@@ -1067,7 +1045,6 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
output string
|
||||
selector string
|
||||
projects []string
|
||||
repo string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
@@ -1086,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)
|
||||
@@ -1105,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
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ 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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -7,13 +7,11 @@ 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/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||
@@ -64,10 +62,6 @@ type AppOptions struct {
|
||||
Validate bool
|
||||
directoryExclude string
|
||||
directoryInclude string
|
||||
retryLimit int64
|
||||
retryBackoffDuration time.Duration
|
||||
retryBackoffMaxDuration time.Duration
|
||||
retryBackoffFactor int64
|
||||
}
|
||||
|
||||
func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
@@ -111,10 +105,6 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
command.Flags().StringArrayVar(&opts.kustomizeCommonAnnotations, "kustomize-common-annotation", []string{}, "Set common labels 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", common.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", common.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", common.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed sync retry")
|
||||
}
|
||||
|
||||
func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions) int {
|
||||
@@ -248,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") {
|
||||
|
||||
@@ -164,11 +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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,26 +24,21 @@ func PrintResources(resources []interface{}, output string) error {
|
||||
}
|
||||
resources[i] = filteredResource
|
||||
}
|
||||
var obj interface{} = resources
|
||||
if len(resources) == 1 {
|
||||
obj = resources[0]
|
||||
}
|
||||
|
||||
switch output {
|
||||
case "json":
|
||||
jsonBytes, err := json.MarshalIndent(obj, "", " ")
|
||||
jsonBytes, err := json.MarshalIndent(resources, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(obj)
|
||||
yamlBytes, err := yaml.Marshal(resources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// marshaled YAML already ends with the new line character
|
||||
fmt.Print(string(yamlBytes))
|
||||
fmt.Println(string(yamlBytes))
|
||||
default:
|
||||
return fmt.Errorf("unknown output format: %s", output)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
@@ -20,17 +19,12 @@ import (
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
}
|
||||
@@ -53,8 +53,6 @@ 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 (
|
||||
@@ -127,15 +125,9 @@ const (
|
||||
AnnotationKeyManagedBy = "managed-by"
|
||||
// AnnotationValueManagedByArgoCD is a 'managed-by' annotation value for resources managed by Argo CD
|
||||
AnnotationValueManagedByArgoCD = "argocd.argoproj.io"
|
||||
// ResourcesFinalizerName is the finalizer value which we inject to finalize deletion of an application
|
||||
// ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application
|
||||
ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io"
|
||||
|
||||
// ForegroundPropagationPolicyFinalizer is the finalizer we inject to delete application with foreground propagation policy
|
||||
ForegroundPropagationPolicyFinalizer = "resources-finalizer.argocd.argoproj.io/foreground"
|
||||
|
||||
// BackgroundPropagationPolicyFinalizer is the finalizer we inject to delete application with background propagation policy
|
||||
BackgroundPropagationPolicyFinalizer = "resources-finalizer.argocd.argoproj.io/background"
|
||||
|
||||
// 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
|
||||
@@ -196,10 +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"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -839,16 +839,9 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig())
|
||||
|
||||
filteredObjs := FilterObjectsForDeletion(objs)
|
||||
|
||||
propagationPolicy := metav1.DeletePropagationForeground
|
||||
if app.GetPropagationPolicy() == common.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})
|
||||
return ctrl.kubectl.DeleteResource(context.Background(), config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), false)
|
||||
})
|
||||
if err != nil {
|
||||
return objs, err
|
||||
@@ -876,8 +869,14 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
if err != nil {
|
||||
return objs, err
|
||||
}
|
||||
|
||||
err = ctrl.removeCascadeFinalizer(app)
|
||||
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
|
||||
}
|
||||
@@ -887,19 +886,6 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
return objs, nil
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) removeCascadeFinalizer(app *appv1.Application) error {
|
||||
app.UnSetCascadedDeletion()
|
||||
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{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) setAppCondition(app *appv1.Application, condition appv1.ApplicationCondition) {
|
||||
// do nothing if app already has same condition
|
||||
for _, c := range app.Status.Conditions {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -275,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)
|
||||
@@ -348,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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -136,16 +136,6 @@ 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
|
||||
}
|
||||
|
||||
syncCtx, err := sync.NewSyncContext(
|
||||
compareResult.syncStatus.Revision,
|
||||
compareResult.reconciliationResult,
|
||||
@@ -169,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)
|
||||
@@ -178,10 +168,8 @@ 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 {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Please refer to [the Contribution Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/contributing/)
|
||||
@@ -1,6 +0,0 @@
|
||||
# Support
|
||||
|
||||
1. Make sure you've read [understanding the basics](understand_the_basics.md) the [getting started guide](getting_started.md).
|
||||
2. Looked for an answer in [the frequently asked questions](faq.md).
|
||||
3. Ask a question in [the Argo CD Slack channel ⧉](https://argoproj.github.io/community/join-slack).
|
||||
4. [Read issues, report a bug, or request a feature ⧉](https://github.com/argoproj/argo-cd/issues).
|
||||
0
docs/advanced/sync_waves.md
Normal file
BIN
docs/assets/argocd-per-cluster.jpg
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/assets/argocd-ui-example.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
docs/assets/centralized.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/assets/cluster-remove-ui.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/assets/disabled-sync-window.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
docs/assets/edit-window.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 145 KiB |
BIN
docs/assets/hooks.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/assets/how-it-works.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
docs/assets/hybrid1.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
docs/assets/hybrid2.jpg
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
docs/assets/list-of-windows.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/assets/multi-cluster-argocd.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
docs/assets/public-instance.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 64 KiB |
BIN
docs/assets/screens/projects-01-where.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
docs/assets/sync-phases.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/assets/sync-waves.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
@@ -1,172 +0,0 @@
|
||||
.md-header__title {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dropdown-caret {
|
||||
display: inline-block !important;
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
}
|
||||
|
||||
.fa .fa-caret-down {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.rst-other-versions {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.rst-other-versions > dl, .rst-other-versions dt, .rst-other-versions small {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rst-other-versions > dl:first-child {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
line-height: 0px !important;
|
||||
}
|
||||
|
||||
.rst-versions.shift-up .rst-other-versions {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.rst-versions .rst-other-versions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Version Warning */
|
||||
div[data-md-component=announce] {
|
||||
background-color: rgba(255,145,0,.1);
|
||||
}
|
||||
div[data-md-component=announce]>div#announce-msg{
|
||||
color: var(--md-admonition-fg-color);
|
||||
font-size: .8rem;
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
div[data-md-component=announce]>div#announce-msg>a{
|
||||
color: var(--md-typeset-a-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* from https://assets.readthedocs.org/static/css/badge_only.css,
|
||||
most styles have to be overriden here */
|
||||
.rst-versions{
|
||||
position: relative !important;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100px !important;
|
||||
background: hsla(173, 100%, 24%, 1) !important;
|
||||
font-family: inherit !important;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
.rst-versions a{
|
||||
color:#2980B9;
|
||||
text-decoration:none
|
||||
}
|
||||
.rst-versions .rst-badge-small{
|
||||
display:none
|
||||
}
|
||||
.rst-versions .rst-current-version{
|
||||
padding:12px;
|
||||
background: hsla(173, 100%, 24%, 1) !important;
|
||||
display:block;
|
||||
text-align:right;
|
||||
font-size:90%;
|
||||
cursor:pointer;
|
||||
color: white !important;
|
||||
*zoom:1
|
||||
}
|
||||
.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{
|
||||
display:table;content:""
|
||||
}
|
||||
.rst-versions .rst-current-version:after{
|
||||
clear:both
|
||||
}
|
||||
.rst-versions .rst-current-version .fa{
|
||||
color:#fcfcfc
|
||||
}
|
||||
.rst-versions .rst-current-version .fa-caret-down{
|
||||
display: none;
|
||||
}
|
||||
.rst-versions.shift-up .rst-other-versions{
|
||||
display:block
|
||||
}
|
||||
.rst-versions .rst-other-versions{
|
||||
font-size:90%;
|
||||
padding:12px;
|
||||
color:gray;
|
||||
display:none
|
||||
}
|
||||
.rst-versions .rst-other-versions hr{
|
||||
display: none !important;
|
||||
height: 0px !important;
|
||||
border: 0px;
|
||||
margin: 0px !important;
|
||||
padding: 0px;
|
||||
border-top: none !important;
|
||||
}
|
||||
.rst-versions .rst-other-versions dd{
|
||||
display:inline-block;
|
||||
margin:0
|
||||
}
|
||||
.rst-versions .rst-other-versions dd a{
|
||||
display:inline-block;
|
||||
padding: 1em 0em !important;
|
||||
color:#fcfcfc;
|
||||
font-size: .6rem !important;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
width: 80px;
|
||||
}
|
||||
.rst-versions .rst-other-versions dd a:hover{
|
||||
font-size: .7rem !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
.rst-versions.rst-badge{
|
||||
display: block !important;
|
||||
width: 100px !important;
|
||||
bottom: 0px !important;
|
||||
right: 0px !important;
|
||||
left:auto;
|
||||
border:none;
|
||||
text-align: center !important;
|
||||
line-height: 0;
|
||||
}
|
||||
.rst-versions.rst-badge .icon-book{
|
||||
display: none;
|
||||
}
|
||||
.rst-versions.rst-badge .fa-book{
|
||||
display: none !important;
|
||||
}
|
||||
.rst-versions.rst-badge.shift-up .rst-current-version{
|
||||
text-align: left !important;
|
||||
}
|
||||
.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{
|
||||
display: none !important;
|
||||
}
|
||||
.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{
|
||||
display: none !important;
|
||||
}
|
||||
.rst-versions.rst-badge .rst-current-version{
|
||||
width: 70px !important;
|
||||
height: 2.4rem !important;
|
||||
line-height:2.4rem !important;
|
||||
padding: 0px 5px !important;
|
||||
display: inline-block !important;
|
||||
font-size: .6rem !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: ellipsis !important;
|
||||
white-space: nowrap !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
@media screen and (max-width: 768px){
|
||||
.rst-versions{
|
||||
width:85%;
|
||||
display:none
|
||||
}
|
||||
.rst-versions.shift{
|
||||
display:block
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
setTimeout(function() {
|
||||
const callbackName = 'callback_' + new Date().getTime();
|
||||
window[callbackName] = function (response) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = response.html;
|
||||
document.querySelector(".md-header__inner > .md-header__title").appendChild(div);
|
||||
const container = div.querySelector('.rst-versions');
|
||||
var caret = document.createElement('div');
|
||||
caret.innerHTML = "<i class='fa fa-caret-down dropdown-caret'></i>"
|
||||
caret.classList.add('dropdown-caret')
|
||||
div.querySelector('.rst-current-version').appendChild(caret);
|
||||
div.querySelector('.rst-current-version').addEventListener('click', function() {
|
||||
const classes = container.className.split(' ');
|
||||
const index = classes.indexOf('shift-up');
|
||||
if (index === -1) {
|
||||
classes.push('shift-up');
|
||||
} else {
|
||||
classes.splice(index, 1);
|
||||
}
|
||||
container.className = classes.join(' ');
|
||||
});
|
||||
}
|
||||
|
||||
var CSSLink = document.createElement('link');
|
||||
CSSLink.rel='stylesheet';
|
||||
CSSLink.href = '/assets/versions.css';
|
||||
document.getElementsByTagName('head')[0].appendChild(CSSLink);
|
||||
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://argo-cd.readthedocs.io/_/api/v2/footer_html/?'+
|
||||
'callback=' + callbackName + '&project=argo-cd&page=&theme=mkdocs&format=jsonp&docroot=docs&source_suffix=.md&version=' + (window['READTHEDOCS_DATA'] || { version: 'latest' }).version;
|
||||
document.getElementsByTagName('head')[0].appendChild(script);
|
||||
}, 0);
|
||||
|
||||
// VERSION WARNINGS
|
||||
window.addEventListener("DOMContentLoaded", function() {
|
||||
var rtdData = window['READTHEDOCS_DATA'] || { version: 'latest' };
|
||||
if (rtdData.version === "latest") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for an unreleased version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
|
||||
}
|
||||
else if (rtdData.version !== "stable") {
|
||||
document.querySelector("div[data-md-component=announce]").innerHTML = "<div id='announce-msg'>You are viewing the docs for a previous version of Argo CD, <a href='https://argo-cd.readthedocs.io/en/stable/'>click here to go to the latest stable version.</a></div>"
|
||||
}
|
||||
});
|
||||
62
docs/basics/apps/destination.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Application destination
|
||||
|
||||
The *Application destination* defines where the *Application* should be synced
|
||||
to. The *Destination* is defined in the `.spec.destination` part of the
|
||||
*Application* Custom Resource.
|
||||
|
||||
A *Destination* consists of a tuple of the *target cluster* and the target
|
||||
*namespace*.
|
||||
|
||||
A *Destination* must be permitted in the *Application's* parent
|
||||
[Project](../projects/).
|
||||
|
||||
## Target cluster
|
||||
|
||||
The *target cluster*, as the name implies, defines the cluster where the
|
||||
application's resource manifests should be deployed to. The target cluster is
|
||||
specified using the `spec.destination.server` field, which contains either the
|
||||
URL to the Kubernetes API of the cluster, or its
|
||||
[symbolic name](../clusters/).
|
||||
|
||||
There are two distinct types of values you can use here:
|
||||
|
||||
* Either the local cluster where Argo CD is installed to, which is usually
|
||||
`https://kubernetes.default.svc` with a symbolic name of `in-cluster`, or
|
||||
|
||||
* A remote cluster, referenced by its API URL. Be aware that before you can
|
||||
specify a remote cluster as a target cluster, it needs to be
|
||||
[added to Argo CD's configuration properly](../clusters/).
|
||||
|
||||
## Target namespace
|
||||
|
||||
Depending on your Argo CD
|
||||
[installation type](../../getting_started/install.md#installation-types),
|
||||
your
|
||||
[target cluster's configuration](../clusters/)
|
||||
and your
|
||||
[project settings](../projects/#cluster-resources),
|
||||
your *Application* resource manifests may consist of cluster-scoped and
|
||||
namespace-scoped resources.
|
||||
|
||||
Cluster-scoped resources obviously won't need a target namespace, but Argo CD
|
||||
needs to know to which target namespace the namespace-scoped resources shall
|
||||
be deployed to. This is set via the `.spec.destination.namespace` field.
|
||||
|
||||
The target namespace has to exist in the target cluster unless the
|
||||
[sync option](../../syncing/)
|
||||
[namespace auto-creation](../../syncing/)
|
||||
has been set in the *Application* or an appropriate `Namespace` resource is part
|
||||
of the *Application's* resource manifests.
|
||||
|
||||
Argo CD will not overwrite existing namespace configuration in any resource,
|
||||
so the final decision about a resource's target namespace will be made according
|
||||
to these rules:
|
||||
|
||||
* If a resource has set `.metadata.namespace`, its value will be used as the
|
||||
target namespace for that resource. In this case, the namespace has either to
|
||||
exist in the target cluster, or an appropriate `Namespace` resource has to
|
||||
be delivered together with the application's resource manifests.
|
||||
|
||||
* Otherwise, the *target namespace* as defined in the *Application's*
|
||||
`.spec.destination.namespace` field will be used as the target namespace for
|
||||
the resource.
|
||||
0
docs/basics/apps/health.md
Normal file
0
docs/basics/apps/history.md
Normal file
92
docs/basics/apps/index.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Argo CD Applications
|
||||
|
||||
## Overview
|
||||
|
||||
*Applications* are at the heart of Argo CD. An *Application* is the entity that
|
||||
tells Argo CD where to find resources to deploy, where to deploy them and when
|
||||
to do it.
|
||||
|
||||
You can think of an *Application* as a collection of one or more Kubernetes
|
||||
resources that are managed together on a Kubernetes cluster. These resources can
|
||||
be comprised of anything that is managable by the target Kubernetes cluster,
|
||||
and can also possibly span over multiple namespaces. There is no artifical limit
|
||||
of how many *Applications* you can configure in Argo CD, however, there might
|
||||
be other limits (such as, compute resource constraints).
|
||||
|
||||
Each *Application* must be configured to have at least
|
||||
|
||||
* a unique
|
||||
[Name](#application-name),
|
||||
* a relationship to a
|
||||
[Project](../projects/),
|
||||
* a [Source](source.md)
|
||||
to define the source of the *Application's* resources and
|
||||
* a [Destination](destination.md)
|
||||
to define the target of the *Application's* resources.
|
||||
|
||||
Optionally, each *Application* can also have a
|
||||
[Sync Policy](../../syncing/policy.md)
|
||||
that controls how it will be synced to its destination.
|
||||
|
||||
The relationship between a *Source* and an *Application* is always 1:n. That
|
||||
is, each *Application* must have exactly one *Source*, while you can create
|
||||
multiple *Applications* from a single *Source*.
|
||||
|
||||
The same is true for the relationship between a *Destination* and an
|
||||
*Application*, which is also alway 1:n. Each *Application* is managed on
|
||||
exactly one *Destination*, but your *Destination* can contain multiple
|
||||
*Applications*. This also means, you cannot install the same application to
|
||||
multiple clusters, or multiple times on the same cluster.
|
||||
|
||||
Along with its configuration, each *Application* also has a
|
||||
[sync state](../../syncing/states.md)
|
||||
that represents its current reconciliation status, and a
|
||||
[history](history.md)
|
||||
which contains recordings of previous states and reconciliation results.
|
||||
|
||||
## Application name
|
||||
|
||||
An *Application name* defines the name of the application. Application names
|
||||
are also the names of the Custom Resource in your cluster (defined using the
|
||||
`.metadata.name` field of the CR) and therefore must be unique within your Argo
|
||||
CD installation. It is not possible to have two applications with the same
|
||||
name, regardless of their *Source* and *Destination* configuration.
|
||||
|
||||
It is recommended to use an easy to memorize naming scheme for applications,
|
||||
especially if you are going to install a similar application to multiple
|
||||
destinations. For example, if you have an *Application* you want to name
|
||||
`monitoring`, and this application would be deployed to multiple clusters,
|
||||
|
||||
## Parent project
|
||||
|
||||
Each *Application* must belong to a parent
|
||||
[project](../projects/)
|
||||
that specifies certain rules and additional configuration for *Applications*
|
||||
that belong to it. The project is specified using the `.spec.project` field,
|
||||
which must contain the *name* of the project to associate the application to.
|
||||
|
||||
Argo CD ships a default project named `default`, which can be used if you
|
||||
haven't created other projects yet.
|
||||
|
||||
## Sync Policy
|
||||
|
||||
Each *Application* has a *Sync Policy* that defines how the *Application* should
|
||||
be synced to the target *Cluster*. This policy is set in the `.spec.syncPolicy`
|
||||
part of the *Application*.
|
||||
|
||||
Specifying a *Sync Policy* for an *Application* is *optional*. If no policy is
|
||||
configured, the default policy will be used.
|
||||
|
||||
You can read more about *Sync Policies* in the
|
||||
[Sync Policy documentation](../../syncing/policy.md).
|
||||
|
||||
## Implementation details
|
||||
|
||||
*Applications* are implemented as Kubernetes Custom Resources of kind
|
||||
`Application` in the `argoproj.io/v1alpha1` API and can be managed either using
|
||||
the Argo CD CLI, the web UI or the Kubernetes API.
|
||||
|
||||
!!! note "About the location of Application resources"
|
||||
*Application* resources live in the installation namespace in the cluster of
|
||||
your Argo CD installation, which is `argocd` by default. *Application* resources
|
||||
created in other namespaces or clusters will not be used up by Argo CD.
|
||||
72
docs/basics/apps/manage.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Managing Applications
|
||||
|
||||
You can manage *Applications* via the argocd CLI, the web UI or the Kubernetes
|
||||
API. For managing *Applications* using either Argo CD's CLI or UI,
|
||||
[RBAC permissions](../rbac.md).
|
||||
must be set-up for your user to allow manipulation of `applications` objects.
|
||||
|
||||
The default `admin` user already has appropriate permissions to manipulate all
|
||||
existing *Applications* in your Argo CD installation.
|
||||
|
||||
## Using the argocd CLI
|
||||
|
||||
### List existing applications
|
||||
|
||||
To list all applications that you have authorization for, use the
|
||||
`argocd app list` command. This will also give you the most important details
|
||||
about the applications:
|
||||
|
||||
```bash
|
||||
$ argocd app list
|
||||
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO PATH TARGET
|
||||
helm-guestbook https://kubernetes.default.svc helm-guestbook default Synced Healthy <none> <none> https://github.com/argoproj/argocd-example-apps helm-guestbook HEAD
|
||||
```
|
||||
|
||||
### Get details about an application
|
||||
|
||||
To get more detailed information about a specific *Application*, you can use the
|
||||
`argocd app get` command. This will also display the resources that are managed
|
||||
by the application:
|
||||
|
||||
```bash
|
||||
$ argocd app get helm-guestbook
|
||||
Name: helm-guestbook
|
||||
Project: default
|
||||
Server: https://kubernetes.default.svc
|
||||
Namespace: helm-guestbook
|
||||
URL: http://127.0.0.1:8088/applications/helm-guestbook
|
||||
Repo: https://github.com/argoproj/argocd-example-apps
|
||||
Target: HEAD
|
||||
Path: helm-guestbook
|
||||
SyncWindow: Sync Allowed
|
||||
Sync Policy: <none>
|
||||
Sync Status: Synced to HEAD (0d3eec0)
|
||||
Health Status: Healthy
|
||||
|
||||
GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
|
||||
Endpoints helm-guestbook helm-guestbook Succeeded PreSync helm-guestbook created
|
||||
Service helm-guestbook helm-guestbook Synced Healthy service/helm-guestbook unchanged
|
||||
apps Deployment helm-guestbook helm-guestbook Synced Healthy deployment.apps/helm-guestbook configured
|
||||
```
|
||||
|
||||
### Create new applications
|
||||
|
||||
To create a new *Application*, use the `argocd app create` command. The
|
||||
following example creates an application `kustomize-guestbook` from the Argo
|
||||
CD example applications repository, with a sync option to auto create the
|
||||
target namespace:
|
||||
|
||||
```bash
|
||||
$ argocd app create kustomize-guestbook \
|
||||
--project default \
|
||||
--repo https://github.com/argoproj/argocd-example-apps \
|
||||
--path kustomize-guestbook \
|
||||
--dest-namespace kustomize-guestbook \
|
||||
--dest-server https://kubernetes.default.svc \
|
||||
--sync-option CreateNamespace=true
|
||||
application 'kustomize-guestbook' created
|
||||
```
|
||||
|
||||
## Using the web UI
|
||||
|
||||
## Using the Kubernetes API
|
||||
@@ -1,8 +1,8 @@
|
||||
# Parameter Overrides
|
||||
# Override Parameters
|
||||
|
||||
Argo CD provides a mechanism to override the parameters of Argo CD applications that leverages config management
|
||||
tools. This provides flexibility in having most of the application manifests defined in Git, while leaving room
|
||||
for *some* parts of the k8s manifests determined dynamically, or outside of Git. It also serves as an alternative way of
|
||||
for *some* parts of the k8s manifests to be determined dynamically, or outside of Git. It also serves as an alternative way of
|
||||
redeploying an application by changing application parameters via Argo CD, instead of making the
|
||||
changes to the manifests in Git.
|
||||
|
||||
@@ -26,8 +26,56 @@ argocd app set guestbook -p ingress.enabled=true
|
||||
argocd app set guestbook -p ingress.hosts[0]=guestbook.myclusterurl
|
||||
```
|
||||
|
||||
The `argocd app set` [command](./commands/argocd_app_set.md) supports more tool-specific flags such as `--kustomize-image`, `--jsonnet-ext-var-str` etc
|
||||
flags. You can also specify overrides directly in the source field on application spec. Read more about supported options in corresponded tool [documentation](./application_sources.md).
|
||||
The `argocd app set` command supports more tool-specific flags such as `--kustomize-image`, `--jsonnet-ext-var-str` etc
|
||||
flags. You can also specify overrides directly in the source field on application spec.
|
||||
|
||||
## RBAC Policy for Overrides
|
||||
In order to make changes outside of the GitOps pattern, someone needs to have the authorization
|
||||
to do so. This will include the `override` RBAC action, at a minimum, including the ability to `get`
|
||||
the application, project and repository.
|
||||
|
||||
For example, you have a QA team that would like to modify values for many Applications and
|
||||
Projects during the testing phase and Sarah is assigned to the processing Application within the qa
|
||||
Project. Sarah will need to override the Helm parameters of the Application during their work so the
|
||||
RBAC will need to be configured correctly. They run `argocd account can-i override application
|
||||
'qa/processing'` and find out they do not have the permission.
|
||||
|
||||
1. If needed, create a Role, `maintainer-qa-processing`, and assign Sarah to it. Give the Role
|
||||
access to read the needed repository.
|
||||
|
||||
```yaml
|
||||
policy.csv: |
|
||||
p, role:maintainer-qa-processing, repositories, get, processing-repo, allow
|
||||
g, sarah@company.example, role:maintainer-qa-processing
|
||||
```
|
||||
|
||||
2. In the qa Project yaml, add a new role that would look like:
|
||||
|
||||
```yaml
|
||||
roles:
|
||||
- name: processing-maintainer
|
||||
description: Can override deployment variables
|
||||
policies:
|
||||
# Allow this group to override this specific Application
|
||||
- p, proj:qa:processing-maintainer, applications, override, qa/processing, allow
|
||||
groups:
|
||||
- maintainer-qa-processing
|
||||
```
|
||||
|
||||
or via the CLI
|
||||
|
||||
```bash
|
||||
argocd proj role create qa processing-maintainer
|
||||
argocd proj role add-policy qa processing-maintainer -a override -o qa/processing
|
||||
argocd proj role add-group qa processing-maintainer maintainer-qa-processing
|
||||
```
|
||||
|
||||
3. Test that the the changes you want to commit will work with the `argocd admin
|
||||
settings rbac can` command.
|
||||
|
||||
```bash
|
||||
argocd admin settings rbac can sarah@company.exampmle override 'qa/processing'
|
||||
```
|
||||
|
||||
## When To Use Overrides?
|
||||
|
||||
@@ -46,7 +94,7 @@ would result in the application being redeployed with the new image.
|
||||
Since commit access to the repository is unavailable, it is useful to be able to install charts from
|
||||
the public repository and customize the deployment with different parameters, without resorting to
|
||||
forking the repository to make the changes. For example, to install Redis from the Helm chart
|
||||
repository and customize the the database password, you would run:
|
||||
repository and customize the database password, you would run:
|
||||
|
||||
```bash
|
||||
argocd app create redis --repo https://github.com/helm/charts.git --path stable/redis --dest-server https://kubernetes.default.svc --dest-namespace default -p password=abc123
|
||||
@@ -54,14 +102,9 @@ argocd app create redis --repo https://github.com/helm/charts.git --path stable/
|
||||
|
||||
## Store Overrides In Git
|
||||
|
||||
> The following is available from v1.8 or later
|
||||
|
||||
The config management tool specific overrides can be specified in `.argocd-source.yaml` file stored in the source application
|
||||
directory in the Git repository.
|
||||
|
||||
!!! warn
|
||||
The `.argocd-source` is a beta feature and subject to change.
|
||||
|
||||
The `.argocd-source.yaml` file is used during manifest generation and overrides
|
||||
application source fields, such as `kustomize`, `helm` etc.
|
||||
|
||||
@@ -75,12 +118,10 @@ kustomize:
|
||||
|
||||
The `.argocd-source` is trying to solve two following main use cases:
|
||||
|
||||
- Provide the unifed way to "override" application parameters in Git and enable the "write back" feature
|
||||
- Provide the unified way to "override" application parameters in Git and enable the "write back" feature
|
||||
for projects like [argocd-image-updater](https://github.com/argoproj-labs/argocd-image-updater).
|
||||
- Support "discovering" applications in the Git repository by projects like [applicationset](https://github.com/argoproj-labs/applicationset)
|
||||
(see [git files generator](https://github.com/argoproj-labs/applicationset/blob/master/examples/git-files-discovery.yaml))
|
||||
|
||||
> The following is available from v1.9 or later
|
||||
- Support "discovering" applications in the Git repository by projects like [applicationset](https://github.com/argoproj/applicationset)
|
||||
(see [git files generator](https://github.com/argoproj/argo-cd/blob/master/applicationset/examples/git-generator-files-discovery/git-generator-files.yaml))
|
||||
|
||||
You can also store parameter overrides in an application specific file, if you
|
||||
are sourcing multiple applications from a single path in your repository.
|
||||
@@ -91,4 +132,4 @@ where `<appname>` is the name of the application the overrides are valid for.
|
||||
If there exists an non-application specific `.argocd-source.yaml`, parameters
|
||||
included in that file will be merged first, and then the application specific
|
||||
parameters are merged, which can also contain overrides to the parameters
|
||||
stored in the non-application specific file.
|
||||
stored in the non-application specific file.
|
||||
78
docs/basics/apps/source.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Application Source
|
||||
|
||||
An *Application Source* tells Argo CD where the manifests for the application
|
||||
resources reside, how to generate them and which version or revision of the
|
||||
source to use. The application source is defined in `.spec.source` part of the
|
||||
*Application* Custom Resource.
|
||||
|
||||
Argo CD currently supports two types of sources to track application resources
|
||||
from: Git and Helm repositories. Depending on the type of source you are
|
||||
configuring for the application, specific configuration has to be performed.
|
||||
|
||||
## Source Repository
|
||||
|
||||
The `.spec.source.repoURL` field defines the repository where the resource
|
||||
manifests are stored. It must be an URL that points to either a Git or a Helm
|
||||
repository. For more details about supported URLs, authentication, etc. please
|
||||
read the
|
||||
[repositories documentation](../repositories/).
|
||||
|
||||
### Manifests from a Git repository
|
||||
|
||||
If you source manifests from a Git repository, you can use all the generators
|
||||
that are supported by Argo CD to render the manifests before they are being
|
||||
reconciled into your cluster. For list of available generator tools, please
|
||||
have a look at the
|
||||
[tools documentation](../../tools/)
|
||||
|
||||
#### Path within repository
|
||||
|
||||
The `.spec.source.path` defines the path within the repository that contains
|
||||
your resources manifests. The path must be specified relative to the repository
|
||||
root. If you want to use the top-level directory in your repository, use the
|
||||
value `.` as path.
|
||||
|
||||
The corresponding parameter for `argocd app create` to specify the path is
|
||||
`--path`.
|
||||
|
||||
#### Target revision
|
||||
|
||||
The `.spec.source.targetRevision` defines the Git target revision to track.
|
||||
This can take various formats, please refer to the
|
||||
[Tracking strategies documentation](../../syncing/tracking.md)
|
||||
for more information.
|
||||
|
||||
The corresponding parameter for `argocd app create` to specify the target
|
||||
revision is `--targetRevision`.
|
||||
|
||||
### Manifests from a Helm repository
|
||||
|
||||
If you source your manifests from a Helm repository, only Helm can be used to
|
||||
render the manifests, obviously.
|
||||
|
||||
#### Specifying the name of the Chart
|
||||
|
||||
Instead of using `.spec.source.path`, you need to set `.spec.source.chart` to
|
||||
the name of the Helm chart to be used.
|
||||
|
||||
#### Specifying the version of the Chart
|
||||
|
||||
Also, `.spec.source.targetRevision` specifies the version of the Helm chart to
|
||||
use instead of a Git revision.
|
||||
|
||||
For more information, refer to the
|
||||
[Helm tooling documentation](../../tools/helm.md).
|
||||
|
||||
## Source specific configuration
|
||||
|
||||
Depending on the tool you use to render the manifests for a given *Application*,
|
||||
additional configuration can or must be given. These can be simple options to
|
||||
recursively consider all of the manifests found in directories below
|
||||
`.spec.source.path`, a directive to use a pinned version of a given tool or more
|
||||
complex settings, like
|
||||
[parameter overrides](../../tools/)
|
||||
|
||||
Please refer to the
|
||||
[tool specific documenation](../../tools/)
|
||||
for more information about the possible configuration options for each of the
|
||||
supported tools.
|
||||
66
docs/basics/apps/state.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Application state & health
|
||||
|
||||
## Sync Status
|
||||
|
||||
The *Sync Status* represents the current state of reconciliation between the
|
||||
*Source* and the *Destination*. The *Sync Status* can take one of the values:
|
||||
|
||||
* `SYNCED` - All resources of the *Application* are in the desired state on the
|
||||
destination. There is no deviation between the desired and the actual state.
|
||||
|
||||
* `OUT OF SYNC` - Argo CD has determined a deviation between the desired state
|
||||
and the actual state. When an *Application* transitions to this state, the
|
||||
[Automated Sync Policy](../../syncing/policy.md)
|
||||
(if enabled) will trigger a sync for the *Application*.
|
||||
|
||||
* `UNKNOWN` - Argo CD currently cannot determine the desired state from the
|
||||
*Application's* source or the actual state on the *Application's* destination.
|
||||
This state usually occurs when a non-transient error occurs while comparing
|
||||
actual and desired states. Argo CD will also let you know about the error.
|
||||
|
||||
Argo CD determines the *Sync Status* by performing a *diff* between the
|
||||
resources defined by the *Application Source* and the resources that actually
|
||||
exist in the *Application Destination*.
|
||||
|
||||
In some cases, resources on the target cluster get modified by other actors,
|
||||
such as an operator or a controller, after they have been reconciled into the
|
||||
target cluster. In such cases, the *Sync Status* would be constantly `OUT OF
|
||||
SYNC`.
|
||||
|
||||
The diffing behaviour can be changed to ignore such expected deviations, so that
|
||||
they won't affect the *Sync Status*. You can read more about this in the
|
||||
[Diffing Customization documentation](../../syncing/diffing.md)
|
||||
|
||||
## Application Health
|
||||
|
||||
The *Application Health* is an aggregate representation of the health of your
|
||||
*Application's* resources. Whereas the *Sync Status* determines whether all of
|
||||
the *Application's* resource manifests have been successfully reconciled into
|
||||
the target Kubernetes cluster, the *Application Health* is an indicator whether
|
||||
all of the resources also have been succesfully brought into a usable state by
|
||||
Kubernetes.
|
||||
|
||||
The *Application Health* can have one of the following states:
|
||||
|
||||
* `HEALTHY` - all of the *Application's* resources *Application* are considered
|
||||
healthy
|
||||
|
||||
* `PROGRESSING` - at least one of the *Application's* resources is still in the
|
||||
process of being brought to a healthy state
|
||||
|
||||
* `DEGRADED` - at least one of the *Application's* resources is marked as being
|
||||
in an erroneous state or is otherwise unhealthy.
|
||||
|
||||
* `UNKNOWN` - the health state of the *Application's* resources could not be
|
||||
determined. Argo CD will let you know about the reason for this.
|
||||
|
||||
* `MISSING` - the *Application's* resources are missing, and Argo CD cannot
|
||||
reliably determine the health status. This usually happens when *Application*
|
||||
has not been synced, or when there is an error with the cache.
|
||||
|
||||
* `SUSPENDED` - to be written
|
||||
|
||||
To illustrate this a little, imagine a `Service` resource in your cluster of
|
||||
type `LoadBalancer`.
|
||||
|
||||
## History
|
||||
0
docs/basics/clusters/external.md
Normal file
99
docs/basics/clusters/index.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Argo CD target clusters
|
||||
|
||||
By default an Argo CD instance can deploy applications to the [same cluster](internal/) it is installed on. Additionally, Argo CD has the capability to connect and deploy to [external clusters](external).
|
||||
|
||||
This means that there are several topologies for handling multiple clusters with Argo CD
|
||||
|
||||
1. Using one Argo CD instance per cluster
|
||||
1. Using a centralized Argo CD instance that handles all clusters
|
||||
1. A mixture of both strategies. For example you can have different groups of children clusters managed by multiple parent Argo CD instances
|
||||
|
||||
Let's see those options in turn:
|
||||
|
||||
## Installing Argo CD on each deployment cluster
|
||||
|
||||
This is arguably the simplest way to install Argo CD. Each deployment cluster gets its own instance and each Argo CD installation controls the same cluster it is installed on.
|
||||
|
||||

|
||||
|
||||
This approach has several advantages when it comes to maintenance and upgrades but also suffers from several issues when you have too many clusters.
|
||||
|
||||
Advantages
|
||||
|
||||
* Straightforward installation
|
||||
* Target clusters do not need network connectivity which each other and can even work fully isolated from each other
|
||||
* Clusters operate independently without reliance on an external instance of Argo CD
|
||||
* Security surface is limited to each individual cluster, (compromising one does not compromise the rest)
|
||||
* [Upgrades](../../operations/upgrading/) can happen in a gradual way by moving Argo CD instances to a new version one-by-one or in waves
|
||||
* Different plugins and configurations/policies can be set on each deployment cluster
|
||||
* Easy to split environments (QA/Staging/Production)
|
||||
|
||||
Disadvantages
|
||||
|
||||
* Difficult to manage in a uniform way
|
||||
* Can lead to many instances being out-of-date, introducing a new class of security problems
|
||||
* Poor visibility across organizations
|
||||
* Difficult to implement policy across many instances
|
||||
* More resources used for each cluster in order to run Argo CD itself
|
||||
* Multiple dashboards, users, SSO setups and possible RBAC setups are confusing for end users.
|
||||
|
||||
|
||||
## Using a central Argo CD installation
|
||||
|
||||
At the other end of the spectrum we can have a centralized Argo CD installation which controls and manages all deployment clusters. The cluster that hosts Argo CD can be dedicated to Argo CD only or it can also be used for deploying applications as well.
|
||||
|
||||

|
||||
|
||||
Advantages
|
||||
|
||||
* Easy to maintain and upgrade
|
||||
* Centralized control (including SSO and RBAC)
|
||||
* Better visibility across organization
|
||||
* Single dashboard for all clusters
|
||||
|
||||
Disadvantages
|
||||
|
||||
* Single point of failure
|
||||
* Target cluster API’s must be accessible to the central instance
|
||||
* Single attack surface
|
||||
* RBAC and SSO might not match organizational requirements
|
||||
* Impossible to upgrade different clusters in waves
|
||||
* No separation between QA/Staging/Prod installations
|
||||
* Argo CD performance may degrade with many applications and clusters as they scale - see [High Availability](../../operator-manual/high_availability/) for more information.
|
||||
* In theory requires less resources per each deployment cluster (as it only runs apps and not Argo CD itself)
|
||||
|
||||
While a single Argo CD instance might be tempting (especially for big organizations) it is important to understand the tradeoffs regarding security and performance if you go down that route. Also note the networking requirements.
|
||||
|
||||
## Hybrid approaches
|
||||
|
||||
An alternative approach (and most often the recommended one) is a hybrid architecture that either combines Argo CD instances that control other clusters or mixes internal and external clusters in the different Argo CD installations.
|
||||
|
||||
!!! information
|
||||
It is important to remember that Argo CD can only manage other [external clusters](external). It is **NOT** possible to manage other Argo CD instances or have a parent-child relationship between different Argo CD installations.
|
||||
|
||||
A hybrid approach combines the best of both worlds by allowing you to mitigate the risks of each one according to your own requirements.
|
||||
|
||||
Example 1 - Different Argo CD instances per environment
|
||||
|
||||

|
||||
|
||||
In this example there are 3 management Argo CD instances. This allows the Production instance to have different constraints than the non-production instances while also making upgrades of Argo CD itself more granular (Production instance can be updated at the end after QA and staging).
|
||||
|
||||
Example 2 - Mix of Argo CD instances per region and environment
|
||||
|
||||

|
||||
|
||||
In this example there are different Argo CD instances per region that manage other clusters, while each team also gets its own Argo CD instance. This allows developer teams to have their own config for their Argo CD instance and there is also no fear of affecting other teams during upgrades.
|
||||
|
||||
## Which strategy to choose
|
||||
|
||||
There is no right or wrong Argo CD topology. Each organization has different needs. It is also possible to start with one topology and change to another as the organization needs are changing.
|
||||
|
||||
As a starting point we suggest we have at least 2 Argo CD categories (which might be individual instances or multiple installations)
|
||||
|
||||
* Argo CD instance(s) for production systems
|
||||
* Argo CD instance(s) for NON production systems
|
||||
|
||||
This would help you during [upgrades](../../operations/upgrading/) as you can test a new version of Argo CD itself without affecting production.
|
||||
|
||||
|
||||
0
docs/basics/clusters/internal.md
Normal file
117
docs/basics/clusters/manage.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Managing Clusters
|
||||
|
||||
## Using the `argocd` CLI
|
||||
|
||||
### Listing all clusters
|
||||
|
||||
To list all clusters, run `argocd cluster list`. You should see a list of output with details about the clusters.
|
||||
|
||||
```bash
|
||||
$ argocd cluster list
|
||||
SERVER NAME VERSION STATUS MESSAGE
|
||||
https://kubernetes.default.svc in-cluster 1.16+ Successful
|
||||
```
|
||||
|
||||
### Get details about a cluster
|
||||
|
||||
To get more detailed information about a specific cluster, you can use `argocd cluster get <server>`, where `server` is the server URL of the cluster.
|
||||
|
||||
```bash
|
||||
$ argocd cluster get https://kubernetes.default.svc
|
||||
config:
|
||||
tlsClientConfig:
|
||||
insecure: false
|
||||
connectionState:
|
||||
attemptedAt: "2021-03-19T16:42:09Z"
|
||||
message: ""
|
||||
status: Successful
|
||||
info:
|
||||
applicationsCount: 6
|
||||
cacheInfo:
|
||||
apisCount: 54
|
||||
lastCacheSyncTime: "2021-03-19T16:39:36Z"
|
||||
resourcesCount: 504
|
||||
connectionState:
|
||||
attemptedAt: "2021-03-19T16:42:09Z"
|
||||
message: ""
|
||||
status: Successful
|
||||
serverVersion: 1.16+
|
||||
name: in-cluster
|
||||
server: https://kubernetes.default.svc
|
||||
serverVersion: 1.16+
|
||||
```
|
||||
|
||||
### Add a cluster
|
||||
|
||||
To add a cluster using the CLI:
|
||||
|
||||
1. Ensure you have a valid context in your kubeconfig for the cluster. Running `argocd cluster add` will list all available contexts.
|
||||
2. Add the cluster with `argocd cluster add <context name>`.
|
||||
|
||||
Adding a cluster with `argocd cluster add` installs a ServiceAccount named `argocd-manager` into the `kube-system` namespace of that context and binds the service account to an admin-level ClusterRole. Argo CD uses this service account token to perform its management tasks (i.e. deploy/monitoring).
|
||||
|
||||
!!! Tip
|
||||
To register the `in-cluster` cluster as a cluster with its own secret, run
|
||||
`argocd cluster add <cluster name> --in-cluster`
|
||||
|
||||
### Removing a cluster
|
||||
|
||||
To remove a cluster using the CLI:
|
||||
|
||||
1. Identify the server URL for the cluster. Running `argocd cluster list` will show a list of all clusters with their name and server.
|
||||
2. Remove the cluster with `argocd cluster rm <server>`
|
||||
|
||||
!!! Note
|
||||
Removing a cluster will not remove the Applications associated with that cluster
|
||||
|
||||
## Using the web UI
|
||||
|
||||
### Get details about a cluster
|
||||
|
||||
To view details about a cluster in the web UI, first go to `/settings/clusters/` in your Argo CD instance in your browser, then click on the row for a cluster. You can also click on the "edit" button in the top right corner to edit the cluster name or allowed namespaces.
|
||||
|
||||
### Removing a cluster
|
||||
|
||||
To remove a cluster using the web UI, first go to `/settings/clusters` in your Argo CD instance in your browser
|
||||
Then, find the cluster and click on the three dots on the right hand side of the cluster row, then click "delete".
|
||||
|
||||

|
||||
|
||||
## Using the kubernetes API
|
||||
|
||||
Clusters are stored as kubernetes secrets, so it is possible (but not typically recommended) to manipulate them using the kubernetes API.
|
||||
|
||||
### Listing all clusters
|
||||
|
||||
To view all cluster secrets, you can run `kubectl get secret -l argocd.argoproj.io/secret-type="cluster"` in the namespace for your Argo CD instance. The secrets should be in the format `cluster-<server url>-<hash>`, and have keys for `config`, `name`, `server`, and (optionally) `shard`.
|
||||
|
||||
```bash
|
||||
$ kubectl get secret -l argocd.argoproj.io/secret-type="cluster" -n argocd
|
||||
NAME TYPE DATA AGE
|
||||
cluster-kubernetes.default.svc-3396314289 Opaque 3 3s
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl get secret -n argocd cluster-kubernetes.default.svc-3396314289 -o yaml
|
||||
apiVersion: v1
|
||||
data:
|
||||
config: ****
|
||||
name: ZG9ja2VyLWRlc2t0b3A=
|
||||
server: aHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3Zj
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
managed-by: argocd.argoproj.io
|
||||
creationTimestamp: "2021-03-19T16:59:50Z"
|
||||
labels:
|
||||
argocd.argoproj.io/secret-type: cluster
|
||||
name: cluster-kubernetes.default.svc-3396314289
|
||||
namespace: argocd
|
||||
resourceVersion: "8980461"
|
||||
selfLink: /api/v1/namespaces/argocd/secrets/cluster-kubernetes.default.svc-3396314289
|
||||
uid: 19b453ce-93e7-41f0-b59d-0c4e3b51f3a0
|
||||
```
|
||||
|
||||
### Removing a cluster
|
||||
|
||||
To remove a cluster, identify the name of the cluster secret and run `kubectl delete secret <cluster secret name>`.
|
||||
35
docs/basics/projects/index.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Projects
|
||||
|
||||
## Overview
|
||||
|
||||
The so-called *Projects* (or, *AppProject* alternatively) play a vital role in
|
||||
the multi-tenancy and governance model of Argo CD. It is important to understand
|
||||
how *Projects* work and how they impact *Applications* and permissions.
|
||||
|
||||
You can think of a *Project* as a way to group specific *Applications* together
|
||||
to enforce a common set of governance rules and settings on those Applications,
|
||||
with the settings being defined in the *Project*. For example, you can restrict
|
||||
the kind of resources allowed in an *Application*, or restrict the *Application*
|
||||
to source its manifests only from a certain repository, etc etc. Furthermore,
|
||||
projects can issue *access tokens* scoped to applications within the given
|
||||
project. These tokens can be used to access the Argo CD API for manipulation
|
||||
of *Applications* associated with the project, and their permissions can be
|
||||
configured using *Project* specific RBAC configuration.
|
||||
|
||||
*Projects* and Applications have a *1:n* relationship, that is, multiple
|
||||
*Applications* can belong to the same *Project*, while each *Application* can
|
||||
only belong to one *Project*. Furthermore, the association of an *Application*
|
||||
to a *Project* is mandatory. It is not possible to have an *Application* that
|
||||
is not associated to a *Project*.
|
||||
|
||||
An Argo CD *Project* is implemented as a Custom Resource `AppProject` in the
|
||||
`argoproj.io/v1alpha1` API.
|
||||
|
||||
All `AppProject` resources must exist in Argo CD's installation namespace
|
||||
(`argocd` by default) in the cluster Argo CD is installed to in order to be
|
||||
used by Argo CD. They cannot be installed in other clusters or namespaces.
|
||||
|
||||
!!! tip "The default project"
|
||||
Argo CD installs a default *Project* which permits everything and restricts
|
||||
nothing. The default *Project* is called, well, `default`.
|
||||
|
||||
0
docs/basics/projects/manage.md
Normal file
7
docs/basics/projects/roles.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Project roles
|
||||
|
||||
## Overview
|
||||
|
||||
## Access tokens
|
||||
|
||||
## Project specific RBAC rules
|
||||
135
docs/basics/projects/settings.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Project level settings
|
||||
|
||||
A project can enforce restrictions on the following entities:
|
||||
|
||||
## Destinations
|
||||
|
||||
A *Project* can define allowed *destinations* for any of the *Applications*
|
||||
associated with the *Project*. A *Project's* destination restriction is a
|
||||
tuple of a target cluster and a namespace, with wildcard pattern matching
|
||||
supported.
|
||||
|
||||
|Cluster|Namespace|Outcome|
|
||||
|-|-|-|
|
||||
|*|*|Allow all namespaces in all clusters|
|
||||
|https://kubernetes.default.svc|*|Allow all namespaces in the local cluster (`kubernetes.default.svc`)|
|
||||
|https://prod-*|*|Allow all namespaces in target clusters with URL starting with `https://prod-*`|
|
||||
|*|dev-apps-*|Allow namespaces starting with `dev-apps-*` in all target clusters|
|
||||
|
||||
When an *Application's* destination or one of the *Application's* resources
|
||||
with a hardcoded target namespace do not match an allowed destination of the
|
||||
*Project*, any sync operation will not be allowed.
|
||||
|
||||
## Sources
|
||||
|
||||
A *Project* can define allowed *sources* for any of the *Applications*
|
||||
associated with the *Project*. A *Project's* allowed sources is a list of one
|
||||
or more URL patterns that must match an *Application's* source repository.
|
||||
|
||||
The corresponding CLI commands for adding or removing constraints on
|
||||
project sources are:
|
||||
|
||||
* `argocd proj add-source` - adds a source
|
||||
* `argocd proj remove-source` - removes a source
|
||||
|
||||
## Cluster resources
|
||||
|
||||
A *Project* must define what kind of cluster-scoped resources *Applications* are
|
||||
allowed to deploy. If an *Application's* resources contain any cluster-scoped
|
||||
resources not allowed by the *Project*, any sync operation will not be allowed.
|
||||
|
||||
Allowance of cluster-scoped resources is evaluated from two lists:
|
||||
|
||||
* A positive-list to allow specific resources
|
||||
* A negative-list to deny specific resources
|
||||
|
||||
In order to decide if a resource is allowed, it is first matched against the
|
||||
positive list. If it matches the positive-list, and is not found in the
|
||||
negative-list, the resource is allowed. If it doesn't match the positive-list,
|
||||
or is matched in the negative-list, the resource - and therefore the sync
|
||||
operation - is not allowed.
|
||||
|
||||
Each list is using tuples of Kubernetes' API `Group` and `Kind` to match the
|
||||
resources of the *Application* against. Wildcard patterns are supported. Each
|
||||
resource **must** match against the positive-list, and **must not** match
|
||||
against the negative-list.
|
||||
|
||||
The following table shows matching for a cluster-wide resource of Group/Kind
|
||||
`rbac.authorization.k8s.io/ClusterRole` (the dash `-` means, not configured)
|
||||
|
||||
|Positive Group| Positive Kind|Negative Group|Negative Kind|Allowed|
|
||||
|-|-|-|-|-|
|
||||
|`*`|`*`|-|-|Yes|
|
||||
|`*`|`*`|`*`|`*`|No|
|
||||
|`rbac*`|`*`|-|-|Yes|
|
||||
|`*`|`*`|`rbac.authorization.k8s.io`|`ClusterRoleBinding`|Yes|
|
||||
|
||||
A newly created *Project* without further configuration will forbid all
|
||||
cluster-scoped resources to be managed. The `default` *Project* allows all
|
||||
cluster-scoped resources to be managed.
|
||||
|
||||
The corresponding CLI commands for adding or removing constraints on
|
||||
cluster-scoped resources are:
|
||||
|
||||
* `argocd proj allow-cluster-resource` - adds a cluster-scoped resource to the
|
||||
positive-list
|
||||
* `argocd proj deny-cluster-resource` - adds a cluster-scoped resource to the
|
||||
negative-list
|
||||
|
||||
## Namespaced resources
|
||||
|
||||
A *Project* must define what kind of namespace-scoped resources *Applications*
|
||||
are allowed to deploy. If an *Application's* resources contain any
|
||||
namespace-scoped resources not allowed by the *Project*, any sync operation will
|
||||
not be allowed.
|
||||
|
||||
The decision tree for whether allowing a namespaced resource for deployment is
|
||||
the same as for
|
||||
[cluster scoped resources](#cluster-resources).
|
||||
|
||||
A newly created *Project* without further configuration will forbid all
|
||||
namespaced-scoped resources to be managed. The `default` *Project* allows all
|
||||
namespaced-scoped resources to be managed.
|
||||
|
||||
The corresponding CLI commands for adding or removing constraints on namespaced
|
||||
resources are:
|
||||
|
||||
* `argocd proj allow-namespace-resource` - adds a namespace-scoped resource to the
|
||||
positive-list
|
||||
* `argocd proj deny-namespace-resource` - adds a namespace-scoped resource to the
|
||||
negative-list
|
||||
|
||||
!!! tip "Resources in the core API group"
|
||||
If you need to add resources from the *Core* API group, i.e. *Secret* or
|
||||
*ConfigMap* resources, use the empty string `''` as API group.
|
||||
|
||||
## GnuPG keys used for signature verification
|
||||
|
||||
An advanced feature of Argo CD is to only allow syncs from Git revisions that
|
||||
are signed using GnuPG (e.g. commited using `git commit -S`). You can read more
|
||||
about this feature in its
|
||||
[documentation](/advanced/gnupg.md).
|
||||
|
||||
You can configure the GnuPG key IDs that commits need to be signed by for all
|
||||
applications belonging to a certain project. Once at least one key ID is added,
|
||||
signature verification will be enforced and any sync operation to a non-signed
|
||||
revision, or a revision that is signed with a GnuPG key not in the allow-list
|
||||
will be denied.
|
||||
|
||||
The corresponding CLI commands for adding and removing GnuPG key IDs are:
|
||||
|
||||
* `argocd proj add-signature-key`
|
||||
* `argocd proj remove-signature-key`
|
||||
|
||||
By default, GnuPG commit verification is disabled.
|
||||
|
||||
## Sync windows
|
||||
|
||||
A *Project* can define time windows that determine when an *Application* is
|
||||
allowed to be synced to a cluster. You can read more about this feature in the
|
||||
[Sync Windows documentation](/advanced/sync_windows.md).
|
||||
|
||||
By default, a *Project* does not restrict syncing to any time windows and the
|
||||
sync is allowed at all times.
|
||||
|
||||
To manage sync windows, you can use the `argocd proj windows` command.
|
||||
0
docs/basics/rbac.md
Normal file
77
docs/basics/repos/auth.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Repository authentication
|
||||
|
||||
## Available authentication methods
|
||||
|
||||
If your repository needs authentication to be accessed, the following methods
|
||||
are currently supported:
|
||||
|
||||
||Basic Auth|TLS client certs|SSH private keys|
|
||||
|-|-|-|-|
|
||||
|Git via https|X|v1.3+|-|
|
||||
|Git via ssh|-|-|X|
|
||||
|Helm via https|v1.3+|v1.3+|-|
|
||||
|
||||
Other authentication methods, such as AWS IAM or Google ServiceAccounts, are
|
||||
not (yet) supported by ArgoCD.
|
||||
|
||||
!!! note "Optional vs mandatory authentication"
|
||||
Authentication is optional for Git and Helm repositories connected using the
|
||||
HTTPS protocol. For Git repositories connected using SSH, authentication is
|
||||
mandatory and you need to supply a private key for these connections.
|
||||
|
||||
## Personal Access Token (PAT)
|
||||
|
||||
Some Git providers require you to use a personal access token (PAT) instead of
|
||||
username/password combination when accessing the repositories hosted there
|
||||
via HTTPS.
|
||||
|
||||
Providers known to enforce the use of PATs are:
|
||||
|
||||
* GitHub
|
||||
* GitLab
|
||||
* BitBucket
|
||||
|
||||
You can specify the PAT simply as the password (see below) when connecting
|
||||
the custom repository to ArgoCD, using any or the empty string as the username.
|
||||
The value for the username (any, empty or your actual username) varies from
|
||||
provider to provider.
|
||||
|
||||
## Credential templates
|
||||
|
||||
Credential templates are a convinient method for accessing multiple repositories
|
||||
with the same set of credentials, so you don't have to configure (and possibly
|
||||
change regulary) credentials for every repository that you might want to access
|
||||
from ArgoCD. Instead, you set up the credentials once using the template and all
|
||||
repositories whose URL matches the templated one will re-use these credentials,
|
||||
as long as they don't have credentials set up specifically.
|
||||
|
||||
For example, you have a bunch of private repositories in the GitHub organisation
|
||||
`yourorg`, all accessible using the same SSH key, you can set up a credential
|
||||
template for accessing the repositories via SSH like follows:
|
||||
|
||||
```bash
|
||||
argocd repocreds add git@github.com:yourorg/ --ssh-private-key-path yourorg.key
|
||||
```
|
||||
|
||||
Since the URL here is a pattern, no validation of the credentials supplied will
|
||||
be performed at all during creation of the template.
|
||||
|
||||
### Matching templates against repository URLs
|
||||
|
||||
Pattern matching will be done on a *best match* basis, so you can have more than
|
||||
one matching pattern for any given URL. The pattern that matches best (i.e. is
|
||||
the more specific) will win.
|
||||
|
||||
Consider you have templates for the following two patterns:
|
||||
|
||||
* `https://github.com/yourorg`
|
||||
|
||||
* `https://github.com/yourorg/special-`
|
||||
|
||||
Now, for the repository `https://github.com/yourorg/yourrepo`, the first pattern
|
||||
would match while for the repository `https://github.com/yourorg/special-repo`
|
||||
both pattern will match, but the second one will win because it is more specific.
|
||||
|
||||
The syntax for the `argocd repocreds` command is similar to that of the
|
||||
`argocd repo` command, however it does not support any repository specific
|
||||
configuration such as LFS support.
|
||||
126
docs/basics/repos/index.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Repositories
|
||||
|
||||
## Introduction
|
||||
|
||||
Since ArgoCD is a GitOps centric tool, the repositories containing your
|
||||
application manifest(s) play a very vital role in the configuration of
|
||||
ArgoCD.
|
||||
|
||||
ArgoCD supports pulling the manifests from two distinct types of repositories:
|
||||
|
||||
* Git repositories, such as GitHub, GitLab or privately hosted ones
|
||||
* Helm repositories, such as Helm's stable charts, Harbor or Chart museum
|
||||
|
||||
Git repositories can hold any kind of manifests or sources that ArgoCD
|
||||
supports. You can even store Helm charts in your Git repositories. Git
|
||||
repositories can be connected using either HTTPS or SSH protocols.
|
||||
|
||||
Helm repositories, however, can only hold Helm charts by definition. Helm
|
||||
repositories can only be connected using HTTPS.
|
||||
|
||||
!!! note
|
||||
Each application defined in ArgoCD is mapped to exactly one repository.
|
||||
It is not possible to map two or more repositories to a single
|
||||
application. If you need resources from more than one repository to define
|
||||
your application, you can look at the advanced repository topics below.
|
||||
|
||||
## Unconfigured vs. Configured repositories
|
||||
|
||||
ArgoCD differentiates between *unconfigured* and *configured* repositories.
|
||||
Unconfigured repositories are those that you can access without any further
|
||||
configuration, while a configured repository is required when you need to
|
||||
authenticate to the repository (and don't use credential templates as
|
||||
described below), or when you need additional custom settings.
|
||||
|
||||
Configured repositories were previously known as *private* repositories, but
|
||||
have now evolved to be named *configured* repositories - because they don't
|
||||
necessarily need to be private.
|
||||
|
||||
You don't have to configure a repository in ArgoCD in order to use it as a
|
||||
manifest source for your application - you can simply specify the URL of the
|
||||
repository when creating an application, as long as the repository is allowed
|
||||
as a source in the
|
||||
[project's configuration](projects/#sources) and is publicly accesible or matches one of
|
||||
the configured credential templates. Using an unconfigured repository as source
|
||||
for your application is as simple as specifying its URL using the `--repo`
|
||||
parameter to the `argocd app create` command.
|
||||
|
||||
!!! note
|
||||
Only Git repositories accessed using HTTPS are currently supported to be
|
||||
connected without further configuration. Git repositories connected using
|
||||
SSH must always be configured in ArgoCD as a repository or have a matching
|
||||
credential template. Helm repositories must always have an explicit
|
||||
configuration before they can be used.
|
||||
|
||||
Using a repository that requires further configuration as the source for an
|
||||
Application requires the repository to be configured, or *connected* first.
|
||||
For further information on how to connect a repository, please see below.
|
||||
|
||||
It is suggested that each repository that you will use as an
|
||||
application's source is configured in ArgoCD first.
|
||||
|
||||
## Specifying repository URLs
|
||||
|
||||
Repository URLs should always be specified in a fully-qualified manner, that
|
||||
is they should contain the protocol modifier (i.e. `https://` or `ssh://`) as
|
||||
a prefix. Specifying custom ports for the connection to the repository server
|
||||
is possible using the `:port` modifier in the `hostname` portion of the URL.
|
||||
If a port is not specified, the default ports for the requested protocol
|
||||
will be used:
|
||||
|
||||
* Port 443 for HTTPS connections
|
||||
* Port 22 for SSH connections
|
||||
|
||||
Generally, URLs for repositories take the following form
|
||||
|
||||
```bash
|
||||
protocol://[username@]hostname[:port]/path/to/repo
|
||||
```
|
||||
|
||||
The `username` URL modifier is only valid (and mandatory!) for connecting Git
|
||||
repositories using SSH. Likewise, the `--username` parameter for the appropriate
|
||||
CLI commands is only valid for connecting Git or Helm repositories via HTTPS.
|
||||
|
||||
!!! note "Usernames for SSH repositories"
|
||||
When using SSH to connect to the repository, you *must* specify the remote
|
||||
username in the URL, i.e. using `ssh://user@example.com/your/repo`. Most
|
||||
Git providers use `git` as remote username, further information should be
|
||||
taken from the provider's documentation.
|
||||
|
||||
There is an exception when specifying repository URLs for repositories that
|
||||
are to be connected using SSH. These URLs can also be of `scp` style syntax
|
||||
in the following form:
|
||||
|
||||
```bash
|
||||
username@hostname:path/to/repo
|
||||
```
|
||||
|
||||
!!! warning "Remote port in SSH URLs"
|
||||
Please note that with the `scp` style syntax, it is not possible to specify
|
||||
a custom SSH server port in the URL, because the colon denominates the
|
||||
beginning of the path, and the path will be relative to the SSH server's
|
||||
working directory. If you need to connect via SSH to a non-standard port,
|
||||
you **must** use `ssh://` style URLs to specify the repository to use.
|
||||
|
||||
The following are some examples for valid repository URLs
|
||||
|
||||
* `https://example.com/yourorg/repo` - specifies repository `/yourorg/repo` on
|
||||
remote server `example.com`, connected via HTTPS on standard port.
|
||||
* `https://example.com:9443/yourorg/repo` - specifies repository `/yourorg/repo`
|
||||
on remote server `example.com`, connected via HTTPS on non-standard port
|
||||
`9443`.
|
||||
* `ssh://git@example.com/yourorg/repo` - specifies repository `/yourorg/repo`
|
||||
on remote server `example.com`, connected via SSH on standard port and using
|
||||
the remote username `git`.
|
||||
* `git@example.com:yourorg/repo` - same as above, but denoted using an `scp`
|
||||
URL.
|
||||
* `ssh://git@example.com:2222/yourorg/repo` - specifies repository
|
||||
`/yourorg/repo` on remote server `example.com`, connected via SSH on the
|
||||
non-standard port `2222` and using `git` as the remote username.
|
||||
|
||||
A common pitfall is the following `scp` style URL:
|
||||
|
||||
* `git@example.com:2222/yourorg/repo` - This would **not** specify a repository
|
||||
`/yourorg/repo` on remote server `example.com` with a non-standard port of
|
||||
`2222`, but rather the repository `2222/yourorg/repo` on the remote server
|
||||
`example.com` with the default SSH port `22`.
|
||||
221
docs/basics/repos/manage.md
Normal file
@@ -0,0 +1,221 @@
|
||||
# Managing configured repositories
|
||||
|
||||
## Overview
|
||||
|
||||
You can manage configured repositories for use with ArgoCD in three ways:
|
||||
|
||||
* Using the CLI's `repo` sub-command
|
||||
* Using the web UI repository configuration, to be found at the `Repositories`
|
||||
module in the `Settings` sections
|
||||
* Using declarative setup. For further information, please refer to the
|
||||
appropriate chapter in the
|
||||
[Operator Manual]().
|
||||
|
||||
With each of the methods above, you can add, edit and remove custom repositories
|
||||
and their configuration.
|
||||
|
||||
## Using the CLI
|
||||
|
||||
### Listing all configured repositories
|
||||
|
||||
You can list all currently configured repositories using the CLI:
|
||||
|
||||
```shell
|
||||
argocd repo list
|
||||
```
|
||||
|
||||
If you prefer to use the web UI, you find the list of configured repositories
|
||||
at the `Settings` -> `Repositories` page.
|
||||
|
||||
### Adding a repository configuration
|
||||
|
||||
Connecting a repository via HTTPS (TLS) is supported for both repository
|
||||
types, `git` and `helm`. The URL for a Git repository connected using HTTPS
|
||||
must be fully-qualified and prefixed with the protocol, i.e. `https://`. The
|
||||
URL may have an optional port modifier if the repository is served from a non
|
||||
default port, i.e. `https://example.com:9443`.
|
||||
|
||||
!!! note "A few words on HTTP redirects"
|
||||
ArgoCD does not follow HTTP redirects when handling repositories. Some Git
|
||||
providers, notably GitLab and possibly also self-hosted GitLab, will send
|
||||
you a HTTP redirect if your repository URL is not suffixed with `.git`. If
|
||||
you receive a HTTP redirect on connecting the repository, try appending
|
||||
the `.git` suffix to your URL. For example, if you use the URL
|
||||
`https://gitlab.com/you/repo` and GitLab sends you a HTTP 301, try to use
|
||||
`https://gitlab.com/you/repo.git` as the URL to your repository.
|
||||
|
||||
To add a configuration for a Git repository to be connected using HTTPS, you
|
||||
can use the `argocd repo add` command, specifying a repository URL starting
|
||||
with `https://`.
|
||||
|
||||
In its most simple form, the command
|
||||
|
||||
```bash
|
||||
argocd repo add https://example.com/your/repo
|
||||
```
|
||||
|
||||
will add the Git repository at `https://example.com/your/repo` to the ArgoCD
|
||||
configuration. This simple form however is not different from using an
|
||||
unconfigured repository, except it will give you the perks from selecting
|
||||
the repository as an application's source in the UI from a dropdown list.
|
||||
|
||||
You can add custom configuration for the repository by using the following set
|
||||
of command line switches to the `repo add` command:
|
||||
|
||||
|Switch|Argument|Description|
|
||||
|-|-|-|
|
||||
|`--insecure-skip-server-verification`|None|Disables verification of the server's TLS certificate or SSH known host signature, depending on the connection method. You do not want use this switch for production environments.|
|
||||
|`--username`|`username`|Use `username` for authenticating at the server (only valid for HTTPS repositories and in combination with `--password`)|
|
||||
|`--password`|`password`|Use `password` for authenticating at the server (only valid for HTTPS repositories and in combination with `--username`)|
|
||||
|`--ssh-private-key-path`|`path`|Use SSH private key from `path` to authenticate at the remote repository. Only valid and also mandatory for SSH repositories. The private key will be stored in a secret on the cluster ArgoCD runs on.|
|
||||
|`--type`|`type`|Specify that repository is of type `repotype`. Current possible values are `helm` and `git` (defaults to `git`)|
|
||||
|`--name`|`name`|Specify the name of the repository to be `name`. This is mandatory when adding Helm repositories and optional when adding Git repositories.|
|
||||
|`--tls-client-cert-path`|`path`|Specifies to read the TLS client certificate used for authentication from `path` on the local machine. The certificate will be stored in a secret on the cluster ArgoCD is running on.|
|
||||
|`--tls-client-cert-key-path`|`path`|Specifies to read the key for TLS client certificate used for authentication from `path` on the local machine. The key will be stored in a secret on the cluster ArgoCD is running on.|
|
||||
|`--enable-lfs`|None|Enables the support for Git Large File Storage (LFS) on the repository. Only valid for Git repositories.|
|
||||
|
||||
### Examples: Adding repositories via CLI
|
||||
|
||||
The following command adds a Git repository from `https://github.com/foo/repo`,
|
||||
using `foo` as the username and `bar` as the password for authentication:
|
||||
|
||||
```bash
|
||||
argocd repo add --username foo --password bar https://github.com/foo/repo
|
||||
```
|
||||
|
||||
The following command uses a TLS client certificate in addition to the
|
||||
username/password combination to connect the repository. The cert is read
|
||||
from `~/mycert.crt`, the corresponding key from `~/mycert.key`:
|
||||
|
||||
```bash
|
||||
argocd repo add --username foo --password \
|
||||
--tls-client-cert-path ~/mycert.key \
|
||||
--tls-client-cert-key-path ~/mykey.key \
|
||||
https://secure.example.com/repos/myrepo
|
||||
```
|
||||
|
||||
The following command adds the repository without any authentication, but will
|
||||
ignore the TLS certificate presented by the server. Needless to say, this should
|
||||
only be used for testing purposes in non-prod environments. Instead of using
|
||||
this insecure option, you should consider adding the appropriate TLS certificate
|
||||
or CA certificate to ArgoCD so it will be able to correctly verify the server's
|
||||
certificate:
|
||||
|
||||
```bash
|
||||
argocd repo add --insecure-skip-server-verification \
|
||||
https://self-hosted.example.com/repos/myrepo
|
||||
```
|
||||
|
||||
Finally, the following command adds a repository using the SSH protocol, the
|
||||
private SSH key from your local path `~/.ssh/id_rsa` for authentication and
|
||||
`git` as the remote username:
|
||||
|
||||
```bash
|
||||
argocd repo add --ssh-private-key-path ~/.ssh/id_rsa \
|
||||
ssh://git@example.com/yourorg/repo
|
||||
```
|
||||
|
||||
## Using the web UI
|
||||
|
||||
Repositories can also be configured using the web UI. The configuration module
|
||||
can be found by clicking on `Settings` and then `Repositories`.
|
||||
|
||||
You first need to chose what type of connection your repository should use, and
|
||||
then click on the appropriate button:
|
||||
|
||||

|
||||
|
||||
The following will walk you through the dialogues for connecting the repository,
|
||||
depending on which method you chose:
|
||||
|
||||
**SSH:**
|
||||
|
||||

|
||||
|
||||
1. The name of the repository. This is optional for Git repositories.
|
||||
|
||||
1. The URL to the repository. This must be either a `ssh://` or `scp` style
|
||||
URL (see discussions about URLs above)
|
||||
|
||||
1. Paste the SSH private key to use. This must be a valid SSH private key,
|
||||
including the start and end denominators.
|
||||
|
||||
1. If you want to skip the server's SSH host key signature verification, tick
|
||||
this box. You should **not** use this in production environments.
|
||||
|
||||
1. If you require Git LFS, tick this box.
|
||||
|
||||
1. Click on "Connect" to connect the repository to ArgoCD.
|
||||
|
||||
!!! note "Note about SSH private keys"
|
||||
You should make sure that the SSH private key you are pasting does not
|
||||
contain any unintentional line breaks. If using a terminal, you should
|
||||
use `cat ~/yourkey`, mark everything including the
|
||||
`-----BEGIN OPENSSH PRIVATE KEY-----` and
|
||||
`-----END OPENSSH PRIVATE KEY-----` markers, copy the selection to your
|
||||
clipboard and paste it into the UI's field.
|
||||
|
||||
**HTTPS:**
|
||||
|
||||

|
||||
|
||||
1. The type of the repository. This can either be `git` or `helm`. Please note
|
||||
that when `helm` is selected, another input field for `Repository name` will
|
||||
appear, which you need to fill out as well.
|
||||
|
||||
1. The URL to the repository. This must be a `https://` URL.
|
||||
|
||||
1. The username to use for authenticating at the repository (optional)
|
||||
|
||||
1. The password to use for authenticating at the repository (optional)
|
||||
|
||||
1. An optional TLS client certificate to use for authentication. This should
|
||||
be a paste of the full Base64-encoded TLS certificate, including the
|
||||
`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` markers.
|
||||
The certificate will be stored in a secret on the cluster ArgoCD is running
|
||||
on.
|
||||
|
||||
1. If you have specified a TLS client certificate, you must provide the
|
||||
corresponding private key as well. This should be a paste of the full
|
||||
Base64-encoded private key, including the
|
||||
`-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` markers. The
|
||||
private key will be stored in a secret on the cluster ArgoCD is running on.
|
||||
|
||||
1. To skip verification of the repository server's certificate, tick this box.
|
||||
Using this setting in production environments is not recommended.
|
||||
|
||||
1. If you require Git LFS support with this repository, tick this box.
|
||||
|
||||
1. Click "Connect" to validate configuration and add the repository to ArgoCD
|
||||
configuration.
|
||||
|
||||
### Removing a repository configuration
|
||||
|
||||
!!! warning
|
||||
If you remove a repository configuration that is in active use by any of
|
||||
your applications, ArgoCD will not prevent you to do so. All applications
|
||||
that use the repository whose configuration has been removed as source,
|
||||
will now access the repository as if it would be unconfigured - this could
|
||||
lead to breakage due to unaccessible manifests.
|
||||
|
||||
#### Remove using the CLI
|
||||
|
||||
To remove a repository configuration from ArgoCD using the CLI, simply issue
|
||||
the following command:
|
||||
|
||||
```bash
|
||||
argocd repo rm https://example.com/your/repo
|
||||
```
|
||||
|
||||
#### Using the web UI
|
||||
|
||||
Navigate to the repositories configuration at `Settings` -> `Repositories` and
|
||||
find the repository you want to unconfigure in the list of configured
|
||||
repositories. Then click on the three vertical dots next to the entry and select
|
||||
`Disconnect` from the dropdown, as shown on the following screenshot:
|
||||
|
||||

|
||||
|
||||
The UI will ask for your final confirmation before removing the repository from
|
||||
the configuration.
|
||||
|
||||
433
docs/basics/repositories.md
Normal file
@@ -0,0 +1,433 @@
|
||||
# Working with repositories
|
||||
|
||||
## Introduction
|
||||
|
||||
Since ArgoCD is a GitOps centric tool, the repositories containing your
|
||||
application manifest(s) play a very vital role in the configuration of
|
||||
ArgoCD.
|
||||
|
||||
ArgoCD supports pulling the manifests from two distinct types of repositories:
|
||||
|
||||
* Git repositories, such as GitHub, GitLab or privately hostes ones
|
||||
* Helm repositories, such as Helm's stable charts, Harbor or Chart museum
|
||||
|
||||
Git repositories can hold any kind of manifests or sources that ArgoCD
|
||||
supports. You can even store Helm charts in your Git repositories. Git
|
||||
repositories can be connected using either HTTPS or SSH protocols.
|
||||
|
||||
Helm repositories, however, can only hold Helm charts by definition. Helm
|
||||
repositories can only be connected using HTTPS.
|
||||
|
||||
!!! note
|
||||
Each application defined in ArgoCD is mapped to exactly one repository.
|
||||
It is not possible to map two or more repositories to a single
|
||||
application. If you need resources from more than one repository to define
|
||||
your application, you can look at the advanced repository topics below.
|
||||
|
||||
## Unconfigured vs. Configured repositories
|
||||
|
||||
ArgoCD differentiates between *unconfigured* and *configured* repositories.
|
||||
Unconfigured repositories are those that you can access without any further
|
||||
configuration, while a configured repository is required when you need to
|
||||
authenticate to the repository (and don't use credential templates as
|
||||
described below), or when you need additional custom settings.
|
||||
|
||||
Configured repositories were previously known as *private* repositories, but
|
||||
have now evolved to be named *configured* repositories - because they don't
|
||||
necessarily need to be private.
|
||||
|
||||
You don't have to configure a repository in ArgoCD in order to use it as a
|
||||
manifest source for your application - you can simply specify the URL of the
|
||||
repository when creating an application, as long as the repository is allowed
|
||||
as a source in the
|
||||
[project's configuration](projects/#sources) and is publicly accesible or matches one of
|
||||
the configured credential templates. Using an unconfigured repository as source
|
||||
for your application is as simple as specifying its URL using the `--repo`
|
||||
parameter to the `argocd app create` command.
|
||||
|
||||
!!! note
|
||||
Only Git repositories accessed using HTTPS are currently supported to be
|
||||
connected without further configuration. Git repositories connected using
|
||||
SSH must always be configured in ArgoCD as a repository or have a matching
|
||||
credential template. Helm repositories must always have an explicit
|
||||
configuration before they can be used.
|
||||
|
||||
Using a repository that requires further configuration as the source for an
|
||||
Application requires the repository to be configured, or *connected* first.
|
||||
For further information on how to connect a repository, please see below.
|
||||
|
||||
It is suggested that you configure each repository that you will use as an
|
||||
application's source is configured in ArgoCD first.
|
||||
|
||||
## Repository authentication
|
||||
|
||||
### Available authentication methods
|
||||
|
||||
If your repository needs authentication to be accessed, the following methods
|
||||
are currently supported:
|
||||
|
||||
||Basic Auth|TLS client certs|SSH private keys|
|
||||
|-|-|-|-|
|
||||
|Git via https|X|v1.3+|-|
|
||||
|Git via ssh|-|-|X|
|
||||
|Helm via https|v1.3+|v1.3+|-|
|
||||
|
||||
Other authentication methods, such as AWS IAM or Google ServiceAccounts, are
|
||||
not (yet) supported by ArgoCD.
|
||||
|
||||
!!! note "Optional vs mandatory authentication"
|
||||
Authentication is optional for Git and Helm repositories connected using the
|
||||
HTTPS protocol. For Git repositories connected using SSH, authentication is
|
||||
mandatory and you need to supply a private key for these connections.
|
||||
|
||||
### Personal Access Token (PAT)
|
||||
|
||||
Some Git providers require you to use a personal access token (PAT) instead of
|
||||
username/password combination when accessing the repositories hosted there
|
||||
via HTTPS.
|
||||
|
||||
Providers known to enforce the use of PATs are:
|
||||
|
||||
* GitHub
|
||||
* GitLab
|
||||
* BitBucket
|
||||
|
||||
You can specify the PAT simply as the password (see below) when connecting
|
||||
the custom repository to ArgoCD, using any or the empty string as the username.
|
||||
The value for the username (any, empty or your actual username) varies from
|
||||
provider to provider.
|
||||
|
||||
## Specifying repository URLs
|
||||
|
||||
Repository URLs should always be specified in a fully-qualified manner, that
|
||||
is they should contain the protocol modifier (i.e. `https://` or `ssh://`) as
|
||||
a prefix. Specifying custom ports for the connection to the repository server
|
||||
is possible using the `:port` modifier in the `hostname` portion of the URL.
|
||||
If a port is not specified, the default ports for the requested protocol
|
||||
will be used:
|
||||
|
||||
* Port 443 for HTTPS connections
|
||||
* Port 22 for SSH connections
|
||||
|
||||
Generally, URLs for repositories take the following form
|
||||
|
||||
```bash
|
||||
protocol://[username@]hostname[:port]/path/to/repo
|
||||
```
|
||||
|
||||
The `username` URL modifier is only valid (and mandatory!) for connecting Git
|
||||
repositories using SSH. Likewise, the `--username` parameter for the appropriate
|
||||
CLI commands is only valid for connecting Git or Helm repositories via HTTPS.
|
||||
|
||||
!!! note "Usernames for SSH repositories"
|
||||
When using SSH to connect to the repository, you *must* specify the remote
|
||||
username in the URL, i.e. using `ssh://user@example.com/your/repo`. Most
|
||||
Git providers use `git` as remote username, further information should be
|
||||
taken from the provider's documentation.
|
||||
|
||||
There is an exception when specifying repository URLs for repositories that
|
||||
are to be connected using SSH. These URLs can also be of `scp` style syntax
|
||||
in the following form:
|
||||
|
||||
```bash
|
||||
username@hostname:path/to/repo
|
||||
```
|
||||
|
||||
!!! warning "Remote port in SSH URLs"
|
||||
Please note that with the `scp` style syntax, it is not possible to specify
|
||||
a custom SSH server port in the URL, because the colon denominates the
|
||||
beginning of the path, and the path will be relative to the SSH server's
|
||||
working directory. If you need to connect via SSH to a non-standard port,
|
||||
you **must** use `ssh://` style URLs to specify the repository to use.
|
||||
|
||||
The following are some examples for valid repository URLs
|
||||
|
||||
* `https://example.com/yourorg/repo` - specifies repository `/yourorg/repo` on
|
||||
remote server `example.com`, connected via HTTPS on standard port.
|
||||
* `https://example.com:9443/yourorg/repo` - specifies repository `/yourorg/repo`
|
||||
on remote server `example.com`, connected via HTTPS on non-standard port
|
||||
`9443`.
|
||||
* `ssh://git@example.com/yourorg/repo` - specifies repository `/yourorg/repo`
|
||||
on remote server `example.com`, connected via SSH on standard port and using
|
||||
the remote username `git`.
|
||||
* `git@example.com:yourorg/repo` - same as above, but denoted using an `scp`
|
||||
URL.
|
||||
* `ssh://git@example.com:2222/yourorg/repo` - specifies repository
|
||||
`/yourorg/repo` on remote server `example.com`, connected via SSH on the
|
||||
non-standard port `2222` and using `git` as the remote username.
|
||||
|
||||
A common pitfall is the following `scp` style URL:
|
||||
|
||||
* `git@example.com:2222/yourorg/repo` - This would **not** specify a repository
|
||||
`/yourorg/repo` on remote server `example.com` with a non-standard port of
|
||||
`2222`, but rather the repository `2222/yourorg/repo` on the remote server
|
||||
`example.com` with the default SSH port `22`.
|
||||
|
||||
## Managing configured repositories
|
||||
|
||||
You can manage configured repositories for use with ArgoCD in three ways:
|
||||
|
||||
* Using the CLI's `repo` sub-command
|
||||
* Using the web UI repository configuration, to be found at the `Repositories`
|
||||
module in the `Settings` sections
|
||||
* Using declarative setup. For further information, please refer to the
|
||||
appropriate chapter in the
|
||||
[Operator Manual]().
|
||||
|
||||
With each of the methods above, you can add, edit and remove custom repositories
|
||||
and their configuration.
|
||||
|
||||
### Listing all configured repositories
|
||||
|
||||
You can list all currently configured repositories using the CLI:
|
||||
|
||||
```shell
|
||||
argocd repo list
|
||||
```
|
||||
|
||||
If you prefer to use the web UI, you find the list of configured repositories
|
||||
at the `Settings` -> `Repositories` page.
|
||||
|
||||
### Adding a repository configuration
|
||||
|
||||
Connecting a repository via HTTPS (TLS) is supported for both repository
|
||||
types, `git` and `helm`. The URL for a Git repository connected using HTTPS
|
||||
must be fully-qualified and prefixed with the protocol, i.e. `https://`. The
|
||||
URL may have an optional port modifier if the repository is served from a non
|
||||
default port, i.e. `https://example.com:9443`.
|
||||
|
||||
!!! note "A few words on HTTP redirects"
|
||||
ArgoCD does not follow HTTP redirects when handling repositories. Some Git
|
||||
providers, notably GitLab and possibly also self-hosted GitLab, will send
|
||||
you a HTTP redirect if your repository URL is not suffixed with `.git`. If
|
||||
you receive a HTTP redirect on connecting the repository, try appending
|
||||
the `.git` suffix to your URL. For example, if you use the URL
|
||||
`https://gitlab.com/you/repo` and GitLab sends you a HTTP 301, try to use
|
||||
`https://gitlab.com/you/repo.git` as the URL to your repository.
|
||||
|
||||
#### Configuration using the CLI
|
||||
|
||||
To add a configuration for a Git repository to be connected using HTTPS, you
|
||||
can use the `argocd repo add` command, specifying a repository URL starting
|
||||
with `https://`.
|
||||
|
||||
In its most simple form, the command
|
||||
|
||||
```bash
|
||||
argocd repo add https://example.com/your/repo
|
||||
```
|
||||
|
||||
will add the Git repository at `https://example.com/your/repo` to the ArgoCD
|
||||
configuration. This simple form however is not different from using an
|
||||
unconfigured repository, except it will give you the perks from selecting
|
||||
the repository as an application's source in the UI from a dropdown list.
|
||||
|
||||
You can add custom configuration for the repository by using the following set
|
||||
of command line switches to the `repo add` command:
|
||||
|
||||
|Switch|Argument|Description|
|
||||
|-|-|-|
|
||||
|`--insecure-skip-server-verification`|None|Disables verification of the server's TLS certificate or SSH known host signature, depending on the connection method. You do not want use this switch for production environments.|
|
||||
|`--username`|`username`|Use `username` for authenticating at the server (only valid for HTTPS repositories and in combination with `--password`)|
|
||||
|`--password`|`password`|Use `password` for authenticating at the server (only valid for HTTPS repositories and in combination with `--username`)|
|
||||
|`--ssh-private-key-path`|`path`|Use SSH private key from `path` to authenticate at the remote repository. Only valid and also mandatory for SSH repositories. The private key will be stored in a secret on the cluster ArgoCD runs on.|
|
||||
|`--type`|`type`|Specify that repository is of type `repotype`. Current possible values are `helm` and `git` (defaults to `git`)|
|
||||
|`--name`|`name`|Specify the name of the repository to be `name`. This is mandatory when adding Helm repositories and optional when adding Git repositories.|
|
||||
|`--tls-client-cert-path`|`path`|Specifies to read the TLS client certificate used for authentication from `path` on the local machine. The certificate will be stored in a secret on the cluster ArgoCD is running on.|
|
||||
|`--tls-client-cert-key-path`|`path`|Specifies to read the key for TLS client certificate used for authentication from `path` on the local machine. The key will be stored in a secret on the cluster ArgoCD is running on.|
|
||||
|`--enable-lfs`|None|Enables the support for Git Large File Storage (LFS) on the repository. Only valid for Git repositories.|
|
||||
|
||||
**Some examples:**
|
||||
|
||||
The following command adds a Git repository from `https://github.com/foo/repo`,
|
||||
using `foo` as the username and `bar` as the password for authentication:
|
||||
|
||||
```bash
|
||||
argocd repo add --username foo --password bar https://github.com/foo/repo
|
||||
```
|
||||
|
||||
The following command uses a TLS client certificate in addition to the
|
||||
username/password combination to connect the repository. The cert is read
|
||||
from `~/mycert.crt`, the corresponding key from `~/mycert.key`:
|
||||
|
||||
```bash
|
||||
argocd repo add --username foo --password \
|
||||
--tls-client-cert-path ~/mycert.key \
|
||||
--tls-client-cert-key-path ~/mykey.key \
|
||||
https://secure.example.com/repos/myrepo
|
||||
```
|
||||
|
||||
The following command adds the repository without any authentication, but will
|
||||
ignore the TLS certificate presented by the server. Needless to say, this should
|
||||
only be used for testing purposes in non-prod environments. Instead of using
|
||||
this insecure option, you should consider adding the appropriate TLS certificate
|
||||
or CA certificate to ArgoCD so it will be able to correctly verify the server's
|
||||
certificate:
|
||||
|
||||
```bash
|
||||
argocd repo add --insecure-skip-server-verification \
|
||||
https://self-hosted.example.com/repos/myrepo
|
||||
```
|
||||
|
||||
Finally, the following command adds a repository using the SSH protocol, the
|
||||
private SSH key from your local path `~/.ssh/id_rsa` for authentication and
|
||||
`git` as the remote username:
|
||||
|
||||
```bash
|
||||
argocd repo add --ssh-private-key-path ~/.ssh/id_rsa \
|
||||
ssh://git@example.com/yourorg/repo
|
||||
```
|
||||
|
||||
#### Configuration using the web UI
|
||||
|
||||
Repositories can also be configured using the web UI. The configuration module
|
||||
can be found by clicking on `Settings` and then `Repositories`.
|
||||
|
||||
You first need to chose what type of connection your repository should use, and
|
||||
then click on the appropriate button:
|
||||
|
||||

|
||||
|
||||
The following will walk you through the dialogues for connecting the repository,
|
||||
depending on which method you chose:
|
||||
|
||||
**SSH:**
|
||||
|
||||

|
||||
|
||||
1. The name of the repository. This is optional for Git repositories.
|
||||
|
||||
1. The URL to the repository. This must be either a `ssh://` or `scp` style
|
||||
URL (see discussions about URLs above)
|
||||
|
||||
1. Paste the SSH private key to use. This must be a valid SSH private key,
|
||||
including the start and end denominators.
|
||||
|
||||
1. If you want to skip the server's SSH host key signature verification, tick
|
||||
this box. You should **not** use this in production environments.
|
||||
|
||||
1. If you require Git LFS, tick this box.
|
||||
|
||||
1. Click on "Connect" to connect the repository to ArgoCD.
|
||||
|
||||
!!! note "Note about SSH private keys"
|
||||
You should make sure that the SSH private key you are pasting does not
|
||||
contain any unintentional line breaks. If using a terminal, you should
|
||||
use `cat ~/yourkey`, mark everything including the
|
||||
`-----BEGIN OPENSSH PRIVATE KEY-----` and
|
||||
`-----END OPENSSH PRIVATE KEY-----` markers, copy the selection to your
|
||||
clipboard and paste it into the UI's field.
|
||||
|
||||
**HTTPS:**
|
||||
|
||||

|
||||
|
||||
1. The type of the repository. This can either be `git` or `helm`. Please note
|
||||
that when `helm` is selected, another input field for `Repository name` will
|
||||
appear, which you need to fill out as well.
|
||||
|
||||
1. The URL to the repository. This must be a `https://` URL.
|
||||
|
||||
1. The username to use for authenticating at the repository (optional)
|
||||
|
||||
1. The password to use for authenticating at the repository (optional)
|
||||
|
||||
1. An optional TLS client certificate to use for authentication. This should
|
||||
be a paste of the full Base64-encoded TLS certificate, including the
|
||||
`-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` markers.
|
||||
The certificate will be stored in a secret on the cluster ArgoCD is running
|
||||
on.
|
||||
|
||||
1. If you have specified a TLS client certificate, you must provide the
|
||||
corresponding private key as well. This should be a paste of the full
|
||||
Base64-encoded private key, including the
|
||||
`-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` markers. The
|
||||
private key will be stored in a secret on the cluster ArgoCD is running on.
|
||||
|
||||
1. To skip verification of the repository server's certificate, tick this box.
|
||||
Using this setting in production environments is not recommended.
|
||||
|
||||
1. If you require Git LFS support with this repository, tick this box.
|
||||
|
||||
1. Click "Connect" to validate configuration and add the repository to ArgoCD
|
||||
configuration.
|
||||
|
||||
### Removing a repository configuration
|
||||
|
||||
!!! warning
|
||||
If you remove a repository configuration that is in active use by any of
|
||||
your applications, ArgoCD will not prevent you to do so. All applications
|
||||
that use the repository whose configuration has been removed as source,
|
||||
will now access the repository as if it would be unconfigured - this could
|
||||
lead to breakage due to unaccessible manifests.
|
||||
|
||||
#### Remove using the CLI
|
||||
|
||||
To remove a repository configuration from ArgoCD using the CLI, simply issue
|
||||
the following command:
|
||||
|
||||
```bash
|
||||
argocd repo rm https://example.com/your/repo
|
||||
```
|
||||
|
||||
#### Using the web UI
|
||||
|
||||
Navigate to the repositories configuration at `Settings` -> `Repositories` and
|
||||
find the repository you want to unconfigure in the list of configured
|
||||
repositories. Then click on the three vertical dots next to the entry and select
|
||||
`Disconnect` from the dropdown, as shown on the following screenshot:
|
||||
|
||||

|
||||
|
||||
The UI will ask for your final confirmation before removing the repository from
|
||||
the configuration.
|
||||
|
||||
## Managing credential templates
|
||||
|
||||
Credential templates are a convinient method for accessing multiple repositories
|
||||
with the same set of credentials, so you don't have to configure (and possibly
|
||||
change regulary) credentials for every repository that you might want to access
|
||||
from ArgoCD. Instead, you set up the credentials once using the template and all
|
||||
repositories whose URL matches the templated one will re-use these credentials,
|
||||
as long as they don't have credentials set up specifically.
|
||||
|
||||
For example, you have a bunch of private repositories in the GitHub organisation
|
||||
`yourorg`, all accessible using the same SSH key, you can set up a credential
|
||||
template for accessing the repositories via SSH like follows:
|
||||
|
||||
```bash
|
||||
argocd repocreds add git@github.com:yourorg/ --ssh-private-key-path yourorg.key
|
||||
```
|
||||
|
||||
Since the URL here is a pattern, no validation of the credentials supplied will
|
||||
be performed at all during creation of the template.
|
||||
|
||||
### Matching templates against repository URLs
|
||||
|
||||
Pattern matching will be done on a *best match* basis, so you can have more than
|
||||
one matching pattern for any given URL. The pattern that matches best (i.e. is
|
||||
the more specific) will win.
|
||||
|
||||
Consider you have templates for the following two patterns:
|
||||
|
||||
* `https://github.com/yourorg`
|
||||
|
||||
* `https://github.com/yourorg/special-`
|
||||
|
||||
Now, for the repository `https://github.com/yourorg/yourrepo`, the first pattern
|
||||
would match while for the repository `https://github.com/yourorg/special-repo`
|
||||
both pattern will match, but the second one will win because it is more specific.
|
||||
|
||||
The syntax for the `argocd repocreds` command is similar to that of the
|
||||
`argocd repo` command, however it does not support any repository specific
|
||||
configuration such as LFS support.
|
||||
|
||||
## Self-signed TLS certificates, custom CAs and SSH Known Hosts
|
||||
|
||||
## Advanced repository topics
|
||||
|
||||
### Git LFS
|
||||
|
||||
### Git submodules
|
||||
|
||||
### Separating Helm values and Helm charts
|
||||
111
docs/basics/terminology.md
Normal file
@@ -0,0 +1,111 @@
|
||||
Before using Argo CD we assume that you already know about containers, tags and registries. Ideally you should also know how to build containers and publish them to a registry.
|
||||
|
||||
In addition, you should also be familiar with basic Kubernetes concepts such as:
|
||||
|
||||
* [manifest](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/)
|
||||
* [namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)
|
||||
* [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
|
||||
* other resources such as [secrets](https://kubernetes.io/docs/concepts/configuration/secret/), [services](https://kubernetes.io/docs/concepts/services-networking/service/), [configuration maps](https://kubernetes.io/docs/concepts/configuration/configmap/) etc.
|
||||
|
||||
Argo CD is a Continuous Delivery solution specifically created
|
||||
for deploying containers to Kubernetes clusters, so a basic understanding
|
||||
of both is required in order to use Argo CD effectively.
|
||||
|
||||
|
||||
## GitOps
|
||||
|
||||
GitOps is a set of best practices for application (and infrastructure) deployments. Argo CD implements these practices, so if you understand the principles of GitOps you can understand the decisions behind Argo CD. The GitOps principles are explained at [opengitops.dev](https://opengitops.dev/). In summary Argo CD works as GitOps controller that pulls automatically updates (principle 3) from manifests stored in Git (principle 2) that describe Kubernetes objects in a declarative manner (principle 1). The syncing process between Git and cluster is happening at regular intervals and works both ways (principle 4).
|
||||
|
||||
## Application
|
||||
|
||||
The Application is one of the central entities in Argo CD. Kubernetes by itself does not describe what exactly constitutes an application. Argo CD fills this gap by introducing the Application entity that not only groups associated Kubernetes manifests but also defines the source of truth for these manifests in the form of a Git repository. At its simplest form an Argo CD application is an association between a Git repository and a target cluster.
|
||||
|
||||
## Project
|
||||
|
||||
Project is another entity introduced by Argo CD and is used as a way to group applications. You can use projects in any way you see fit (e.g. per team, per department) but in most cases each project is used to define different security constraints and rules. Projects are the way an operator
|
||||
can segment and secure an Argo CD instance for different teams of developers.
|
||||
Note that using projects is completely optional. Argo CD comes with a "default" project.
|
||||
|
||||
## Cluster
|
||||
|
||||
A cluster is any compliant Kubernetes platform that you want to deploy an application to. A single Argo CD instance can manage multiple clusters. By default Argo CD can manage the cluster it was installed on, but you can add extra clusters as deployment targets which themselves do not need to run an Argo CD installation on their own. It is also possible to do the opposite and install multiple Argo CD instances in a single cluster and manage only specific namespaces with each instance.
|
||||
|
||||
## Git repository
|
||||
|
||||
A Git source is one of the central concepts under GitOps as it holds the source of truth for all your application. The basic Argo CD control loop is to compare each associated Git repository with each cluster deployment and see if there any differences. Argo CD can work with different types of Git providers and protocols and also supports both private and public Git repositories.
|
||||
|
||||
## Apps-of-apps pattern
|
||||
|
||||
At its most simple form an Application is grouping individual Kubernetes resources (deployments, services etc.). But since the Application itself is a custom Kubernetes object, you can create Argo CD applications that recursively include other Applications. This pattern is called App-of-Apps and is a great way to group micro-services or otherwise related applications. Argo CD supports this pattern in many ways, (for example you can easily drill down in the application hierarchy in the Web UI), but does not enforce it in any way.
|
||||
|
||||
## Application set
|
||||
|
||||
The [Application Set controller](https://argocd-applicationset.readthedocs.io/en/stable/) is a subproject of Argo CD geared towards multi-cluster and multi-application Argo CD installations. It is a way to automatically create applications/clusters using different generators. For example you can create multiple applications [according to a set of folders in Git](https://argocd-applicationset.readthedocs.io/en/stable/Generators-Git/), or a set of environments according to the [Pull Requests](https://argocd-applicationset.readthedocs.io/en/stable/Generators-Pull-Request/) that you have open. Using Application Sets is optional.
|
||||
|
||||
## Command Line Interface (CLI)
|
||||
|
||||
Argo CD has a CLI called `argocd` that can be used to manage various aspects of Argo CD, such as applications, clusters and other entities. Using the CLI is great for automation and scripting. It can also be used from a Continuous Integration (CI) pipeline to automate deployments as part of a workflow. The CLI is optional and some people might prefer to use the web UI for the same actions.
|
||||
|
||||
## Web User Interface (UI)
|
||||
|
||||
Argo CD has a very powerful web interface that can be used to inspect your applications as well as drill down on their individual components. You can also perform common actions from within the UI (including deleting applications). Installing the UI is optional, and you can deploy Argo CD in headless mode without it. You can also lock down the UI using Access Control and User management.
|
||||
|
||||
## Application Programming Interface (API)
|
||||
|
||||
Argo CD exposes a RESTful API that allows you to automate all possible actions from your own program or script. Everything that is available from the CLI and the UI is also available through Argo CD's API.
|
||||
|
||||
## Target state
|
||||
|
||||
The desired state of an application as described in Git. Typically it consists of Kubernetes manifests either in raw form or templated with Helm/Kustomize or other configuration management tool.
|
||||
|
||||
## Live state
|
||||
|
||||
The state of the application as found in the cluster. It typically includes pods, services, secrets, configmaps and other Kubernetes objects.
|
||||
|
||||
## Diff process
|
||||
|
||||
The operation when the live state and target state are compared. If they are the same we know that what is in the cluster is also in Git and thus no action needs to be taken. If they are not the same, Argo CD can take an action according to its configuration. A possible action is to apply changes from the Git state to the live state starting the sync process. It is also possible to customize the diff process to ignore specific fields.
|
||||
|
||||
## Sync process
|
||||
|
||||
The sync process takes places after the diff process and makes all the necessary changes to the live state in order to bring the cluster back to the same state as what is described in Git. There are many settings for the sync process that control if it is automated or not, what objects to ignore, if removing objects is allowed etc.
|
||||
|
||||
## Sync status
|
||||
|
||||
The sync status for all applications is monitored by Argo CD at all times. "Synced" denotes the case where live state and target state are the same. "Out Of Sync" means that there is a drift between the two of them.
|
||||
|
||||
## Refresh status
|
||||
|
||||
By default Argo CD will compare live state and target state every 3 minutes. You can change this period with a configuration setting. You can also "refresh" on demand any application from the UI or CLI forcing Argo CD to start the diff process. You can also start the diff process with webhooks from your Git provider.
|
||||
|
||||
## Sync waves
|
||||
|
||||
Sync waves allow you to customize the order of resource creation when an application is synced. For example you might want a service account object to be created before another Kubernetes resource that needs it.
|
||||
|
||||
## Resource Hooks
|
||||
|
||||
For each sync operation Argo CD can optionally run other actions before/during/after the sync process. For example you can use the `PostSync` hook
|
||||
to run some smoke tests in your new deployments. In the case of Helm, Argo CD also tries to translate [Helm hooks](https://helm.sh/docs/topics/charts_hooks/) to Argo CD hooks.
|
||||
|
||||
## Health status
|
||||
|
||||
This is an Argo CD specific status that is monitored for all applications. The "healthy" state is different per application type. For example, a Kubernetes ReplicaSet is deemed healthy if the live generation and live replicas match the target generation and desired replicas. Argo CD also has built-in health checks for other common resources such as [Argo Rollout Objects](https://argoproj.github.io/argo-rollouts/) or [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets).
|
||||
You can create your own Health checks with the [Lua programming language](https://www.lua.org/).
|
||||
|
||||
## Configuration management tool
|
||||
|
||||
Argo CD can use any supported templating tool such as Helm, Kustomize, JSonnet. You can add your own third party tool for preparing/templating Kubernetes manifests. Note that in the case of Helm, Argo CD uses it as a pure templating tool. Helm applications installed with Argo CD are not visible to normal Helm commands, and not all of Helm's features and concepts are supported.
|
||||
|
||||
## Custom Resource Definitions (CRDs)
|
||||
|
||||
Argo CD defines CRDs for all its entities (applications, projects etc). Since CRDs can be stored declaratively in Git on their own, you can fully manage Argo CD configuration with Argo CD itself.
|
||||
|
||||
## Role Based Access Control
|
||||
|
||||
Argo CD includes a powerful RBAC mechanism on top of applications, projects, clusters and git repositories. With this mechanism Argo CD operators can lock down resources and restrict them only to a specific subset of users. RBAC configuration will also integrate with any of the supported Single Sign On (SSO) providers that might use in your organization.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
# Bug triage proposal for ArgoCD
|
||||
|
||||
## Situation
|
||||
|
||||
Lots of issues on our issue tracker. Many of them not bugs, but questions,
|
||||
or very environment related. It's easy to lose oversight.
|
||||
|
||||
Also, it's not obvious which bugs are important. Which bugs should be fixed
|
||||
first? Can we make a new release with the current inventory of open bugs?
|
||||
Is there still a bug that should make it to the new release?
|
||||
|
||||
## Proposal
|
||||
|
||||
We should agree upon a common issue triage process. The process must be lean
|
||||
and efficient, and should support us and the community looking into the GH
|
||||
issue tracker at making the following decisions:
|
||||
|
||||
* Is it even a real bug?
|
||||
* If it is a real bug, what is the current status of the bug (next to "open" or "closed")?
|
||||
* How important is it to fix the bug?
|
||||
* How urgent is it to fix the bug?
|
||||
* Who will be working to fix the bug?
|
||||
|
||||
We need new methods to classify our bugs, at least into these categories:
|
||||
|
||||
* validity: Does the issue indeed represent a true bug
|
||||
* severity: Denominates what impact the bug has
|
||||
* priority: Denominates the urgency of the fix
|
||||
|
||||
## Triage process
|
||||
|
||||
GH issue tracker provides us with the possibility to label issues. Using these
|
||||
labels is not perfect, but should give a good start. Each new issue created in
|
||||
our issue tracker should be correctly labeled during its lifecycle, so keeping
|
||||
an overview would be simplified by the ability to filter for labels.
|
||||
|
||||
The triage process could be as follows:
|
||||
|
||||
1. A new bug issue is created by someone on the tracker
|
||||
|
||||
1. The first person of the core team to see it will start the triage by classifying
|
||||
the issue (see below). This will indicate the creator that we have noticed the
|
||||
issue, and that it's not "fire & forget" tracker.
|
||||
|
||||
1. Initial classification should be possible even when much of the information is
|
||||
missing yet. In this case, the issue would be classified as such (see below).
|
||||
Again, this indicates that someone has noticed the issue, and there is activity
|
||||
in progress to get the required information.
|
||||
|
||||
1. Classification of the issue can change over its life-cycle. However, once the
|
||||
issue has been initially classified correctly (that is, with something else than
|
||||
the "placeholder" classification discussed above), changes to the classification
|
||||
should be discussed first with the person who initially classified the issue.
|
||||
|
||||
## Classification
|
||||
|
||||
We have introduced some new labels in the GH issue tracker for classifying the
|
||||
bug issues. These labels are prefixed with the string `bug/`, and should be
|
||||
applied to all new issues in our tracker.
|
||||
|
||||
### Classification requires more information
|
||||
|
||||
If it is not yet possible to classify the bug, i.e. because more information is
|
||||
required to correctly classify the bug, you should always set the label
|
||||
`bug/in-triage` to make it clear that triage process has started but could not
|
||||
yet be completed.
|
||||
|
||||
### Issue type
|
||||
|
||||
If it's clear that a bug issue is not a bug, but a question or reach for support,
|
||||
it should be marked as such:
|
||||
|
||||
* Remove any of the labels prefixed `bug/` that might be attached to the issue
|
||||
* Remove the label `bug` from the issue
|
||||
* Add the label `inquiry` to the issue
|
||||
|
||||
If the inquiry turns out to be something that should be covered by the docs, but
|
||||
is not, the following actions should be taken:
|
||||
|
||||
* The title of the issue should be adapted that it will be clear that the bug
|
||||
affects the docs, not the code
|
||||
* The label `documentation` should be attached to the issue
|
||||
|
||||
If the issue is too confusing (can happen), another possibility is to close the
|
||||
issue and create a new one as described in above (with a meaningful title and
|
||||
the label `documentation` attached to it).
|
||||
|
||||
### Validity
|
||||
|
||||
Some reported bugs may be invalid. It could be a user error, a misconfiguration
|
||||
or something along these lines. If it is clear that the bug falls into one of
|
||||
these categories:
|
||||
|
||||
* Remove any of the labels prefixed `bug/` that might be attached to the issue
|
||||
* Add the label `invalid` to the issue
|
||||
* Retain the `bug` label to the issue
|
||||
* Close the issue
|
||||
|
||||
When closing the issue, it is important to let requester know why the issue
|
||||
has been closed. The optimum would be to provide a solution to his request
|
||||
in the comments of the issue, or at least pointers to possible solutions.
|
||||
|
||||
### Regressions
|
||||
|
||||
Sometimes it happens that something that worked in a previous release does
|
||||
not work now when it should still work. If this is the case, the following
|
||||
actions should be done
|
||||
|
||||
* Add the label `regression` to the issue
|
||||
* Continue with triage
|
||||
|
||||
### Severity
|
||||
|
||||
It is important to find out how severe the impact of a bug is, and to label
|
||||
the bug with this information. For this purpose, the following labels exist
|
||||
in our tracker:
|
||||
|
||||
* `bug/severity:minor`: Bug has limited impact and maybe affects only an
|
||||
edge-case. Core functionality is not affected, and there is no data loss
|
||||
involved. Something might not work as expected. Example of these kind of
|
||||
bugs could be a CLI command that is not working as expected, a glitch in
|
||||
the UI, wrong documentation, etc.
|
||||
|
||||
* `bug/severity:major`: Malfunction in one of the core components, impacting
|
||||
a majority of users or one of the core functionalities in ArgoCD. There is
|
||||
no data loss involved, but for example a sync is not working due to a bug
|
||||
in ArgoCD (and not due to user error), manifests fail to render, etc.
|
||||
|
||||
* `bug/severity:critical`: A critical bug in ArgoCD, possibly resulting in
|
||||
data loss, integrity breach or severe degraded overall functionality.
|
||||
|
||||
### Priority
|
||||
|
||||
The priority of an issue indicates how quickly the issue should be fixed and
|
||||
released. This information should help us in deciding the target release for
|
||||
the fix, and whether a bug would even justify a dedicated patch release. The
|
||||
following labels can be used to classify bugs into their priority:
|
||||
|
||||
* `bug/priority:low`: Will be fixed without any specific target release.
|
||||
|
||||
* `bug/priority:medium`: Should be fixed in the minor or major release, which
|
||||
ever comes first.
|
||||
|
||||
* `bug/priority:high`: Should be fixed with the next patch release.
|
||||
|
||||
* `bug/priority:urgent`: Should be fixed immediately and might even justify a
|
||||
dedicated patch release.
|
||||
|
||||
The priority should be set according to the value of the fix and the attached
|
||||
severity. This means. a bug with a severity of `minor` could still be classified
|
||||
with priority `high`, when it is a *low hanging fruit* (i.e. the bug is easy to
|
||||
fix with low effort) and contributes to overall user experience of ArgoCD.
|
||||
|
||||
Likewise, a bug classified with a severity of `major` could still have a
|
||||
priority of `medium`, if there is a workaround available for the bug which
|
||||
mitigates the effects of the bug to a bearable extend.
|
||||
|
||||
Bugs classified with a severity of `critical` most likely belong to either
|
||||
the `urgent` priority, or to the `high` category when there is a workaround
|
||||
available.
|
||||
|
||||
Bugs that have a `regression`label attached (see Regression above) should
|
||||
usually be handled with higher priority, so those kind of issues will most
|
||||
likely have a priority of `high` or `urgent` attached to it.
|
||||
|
||||
## Summary
|
||||
|
||||
Applying a little discipline when working with our issue tracker could greatly
|
||||
help us in making informed decision about which bugs to fix when. Also, it
|
||||
would help us to get a clear view whether we can do for example a new minor
|
||||
release without having forgot any outstanding issues that should make it into
|
||||
that release.
|
||||
|
||||
If we are able to work with classification of bug issues, we might want to
|
||||
extend the triage for enhancement proposals and PRs as well.
|
||||
@@ -1,79 +0,0 @@
|
||||
# Installation
|
||||
|
||||
You can download the latest Argo CD version from [the latest release page of this repository](https://github.com/argoproj/argo-cd/releases/latest), which will include the `argocd` CLI.
|
||||
|
||||
## Linux
|
||||
|
||||
You can view the latest version of Argo CD at the link above or run the following command to grab the version:
|
||||
|
||||
```bash
|
||||
VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||
```
|
||||
|
||||
Replace `VERSION` in the command below with the version of Argo CD you would like to download:
|
||||
|
||||
```bash
|
||||
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64
|
||||
```
|
||||
|
||||
Make the `argocd` CLI executable:
|
||||
|
||||
```bash
|
||||
chmod +x /usr/local/bin/argocd
|
||||
```
|
||||
|
||||
You should now be able to run `argocd` commands.
|
||||
|
||||
## Mac
|
||||
|
||||
### Homebrew
|
||||
|
||||
```bash
|
||||
brew install argocd
|
||||
```
|
||||
|
||||
### Download With Curl
|
||||
|
||||
You can view the latest version of Argo CD at the link above or run the following command to grab the version:
|
||||
|
||||
```bash
|
||||
VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||
```
|
||||
|
||||
Replace `VERSION` in the command below with the version of Argo CD you would like to download:
|
||||
|
||||
```bash
|
||||
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-darwin-amd64
|
||||
```
|
||||
|
||||
Make the `argocd` CLI executable:
|
||||
|
||||
```bash
|
||||
chmod +x /usr/local/bin/argocd
|
||||
```
|
||||
|
||||
After finishing either of the instructions above, you should now be able to run `argocd` commands.
|
||||
|
||||
|
||||
## Windows
|
||||
|
||||
### Download With Powershell: Invoke-WebRequest
|
||||
|
||||
You can view the latest version of Argo CD at the link above or run the following command to grab the version:
|
||||
|
||||
```powershell
|
||||
$version = (Invoke-RestMethod https://api.github.com/repos/argoproj/argo-cd/releases/latest).tag_name
|
||||
```
|
||||
|
||||
Replace `$version` in the command below with the version of Argo CD you would like to download:
|
||||
|
||||
```powershell
|
||||
$url = "https://github.com/argoproj/argo-cd/releases/download/" + $version + "/argocd-windows-amd64.exe"
|
||||
$output = "argocd.exe"
|
||||
|
||||
Invoke-WebRequest -Uri $url -OutFile $output
|
||||
```
|
||||
Also please note you will probably need to move the file into your PATH.
|
||||
|
||||
|
||||
After finishing the instructions above, you should now be able to run `argocd` commands.
|
||||
7
docs/community.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Community & Ecosystem
|
||||
|
||||
The following is a curated list of community projects related to Argo CD. If
|
||||
you have a cool project that benefits the general Argo CD community and want
|
||||
to have it listed here, please feel free to
|
||||
[submit a PR]()
|
||||
with the addition to this document!
|
||||
@@ -1,16 +0,0 @@
|
||||
# Core Concepts
|
||||
|
||||
Let's assume you're familiar with core Git, Docker, Kubernetes, Continuous Delivery, and GitOps concepts.
|
||||
|
||||
* **Application** A group of Kubernetes resources as defined by a manifest. This is a Custom Resource Definition (CRD).
|
||||
* **Application source type** Which **Tool** is used to build the application.
|
||||
* **Target state** The desired state of an application, as represented by files in a Git repository.
|
||||
* **Live state** The live state of that application. What pods etc are deployed.
|
||||
* **Sync status** Whether or not the live state matches the target state. Is the deployed application the same as Git says it should be?
|
||||
* **Sync** The process of making an application move to its target state. E.g. by applying changes to a Kubernetes cluster.
|
||||
* **Sync operation status** Whether or not a sync succeeded.
|
||||
* **Refresh** Compare the latest code in Git with the live state. Figure out what is different.
|
||||
* **Health** The health of the application, is it running correctly? Can it serve requests?
|
||||
* **Tool** A tool to create manifests from a directory of files. E.g. Kustomize or Ksonnet. See **Application Source Type**.
|
||||
* **Configuration management tool** See **Tool**.
|
||||
* **Configuration management plugin** A custom tool.
|
||||
@@ -1,31 +0,0 @@
|
||||
# API Docs
|
||||
|
||||
You can find the Swagger docs by setting the path to `/swagger-ui` in your Argo CD UI's. E.g. [http://localhost:8080/swagger-ui](http://localhost:8080/swagger-ui).
|
||||
|
||||
## Authorization
|
||||
|
||||
You'll need to authorize your API using a bearer token. To get a token:
|
||||
|
||||
```bash
|
||||
$ curl $ARGOCD_SERVER/api/v1/session -d $'{"username":"admin","password":"password"}'
|
||||
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1Njc4MTIzODcsImlzcyI6ImFyZ29jZCIsIm5iZiI6MTU2NzgxMjM4Nywic3ViIjoiYWRtaW4ifQ.ejyTgFxLhuY9mOBtKhcnvobg3QZXJ4_RusN_KIdVwao"}
|
||||
```
|
||||
|
||||
> <=v1.2
|
||||
|
||||
Then pass using the HTTP `SetCookie` header, prefixing with `argocd.token`:
|
||||
|
||||
```bash
|
||||
$ curl $ARGOCD_SERVER/api/v1/applications --cookie "argocd.token=$ARGOCD_TOKEN"
|
||||
{"metadata":{"selfLink":"/apis/argoproj.io/v1alpha1/namespaces/argocd/applications","resourceVersion":"37755"},"items":...}
|
||||
```
|
||||
|
||||
> v1.3
|
||||
|
||||
Then pass using the HTTP `Authorization` header, prefixing with `Bearer `:
|
||||
|
||||
```bash
|
||||
$ curl $ARGOCD_SERVER/api/v1/applications -H "Authorization: Bearer $ARGOCD_TOKEN"
|
||||
{"metadata":{"selfLink":"/apis/argoproj.io/v1alpha1/namespaces/argocd/applications","resourceVersion":"37755"},"items":...}
|
||||
```
|
||||
|
||||
|
Before Width: | Height: | Size: 109 KiB |
@@ -1,74 +0,0 @@
|
||||
# Continuous Integration (CI)
|
||||
|
||||
## Troubleshooting CI checks
|
||||
|
||||
You can click on the "Details" link next to the failed step to get more information about the failure.
|
||||
|
||||

|
||||
|
||||
To read more about The GitHub actions are configured in [`ci-build.yaml`](https://github.com/argoproj/argo-cd/blob/master/.github/workflows/ci-build.yaml).
|
||||
|
||||
### Can I retrigger the checks without pushing a new commit?
|
||||
|
||||
Since the CI pipeline is triggered on Git commits, there is currently no (known) way on how to retrigger the CI checks without pushing a new commit to your branch.
|
||||
|
||||
If you are absolutely sure that the failure was due to a failure in the pipeline, and not an error within the changes you commited, you can push an empty commit to your branch, thus retriggering the pipeline without any code changes. To do so, issue
|
||||
|
||||
```bash
|
||||
git commit --allow-empty -m "Retrigger CI pipeline"
|
||||
git push origin <yourbranch>
|
||||
```
|
||||
|
||||
### Why does the build step fail?
|
||||
|
||||
First, make sure the failing build step succeeds on your machine. Remember the containerized build toolchain is available, too.
|
||||
|
||||
If the build is failing at the `Ensuring Gopkg.lock is up-to-date` step, you need to update the dependencies before you push your commits. Run `make dep-ensure` and `make dep` and commit the changes to `Gopkg.lock` to your branch.
|
||||
|
||||
### Why does the codegen step fail?
|
||||
|
||||
If the codegen step fails with "Check nothing has changed...", chances are high that you did not run `make codegen`, or did not commit the changes it made. You should double check by running `make codegen` followed by `git status` in the local working copy of your branch. Commit any changes and push them to your GH branch to have the CI check it again.
|
||||
|
||||
A second common case for this is, when you modified any of the auto generated assets, as these will be overwritten upon `make codegen`.
|
||||
|
||||
Generally, this step runs `codegen` and compares the outcome against the Git branch it has checked out. If there are differences, the step will fail.
|
||||
|
||||
See [What checked-in code is generated and where does it come from?](faq.md#what-checked-in-code-is-generated-and-how-is-it-generated) for more information.
|
||||
|
||||
### Why does the lint step fail?
|
||||
|
||||
Your code failed to lint correctly, or modifications were performed by the `golangci-lint` process.
|
||||
|
||||
* You should run `make lint`, or `golangci-lint run` on your local branch and fix all the issues.
|
||||
|
||||
* If you receive an error like, ```File is not `goimports`-ed (goimports)```, the file is not formatted correctly. Run `gofmt -w $file.go` to resolve this linter error.
|
||||
|
||||
### Why does the test or e2e steps fail?
|
||||
|
||||
You should check for the cause of the failure in the check's detail page as described above. This will give you the name of the test that has failed, and details about why. If your test are passing locally (using the virtualized toolchain), chances are that the test might be flaky and will pass the next time it is run. Please retrigger the CI pipeline as described above and see if the test step now passes.
|
||||
|
||||
## Updating The Builder Image
|
||||
|
||||
Login to Docker Hub:
|
||||
|
||||
```bash
|
||||
docker login
|
||||
```
|
||||
|
||||
Build image:
|
||||
|
||||
```bash
|
||||
make builder-image IMAGE_NAMESPACE=argoproj IMAGE_TAG=v1.0.0
|
||||
```
|
||||
|
||||
## Public CD
|
||||
|
||||
Every commit to master is built and published to `docker.pkg.github.com/argoproj/argo-cd/argocd:<version>-<short-sha>`. The list of images is available at
|
||||
https://github.com/argoproj/argo-cd/packages.
|
||||
|
||||
!!! note
|
||||
Github docker registry [requires](https://github.community/t5/GitHub-Actions/docker-pull-from-public-GitHub-Package-Registry-fail-with-quot/m-p/32888#M1294) authentication to read
|
||||
even publicly available packages. Follow the steps from Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry)
|
||||
to configure image pull secret if you want to use `docker.pkg.github.com/argoproj/argo-cd/argocd` image.
|
||||
|
||||
The image is automatically deployed to the dev Argo CD instance: [https://cd.apps.argoproj.io/](https://cd.apps.argoproj.io/)
|
||||