mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-25 01:38:46 +01:00
Compare commits
111 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6cd7c5e32 | ||
|
|
dd26b73e40 | ||
|
|
b8910f943d | ||
|
|
fe80a4072e | ||
|
|
d4d767d831 | ||
|
|
f282a33e59 | ||
|
|
3d1f37b0c5 | ||
|
|
1582ca665a | ||
|
|
345b77ecda | ||
|
|
9d1d605e56 | ||
|
|
47507930da | ||
|
|
a066c97fd9 | ||
|
|
0d8f996b85 | ||
|
|
159674ee84 | ||
|
|
73d176bda3 | ||
|
|
bc5ad35238 | ||
|
|
cbc0b2fcd2 | ||
|
|
18628acae9 | ||
|
|
c10ae246ab | ||
|
|
41e72d1247 | ||
|
|
6393d925bf | ||
|
|
faffea357e | ||
|
|
623b4757f4 | ||
|
|
a68f483fa3 | ||
|
|
a17217d9e8 | ||
|
|
a7c776d356 | ||
|
|
247879b424 | ||
|
|
2058cb6374 | ||
|
|
f5bcc1dbcc | ||
|
|
4d7e6945bd | ||
|
|
df65d017a2 | ||
|
|
283645e63e | ||
|
|
0e016a368d | ||
|
|
26d711a6db | ||
|
|
1f12aac601 | ||
|
|
b39e93a4d2 | ||
|
|
97aa28a687 | ||
|
|
e775b8fce8 | ||
|
|
84bece53a3 | ||
|
|
4a6fe4cd31 | ||
|
|
45cb1d5967 | ||
|
|
415d5e569f | ||
|
|
03a0a192ec | ||
|
|
a40f3689b3 | ||
|
|
80515a3b57 | ||
|
|
00f99edf1a | ||
|
|
bc83719037 | ||
|
|
2926f2bd60 | ||
|
|
7b2a95e83c | ||
|
|
d89b7d8a41 | ||
|
|
36da074344 | ||
|
|
2277af2f32 | ||
|
|
ee64a4d9ca | ||
|
|
d9bae2f83f | ||
|
|
a724574ede | ||
|
|
1c5fc0076e | ||
|
|
f826b0e397 | ||
|
|
196eb16244 | ||
|
|
09f3b45e39 | ||
|
|
c914ea0218 | ||
|
|
5ad46b025a | ||
|
|
22dcc1e87d | ||
|
|
91d5d7e37b | ||
|
|
23bf07d206 | ||
|
|
6637ee5c24 | ||
|
|
867282f726 | ||
|
|
8aadc310c9 | ||
|
|
ac097f143c | ||
|
|
4a12cbb231 | ||
|
|
40eb8c79ab | ||
|
|
710d06c800 | ||
|
|
2f2f39c8a6 | ||
|
|
7c831ad781 | ||
|
|
51998e0846 | ||
|
|
313de86941 | ||
|
|
0a4ce77bce | ||
|
|
f95004c428 | ||
|
|
f59391161e | ||
|
|
27e95df536 | ||
|
|
ec23d917eb | ||
|
|
991ee9b771 | ||
|
|
c21a6eae7d | ||
|
|
173a0f011d | ||
|
|
fe8d47e0ea | ||
|
|
192ee93fc4 | ||
|
|
490d4004b1 | ||
|
|
c2ff86e8b2 | ||
|
|
a32f70f207 | ||
|
|
02b3c61fd9 | ||
|
|
f822d098c3 | ||
|
|
dc5ac89f36 | ||
|
|
046406e7d3 | ||
|
|
4f49168c1a | ||
|
|
22bb1dd40f | ||
|
|
cd1de6e680 | ||
|
|
24fa758444 | ||
|
|
9208176e86 | ||
|
|
66e1fb78f7 | ||
|
|
e42102a67e | ||
|
|
887adffcc8 | ||
|
|
c66919f9ff | ||
|
|
301d18820a | ||
|
|
d2d37583af | ||
|
|
e0e995a944 | ||
|
|
1f87950d48 | ||
|
|
9a17103830 | ||
|
|
fd49a4e74f | ||
|
|
e78f61ea37 | ||
|
|
e5d4673eac | ||
|
|
3df4850418 | ||
|
|
1b0421c3aa |
@@ -1,365 +1,16 @@
|
||||
version: 2.1
|
||||
commands:
|
||||
prepare_environment:
|
||||
steps:
|
||||
- run:
|
||||
name: Configure environment
|
||||
command: |
|
||||
set -x
|
||||
echo "export GOCACHE=/tmp/go-build-cache" | tee -a $BASH_ENV
|
||||
echo "export ARGOCD_TEST_VERBOSE=true" | tee -a $BASH_ENV
|
||||
echo "export ARGOCD_TEST_PARALLELISM=8" | tee -a $BASH_ENV
|
||||
echo "export ARGOCD_SONAR_VERSION=4.2.0.1873" | tee -a $BASH_ENV
|
||||
configure_git:
|
||||
steps:
|
||||
- run:
|
||||
name: Configure Git
|
||||
command: |
|
||||
set -x
|
||||
# must be configured for tests to run
|
||||
git config --global user.email you@example.com
|
||||
git config --global user.name "Your Name"
|
||||
echo "export PATH=/home/circleci/.go_workspace/src/github.com/argoproj/argo-cd/hack:\$PATH" | tee -a $BASH_ENV
|
||||
echo "export GIT_ASKPASS=git-ask-pass.sh" | tee -a $BASH_ENV
|
||||
restore_vendor:
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: Restore vendor cache
|
||||
keys:
|
||||
- vendor-v2-{{ checksum "Gopkg.lock" }}
|
||||
save_vendor:
|
||||
steps:
|
||||
- save_cache:
|
||||
name: Save vendor cache
|
||||
key: vendor-v2-{{ checksum "Gopkg.lock" }}
|
||||
paths:
|
||||
- ./vendor
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- vendor
|
||||
save_coverage_info:
|
||||
steps:
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- coverage.out
|
||||
save_node_modules:
|
||||
steps:
|
||||
- persist_to_workspace:
|
||||
root: ~/argo-cd
|
||||
paths:
|
||||
- ui/node_modules
|
||||
attach_vendor:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
install_golang:
|
||||
steps:
|
||||
- run:
|
||||
name: Install Golang v1.14.1
|
||||
command: |
|
||||
go get golang.org/dl/go1.14.1
|
||||
[ -e /home/circleci/sdk/go1.14.1 ] || go1.14.1 download
|
||||
go env
|
||||
echo "export GOPATH=/home/circleci/.go_workspace" | tee -a $BASH_ENV
|
||||
echo "export PATH=/home/circleci/sdk/go1.14.1/bin:\$PATH" | tee -a $BASH_ENV
|
||||
save_go_cache:
|
||||
steps:
|
||||
- save_cache:
|
||||
key: go-v2-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}
|
||||
# https://circleci.com/docs/2.0/language-go/
|
||||
paths:
|
||||
- /home/circleci/.cache/go-build
|
||||
- /home/circleci/sdk/go1.14.1
|
||||
restore_go_cache:
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- go-v2-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}
|
||||
|
||||
save_go_cache_docker:
|
||||
steps:
|
||||
- save_cache:
|
||||
name: Save Go build cache
|
||||
key: go-docker-v2-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}
|
||||
# https://circleci.com/docs/2.0/language-go/
|
||||
paths:
|
||||
- /tmp/go-build-cache
|
||||
restore_go_cache_docker:
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: Restore Go build cache
|
||||
keys:
|
||||
- go-docker-v2-{{ .Branch }}-{{ .Environment.CIRCLE_WORKFLOW_ID }}
|
||||
jobs:
|
||||
build:
|
||||
dummy:
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.4.0
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- restore_vendor
|
||||
- run:
|
||||
name: Ensuring Gopkg.lock is up-to-date
|
||||
command: make dep-check-local
|
||||
- run:
|
||||
name: Syncing vendor dependencies
|
||||
command: dep ensure -v
|
||||
- run: make build-local
|
||||
- run: chmod -R 777 vendor
|
||||
- run: chmod -R 777 ${GOCACHE}
|
||||
- save_vendor
|
||||
- save_go_cache_docker
|
||||
|
||||
codegen:
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.4.0
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- restore_vendor
|
||||
- run: helm2 init --client-only
|
||||
- run: make codegen-local
|
||||
- run:
|
||||
name: Check nothing has changed
|
||||
command: |
|
||||
set -xo pipefail
|
||||
# This makes sure you ran `make pre-commit` before you pushed.
|
||||
# We exclude the Swagger resources; CircleCI doesn't generate them correctly.
|
||||
# When this fails, it will, create a patch file you can apply locally to fix it.
|
||||
# To troubleshoot builds: https://argoproj.github.io/argo-cd/developer-guide/ci/
|
||||
git diff --exit-code -- . ':!Gopkg.lock' ':!assets/swagger.json' | tee codegen.patch
|
||||
- store_artifacts:
|
||||
path: codegen.patch
|
||||
destination: .
|
||||
test:
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.4.0
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- configure_git
|
||||
- restore_vendor
|
||||
- run: dep ensure -v
|
||||
- restore_go_cache_docker
|
||||
- run: make test-local
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: bash <(curl -s https://codecov.io/bash) -f coverage.out
|
||||
- run:
|
||||
name: Output of test-results
|
||||
command: |
|
||||
ls -l test-results || true
|
||||
cat test-results/junit.xml || true
|
||||
- save_coverage_info
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
- store_artifacts:
|
||||
path: test-results
|
||||
destination: .
|
||||
|
||||
lint:
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.4.0
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- configure_git
|
||||
- restore_vendor
|
||||
- run: dep ensure -v
|
||||
- restore_go_cache_docker
|
||||
- run:
|
||||
name: Run golangci-lint
|
||||
command: ARGOCD_LINT_GOGC=10 make lint-local
|
||||
- run:
|
||||
name: Check that nothing has changed
|
||||
command: |
|
||||
gDiff=$(git diff)
|
||||
if test "$gDiff" != ""; then
|
||||
echo
|
||||
echo "###############################################################################"
|
||||
echo "golangci-lint has made automatic corrections to your code. Please check below"
|
||||
echo "diff output and commit this to your local branch, or run make lint locally."
|
||||
echo "###############################################################################"
|
||||
echo
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sonarcloud:
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.4.0
|
||||
environment:
|
||||
NODE_MODULES: /go/src/github.com/argoproj/argo-cd/ui/node_modules
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
command: mkdir -p /tmp/cache/scanner
|
||||
name: Create cache directory if it doesn't exist
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-sonarcloud-scanner-4.2.0.1873
|
||||
- run:
|
||||
command: |
|
||||
set -e
|
||||
VERSION=4.2.0.1873
|
||||
SONAR_TOKEN=$SONAR_TOKEN
|
||||
SCANNER_DIRECTORY=/tmp/cache/scanner
|
||||
export SONAR_USER_HOME=$SCANNER_DIRECTORY/.sonar
|
||||
OS="linux"
|
||||
echo $SONAR_USER_HOME
|
||||
|
||||
if [[ ! -x "$SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner" ]]; then
|
||||
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$VERSION-$OS.zip
|
||||
unzip -qq -o sonar-scanner-cli-$VERSION-$OS.zip -d $SCANNER_DIRECTORY
|
||||
fi
|
||||
|
||||
chmod +x $SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner
|
||||
chmod +x $SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/jre/bin/java
|
||||
|
||||
# Workaround for a possible bug in CircleCI
|
||||
if ! echo $CIRCLE_PULL_REQUEST | grep https://github.com/argoproj; then
|
||||
unset CIRCLE_PULL_REQUEST
|
||||
unset CIRCLE_PULL_REQUESTS
|
||||
fi
|
||||
|
||||
# Explicitly set NODE_MODULES
|
||||
export NODE_MODULES=/go/src/github.com/argoproj/argo-cd/ui/node_modules
|
||||
export NODE_PATH=/go/src/github.com/argoproj/argo-cd/ui/node_modules
|
||||
|
||||
$SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner
|
||||
name: SonarCloud
|
||||
- save_cache:
|
||||
key: v1-sonarcloud-scanner-4.2.0.1873
|
||||
paths:
|
||||
- /tmp/cache/scanner
|
||||
|
||||
e2e:
|
||||
working_directory: /home/circleci/.go_workspace/src/github.com/argoproj/argo-cd
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
environment:
|
||||
ARGOCD_FAKE_IN_CLUSTER: "true"
|
||||
ARGOCD_SSH_DATA_PATH: "/tmp/argo-e2e/app/config/ssh"
|
||||
ARGOCD_TLS_DATA_PATH: "/tmp/argo-e2e/app/config/tls"
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
- image: cimg/base:2020.01
|
||||
steps:
|
||||
- run:
|
||||
name: Install and start K3S v0.5.0
|
||||
name: Dummy step
|
||||
command: |
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
sudo chmod -R a+rw /etc/rancher/k3s
|
||||
kubectl version
|
||||
environment:
|
||||
INSTALL_K3S_EXEC: --docker
|
||||
INSTALL_K3S_VERSION: v0.5.0
|
||||
- prepare_environment
|
||||
- restore_go_cache_docker
|
||||
- checkout
|
||||
- restore_cache:
|
||||
keys: [e2e-dl-v2]
|
||||
- run: sudo ./hack/install.sh dep-linux
|
||||
- save_cache:
|
||||
key: e2e-dl-v2
|
||||
paths: [/tmp/dl]
|
||||
- attach_vendor
|
||||
- run: dep ensure -v
|
||||
- run:
|
||||
name: Update kubectl configuration for container
|
||||
command: |
|
||||
ipaddr=$(ifconfig $IFACE |grep "inet " | awk '{print $2}')
|
||||
if echo $ipaddr | grep -q 'addr:'; then
|
||||
ipaddr=$(echo $ipaddr | awk -F ':' '{print $2}')
|
||||
fi
|
||||
test -d $HOME/.kube || mkdir -p $HOME/.kube
|
||||
kubectl config view --raw | sed -e "s/127.0.0.1:6443/${ipaddr}:6443/g" -e "s/localhost:6443/${ipaddr}:6443/g" > $HOME/.kube/config
|
||||
environment:
|
||||
IFACE: ens4
|
||||
- run:
|
||||
name: Start E2E test server
|
||||
command: make start-e2e
|
||||
background: true
|
||||
environment:
|
||||
DOCKER_SRCDIR: /home/circleci/.go_workspace/src
|
||||
ARGOCD_E2E_TEST: "true"
|
||||
ARGOCD_IN_CI: "true"
|
||||
- run:
|
||||
name: Wait for API server to become available
|
||||
command: |
|
||||
count=1
|
||||
until curl -v http://localhost:8080/healthz; do
|
||||
sleep 10;
|
||||
if test $count -ge 60; then
|
||||
echo "Timeout"
|
||||
exit 1
|
||||
fi
|
||||
count=$((count+1))
|
||||
done
|
||||
- run:
|
||||
name: Run E2E tests
|
||||
command: |
|
||||
make test-e2e
|
||||
environment:
|
||||
ARGOCD_OPTS: "--plaintext"
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
IFACE: ens4
|
||||
DOCKER_SRCDIR: /home/circleci/.go_workspace/src
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
- store_artifacts:
|
||||
path: test-results
|
||||
destination: .
|
||||
ui:
|
||||
docker:
|
||||
- image: node:11.15.0
|
||||
working_directory: ~/argo-cd/ui
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/argo-cd/
|
||||
- restore_cache:
|
||||
keys:
|
||||
- yarn-packages-v4-{{ checksum "yarn.lock" }}
|
||||
- run: yarn install --frozen-lockfile --ignore-optional --non-interactive
|
||||
- save_cache:
|
||||
key: yarn-packages-v4-{{ checksum "yarn.lock" }}
|
||||
paths: [~/.cache/yarn, node_modules]
|
||||
- run: yarn test
|
||||
- run: ./node_modules/.bin/codecov -p ..
|
||||
- run: NODE_ENV='production' yarn build
|
||||
- run: yarn lint
|
||||
- save_node_modules
|
||||
|
||||
orbs:
|
||||
sonarcloud: sonarsource/sonarcloud@1.0.1
|
||||
echo "This is a dummy step to satisfy CircleCI"
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
workflow:
|
||||
jobs:
|
||||
- build
|
||||
- test:
|
||||
requires:
|
||||
- build
|
||||
- codegen:
|
||||
requires:
|
||||
- build
|
||||
- ui:
|
||||
requires:
|
||||
- build
|
||||
- sonarcloud:
|
||||
context: SonarCloud
|
||||
requires:
|
||||
- test
|
||||
- ui
|
||||
- e2e:
|
||||
requires:
|
||||
- build
|
||||
jobs:
|
||||
- dummy
|
||||
324
.circleci/config.yml.off
Normal file
324
.circleci/config.yml.off
Normal file
@@ -0,0 +1,324 @@
|
||||
# CircleCI currently disabled in favor of GH actions
|
||||
version: 2.1
|
||||
commands:
|
||||
prepare_environment:
|
||||
steps:
|
||||
- run:
|
||||
name: Configure environment
|
||||
command: |
|
||||
set -x
|
||||
echo "export GOCACHE=/tmp/go-build-cache" | tee -a $BASH_ENV
|
||||
echo "export ARGOCD_TEST_VERBOSE=true" | tee -a $BASH_ENV
|
||||
echo "export ARGOCD_TEST_PARALLELISM=4" | tee -a $BASH_ENV
|
||||
echo "export ARGOCD_SONAR_VERSION=4.2.0.1873" | tee -a $BASH_ENV
|
||||
configure_git:
|
||||
steps:
|
||||
- run:
|
||||
name: Configure Git
|
||||
command: |
|
||||
set -x
|
||||
# must be configured for tests to run
|
||||
git config --global user.email you@example.com
|
||||
git config --global user.name "Your Name"
|
||||
echo "export PATH=/home/circleci/.go_workspace/src/github.com/argoproj/argo-cd/hack:\$PATH" | tee -a $BASH_ENV
|
||||
echo "export GIT_ASKPASS=git-ask-pass.sh" | tee -a $BASH_ENV
|
||||
setup_go_modules:
|
||||
steps:
|
||||
- run:
|
||||
name: Run go mod download and populate vendor
|
||||
command: |
|
||||
go mod download
|
||||
go mod vendor
|
||||
save_coverage_info:
|
||||
steps:
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- coverage.out
|
||||
save_node_modules:
|
||||
steps:
|
||||
- persist_to_workspace:
|
||||
root: ~/argo-cd
|
||||
paths:
|
||||
- ui/node_modules
|
||||
save_go_cache:
|
||||
steps:
|
||||
- persist_to_workspace:
|
||||
root: /tmp
|
||||
paths:
|
||||
- go-build-cache
|
||||
attach_go_cache:
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp
|
||||
install_golang:
|
||||
steps:
|
||||
- run:
|
||||
name: Install Golang v1.14.1
|
||||
command: |
|
||||
go get golang.org/dl/go1.14.1
|
||||
[ -e /home/circleci/sdk/go1.14.1 ] || go1.14.1 download
|
||||
go env
|
||||
echo "export GOPATH=/home/circleci/.go_workspace" | tee -a $BASH_ENV
|
||||
echo "export PATH=/home/circleci/sdk/go1.14.1/bin:\$PATH" | tee -a $BASH_ENV
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.5.0
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- run: make build-local
|
||||
- run: chmod -R 777 vendor
|
||||
- run: chmod -R 777 ${GOCACHE}
|
||||
- save_go_cache
|
||||
|
||||
codegen:
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.5.0
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- attach_go_cache
|
||||
- run: helm2 init --client-only
|
||||
- run: make codegen-local
|
||||
- run:
|
||||
name: Check nothing has changed
|
||||
command: |
|
||||
set -xo pipefail
|
||||
# This makes sure you ran `make pre-commit` before you pushed.
|
||||
# We exclude the Swagger resources; CircleCI doesn't generate them correctly.
|
||||
# When this fails, it will, create a patch file you can apply locally to fix it.
|
||||
# To troubleshoot builds: https://argoproj.github.io/argo-cd/developer-guide/ci/
|
||||
git diff --exit-code -- . ':!Gopkg.lock' ':!assets/swagger.json' | tee codegen.patch
|
||||
- store_artifacts:
|
||||
path: codegen.patch
|
||||
destination: .
|
||||
test:
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.5.0
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- configure_git
|
||||
- attach_go_cache
|
||||
- run: make test-local
|
||||
- run:
|
||||
name: Uploading code coverage
|
||||
command: bash <(curl -s https://codecov.io/bash) -f coverage.out
|
||||
- run:
|
||||
name: Output of test-results
|
||||
command: |
|
||||
ls -l test-results || true
|
||||
cat test-results/junit.xml || true
|
||||
- save_coverage_info
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
- store_artifacts:
|
||||
path: test-results
|
||||
destination: .
|
||||
|
||||
lint:
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.5.0
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- configure_git
|
||||
- attach_vendor
|
||||
- store_go_cache_docker
|
||||
- run:
|
||||
name: Run golangci-lint
|
||||
command: ARGOCD_LINT_GOGC=10 make lint-local
|
||||
- run:
|
||||
name: Check that nothing has changed
|
||||
command: |
|
||||
gDiff=$(git diff)
|
||||
if test "$gDiff" != ""; then
|
||||
echo
|
||||
echo "###############################################################################"
|
||||
echo "golangci-lint has made automatic corrections to your code. Please check below"
|
||||
echo "diff output and commit this to your local branch, or run make lint locally."
|
||||
echo "###############################################################################"
|
||||
echo
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sonarcloud:
|
||||
working_directory: /go/src/github.com/argoproj/argo-cd
|
||||
docker:
|
||||
- image: argoproj/argocd-test-tools:v0.5.0
|
||||
environment:
|
||||
NODE_MODULES: /go/src/github.com/argoproj/argo-cd/ui/node_modules
|
||||
steps:
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
command: mkdir -p /tmp/cache/scanner
|
||||
name: Create cache directory if it doesn't exist
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-sonarcloud-scanner-4.2.0.1873
|
||||
- run:
|
||||
command: |
|
||||
set -e
|
||||
VERSION=4.2.0.1873
|
||||
SONAR_TOKEN=$SONAR_TOKEN
|
||||
SCANNER_DIRECTORY=/tmp/cache/scanner
|
||||
export SONAR_USER_HOME=$SCANNER_DIRECTORY/.sonar
|
||||
OS="linux"
|
||||
echo $SONAR_USER_HOME
|
||||
|
||||
if [[ ! -x "$SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner" ]]; then
|
||||
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$VERSION-$OS.zip
|
||||
unzip -qq -o sonar-scanner-cli-$VERSION-$OS.zip -d $SCANNER_DIRECTORY
|
||||
fi
|
||||
|
||||
chmod +x $SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner
|
||||
chmod +x $SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/jre/bin/java
|
||||
|
||||
# Workaround for a possible bug in CircleCI
|
||||
if ! echo $CIRCLE_PULL_REQUEST | grep https://github.com/argoproj; then
|
||||
unset CIRCLE_PULL_REQUEST
|
||||
unset CIRCLE_PULL_REQUESTS
|
||||
fi
|
||||
|
||||
# Explicitly set NODE_MODULES
|
||||
export NODE_MODULES=/go/src/github.com/argoproj/argo-cd/ui/node_modules
|
||||
export NODE_PATH=/go/src/github.com/argoproj/argo-cd/ui/node_modules
|
||||
|
||||
$SCANNER_DIRECTORY/sonar-scanner-$VERSION-$OS/bin/sonar-scanner
|
||||
name: SonarCloud
|
||||
- save_cache:
|
||||
key: v1-sonarcloud-scanner-4.2.0.1873
|
||||
paths:
|
||||
- /tmp/cache/scanner
|
||||
|
||||
e2e:
|
||||
working_directory: /home/circleci/.go_workspace/src/github.com/argoproj/argo-cd
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
environment:
|
||||
ARGOCD_FAKE_IN_CLUSTER: "true"
|
||||
ARGOCD_SSH_DATA_PATH: "/tmp/argo-e2e/app/config/ssh"
|
||||
ARGOCD_TLS_DATA_PATH: "/tmp/argo-e2e/app/config/tls"
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
steps:
|
||||
- run:
|
||||
name: Install and start K3S v0.5.0
|
||||
command: |
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
sudo chmod -R a+rw /etc/rancher/k3s
|
||||
kubectl version
|
||||
environment:
|
||||
INSTALL_K3S_EXEC: --docker
|
||||
INSTALL_K3S_VERSION: v0.5.0
|
||||
- prepare_environment
|
||||
- checkout
|
||||
- run:
|
||||
name: Fix permissions on filesystem
|
||||
command: |
|
||||
mkdir -p /home/circleci/.go_workspace/pkg/mod
|
||||
chmod -R 777 /home/circleci/.go_workspace/pkg/mod
|
||||
mkdir -p /tmp/go-build-cache
|
||||
chmod -R 777 /tmp/go-build-cache
|
||||
- attach_go_cache
|
||||
- run:
|
||||
name: Update kubectl configuration for container
|
||||
command: |
|
||||
ipaddr=$(ifconfig $IFACE |grep "inet " | awk '{print $2}')
|
||||
if echo $ipaddr | grep -q 'addr:'; then
|
||||
ipaddr=$(echo $ipaddr | awk -F ':' '{print $2}')
|
||||
fi
|
||||
test -d $HOME/.kube || mkdir -p $HOME/.kube
|
||||
kubectl config view --raw | sed -e "s/127.0.0.1:6443/${ipaddr}:6443/g" -e "s/localhost:6443/${ipaddr}:6443/g" > $HOME/.kube/config
|
||||
environment:
|
||||
IFACE: ens4
|
||||
- run:
|
||||
name: Start E2E test server
|
||||
command: make start-e2e
|
||||
background: true
|
||||
environment:
|
||||
DOCKER_SRCDIR: /home/circleci/.go_workspace/src
|
||||
ARGOCD_E2E_TEST: "true"
|
||||
ARGOCD_IN_CI: "true"
|
||||
GOPATH: /home/circleci/.go_workspace
|
||||
- run:
|
||||
name: Wait for API server to become available
|
||||
command: |
|
||||
count=1
|
||||
until curl -v http://localhost:8080/healthz; do
|
||||
sleep 10;
|
||||
if test $count -ge 60; then
|
||||
echo "Timeout"
|
||||
exit 1
|
||||
fi
|
||||
count=$((count+1))
|
||||
done
|
||||
- run:
|
||||
name: Run E2E tests
|
||||
command: |
|
||||
make test-e2e
|
||||
environment:
|
||||
ARGOCD_OPTS: "--plaintext"
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
IFACE: ens4
|
||||
DOCKER_SRCDIR: /home/circleci/.go_workspace/src
|
||||
GOPATH: /home/circleci/.go_workspace
|
||||
- store_test_results:
|
||||
path: test-results
|
||||
- store_artifacts:
|
||||
path: test-results
|
||||
destination: .
|
||||
ui:
|
||||
docker:
|
||||
- image: node:11.15.0
|
||||
working_directory: ~/argo-cd/ui
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/argo-cd/
|
||||
- restore_cache:
|
||||
keys:
|
||||
- yarn-packages-v4-{{ checksum "yarn.lock" }}
|
||||
- run: yarn install --frozen-lockfile --ignore-optional --non-interactive
|
||||
- save_cache:
|
||||
key: yarn-packages-v4-{{ checksum "yarn.lock" }}
|
||||
paths: [~/.cache/yarn, node_modules]
|
||||
- run: yarn test
|
||||
- run: ./node_modules/.bin/codecov -p ..
|
||||
- run: NODE_ENV='production' yarn build
|
||||
- run: yarn lint
|
||||
- save_node_modules
|
||||
|
||||
orbs:
|
||||
sonarcloud: sonarsource/sonarcloud@1.0.1
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
workflow:
|
||||
jobs:
|
||||
- build
|
||||
- test:
|
||||
requires:
|
||||
- build
|
||||
- codegen:
|
||||
requires:
|
||||
- build
|
||||
- ui:
|
||||
requires:
|
||||
- build
|
||||
- sonarcloud:
|
||||
context: SonarCloud
|
||||
requires:
|
||||
- test
|
||||
- ui
|
||||
- e2e:
|
||||
requires:
|
||||
- build
|
||||
349
.github/workflows/ci-build.yaml
vendored
Normal file
349
.github/workflows/ci-build.yaml
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
name: Integration tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-*'
|
||||
- '!release-1.4'
|
||||
- '!release-1.5'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release-1.6'
|
||||
|
||||
jobs:
|
||||
check-go:
|
||||
name: Ensure Go modules synchronicity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
- name: Download all Go modules
|
||||
run: |
|
||||
go mod download
|
||||
- name: Check for tidyness of go.mod and go.sum
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code -- .
|
||||
|
||||
build-go:
|
||||
name: Build & cache Go code
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
- name: Download all Go modules
|
||||
run: |
|
||||
go mod download
|
||||
- name: Compile all packages
|
||||
run: make build-local
|
||||
|
||||
lint-go:
|
||||
name: Lint Go code
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.29
|
||||
args: --timeout 5m --exclude SA5011
|
||||
|
||||
test-go:
|
||||
name: Run unit tests for Go packages
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-go
|
||||
steps:
|
||||
- name: Create checkout directory
|
||||
run: mkdir -p ~/go/src/github.com/argoproj
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Create symlink in GOPATH
|
||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt-get install git -y
|
||||
- name: Switch to temporal branch so we re-attach head
|
||||
run: |
|
||||
git switch -c temporal-pr-branch
|
||||
git status
|
||||
- name: Fetch complete history for blame information
|
||||
run: |
|
||||
git fetch --prune --no-tags --depth=1 origin +refs/heads/*:refs/remotes/origin/*
|
||||
- name: Add ~/go/bin to PATH
|
||||
run: |
|
||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||
- name: Add /usr/local/bin to PATH
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
- name: Install all tools required for building & testing
|
||||
run: |
|
||||
make install-test-tools-local
|
||||
- name: Setup git username and email
|
||||
run: |
|
||||
git config --global user.name "John Doe"
|
||||
git config --global user.email "john.doe@example.com"
|
||||
- name: Download and vendor all required packages
|
||||
run: |
|
||||
go mod download
|
||||
- name: Run all unit tests
|
||||
run: make test-local
|
||||
- name: Generate code coverage artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: code-coverage
|
||||
path: coverage.out
|
||||
- name: Generate test results artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
||||
|
||||
codegen:
|
||||
name: Check changes to generated code
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
- name: Create symlink in GOPATH
|
||||
run: |
|
||||
mkdir -p ~/go/src/github.com/argoproj
|
||||
cp -a ../argo-cd ~/go/src/github.com/argoproj
|
||||
- name: Add ~/go/bin to PATH
|
||||
run: |
|
||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||
- name: Add /usr/local/bin to PATH
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Download & vendor dependencies
|
||||
run: |
|
||||
# We need to vendor go modules for codegen yet
|
||||
go mod download
|
||||
go mod vendor -v
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
- name: Install toolchain for codegen
|
||||
run: |
|
||||
make install-codegen-tools-local
|
||||
make install-go-tools-local
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
- name: Initialize local Helm
|
||||
run: |
|
||||
helm2 init --client-only
|
||||
- name: Run codegen
|
||||
run: |
|
||||
set -x
|
||||
export GOPATH=$(go env GOPATH)
|
||||
make codegen-local
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
- name: Check nothing has changed
|
||||
run: |
|
||||
set -xo pipefail
|
||||
git diff --exit-code -- . ':!go.sum' ':!go.mod' ':!assets/swagger.json' | tee codegen.patch
|
||||
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||
|
||||
build-ui:
|
||||
name: Build, test & lint UI code
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup NodeJS
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '11.15.0'
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Install node dependencies
|
||||
run: |
|
||||
cd ui && yarn install --frozen-lockfile --ignore-optional --non-interactive
|
||||
- name: Build UI code
|
||||
run: |
|
||||
yarn test
|
||||
yarn build
|
||||
env:
|
||||
NODE_ENV: production
|
||||
working-directory: ui/
|
||||
- name: Run ESLint
|
||||
run: yarn lint
|
||||
working-directory: ui/
|
||||
|
||||
analyze:
|
||||
name: Process & analyze test artifacts
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test-go
|
||||
- build-ui
|
||||
env:
|
||||
sonar_secret: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Restore node dependency cache
|
||||
id: cache-dependencies
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ui/node_modules
|
||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Remove other node_modules directory
|
||||
run: |
|
||||
rm -rf ui/node_modules/argo-ui/node_modules
|
||||
- name: Create test-results directory
|
||||
run: |
|
||||
mkdir -p test-results
|
||||
- name: Get code coverage artifiact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: code-coverage
|
||||
- name: Get test result artifact
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results
|
||||
- name: Upload code coverage information to codecov.io
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
file: coverage.out
|
||||
- name: Perform static code analysis using SonarCloud
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
SCANNER_VERSION: 4.2.0.1873
|
||||
SCANNER_PATH: /tmp/cache/scanner
|
||||
OS: linux
|
||||
run: |
|
||||
# We do not use the provided action, because it does contain an old
|
||||
# version of the scanner, and also takes time to build.
|
||||
set -e
|
||||
mkdir -p ${SCANNER_PATH}
|
||||
export SONAR_USER_HOME=${SCANNER_PATH}/.sonar
|
||||
if [[ ! -x "${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner" ]]; then
|
||||
curl -Ol https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip
|
||||
unzip -qq -o sonar-scanner-cli-${SCANNER_VERSION}-${OS}.zip -d ${SCANNER_PATH}
|
||||
fi
|
||||
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
chmod +x ${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/jre/bin/java
|
||||
|
||||
# Explicitly set NODE_MODULES
|
||||
export NODE_MODULES=${PWD}/ui/node_modules
|
||||
export NODE_PATH=${PWD}/ui/node_modules
|
||||
|
||||
${SCANNER_PATH}/sonar-scanner-${SCANNER_VERSION}-${OS}/bin/sonar-scanner
|
||||
if: env.sonar_secret != ''
|
||||
|
||||
test-e2e:
|
||||
name: Run end-to-end tests
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-go
|
||||
env:
|
||||
GOPATH: /home/runner/go
|
||||
ARGOCD_FAKE_IN_CLUSTER: "true"
|
||||
ARGOCD_SSH_DATA_PATH: "/tmp/argo-e2e/app/config/ssh"
|
||||
ARGOCD_TLS_DATA_PATH: "/tmp/argo-e2e/app/config/tls"
|
||||
ARGOCD_E2E_SSH_KNOWN_HOSTS: "../fixture/certs/ssh_known_hosts"
|
||||
ARGOCD_E2E_K3S: "true"
|
||||
ARGOCD_IN_CI: "true"
|
||||
ARGOCD_E2E_APISERVER_PORT: "8088"
|
||||
ARGOCD_SERVER: "127.0.0.1:8088"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
- name: Install K3S
|
||||
env:
|
||||
INSTALL_K3S_VERSION: v0.5.0
|
||||
run: |
|
||||
set -x
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
sudo chmod -R a+rw /etc/rancher/k3s
|
||||
sudo mkdir -p $HOME/.kube && sudo chown -R runner $HOME/.kube
|
||||
sudo k3s kubectl config view --raw > $HOME/.kube/config
|
||||
sudo chown runner $HOME/.kube/config
|
||||
kubectl version
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||
- name: Add ~/go/bin to PATH
|
||||
run: |
|
||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||
- name: Add /usr/local/bin to PATH
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Download Go dependencies
|
||||
run: |
|
||||
go mod download
|
||||
go get github.com/mattn/goreman
|
||||
- name: Install all tools required for building & testing
|
||||
run: |
|
||||
make install-test-tools-local
|
||||
- name: Setup git username and email
|
||||
run: |
|
||||
git config --global user.name "John Doe"
|
||||
git config --global user.email "john.doe@example.com"
|
||||
- name: Pull Docker image required for tests
|
||||
run: |
|
||||
docker pull quay.io/dexidp/dex:v2.22.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:5.0.3-alpine
|
||||
- name: Run E2E server and wait for it being available
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
set -x
|
||||
# Something is weird in GH runners -- there's a phantom listener for
|
||||
# port 8080 which is not visible in netstat -tulpen, but still there
|
||||
# with a HTTP listener. We have API server listening on port 8088
|
||||
# instead.
|
||||
make start-e2e-local &
|
||||
count=1
|
||||
until curl -f http://127.0.0.1:8088/healthz; do
|
||||
sleep 10;
|
||||
if test $count -ge 60; then
|
||||
echo "Timeout"
|
||||
exit 1
|
||||
fi
|
||||
count=$((count+1))
|
||||
done
|
||||
- name: Run E2E testsuite
|
||||
run: |
|
||||
set -x
|
||||
make test-e2e-local
|
||||
13
.github/workflows/image.yaml
vendored
13
.github/workflows/image.yaml
vendored
@@ -13,19 +13,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.1'
|
||||
go-version: '1.14.12'
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
path: src/github.com/argoproj/argo-cd
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: src/github.com/argoproj/argo-cd/vendor
|
||||
key: ${{ runner.os }}-go-dep-${{ hashFiles('**/Gopkg.lock') }}
|
||||
# download dependencies
|
||||
- run: mkdir -p $GITHUB_WORKSPACE/bin && curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
working-directory: src/github.com/argoproj/argo-cd
|
||||
- run: $GOPATH/bin/dep ensure -v
|
||||
working-directory: ./src/github.com/argoproj/argo-cd
|
||||
|
||||
# get image tag
|
||||
- run: echo ::set-output name=tag::$(cat ./VERSION)-${GITHUB_SHA::8}
|
||||
@@ -56,4 +47,4 @@ jobs:
|
||||
git config --global user.name 'CI'
|
||||
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push)
|
||||
working-directory: argoproj-deployments/argocd
|
||||
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-Github-Package-Registry/m-p/41202/thread-id/9811
|
||||
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-Github-Package-Registry/m-p/41202/thread-id/9811
|
||||
|
||||
289
.github/workflows/release.yaml
vendored
Normal file
289
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
name: Create ArgoCD release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'release-v*'
|
||||
- '!release-v1.5*'
|
||||
- '!release-v1.4*'
|
||||
- '!release-v1.3*'
|
||||
- '!release-v1.2*'
|
||||
- '!release-v1.1*'
|
||||
- '!release-v1.0*'
|
||||
- '!release-v0*'
|
||||
jobs:
|
||||
prepare-release:
|
||||
name: Perform automatic release on trigger ${{ github.ref }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# 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: argoproj
|
||||
# Whether to create & push image and release assets
|
||||
DRY_RUN: false
|
||||
# Whether a draft release should be created, instead of public one
|
||||
DRAFT_RELEASE: false
|
||||
# The name of the repository containing tap formulae
|
||||
TAP_REPOSITORY: argoproj/homebrew-tap
|
||||
# Whether to update homebrew with this release as well
|
||||
# Set RELEASE_HOMEBREW_TOKEN secret in repository for this to work - needs
|
||||
# access to public repositories (or homebrew-tap repo specifically)
|
||||
UPDATE_HOMEBREW: false
|
||||
# Name of the GitHub user for Git config
|
||||
GIT_USERNAME: argo-bot
|
||||
# E-Mail of the GitHub user for Git config
|
||||
GIT_EMAIL: argoproj@gmail.com
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check if the published tag is well formed and setup vars
|
||||
run: |
|
||||
set -xue
|
||||
# Target version must match major.minor.patch and optional -rcX suffix
|
||||
# where X must be a number.
|
||||
TARGET_VERSION=${SOURCE_TAG#*release-v}
|
||||
if ! echo ${TARGET_VERSION} | egrep '^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)*$'; then
|
||||
echo "::error::Target version '${TARGET_VERSION}' is malformed, refusing to continue." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Target branch is the release branch we're going to operate on
|
||||
# Its name is 'release-<major>.<minor>'
|
||||
TARGET_BRANCH="release-${TARGET_VERSION%\.[0-9]*}"
|
||||
|
||||
# The release tag is the source tag, minus the release- prefix
|
||||
RELEASE_TAG="${SOURCE_TAG#*release-}"
|
||||
|
||||
# Whether this is a pre-release (indicated by -rc suffix)
|
||||
PRE_RELEASE=false
|
||||
if echo "${RELEASE_TAG}" | egrep -- '-rc[0-9]+$'; then
|
||||
PRE_RELEASE=true
|
||||
fi
|
||||
|
||||
# We must not have a release trigger within the same release branch,
|
||||
# because that means a release for this branch is already running.
|
||||
if git tag -l | grep "release-v${TARGET_VERSION%\.[0-9]*}" | grep -v "release-v${TARGET_VERSION}"; then
|
||||
echo "::error::Another release for branch ${TARGET_BRANCH} is currently in progress."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure that release do not yet exist
|
||||
if git rev-parse ${RELEASE_TAG}; then
|
||||
echo "::error::Release tag ${RELEASE_TAG} already exists in repository. Refusing to continue."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make the variables available in follow-up steps
|
||||
echo "TARGET_VERSION=${TARGET_VERSION}" >> $GITHUB_ENV
|
||||
echo "TARGET_BRANCH=${TARGET_BRANCH}" >> $GITHUB_ENV
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_ENV
|
||||
echo "PRE_RELEASE=${PRE_RELEASE}" >> $GITHUB_ENV
|
||||
|
||||
- name: Check if our release tag has a correct annotation
|
||||
run: |
|
||||
set -ue
|
||||
# Fetch all tag information as well
|
||||
git fetch --prune --tags --force
|
||||
|
||||
echo "=========== BEGIN COMMIT MESSAGE ============="
|
||||
git show ${SOURCE_TAG}
|
||||
echo "============ END COMMIT MESSAGE =============="
|
||||
|
||||
# Quite dirty hack to get the release notes from the annotated tag
|
||||
# into a temporary file.
|
||||
RELEASE_NOTES=$(mktemp -p /tmp release-notes.XXXXXX)
|
||||
|
||||
prefix=true
|
||||
begin=false
|
||||
git show ${SOURCE_TAG} | while read line; do
|
||||
# Whatever is in commit history for the tag, we only want that
|
||||
# annotation from our tag. We discard everything else.
|
||||
if test "$begin" = "false"; then
|
||||
if echo $line | grep -q "tag ${SOURCE_TAG#refs/tags/}"; then begin="true"; fi
|
||||
continue
|
||||
fi
|
||||
if test "$prefix" = "true"; then
|
||||
if test -z "$line"; then prefix=false; fi
|
||||
else
|
||||
if echo $line | egrep -q '^commit [0-9a-f]+'; then
|
||||
break
|
||||
fi
|
||||
echo $line >> ${RELEASE_NOTES}
|
||||
fi
|
||||
done
|
||||
|
||||
# For debug purposes
|
||||
echo "============BEGIN RELEASE NOTES================="
|
||||
cat ${RELEASE_NOTES}
|
||||
echo "=============END RELEASE NOTES=================="
|
||||
|
||||
# Too short release notes are suspicious. We need at least 100 bytes.
|
||||
relNoteLen=$(stat -c '%s' $RELEASE_NOTES)
|
||||
if test $relNoteLen -lt 100; then
|
||||
echo "::error::No release notes provided in tag annotation (or tag is not annotated)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for magic string '## Quick Start' in head of release notes
|
||||
if ! head -2 ${RELEASE_NOTES} | grep -iq '## Quick Start'; then
|
||||
echo "::error::Release notes seem invalid, quick start section not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We store path to temporary release notes file for later reading, we
|
||||
# need it when creating release.
|
||||
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
|
||||
- name: Setup Git author information
|
||||
run: |
|
||||
set -ue
|
||||
git config --global user.email "${GIT_EMAIL}"
|
||||
git config --global user.name "${GIT_USERNAME}"
|
||||
|
||||
- name: Checkout corresponding release branch
|
||||
run: |
|
||||
set -ue
|
||||
echo "Switching to release branch '${TARGET_BRANCH}'"
|
||||
if ! git checkout ${TARGET_BRANCH}; then
|
||||
echo "::error::Checking out release branch '${TARGET_BRANCH}' for target version '${TARGET_VERSION}' (tagged '${RELEASE_TAG}') failed. Does it exist in repo?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create VERSION information
|
||||
run: |
|
||||
set -ue
|
||||
echo "Bumping version from $(cat VERSION) to ${TARGET_VERSION}"
|
||||
echo "${TARGET_VERSION}" > VERSION
|
||||
git commit -m "Bump version to ${TARGET_VERSION}" VERSION
|
||||
|
||||
- name: Generate new set of manifests
|
||||
run: |
|
||||
set -ue
|
||||
make install-codegen-tools-local
|
||||
helm2 init --client-only
|
||||
make manifests-local VERSION=${TARGET_VERSION}
|
||||
git diff
|
||||
git commit manifests/ -m "Bump version to ${TARGET_VERSION}"
|
||||
|
||||
- name: Create the release tag
|
||||
run: |
|
||||
set -ue
|
||||
echo "Creating release ${RELEASE_TAG}"
|
||||
git tag ${RELEASE_TAG}
|
||||
|
||||
- name: Build Docker image for release
|
||||
run: |
|
||||
set -ue
|
||||
git clean -fd
|
||||
mkdir -p dist/
|
||||
make image IMAGE_TAG="${TARGET_VERSION}" DOCKER_PUSH=false
|
||||
make release-cli
|
||||
chmod +x ./dist/argocd-linux-amd64
|
||||
./dist/argocd-linux-amd64 version --client
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Push docker image to repository
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.RELEASE_DOCKERHUB_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.RELEASE_DOCKERHUB_TOKEN }}
|
||||
run: |
|
||||
set -ue
|
||||
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
|
||||
docker push ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Read release notes file
|
||||
id: release-notes
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ${{ env.RELEASE_NOTES }}
|
||||
|
||||
- name: Push changes to release branch
|
||||
run: |
|
||||
set -ue
|
||||
git push origin ${TARGET_BRANCH}
|
||||
git push origin ${RELEASE_TAG}
|
||||
|
||||
- name: Create GitHub release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: create_release
|
||||
with:
|
||||
tag_name: ${{ env.RELEASE_TAG }}
|
||||
release_name: ${{ env.RELEASE_TAG }}
|
||||
draft: ${{ env.DRAFT_RELEASE }}
|
||||
prerelease: ${{ env.PRE_RELEASE }}
|
||||
body: ${{ steps.release-notes.outputs.content }}
|
||||
|
||||
- name: Upload argocd-linux-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-linux-amd64
|
||||
asset_name: argocd-linux-amd64
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-darwin-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-darwin-amd64
|
||||
asset_name: argocd-darwin-amd64
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-windows-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-windows-amd64.exe
|
||||
asset_name: argocd-windows-amd64.exe
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Check out homebrew tap repository
|
||||
uses: actions/checkout@v2
|
||||
env:
|
||||
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}
|
||||
with:
|
||||
repository: ${{ env.TAP_REPOSITORY }}
|
||||
path: homebrew-tap
|
||||
fetch-depth: 0
|
||||
token: ${{ env.HOMEBREW_TOKEN }}
|
||||
if: ${{ env.HOMEBREW_TOKEN != '' && env.UPDATE_HOMEBREW == 'true' && env.PRE_RELEASE != 'true' }}
|
||||
|
||||
- name: Update homebrew tap formula
|
||||
env:
|
||||
HOMEBREW_TOKEN: ${{ secrets.RELEASE_HOMEBREW_TOKEN }}
|
||||
run: |
|
||||
set -ue
|
||||
cd homebrew-tap
|
||||
./update.sh argocd ${TARGET_VERSION}
|
||||
git commit -am "Update argocd to ${TARGET_VERSION}"
|
||||
git push
|
||||
cd ..
|
||||
rm -rf homebrew-tap
|
||||
if: ${{ env.HOMEBREW_TOKEN != '' && env.UPDATE_HOMEBREW == 'true' && env.PRE_RELEASE != 'true' }}
|
||||
|
||||
- name: Delete original request tag from repository
|
||||
run: |
|
||||
set -ue
|
||||
git push --delete origin ${SOURCE_TAG}
|
||||
if: ${{ always() }}
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,6 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## v1.5.3 (Unreleased)
|
||||
## v1.5.5 (2020-05-16)
|
||||
|
||||
- feat: add Rollout restart action (#3557)
|
||||
- fix: enable redis retries; add redis request duration metric (#3547)
|
||||
- fix: when --rootpath is on, 404 is returned when URL contains encoded URI (#3564)
|
||||
|
||||
## v1.5.4 (2020-05-05)
|
||||
|
||||
- fix: CLI commands with --grpc-web
|
||||
|
||||
## v1.5.3 (2020-05-01)
|
||||
|
||||
This patch release introduces a set of enhancements and bug fixes. Here are most notable changes:
|
||||
|
||||
@@ -432,7 +442,7 @@ https://youtu.be/GP7xtrnNznw
|
||||
|
||||
##### Orphan Resources
|
||||
|
||||
Some users would like to make sure that resources in a namespace are managed only by Argo CD. So we've introduced the concept of an "orphan resource" - any resource that is in namespace associated with an app, but not managed by Argo CD. This is enabled in the project settings. Once enabled, Argo CD will show in the app view any resources in the app's namepspace that is not mananged by Argo CD.
|
||||
Some users would like to make sure that resources in a namespace are managed only by Argo CD. So we've introduced the concept of an "orphan resource" - any resource that is in namespace associated with an app, but not managed by Argo CD. This is enabled in the project settings. Once enabled, Argo CD will show in the app view any resources in the app's namespace that is not managed by Argo CD.
|
||||
|
||||
https://youtu.be/9ZoTevVQf5I
|
||||
|
||||
@@ -475,7 +485,7 @@ There may be instances when you want to control the times during which an Argo C
|
||||
#### Bug Fixes
|
||||
|
||||
- failed parsing on parameters with comma (#1660)
|
||||
- Statefuleset with OnDelete Update Strategy stuck progressing (#1881)
|
||||
- Statefulset with OnDelete Update Strategy stuck progressing (#1881)
|
||||
- Warning during secret diffing (#1923)
|
||||
- Error message "Unable to load data: key is missing" is confusing (#1944)
|
||||
- OIDC group bindings are truncated (#2006)
|
||||
@@ -514,7 +524,7 @@ There may be instances when you want to control the times during which an Argo C
|
||||
- Creating an application from Helm repository should select "Helm" as source type (#2378)
|
||||
- The parameters of ValidateAccess GRPC method should not be logged (#2386)
|
||||
- Maintenance window meaning is confusing (#2398)
|
||||
- UI bug when targetRevision is ommited (#2407)
|
||||
- UI bug when targetRevision is omitted (#2407)
|
||||
- Too many vulnerabilities in Docker image (#2425)
|
||||
- proj windows commands not consistent with other commands (#2443)
|
||||
- Custom resource actions cannot be executed from the UI (#2448)
|
||||
@@ -608,7 +618,7 @@ Support for Git LFS enabled repositories - now you can store Helm charts as tar
|
||||
+ Added 'SyncFail' to possible HookTypes in UI (#2147)
|
||||
+ Support for Git LFS enabled repositories (#1853)
|
||||
+ Server certificate and known hosts management (#1514)
|
||||
+ Client HTTPS certifcates for private git repositories (#1945)
|
||||
+ Client HTTPS certificates for private git repositories (#1945)
|
||||
+ Badge for application status (#1435)
|
||||
+ Make the health check for APIService a built in (#1841)
|
||||
+ Bitbucket Server and Gogs webhook providers (#1269)
|
||||
@@ -648,7 +658,7 @@ Support for Git LFS enabled repositories - now you can store Helm charts as tar
|
||||
- Fix history api fallback implementation to support app names with dots (#2114)
|
||||
- Fixes some code issues related to Kustomize build options. (#2146)
|
||||
- Adds checks around valid paths for apps (#2133)
|
||||
- Enpoint incorrectly considered top level managed resource (#2060)
|
||||
- Endpoint incorrectly considered top level managed resource (#2060)
|
||||
- Allow adding certs for hostnames ending on a dot (#2116)
|
||||
|
||||
#### Other
|
||||
@@ -971,7 +981,7 @@ Argo CD introduces some additional CLI commands:
|
||||
#### Label selector changes, dex-server rename
|
||||
|
||||
The label selectors for deployments were been renamed to use kubernetes common labels
|
||||
(`app.kuberentes.io/name=NAME` instead of `app=NAME`). Since K8s deployment label selectors are
|
||||
(`app.kubernetes.io/name=NAME` instead of `app=NAME`). Since K8s deployment label selectors are
|
||||
immutable, during an upgrade from v0.11 to v0.12, the old deployments should be deleted using
|
||||
`--cascade=false` which allows the new deployments to be created without introducing downtime.
|
||||
Once the new deployments are ready, the older replicasets can be deleted. Use the following
|
||||
@@ -1068,7 +1078,7 @@ has a minimum client version of v0.12.0. Older CLI clients will be rejected.
|
||||
- Fix CRD creation/deletion handling (#1249)
|
||||
- Git cloning via SSH was not verifying host public key (#1276)
|
||||
- Fixed multiple goroutine leaks in controller and api-server
|
||||
- Fix isssue where `argocd app set -p` required repo privileges. (#1280)
|
||||
- Fix issue where `argocd app set -p` required repo privileges. (#1280)
|
||||
- Fix local diff of non-namespaced resources. Also handle duplicates in local diff (#1289)
|
||||
- Deprecated resource kinds from 'extensions' groups are not reconciled correctly (#1232)
|
||||
- Fix issue where CLI would panic after timeout when cli did not have get permissions (#1209)
|
||||
@@ -1246,7 +1256,7 @@ which have a dependency to external helm repositories.
|
||||
|
||||
+ Allow more fine-grained sync (issue #508)
|
||||
+ Display init container logs (issue #681)
|
||||
+ Redirect to /auth/login instead of /login when SSO token is used for authenticaion (issue #348)
|
||||
+ Redirect to /auth/login instead of /login when SSO token is used for authentication (issue #348)
|
||||
+ Support ability to use a helm values files from a URL (issue #624)
|
||||
+ Support public not-connected repo in app creation UI (issue #426)
|
||||
+ Use ksonnet CLI instead of ksonnet libs (issue #626)
|
||||
@@ -1521,7 +1531,7 @@ RBAC policy rules, need to be rewritten to include one extra column with the eff
|
||||
+ Sync/Rollback/Delete is asynchronously handled by controller
|
||||
* Refactor CRUD operation on clusters and repos
|
||||
* Sync will always perform kubectl apply
|
||||
* Synced Status considers last-applied-configuration annotatoin
|
||||
* Synced Status considers last-applied-configuration annotation
|
||||
* Server & namespace are mandatory fields (still inferred from app.yaml)
|
||||
* Manifests are memoized in repo server
|
||||
- Fix connection timeouts to SSH repos
|
||||
|
||||
36
Dockerfile
36
Dockerfile
@@ -4,7 +4,7 @@ ARG BASE_IMAGE=debian:10-slim
|
||||
# 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.14.1 as builder
|
||||
FROM golang:1.14.12 as builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -25,8 +25,8 @@ WORKDIR /tmp
|
||||
|
||||
ADD hack/install.sh .
|
||||
ADD hack/installers installers
|
||||
ADD hack/tool-versions.sh .
|
||||
|
||||
RUN ./install.sh dep-linux
|
||||
RUN ./install.sh packr-linux
|
||||
RUN ./install.sh kubectl-linux
|
||||
RUN ./install.sh ksonnet-linux
|
||||
@@ -50,9 +50,9 @@ RUN groupadd -g 999 argocd && \
|
||||
chmod g=u /home/argocd && \
|
||||
chmod g=u /etc/passwd && \
|
||||
apt-get update && \
|
||||
apt-get install -y git git-lfs python3-pip && \
|
||||
apt-get install -y git git-lfs python3-pip tini && \
|
||||
apt-get clean && \
|
||||
pip3 install awscli==1.17.7 && \
|
||||
pip3 install awscli==1.18.80 && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY hack/git-ask-pass.sh /usr/local/bin/git-ask-pass.sh
|
||||
@@ -75,7 +75,7 @@ RUN mkdir -p /app/config/tls
|
||||
# workaround ksonnet issue https://github.com/ksonnet/ksonnet/issues/298
|
||||
ENV USER=argocd
|
||||
|
||||
USER argocd
|
||||
USER 999
|
||||
WORKDIR /home/argocd
|
||||
|
||||
####################################################################################################
|
||||
@@ -97,28 +97,26 @@ RUN NODE_ENV='production' yarn build
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM golang:1.14.1 as argocd-build
|
||||
FROM golang:1.14.12 as argocd-build
|
||||
|
||||
COPY --from=builder /usr/local/bin/dep /usr/local/bin/dep
|
||||
COPY --from=builder /usr/local/bin/packr /usr/local/bin/packr
|
||||
|
||||
# A dummy directory is created under $GOPATH/src/dummy so we are able to use dep
|
||||
# to install all the packages of our dep lock file
|
||||
COPY Gopkg.toml ${GOPATH}/src/dummy/Gopkg.toml
|
||||
COPY Gopkg.lock ${GOPATH}/src/dummy/Gopkg.lock
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
RUN cd ${GOPATH}/src/dummy && \
|
||||
dep ensure -vendor-only && \
|
||||
mv vendor/* ${GOPATH}/src/ && \
|
||||
rmdir vendor
|
||||
COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
|
||||
RUN go mod download
|
||||
|
||||
# Perform the build
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
COPY . .
|
||||
RUN make cli server controller repo-server argocd-util && \
|
||||
make CLI_NAME=argocd-darwin-amd64 GOOS=darwin cli && \
|
||||
make CLI_NAME=argocd-windows-amd64.exe GOOS=windows cli
|
||||
RUN make cli server controller repo-server argocd-util
|
||||
|
||||
ARG BUILD_ALL_CLIS=true
|
||||
RUN if [ "$BUILD_ALL_CLIS" = "true" ] ; then \
|
||||
make CLI_NAME=argocd-darwin-amd64 GOOS=darwin cli && \
|
||||
make CLI_NAME=argocd-windows-amd64.exe GOOS=windows cli \
|
||||
; fi
|
||||
|
||||
####################################################################################################
|
||||
# Final image
|
||||
|
||||
2164
Gopkg.lock
generated
2164
Gopkg.lock
generated
File diff suppressed because it is too large
Load Diff
130
Gopkg.toml
130
Gopkg.toml
@@ -1,130 +0,0 @@
|
||||
# Packages should only be added to the following list when we use them *outside* of our go code.
|
||||
# (e.g. we want to build the binary to invoke as part of the build process, such as in
|
||||
# generate-proto.sh). Normal use of golang packages should be added via `dep ensure`, and pinned
|
||||
# with a [[constraint]] or [[override]] when version is important.
|
||||
required = [
|
||||
"github.com/golang/protobuf/protoc-gen-go",
|
||||
"github.com/gogo/protobuf/protoc-gen-gofast",
|
||||
"github.com/gogo/protobuf/protoc-gen-gogofast",
|
||||
"k8s.io/code-generator/cmd/go-to-protobuf",
|
||||
"k8s.io/kube-openapi/cmd/openapi-gen",
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
|
||||
"golang.org/x/sync/errgroup",
|
||||
]
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "1.15.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
version = "1.3.1"
|
||||
|
||||
# override github.com/grpc-ecosystem/go-grpc-middleware's constraint on master
|
||||
[[override]]
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/grpc-ecosystem/grpc-gateway"
|
||||
version = "v1.3.1"
|
||||
|
||||
# prometheus does not believe in semversioning yet
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
revision = "7858729281ec582767b20e0d696b6041d995d5e0"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/api"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/kubernetes"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/code-generator"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/apimachinery"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/apiextensions-apiserver"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/apiserver"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/kubectl"
|
||||
|
||||
[[override]]
|
||||
branch = "release-1.16"
|
||||
name = "k8s.io/cli-runtime"
|
||||
|
||||
[[override]]
|
||||
version = "2.0.3"
|
||||
name = "sigs.k8s.io/kustomize"
|
||||
|
||||
# ASCIIRenderer does not implement blackfriday.Renderer
|
||||
[[override]]
|
||||
name = "github.com/russross/blackfriday"
|
||||
version = "1.5.2"
|
||||
|
||||
[[override]]
|
||||
branch = "release-13.0"
|
||||
name = "k8s.io/client-go"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/casbin/casbin"
|
||||
version = "1.9.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.5.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gobuffalo/packr"
|
||||
version = "v1.11.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/argoproj/pkg"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/yudai/gojsondiff"
|
||||
|
||||
# Fixes: Could not introduce sigs.k8s.io/kustomize@v2.0.3, as it has a dependency on github.com/spf13/cobra with constraint ^0.0.2, which has no overlap with existing constraint 0.0.5 from (root)
|
||||
[[override]]
|
||||
name = "github.com/spf13/cobra"
|
||||
revision = "0.0.5"
|
||||
|
||||
# TODO: move off of k8s.io/kube-openapi and use controller-tools for CRD spec generation
|
||||
# (override argoproj/argo contraint on master)
|
||||
[[override]]
|
||||
revision = "30be4d16710ac61bce31eb28a01054596fe6a9f1"
|
||||
name = "k8s.io/kube-openapi"
|
||||
|
||||
# jsonpatch replace operation does not apply: doc is missing key: /metadata/annotations
|
||||
[[override]]
|
||||
name = "github.com/evanphx/json-patch"
|
||||
version = "v4.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/google/uuid"
|
||||
version = "1.1.1"
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/alicebob/miniredis"
|
||||
version = "2.7.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/bsm/redislock"
|
||||
version = "0.4.3"
|
||||
99
Makefile
99
Makefile
@@ -8,8 +8,8 @@ BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
GIT_COMMIT=$(shell git rev-parse HEAD)
|
||||
GIT_TAG=$(shell if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)
|
||||
GIT_TREE_STATE=$(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)
|
||||
PACKR_CMD=$(shell if [ "`which packr`" ]; then echo "packr"; else echo "go run vendor/github.com/gobuffalo/packr/packr/main.go"; fi)
|
||||
VOLUME_MOUNT=$(shell if test "$(go env GOOS)"=="darwin"; then echo ":delegated"; elif test selinuxenabled; then echo ":Z"; else echo ""; fi)
|
||||
PACKR_CMD=$(shell if [ "`which packr`" ]; then echo "packr"; else echo "go run github.com/gobuffalo/packr/packr"; fi)
|
||||
VOLUME_MOUNT=$(shell if test "$(go env GOOS)" = "darwin"; then echo ":delegated"; elif test selinuxenabled; then echo ":Z"; else echo ""; fi)
|
||||
|
||||
GOPATH?=$(shell if test -x `which go`; then go env GOPATH; else echo "$(HOME)/go"; fi)
|
||||
GOCACHE?=$(HOME)/.cache/go-build
|
||||
@@ -22,7 +22,7 @@ ARGOCD_PROCFILE?=Procfile
|
||||
# Configuration for building argocd-test-tools image
|
||||
TEST_TOOLS_NAMESPACE?=argoproj
|
||||
TEST_TOOLS_IMAGE=argocd-test-tools
|
||||
TEST_TOOLS_TAG?=v0.4.0
|
||||
TEST_TOOLS_TAG?=v0.5.0
|
||||
ifdef TEST_TOOLS_NAMESPACE
|
||||
TEST_TOOLS_PREFIX=${TEST_TOOLS_NAMESPACE}/
|
||||
endif
|
||||
@@ -53,6 +53,7 @@ define run-in-test-server
|
||||
-e ARGOCD_E2E_TEST=$(ARGOCD_E2E_TEST) \
|
||||
-e ARGOCD_E2E_YARN_HOST=$(ARGOCD_E2E_YARN_HOST) \
|
||||
-v ${DOCKER_SRCDIR}:/go/src${VOLUME_MOUNT} \
|
||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||
@@ -74,6 +75,7 @@ define run-in-test-client
|
||||
-e GOCACHE=/tmp/go-build-cache \
|
||||
-e ARGOCD_LINT_GOGC=$(ARGOCD_LINT_GOGC) \
|
||||
-v ${DOCKER_SRCDIR}:/go/src${VOLUME_MOUNT} \
|
||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
||||
@@ -129,22 +131,27 @@ all: cli image argocd-util
|
||||
|
||||
.PHONY: gogen
|
||||
gogen:
|
||||
export GO111MODULE=off
|
||||
go generate ./util/argo/...
|
||||
|
||||
.PHONY: protogen
|
||||
protogen:
|
||||
export GO111MODULE=off
|
||||
./hack/generate-proto.sh
|
||||
|
||||
.PHONY: openapigen
|
||||
openapigen:
|
||||
export GO111MODULE=off
|
||||
./hack/update-openapi.sh
|
||||
|
||||
.PHONY: clientgen
|
||||
clientgen:
|
||||
export GO111MODULE=off
|
||||
./hack/update-codegen.sh
|
||||
|
||||
.PHONY: codegen-local
|
||||
codegen-local: gogen protogen clientgen openapigen manifests-local
|
||||
codegen-local: mod-vendor-local gogen protogen clientgen openapigen manifests-local
|
||||
rm -rf vendor/
|
||||
|
||||
.PHONY: codegen
|
||||
codegen:
|
||||
@@ -205,7 +212,7 @@ controller:
|
||||
|
||||
.PHONY: packr
|
||||
packr:
|
||||
go build -o ${DIST_DIR}/packr ./vendor/github.com/gobuffalo/packr/packr/
|
||||
go build -o ${DIST_DIR}/packr github.com/gobuffalo/packr/packr/
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -231,40 +238,32 @@ image:
|
||||
endif
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: armimage
|
||||
# The "BUILD_ALL_CLIS" argument is to skip building the CLIs for darwin and windows
|
||||
# which would take a really long time.
|
||||
armimage:
|
||||
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm . --build-arg BUILD_ALL_CLIS="false"
|
||||
|
||||
.PHONY: builder-image
|
||||
builder-image:
|
||||
docker build -t $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) --target builder .
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argo-cd-ci-builder:$(IMAGE_TAG) ; fi
|
||||
|
||||
# Pulls in all vendor dependencies
|
||||
.PHONY: dep
|
||||
dep:
|
||||
$(call run-in-test-client,dep ensure -v)
|
||||
.PHONY: mod-download
|
||||
mod-download:
|
||||
$(call run-in-test-client,go mod download)
|
||||
|
||||
# Pulls in all vendor dependencies (local version)
|
||||
.PHONY: dep-local
|
||||
dep-local:
|
||||
dep ensure -v
|
||||
.PHONY: mod-download-local
|
||||
mod-download-local:
|
||||
go mod download
|
||||
|
||||
# Pulls in all unvendored dependencies
|
||||
.PHONY: dep-ensure
|
||||
dep-ensure:
|
||||
$(call run-in-test-client,dep ensure -no-vendor -v)
|
||||
.PHONY: mod-vendor
|
||||
mod-vendor:
|
||||
$(call run-in-test-client,go mod vendor)
|
||||
|
||||
# Pulls in all unvendored dependencies (local version)
|
||||
.PHONY: dep-ensure-local
|
||||
dep-ensure-local:
|
||||
dep ensure -no-vendor
|
||||
|
||||
# Runs dep check in a container to ensure Gopkg.lock is up-to-date with dependencies
|
||||
.PHONY: dep-check
|
||||
dep-check:
|
||||
$(call run-in-test-client,make dep-check-local)
|
||||
|
||||
# Runs dep check locally to ensure Gopkg.lock is up-to-date with dependencies
|
||||
.PHONY: dep-check-local
|
||||
dep-check-local:
|
||||
if ! dep check -skip-vendor; then echo "Please make sure Gopkg.lock is up-to-date - see https://argoproj.github.io/argo-cd/developer-guide/faq/#why-does-the-build-step-fail"; exit 1; fi
|
||||
.PHONY: mod-vendor-local
|
||||
mod-vendor-local: mod-download-local
|
||||
go mod vendor
|
||||
|
||||
# Deprecated - replace by install-local-tools
|
||||
.PHONY: install-lint-tools
|
||||
@@ -300,8 +299,8 @@ build:
|
||||
|
||||
# Build all Go code (local version)
|
||||
.PHONY: build-local
|
||||
build-local:
|
||||
go build -p 1 -v `go list ./... | grep -v 'resource_customizations\|test/e2e'`
|
||||
build-local:
|
||||
go build -v `go list ./... | grep -v 'resource_customizations\|test/e2e'`
|
||||
|
||||
# Run all unit tests
|
||||
#
|
||||
@@ -331,6 +330,7 @@ test-e2e:
|
||||
.PHONY: test-e2e-local
|
||||
test-e2e-local: cli
|
||||
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
||||
export GO111MODULE=off
|
||||
NO_PROXY=* ./hack/test.sh -timeout 15m -v ./test/e2e
|
||||
|
||||
# Spawns a shell in the test server container for debugging purposes
|
||||
@@ -379,7 +379,7 @@ start:
|
||||
|
||||
# Starts a local instance of ArgoCD
|
||||
.PHONY: start-local
|
||||
start-local:
|
||||
start-local: mod-vendor-local
|
||||
# check we can connect to Docker to start Redis
|
||||
killall goreman || true
|
||||
kubectl create ns argocd || true
|
||||
@@ -388,7 +388,7 @@ start-local:
|
||||
ARGOCD_E2E_TEST=false \
|
||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||
|
||||
# Runs pre-commit validaiton with the virtualized toolchain
|
||||
# Runs pre-commit validation with the virtualized toolchain
|
||||
.PHONY: pre-commit
|
||||
pre-commit: dep-ensure codegen build lint test
|
||||
|
||||
@@ -437,16 +437,27 @@ show-go-version:
|
||||
|
||||
# Installs all tools required to build and test ArgoCD locally
|
||||
.PHONY: install-tools-local
|
||||
install-tools-local:
|
||||
./hack/install.sh dep-linux
|
||||
./hack/install.sh packr-linux
|
||||
./hack/install.sh kubectl-linux
|
||||
./hack/install.sh ksonnet-linux
|
||||
./hack/install.sh helm2-linux
|
||||
./hack/install.sh helm-linux
|
||||
./hack/install.sh codegen-tools
|
||||
install-tools-local: install-test-tools-local install-codegen-tools-local install-go-tools-local
|
||||
|
||||
# Installs all tools required for running unit & end-to-end tests (Linux packages)
|
||||
.PHONY: install-test-tools-local
|
||||
install-test-tools-local:
|
||||
sudo ./hack/install.sh packr-linux
|
||||
sudo ./hack/install.sh kubectl-linux
|
||||
sudo ./hack/install.sh kustomize-linux
|
||||
sudo ./hack/install.sh ksonnet-linux
|
||||
sudo ./hack/install.sh helm2-linux
|
||||
sudo ./hack/install.sh helm-linux
|
||||
|
||||
# Installs all tools required for running codegen (Linux packages)
|
||||
.PHONY: install-codegen-tools-local
|
||||
install-codegen-tools-local:
|
||||
sudo ./hack/install.sh codegen-tools
|
||||
|
||||
# Installs all tools required for running codegen (Go packages)
|
||||
.PHONY: install-go-tools-local
|
||||
install-go-tools-local:
|
||||
./hack/install.sh codegen-go-tools
|
||||
./hack/install.sh lint-tools
|
||||
|
||||
.PHONY: dep-ui
|
||||
dep-ui:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[](https://github.com/argoproj/argo-cd/actions?query=workflow%3A%22Integration+tests%22)
|
||||
[](https://argoproj.github.io/community/join-slack)
|
||||
[](https://codecov.io/gh/argoproj/argo-cd)
|
||||
[](https://github.com/argoproj/argo-cd/releases/latest)
|
||||
|
||||
6
USERS.md
6
USERS.md
@@ -6,6 +6,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
|
||||
1. [127Labs](https://127labs.com/)
|
||||
1. [Adevinta](https://www.adevinta.com/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH ](https://www.arz.at/)
|
||||
1. [Baloise](https://www.baloise.com)
|
||||
@@ -24,6 +25,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Healy](https://www.healyworld.net)
|
||||
1. [hipages](https://hipages.com.au/)
|
||||
1. [Honestbank](https://honestbank.com)
|
||||
1. [Intuit](https://www.intuit.com/)
|
||||
1. [KintoHub](https://www.kintohub.com/)
|
||||
1. [KompiTech GmbH](https://www.kompitech.com/)
|
||||
@@ -38,11 +40,13 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||
1. [Pipefy](https://www.pipefy.com/)
|
||||
1. [Prudential](https://prudential.com.sg)
|
||||
1. [PUBG](https://www.pubg.com)
|
||||
1. [Red Hat](https://www.redhat.com/)
|
||||
1. [Robotinfra](https://www.robotinfra.com)
|
||||
1. [Riskified](https://www.riskified.com/)
|
||||
1. [Saildrone](https://www.saildrone.com/)
|
||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||
1. [Swissquote](https://github.com/swissquote)
|
||||
1. [Syncier](https://syncier.com/)
|
||||
1. [Tesla](https://tesla.com/)
|
||||
1. [ThousandEyes](https://www.thousandeyes.com/)
|
||||
@@ -54,7 +58,9 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
|
||||
1. [Viaduct](https://www.viaduct.ai/)
|
||||
1. [Volvo Cars](https://www.volvocars.com/)
|
||||
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
||||
1. [Walkbase](https://www.walkbase.com/)
|
||||
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
|
||||
1. [Yieldlab](https://www.yieldlab.de/)
|
||||
1. [MTN Group](https://www.mtn.com/)
|
||||
1. [Moengage](https://www.moengage.com/)
|
||||
|
||||
@@ -11,4 +11,4 @@ g = _, _
|
||||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
||||
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && keyMatch(r.res, p.res) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj)
|
||||
m = g(r.sub, p.sub) && globMatch(r.res, p.res) && globMatch(r.act, p.act) && globMatch(r.obj, p.obj)
|
||||
|
||||
@@ -1689,6 +1689,36 @@
|
||||
}
|
||||
},
|
||||
"/api/v1/repositories/{repo}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"RepositoryService"
|
||||
],
|
||||
"summary": "Get returns a repository or its credentials",
|
||||
"operationId": "GetMixin3",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "repo",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"format": "boolean",
|
||||
"description": "Whether to force a cache refresh on repo's connection state.",
|
||||
"name": "forceRefresh",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1alpha1Repository"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"RepositoryService"
|
||||
@@ -2144,6 +2174,12 @@
|
||||
"type": "boolean",
|
||||
"format": "boolean"
|
||||
},
|
||||
"infos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1Info"
|
||||
}
|
||||
},
|
||||
"manifests": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -2481,7 +2517,7 @@
|
||||
},
|
||||
"repocredsRepoCredsResponse": {
|
||||
"type": "object",
|
||||
"title": "RepoCredsResponse is a resonse to most repository credentials requests"
|
||||
"title": "RepoCredsResponse is a response to most repository credentials requests"
|
||||
},
|
||||
"repositoryAppInfo": {
|
||||
"type": "object",
|
||||
@@ -2585,7 +2621,7 @@
|
||||
"$ref": "#/definitions/repositoryKsonnetEnvironmentDestination"
|
||||
},
|
||||
"k8sVersion": {
|
||||
"description": "KubernetesVersion is the kubernetes version the targetted cluster is running on.",
|
||||
"description": "KubernetesVersion is the kubernetes version the targeted cluster is running on.",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
@@ -3872,6 +3908,12 @@
|
||||
"description": "Operation contains requested operation parameters.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"info": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1Info"
|
||||
}
|
||||
},
|
||||
"initiatedBy": {
|
||||
"$ref": "#/definitions/v1alpha1OperationInitiator"
|
||||
},
|
||||
@@ -4560,7 +4602,7 @@
|
||||
},
|
||||
"syncOptions": {
|
||||
"type": "array",
|
||||
"title": "Options allow youe to specify whole app sync-options",
|
||||
"title": "Options allow you to specify whole app sync-options",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/pkg/stats"
|
||||
rediscache "github.com/go-redis/cache"
|
||||
"github.com/go-redis/redis"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -21,13 +22,12 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
cacheutil "github.com/argoproj/argo-cd/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
@@ -47,6 +47,7 @@ func newCommand() *cobra.Command {
|
||||
selfHealTimeoutSeconds int
|
||||
statusProcessors int
|
||||
operationProcessors int
|
||||
logFormat string
|
||||
logLevel string
|
||||
glogLevel int
|
||||
metricsPort int
|
||||
@@ -58,6 +59,7 @@ func newCommand() *cobra.Command {
|
||||
Use: cliName,
|
||||
Short: "application-controller is a controller to operate on applications CRD",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
cli.SetLogFormat(logFormat)
|
||||
cli.SetLogLevel(logLevel)
|
||||
cli.SetGLogLevel(glogLevel)
|
||||
|
||||
@@ -94,14 +96,7 @@ func newCommand() *cobra.Command {
|
||||
metricsPort,
|
||||
kubectlParallelismLimit)
|
||||
errors.CheckError(err)
|
||||
metricsServer := appController.GetMetricsServer()
|
||||
redisClient.WrapProcess(func(oldProcess func(cmd redis.Cmder) error) func(cmd redis.Cmder) error {
|
||||
return func(cmd redis.Cmder) error {
|
||||
err := oldProcess(cmd)
|
||||
metricsServer.IncRedisRequest(err != nil && err != rediscache.ErrCacheMiss)
|
||||
return err
|
||||
}
|
||||
})
|
||||
cacheutil.CollectMetrics(redisClient, appController.GetMetricsServer())
|
||||
|
||||
vers := common.GetVersion()
|
||||
log.Infof("Application Controller (version: %s, built: %s) starting (namespace: %s)", vers.Version, vers.BuildDate, namespace)
|
||||
@@ -122,6 +117,7 @@ func newCommand() *cobra.Command {
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
|
||||
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
|
||||
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
|
||||
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
|
||||
|
||||
@@ -7,15 +7,17 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/pkg/stats"
|
||||
"github.com/go-redis/redis"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
|
||||
"github.com/argoproj/argo-cd/reposerver/metrics"
|
||||
cacheutil "github.com/argoproj/argo-cd/util/cache"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/tls"
|
||||
)
|
||||
@@ -27,17 +29,20 @@ const (
|
||||
|
||||
func newCommand() *cobra.Command {
|
||||
var (
|
||||
logFormat string
|
||||
logLevel string
|
||||
parallelismLimit int64
|
||||
listenPort int
|
||||
metricsPort int
|
||||
cacheSrc func() (*reposervercache.Cache, error)
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
redisClient *redis.Client
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
Short: "Run argocd-repo-server",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
cli.SetLogFormat(logFormat)
|
||||
cli.SetLogLevel(logLevel)
|
||||
|
||||
tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
|
||||
@@ -47,6 +52,7 @@ func newCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
|
||||
metricsServer := metrics.NewMetricsServer()
|
||||
cacheutil.CollectMetrics(redisClient, metricsServer)
|
||||
server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, parallelismLimit)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -67,12 +73,15 @@ func newCommand() *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
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")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
return &command
|
||||
}
|
||||
|
||||
|
||||
@@ -4,24 +4,42 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/pkg/stats"
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/server"
|
||||
servercache "github.com/argoproj/argo-cd/server/cache"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/env"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/tls"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
failureRetryCountEnv = "ARGOCD_K8S_RETRY_COUNT"
|
||||
failureRetryPeriodMilliSecondsEnv = "ARGOCD_K8S_RETRY_DURATION_MILLISECONDS"
|
||||
)
|
||||
|
||||
var (
|
||||
failureRetryCount = 0
|
||||
failureRetryPeriodMilliSeconds = 100
|
||||
)
|
||||
|
||||
func init() {
|
||||
failureRetryCount = env.ParseNumFromEnv(failureRetryCountEnv, failureRetryCount, 0, 10)
|
||||
failureRetryPeriodMilliSeconds = env.ParseNumFromEnv(failureRetryPeriodMilliSecondsEnv, failureRetryPeriodMilliSeconds, 0, 1000)
|
||||
}
|
||||
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
@@ -29,6 +47,7 @@ func NewCommand() *cobra.Command {
|
||||
insecure bool
|
||||
listenPort int
|
||||
metricsPort int
|
||||
logFormat string
|
||||
logLevel string
|
||||
glogLevel int
|
||||
clientConfig clientcmd.ClientConfig
|
||||
@@ -48,6 +67,7 @@ func NewCommand() *cobra.Command {
|
||||
Short: "Run the argocd API server",
|
||||
Long: "Run the argocd API server",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
cli.SetLogFormat(logFormat)
|
||||
cli.SetLogLevel(logLevel)
|
||||
cli.SetGLogLevel(glogLevel)
|
||||
|
||||
@@ -64,7 +84,15 @@ func NewCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
|
||||
kubeclientset := kubernetes.NewForConfigOrDie(config)
|
||||
appclientset := appclientset.NewForConfigOrDie(config)
|
||||
|
||||
appclientsetConfig, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
errors.CheckError(v1alpha1.SetK8SConfigDefaults(appclientsetConfig))
|
||||
|
||||
if failureRetryCount > 0 {
|
||||
appclientsetConfig = kube.AddFailureRetryWrapper(appclientsetConfig, failureRetryCount, failureRetryPeriodMilliSeconds)
|
||||
}
|
||||
appclientset := appclientset.NewForConfigOrDie(appclientsetConfig)
|
||||
repoclientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
|
||||
|
||||
if rootPath != "" {
|
||||
@@ -112,6 +140,7 @@ func NewCommand() *cobra.Command {
|
||||
command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
|
||||
command.Flags().StringVar(&baseHRef, "basehref", "/", "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
|
||||
command.Flags().StringVar(&rootPath, "rootpath", "", "Used if Argo CD is running behind reverse proxy under subpath different from /")
|
||||
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address")
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
|
||||
commands "github.com/argoproj/argo-cd/cmd/argocd-server/commands"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
|
||||
// load the gcp plugin (required to authenticate against GKE clusters).
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
@@ -8,10 +8,13 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
healthutil "github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -19,15 +22,13 @@ import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/health"
|
||||
"github.com/argoproj/argo-cd/util/lua"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
@@ -354,7 +355,8 @@ func NewResourceOverridesCommand(cmdCtx commandContext) *cobra.Command {
|
||||
},
|
||||
}
|
||||
command.AddCommand(NewResourceIgnoreDifferencesCommand(cmdCtx))
|
||||
command.AddCommand(NewResourceActionCommand(cmdCtx))
|
||||
command.AddCommand(NewResourceActionListCommand(cmdCtx))
|
||||
command.AddCommand(NewResourceActionRunCommand(cmdCtx))
|
||||
command.AddCommand(NewResourceHealthCommand(cmdCtx))
|
||||
return command
|
||||
}
|
||||
@@ -448,7 +450,7 @@ argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./
|
||||
return
|
||||
}
|
||||
|
||||
resHealth, err := health.GetResourceHealth(&res, overrides)
|
||||
resHealth, err := healthutil.GetResourceHealth(&res, lua.ResourceHealthOverrides(overrides))
|
||||
errors.CheckError(err)
|
||||
|
||||
_, _ = fmt.Printf("STATUS: %s\n", resHealth.Status)
|
||||
@@ -459,13 +461,56 @@ argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./
|
||||
return command
|
||||
}
|
||||
|
||||
func NewResourceActionCommand(cmdCtx commandContext) *cobra.Command {
|
||||
func NewResourceActionListCommand(cmdCtx commandContext) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "action RESOURCE_YAML_PATH ACTION",
|
||||
Short: "Executes resource action",
|
||||
Long: "Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
|
||||
Use: "list-actions RESOURCE_YAML_PATH",
|
||||
Short: "List available resource actions",
|
||||
Long: "List actions available for given resource action using the lua scripts configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
|
||||
Example: `
|
||||
argocd-util settings resource-overrides action /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`,
|
||||
argocd-util settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) < 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
executeResourceOverrideCommand(cmdCtx, args, func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride) {
|
||||
gvk := res.GroupVersionKind()
|
||||
if override.Actions == "" {
|
||||
_, _ = fmt.Printf("Actions are not configured for '%s/%s'\n", gvk.Group, gvk.Kind)
|
||||
return
|
||||
}
|
||||
|
||||
luaVM := lua.VM{ResourceOverrides: overrides}
|
||||
discoveryScript, err := luaVM.GetResourceActionDiscovery(&res)
|
||||
errors.CheckError(err)
|
||||
|
||||
availableActions, err := luaVM.ExecuteResourceActionDiscovery(&res, discoveryScript)
|
||||
errors.CheckError(err)
|
||||
sort.Slice(availableActions, func(i, j int) bool {
|
||||
return availableActions[i].Name < availableActions[j].Name
|
||||
})
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "NAME\tENABLED\n")
|
||||
for _, action := range availableActions {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", action.Name, strconv.FormatBool(action.Disabled))
|
||||
}
|
||||
_ = w.Flush()
|
||||
})
|
||||
},
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
func NewResourceActionRunCommand(cmdCtx commandContext) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "run-action RESOURCE_YAML_PATH ACTION",
|
||||
Aliases: []string{"action"},
|
||||
Short: "Executes resource action",
|
||||
Long: "Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
|
||||
Example: `
|
||||
argocd-util settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) < 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
utils "github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -33,7 +33,7 @@ func captureStdout(callback func()) (string, error) {
|
||||
}()
|
||||
|
||||
callback()
|
||||
util.Close(w)
|
||||
utils.Close(w)
|
||||
|
||||
data, err := ioutil.ReadAll(r)
|
||||
|
||||
@@ -97,7 +97,7 @@ data:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer util.Close(closer)
|
||||
defer utils.Close(closer)
|
||||
|
||||
opts := settingsOpts{argocdCMPath: f}
|
||||
settingsManager, err := opts.createSettingsManager()
|
||||
@@ -240,7 +240,7 @@ func tempFile(content string) (string, io.Closer, error) {
|
||||
_ = os.Remove(f.Name())
|
||||
return "", nil, err
|
||||
}
|
||||
return f.Name(), util.NewCloser(func() error {
|
||||
return f.Name(), utils.NewCloser(func() error {
|
||||
return os.Remove(f.Name())
|
||||
}), nil
|
||||
}
|
||||
@@ -263,7 +263,7 @@ func TestResourceOverrideIgnoreDifferences(t *testing.T) {
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer util.Close(closer)
|
||||
defer utils.Close(closer)
|
||||
|
||||
t.Run("NoOverridesConfigured", func(t *testing.T) {
|
||||
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{}))
|
||||
@@ -297,7 +297,7 @@ func TestResourceOverrideHealth(t *testing.T) {
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer util.Close(closer)
|
||||
defer utils.Close(closer)
|
||||
|
||||
t.Run("NoHealthAssessment", func(t *testing.T) {
|
||||
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{
|
||||
@@ -332,13 +332,13 @@ func TestResourceOverrideAction(t *testing.T) {
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer util.Close(closer)
|
||||
defer utils.Close(closer)
|
||||
|
||||
t.Run("NoActions", func(t *testing.T) {
|
||||
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{
|
||||
"resource.customizations": `apps/Deployment: {}`}))
|
||||
out, err := captureStdout(func() {
|
||||
cmd.SetArgs([]string{"action", f, "test"})
|
||||
cmd.SetArgs([]string{"run-action", f, "test"})
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
@@ -346,10 +346,15 @@ func TestResourceOverrideAction(t *testing.T) {
|
||||
assert.Contains(t, out, "Actions are not configured")
|
||||
})
|
||||
|
||||
t.Run("HealthAssessmentConfigured", func(t *testing.T) {
|
||||
t.Run("ActionConfigured", func(t *testing.T) {
|
||||
cmd := NewResourceOverridesCommand(newCmdContext(map[string]string{
|
||||
"resource.customizations": `apps/Deployment:
|
||||
actions: |
|
||||
discovery.lua: |
|
||||
actions = {}
|
||||
actions["resume"] = {["disabled"] = false}
|
||||
actions["restart"] = {["disabled"] = false}
|
||||
return actions
|
||||
definitions:
|
||||
- name: test
|
||||
action.lua: |
|
||||
@@ -357,11 +362,22 @@ func TestResourceOverrideAction(t *testing.T) {
|
||||
return obj
|
||||
`}))
|
||||
out, err := captureStdout(func() {
|
||||
cmd.SetArgs([]string{"action", f, "test"})
|
||||
cmd.SetArgs([]string{"run-action", f, "test"})
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out, "test: updated")
|
||||
|
||||
out, err = captureStdout(func() {
|
||||
cmd.SetArgs([]string{"list-actions", f})
|
||||
err := cmd.Execute()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, out, `NAME ENABLED
|
||||
restart false
|
||||
resume false
|
||||
`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -26,11 +28,9 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/cmd/argocd-util/commands"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/dex"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
|
||||
// load the gcp plugin (required to authenticate against GKE clusters).
|
||||
@@ -56,7 +56,8 @@ var (
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
logLevel string
|
||||
logFormat string
|
||||
logLevel string
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
@@ -76,6 +77,7 @@ func NewCommand() *cobra.Command {
|
||||
command.AddCommand(commands.NewProjectsCommand())
|
||||
command.AddCommand(commands.NewSettingsCommand())
|
||||
|
||||
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -10,18 +10,18 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
timeutil "github.com/argoproj/pkg/time"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
accountpkg "github.com/argoproj/argo-cd/pkg/apiclient/account"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/session"
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
sessionutil "github.com/argoproj/argo-cd/util/session"
|
||||
@@ -62,7 +62,7 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, usrIf := acdClient.NewAccountClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
userInfo := getCurrentAccount(acdClient)
|
||||
|
||||
@@ -131,7 +131,7 @@ func NewAccountGetUserInfoCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
}
|
||||
|
||||
conn, client := argocdclient.NewClientOrDie(clientOpts).NewSessionClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
response, err := client.GetUserInfo(ctx, &session.GetUserInfoRequest{})
|
||||
@@ -178,7 +178,7 @@ argocd account can-i create clusters '*'
|
||||
|
||||
Actions: %v
|
||||
Resources: %v
|
||||
`, rbacpolicy.Resources, rbacpolicy.Actions),
|
||||
`, rbacpolicy.Actions, rbacpolicy.Resources),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -186,7 +186,7 @@ Resources: %v
|
||||
}
|
||||
|
||||
conn, client := argocdclient.NewClientOrDie(clientOpts).NewAccountClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
response, err := client.CanI(ctx, &accountpkg.CanIRequest{
|
||||
@@ -226,7 +226,7 @@ func NewAccountListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
|
||||
conn, client := argocdclient.NewClientOrDie(clientOpts).NewAccountClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
ctx := context.Background()
|
||||
response, err := client.ListAccounts(ctx, &accountpkg.ListAccountRequest{})
|
||||
@@ -251,7 +251,7 @@ func NewAccountListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
|
||||
func getCurrentAccount(clientset argocdclient.Client) session.GetUserInfoResponse {
|
||||
conn, client := clientset.NewSessionClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
userInfo, err := client.GetUserInfo(context.Background(), &session.GetUserInfoRequest{})
|
||||
errors.CheckError(err)
|
||||
return *userInfo
|
||||
@@ -278,7 +278,7 @@ argocd account get --account <account-name>`,
|
||||
}
|
||||
|
||||
conn, client := clientset.NewAccountClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
acc, err := client.GetAccount(context.Background(), &accountpkg.GetAccountRequest{Name: account})
|
||||
|
||||
@@ -345,7 +345,7 @@ argocd account generate-token --account <account-name>`,
|
||||
|
||||
clientset := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, client := clientset.NewAccountClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
if account == "" {
|
||||
account = getCurrentAccount(clientset).Username
|
||||
}
|
||||
@@ -387,7 +387,7 @@ argocd account generate-token --account <account-name>`,
|
||||
|
||||
clientset := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, client := clientset.NewAccountClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
if account == "" {
|
||||
account = getCurrentAccount(clientset).Username
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -15,6 +16,13 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -26,7 +34,6 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
|
||||
@@ -36,15 +43,11 @@ import (
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
repoapiclient "github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/argo"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/hook"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/resource/ignore"
|
||||
argokube "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/templates"
|
||||
"github.com/argoproj/argo-cd/util/text/label"
|
||||
)
|
||||
@@ -171,10 +174,11 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
}
|
||||
|
||||
conn, appIf := argocdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
appCreateRequest := applicationpkg.ApplicationCreateRequest{
|
||||
Application: app,
|
||||
Upsert: &upsert,
|
||||
Validate: &appOpts.validate,
|
||||
}
|
||||
created, err := appIf.Create(context.Background(), &appCreateRequest)
|
||||
errors.CheckError(err)
|
||||
@@ -200,6 +204,18 @@ func setLabels(app *argoappv1.Application, labels []string) {
|
||||
app.SetLabels(mapLabels)
|
||||
}
|
||||
|
||||
func getInfos(infos []string) []*argoappv1.Info {
|
||||
mapInfos, err := label.Parse(infos)
|
||||
errors.CheckError(err)
|
||||
sliceInfos := make([]*argoappv1.Info, len(mapInfos))
|
||||
i := 0
|
||||
for key, element := range mapInfos {
|
||||
sliceInfos[i] = &argoappv1.Info{Name: key, Value: element}
|
||||
i++
|
||||
}
|
||||
return sliceInfos
|
||||
}
|
||||
|
||||
func getRefreshType(refresh bool, hardRefresh bool) *string {
|
||||
if hardRefresh {
|
||||
refreshType := string(argoappv1.RefreshTypeHard)
|
||||
@@ -233,13 +249,13 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
appName := args[0]
|
||||
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName, Refresh: getRefreshType(refresh, hardRefresh)})
|
||||
errors.CheckError(err)
|
||||
|
||||
pConn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(pConn)
|
||||
defer argoio.Close(pConn)
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: app.Spec.Project})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -358,7 +374,7 @@ func printAppSummaryTable(app *argoappv1.Application, appURL string, windows *ar
|
||||
syncStatusStr += fmt.Sprintf(" (%s)", app.Status.Sync.Revision[0:7])
|
||||
}
|
||||
fmt.Printf(printOpFmtStr, "Sync Status:", syncStatusStr)
|
||||
healthStr := app.Status.Health.Status
|
||||
healthStr := string(app.Status.Health.Status)
|
||||
if app.Status.Health.Message != "" {
|
||||
healthStr = fmt.Sprintf("%s (%s)", app.Status.Health.Status, app.Status.Health.Message)
|
||||
}
|
||||
@@ -449,7 +465,7 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
appName := args[0]
|
||||
argocdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, appIf := argocdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
visited := setAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
|
||||
@@ -460,8 +476,9 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
}
|
||||
setParameterOverrides(app, appOpts.parameters)
|
||||
_, err = appIf.UpdateSpec(ctx, &applicationpkg.ApplicationUpdateSpecRequest{
|
||||
Name: &app.Name,
|
||||
Spec: app.Spec,
|
||||
Name: &app.Name,
|
||||
Spec: app.Spec,
|
||||
Validate: &appOpts.validate,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
@@ -490,6 +507,18 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
spec.RevisionHistoryLimit = &i
|
||||
case "values":
|
||||
setHelmOpt(&spec.Source, helmOpts{valueFiles: appOpts.valuesFiles})
|
||||
case "values-literal-file":
|
||||
var data []byte
|
||||
|
||||
// read uri
|
||||
parsedURL, err := url.ParseRequestURI(appOpts.values)
|
||||
if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
|
||||
data, err = ioutil.ReadFile(appOpts.values)
|
||||
} else {
|
||||
data, err = config.ReadRemoteFile(appOpts.values)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
setHelmOpt(&spec.Source, helmOpts{values: string(data)})
|
||||
case "release-name":
|
||||
setHelmOpt(&spec.Source, helmOpts{releaseName: appOpts.releaseName})
|
||||
case "helm-set":
|
||||
@@ -567,7 +596,7 @@ func setAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
}
|
||||
if flags.Changed("self-heal") {
|
||||
if spec.SyncPolicy == nil || spec.SyncPolicy.Automated == nil {
|
||||
log.Fatal("Cannot set --self-helf: application not configured with automatic sync")
|
||||
log.Fatal("Cannot set --self-heal: application not configured with automatic sync")
|
||||
}
|
||||
spec.SyncPolicy.Automated.SelfHeal = appOpts.selfHeal
|
||||
}
|
||||
@@ -611,6 +640,7 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
|
||||
|
||||
type helmOpts struct {
|
||||
valueFiles []string
|
||||
values string
|
||||
releaseName string
|
||||
helmSets []string
|
||||
helmSetStrings []string
|
||||
@@ -624,6 +654,9 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
|
||||
if len(opts.valueFiles) > 0 {
|
||||
src.Helm.ValueFiles = opts.valueFiles
|
||||
}
|
||||
if len(opts.values) > 0 {
|
||||
src.Helm.Values = opts.values
|
||||
}
|
||||
if opts.releaseName != "" {
|
||||
src.Helm.ReleaseName = opts.releaseName
|
||||
}
|
||||
@@ -682,6 +715,7 @@ type appOptions struct {
|
||||
destNamespace string
|
||||
parameters []string
|
||||
valuesFiles []string
|
||||
values string
|
||||
releaseName string
|
||||
helmSets []string
|
||||
helmSetStrings []string
|
||||
@@ -701,6 +735,7 @@ type appOptions struct {
|
||||
jsonnetExtVarCode []string
|
||||
kustomizeImages []string
|
||||
kustomizeVersion string
|
||||
validate bool
|
||||
}
|
||||
|
||||
func addAppFlags(command *cobra.Command, opts *appOptions) {
|
||||
@@ -714,6 +749,7 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
|
||||
command.Flags().StringVar(&opts.destNamespace, "dest-namespace", "", "K8s target namespace (overrides the namespace specified in the ksonnet app.yaml)")
|
||||
command.Flags().StringArrayVarP(&opts.parameters, "parameter", "p", []string{}, "set a parameter override (e.g. -p guestbook=image=example/guestbook:latest)")
|
||||
command.Flags().StringArrayVar(&opts.valuesFiles, "values", []string{}, "Helm values file(s) to use")
|
||||
command.Flags().StringVar(&opts.values, "values-literal-file", "", "Filename or URL to import as a literal Helm values block")
|
||||
command.Flags().StringVar(&opts.releaseName, "release-name", "", "Helm release-name")
|
||||
command.Flags().StringArrayVar(&opts.helmSets, "helm-set", []string{}, "Helm set values on the command line (can be repeated to set several values: --helm-set key1=val1 --helm-set key2=val2)")
|
||||
command.Flags().StringArrayVar(&opts.helmSetStrings, "helm-set-string", []string{}, "Helm set STRING values on the command line (can be repeated to set several values: --helm-set-string key1=val1 --helm-set-string key2=val2)")
|
||||
@@ -725,7 +761,7 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
|
||||
command.Flags().BoolVar(&opts.selfHeal, "self-heal", false, "Set self healing when sync is automated")
|
||||
command.Flags().StringVar(&opts.namePrefix, "nameprefix", "", "Kustomize nameprefix")
|
||||
command.Flags().StringVar(&opts.nameSuffix, "namesuffix", "", "Kustomize namesuffix")
|
||||
command.Flags().StringVar(&opts.nameSuffix, "kustomize-version", "", "Kustomize version")
|
||||
command.Flags().StringVar(&opts.kustomizeVersion, "kustomize-version", "", "Kustomize version")
|
||||
command.Flags().BoolVar(&opts.directoryRecurse, "directory-recurse", false, "Recurse directory")
|
||||
command.Flags().StringVar(&opts.configManagementPlugin, "config-management-plugin", "", "Config management plugin name")
|
||||
command.Flags().StringArrayVar(&opts.jsonnetTlaStr, "jsonnet-tla-str", []string{}, "Jsonnet top level string arguments")
|
||||
@@ -733,17 +769,20 @@ func addAppFlags(command *cobra.Command, opts *appOptions) {
|
||||
command.Flags().StringArrayVar(&opts.jsonnetExtVarStr, "jsonnet-ext-var-str", []string{}, "Jsonnet string ext var")
|
||||
command.Flags().StringArrayVar(&opts.jsonnetExtVarCode, "jsonnet-ext-var-code", []string{}, "Jsonnet ext var")
|
||||
command.Flags().StringArrayVar(&opts.kustomizeImages, "kustomize-image", []string{}, "Kustomize images (e.g. --kustomize-image node:8.15.0 --kustomize-image mysql=mariadb,alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d)")
|
||||
command.Flags().BoolVar(&opts.validate, "validate", true, "Validation of repo and cluster")
|
||||
}
|
||||
|
||||
// NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command
|
||||
func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
parameters []string
|
||||
valuesLiteral bool
|
||||
valuesFiles []string
|
||||
nameSuffix bool
|
||||
namePrefix bool
|
||||
kustomizeVersion bool
|
||||
kustomizeImages []string
|
||||
appOpts appOptions
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "unset APPNAME parameters",
|
||||
@@ -764,7 +803,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -820,7 +859,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
}
|
||||
if app.Spec.Source.Helm != nil {
|
||||
if len(parameters) == 0 && len(valuesFiles) == 0 {
|
||||
if len(parameters) == 0 && len(valuesFiles) == 0 && !valuesLiteral {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -834,31 +873,37 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
}
|
||||
}
|
||||
specValueFiles := app.Spec.Source.Helm.ValueFiles
|
||||
if valuesLiteral {
|
||||
app.Spec.Source.Helm.Values = ""
|
||||
updated = true
|
||||
}
|
||||
for _, valuesFile := range valuesFiles {
|
||||
specValueFiles := app.Spec.Source.Helm.ValueFiles
|
||||
for i, vf := range specValueFiles {
|
||||
if vf == valuesFile {
|
||||
specValueFiles = append(specValueFiles[0:i], specValueFiles[i+1:]...)
|
||||
app.Spec.Source.Helm.ValueFiles = append(specValueFiles[0:i], specValueFiles[i+1:]...)
|
||||
updated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
setHelmOpt(&app.Spec.Source, helmOpts{valueFiles: specValueFiles})
|
||||
if !updated {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
setAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
|
||||
_, err = appIf.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{
|
||||
Name: &app.Name,
|
||||
Spec: app.Spec,
|
||||
Name: &app.Name,
|
||||
Spec: app.Spec,
|
||||
Validate: &appOpts.validate,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
command.Flags().StringArrayVarP(¶meters, "parameter", "p", []string{}, "unset a parameter override (e.g. -p guestbook=image)")
|
||||
command.Flags().StringArrayVar(&valuesFiles, "values", []string{}, "unset one or more helm values files")
|
||||
command.Flags().StringArrayVarP(¶meters, "parameter", "p", []string{}, "Unset a parameter override (e.g. -p guestbook=image)")
|
||||
command.Flags().StringArrayVar(&valuesFiles, "values", []string{}, "Unset one or more Helm values files")
|
||||
command.Flags().BoolVar(&valuesLiteral, "values-literal", false, "Unset literal Helm values block")
|
||||
command.Flags().BoolVar(&nameSuffix, "namesuffix", false, "Kustomize namesuffix")
|
||||
command.Flags().BoolVar(&namePrefix, "nameprefix", false, "Kustomize nameprefix")
|
||||
command.Flags().BoolVar(&kustomizeVersion, "kustomize-version", false, "Kustomize version")
|
||||
@@ -928,7 +973,7 @@ type resourceInfoProvider struct {
|
||||
|
||||
// Infer if obj is namespaced or not from corresponding live objects list. If corresponding live object has namespace then target object is also namespaced.
|
||||
// If live object is missing then it does not matter if target is namespaced or not.
|
||||
func (p *resourceInfoProvider) IsNamespaced(server string, gk schema.GroupKind) (bool, error) {
|
||||
func (p *resourceInfoProvider) IsNamespaced(gk schema.GroupKind) (bool, error) {
|
||||
return p.namespacedByGk[gk], nil
|
||||
}
|
||||
|
||||
@@ -940,7 +985,7 @@ func groupLocalObjs(localObs []*unstructured.Unstructured, liveObjs []*unstructu
|
||||
namespacedByGk[schema.GroupKind{Group: key.Group, Kind: key.Kind}] = key.Namespace != ""
|
||||
}
|
||||
}
|
||||
localObs, _, err := controller.DeduplicateTargetObjects("", appNamespace, localObs, &resourceInfoProvider{namespacedByGk: namespacedByGk})
|
||||
localObs, _, err := controller.DeduplicateTargetObjects(appNamespace, localObs, &resourceInfoProvider{namespacedByGk: namespacedByGk})
|
||||
errors.CheckError(err)
|
||||
objByKey := make(map[kube.ResourceKey]*unstructured.Unstructured)
|
||||
for i := range localObs {
|
||||
@@ -972,7 +1017,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
|
||||
clientset := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, appIf := clientset.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
appName := args[0]
|
||||
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName, Refresh: getRefreshType(refresh, hardRefresh)})
|
||||
errors.CheckError(err)
|
||||
@@ -987,13 +1032,13 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}, 0)
|
||||
|
||||
conn, settingsIf := clientset.NewSettingsClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
argoSettings, err := settingsIf.Get(context.Background(), &settingspkg.SettingsQuery{})
|
||||
errors.CheckError(err)
|
||||
|
||||
if local != "" {
|
||||
conn, clusterIf := clientset.NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
localObjs := groupLocalObjs(getLocalObjects(app, local, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins), liveObjs, app.Spec.Destination.Namespace)
|
||||
@@ -1010,7 +1055,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
if local, ok := localObjs[key]; ok || live != nil {
|
||||
if local != nil && !kube.IsCRD(local) {
|
||||
err = kube.SetAppInstanceLabel(local, argoSettings.AppLabelKey, appName)
|
||||
err = argokube.SetAppInstanceLabel(local, argoSettings.AppLabelKey, appName)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
@@ -1073,7 +1118,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
normalizer, err := argo.NewDiffNormalizer(app.Spec.IgnoreDifferences, overrides)
|
||||
errors.CheckError(err)
|
||||
|
||||
diffRes, err := diff.Diff(item.target, item.live, normalizer)
|
||||
diffRes, err := diff.Diff(item.target, item.live, normalizer, diff.GetDefaultDiffOptions())
|
||||
errors.CheckError(err)
|
||||
|
||||
if diffRes.Modified || item.target == nil || item.live == nil {
|
||||
@@ -1120,7 +1165,7 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
for _, appName := range args {
|
||||
appDeleteReq := applicationpkg.ApplicationDeleteRequest{
|
||||
Name: &appName,
|
||||
@@ -1192,7 +1237,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
argocd app list -l app.kubernetes.io/instance=my-app`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
apps, err := appIf.List(context.Background(), &applicationpkg.ApplicationQuery{Selector: selector})
|
||||
errors.CheckError(err)
|
||||
appList := apps.Items
|
||||
@@ -1316,7 +1361,7 @@ func NewApplicationWaitCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
appNames := args
|
||||
acdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
closer, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(closer)
|
||||
defer argoio.Close(closer)
|
||||
if selector != "" {
|
||||
list, err := appIf.List(context.Background(), &applicationpkg.ApplicationQuery{Selector: selector})
|
||||
errors.CheckError(err)
|
||||
@@ -1362,6 +1407,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
force bool
|
||||
async bool
|
||||
local string
|
||||
infos []string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "sync [APPNAME... | -l selector]",
|
||||
@@ -1386,7 +1432,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
selectedLabels, err := label.Parse(labels)
|
||||
errors.CheckError(err)
|
||||
@@ -1452,13 +1498,13 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
conn, settingsIf := acdClient.NewSettingsClientOrDie()
|
||||
argoSettings, err := settingsIf.Get(context.Background(), &settingspkg.SettingsQuery{})
|
||||
errors.CheckError(err)
|
||||
util.Close(conn)
|
||||
argoio.Close(conn)
|
||||
|
||||
conn, clusterIf := acdClient.NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
util.Close(conn)
|
||||
argoio.Close(conn)
|
||||
localObjsStrings = getLocalObjectsString(app, local, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins)
|
||||
}
|
||||
|
||||
@@ -1469,6 +1515,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
Resources: selectedResources,
|
||||
Prune: prune,
|
||||
Manifests: localObjsStrings,
|
||||
Infos: getInfos(infos),
|
||||
}
|
||||
switch strategy {
|
||||
case "apply":
|
||||
@@ -1508,12 +1555,13 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringVar(&revision, "revision", "", "Sync to a specific revision. Preserves parameter overrides")
|
||||
command.Flags().StringArrayVar(&resources, "resource", []string{}, fmt.Sprintf("Sync only specific resources as GROUP%sKIND%sNAME. Fields may be blank. This option may be specified repeatedly", resourceFieldDelimiter, resourceFieldDelimiter))
|
||||
command.Flags().StringVarP(&selector, "selector", "l", "", "Sync apps that match this label")
|
||||
command.Flags().StringArrayVar(&labels, "label", []string{}, fmt.Sprintf("Sync only specific resources with a label. This option may be specified repeatedly."))
|
||||
command.Flags().StringArrayVar(&labels, "label", []string{}, "Sync only specific resources with a label. This option may be specified repeatedly.")
|
||||
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
|
||||
command.Flags().StringVar(&strategy, "strategy", "", "Sync strategy (one of: apply|hook)")
|
||||
command.Flags().BoolVar(&force, "force", false, "Use a force apply")
|
||||
command.Flags().BoolVar(&async, "async", false, "Do not wait for application to sync before continuing")
|
||||
command.Flags().StringVar(&local, "local", "", "Path to a local directory. When this flag is present no git queries will be made")
|
||||
command.Flags().StringArrayVar(&infos, "info", []string{}, "A list of key-value pairs during sync process. These infos will be persisted in app.")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1574,7 +1622,7 @@ func getResourceStates(app *argoappv1.Application, selectedResources []argoappv1
|
||||
if resource, ok := resourceByKey[key]; ok && res.HookType == "" {
|
||||
health = ""
|
||||
if resource.Health != nil {
|
||||
health = resource.Health.Status
|
||||
health = string(resource.Health.Status)
|
||||
}
|
||||
sync = string(resource.Status)
|
||||
}
|
||||
@@ -1595,7 +1643,7 @@ func getResourceStates(app *argoappv1.Application, selectedResources []argoappv1
|
||||
res := resourceByKey[resKey]
|
||||
health := ""
|
||||
if res.Health != nil {
|
||||
health = res.Health.Status
|
||||
health = string(res.Health.Status)
|
||||
}
|
||||
states = append(states, &resourceState{
|
||||
Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name, Status: string(res.Status), Health: health, Hook: "", Message: ""})
|
||||
@@ -1628,12 +1676,12 @@ func groupResourceStates(app *argoappv1.Application, selectedResources []argoapp
|
||||
func checkResourceStatus(watchSync bool, watchHealth bool, watchOperation bool, watchSuspended bool, healthStatus string, syncStatus string, operationStatus *argoappv1.Operation) bool {
|
||||
healthCheckPassed := true
|
||||
if watchSuspended && watchHealth {
|
||||
healthCheckPassed = healthStatus == argoappv1.HealthStatusHealthy ||
|
||||
healthStatus == argoappv1.HealthStatusSuspended
|
||||
healthCheckPassed = healthStatus == string(health.HealthStatusHealthy) ||
|
||||
healthStatus == string(health.HealthStatusSuspended)
|
||||
} else if watchSuspended {
|
||||
healthCheckPassed = healthStatus == argoappv1.HealthStatusSuspended
|
||||
healthCheckPassed = healthStatus == string(health.HealthStatusSuspended)
|
||||
} else if watchHealth {
|
||||
healthCheckPassed = healthStatus == argoappv1.HealthStatusHealthy
|
||||
healthCheckPassed = healthStatus == string(health.HealthStatusHealthy)
|
||||
}
|
||||
|
||||
synced := !watchSync || syncStatus == string(argoappv1.SyncStatusCodeSynced)
|
||||
@@ -1690,7 +1738,7 @@ func waitOnApplicationStatus(acdClient apiclient.Client, appName string, timeout
|
||||
prevStates := make(map[string]*resourceState)
|
||||
appEventCh := acdClient.WatchApplicationWithRetry(ctx, appName)
|
||||
conn, appClient := acdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
app, err := appClient.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -1727,7 +1775,7 @@ func waitOnApplicationStatus(acdClient apiclient.Client, appName string, timeout
|
||||
}
|
||||
} else {
|
||||
// Wait on the application as a whole
|
||||
selectedResourcesAreReady = checkResourceStatus(watchSync, watchHealth, watchOperation, watchSuspended, app.Status.Health.Status, string(app.Status.Sync.Status), appEvent.Application.Operation)
|
||||
selectedResourcesAreReady = checkResourceStatus(watchSync, watchHealth, watchOperation, watchSuspended, string(app.Status.Health.Status), string(app.Status.Sync.Status), appEvent.Application.Operation)
|
||||
}
|
||||
|
||||
if selectedResourcesAreReady && !operationInProgress {
|
||||
@@ -1740,7 +1788,7 @@ func waitOnApplicationStatus(acdClient apiclient.Client, appName string, timeout
|
||||
var doPrint bool
|
||||
stateKey := newState.Key()
|
||||
if prevState, found := prevStates[stateKey]; found {
|
||||
if watchHealth && prevState.Health != argoappv1.HealthStatusUnknown && prevState.Health != argoappv1.HealthStatusDegraded && newState.Health == argoappv1.HealthStatusDegraded {
|
||||
if watchHealth && prevState.Health != string(health.HealthStatusUnknown) && prevState.Health != string(health.HealthStatusDegraded) && newState.Health == string(health.HealthStatusDegraded) {
|
||||
_ = printFinalStatus(app)
|
||||
return nil, fmt.Errorf("application '%s' health state has transitioned from %s to %s", appName, prevState.Health, newState.Health)
|
||||
}
|
||||
@@ -1864,7 +1912,7 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
appName := args[0]
|
||||
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
@@ -1898,7 +1946,7 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
errors.CheckError(err)
|
||||
acdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
ctx := context.Background()
|
||||
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
@@ -1971,7 +2019,7 @@ func NewApplicationManifestsCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
ctx := context.Background()
|
||||
resources, err := appIf.ManagedResources(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
@@ -2029,7 +2077,7 @@ func NewApplicationTerminateOpCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
ctx := context.Background()
|
||||
_, err := appIf.TerminateOperation(ctx, &applicationpkg.OperationTerminateRequest{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
@@ -2050,7 +2098,7 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
app, err := appIf.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
appData, err := json.Marshal(app.Spec)
|
||||
@@ -2068,7 +2116,10 @@ func NewApplicationEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = appIf.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, Spec: updatedSpec})
|
||||
|
||||
var appOpts appOptions
|
||||
setAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
|
||||
_, err = appIf.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, Spec: updatedSpec, Validate: &appOpts.validate})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update application spec:\n%v", err)
|
||||
}
|
||||
@@ -2099,7 +2150,7 @@ func NewApplicationPatchCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
patchedApp, err := appIf.Patch(context.Background(), &applicationpkg.ApplicationPatchRequest{
|
||||
Name: &appName,
|
||||
@@ -2186,7 +2237,7 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
appName := args[0]
|
||||
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
ctx := context.Background()
|
||||
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -4,18 +4,18 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
)
|
||||
|
||||
type DisplayedAction struct {
|
||||
@@ -59,7 +59,7 @@ func NewApplicationResourceActionsListCommand(clientOpts *argocdclient.ClientOpt
|
||||
}
|
||||
appName := args[0]
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
ctx := context.Background()
|
||||
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
@@ -144,7 +144,7 @@ func NewApplicationResourceActionsRunCommand(clientOpts *argocdclient.ClientOpti
|
||||
actionName := args[1]
|
||||
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
ctx := context.Background()
|
||||
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -2,22 +2,21 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
certutil "github.com/argoproj/argo-cd/util/cert"
|
||||
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
// NewCertCommand returns a new instance of an `argocd repo` command
|
||||
@@ -66,7 +65,7 @@ func NewCertAddTLSCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
Short: "Add TLS certificate data for connecting to repository server SERVERNAME",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -149,7 +148,7 @@ func NewCertAddSSHCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
|
||||
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
var sshKnownHostsLists []string
|
||||
var err error
|
||||
@@ -221,7 +220,7 @@ func NewCertRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
hostNamePattern := args[0]
|
||||
|
||||
// Prevent the user from specifying a wildcard as hostname as precaution
|
||||
@@ -276,7 +275,7 @@ func NewCertListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
}
|
||||
|
||||
conn, certIf := argocdclient.NewClientOrDie(clientOpts).NewCertClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
certificates, err := certIf.ListCertificates(context.Background(), &certificatepkg.RepositoryCertificateQuery{HostNamePattern: hostNamePattern, CertType: certType})
|
||||
errors.CheckError(err)
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
@@ -16,11 +18,9 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/clusterauth"
|
||||
)
|
||||
|
||||
@@ -110,7 +110,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
errors.CheckError(err)
|
||||
}
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
clst := newCluster(contextName, namespaces, conf, managerBearerToken, awsAuthConf)
|
||||
if inCluster {
|
||||
clst.Server = common.KubernetesInternalAPIServerAddr
|
||||
@@ -179,22 +179,42 @@ func newCluster(name string, namespaces []string, conf *rest.Config, managerBear
|
||||
Insecure: conf.TLSClientConfig.Insecure,
|
||||
ServerName: conf.TLSClientConfig.ServerName,
|
||||
CAData: conf.TLSClientConfig.CAData,
|
||||
CertData: conf.TLSClientConfig.CertData,
|
||||
KeyData: conf.TLSClientConfig.KeyData,
|
||||
}
|
||||
if len(conf.TLSClientConfig.CAData) == 0 && conf.TLSClientConfig.CAFile != "" {
|
||||
data, err := ioutil.ReadFile(conf.TLSClientConfig.CAFile)
|
||||
errors.CheckError(err)
|
||||
tlsClientConfig.CAData = data
|
||||
}
|
||||
if len(conf.TLSClientConfig.CertData) == 0 && conf.TLSClientConfig.CertFile != "" {
|
||||
data, err := ioutil.ReadFile(conf.TLSClientConfig.CertFile)
|
||||
errors.CheckError(err)
|
||||
tlsClientConfig.CertData = data
|
||||
}
|
||||
if len(conf.TLSClientConfig.KeyData) == 0 && conf.TLSClientConfig.KeyFile != "" {
|
||||
data, err := ioutil.ReadFile(conf.TLSClientConfig.KeyFile)
|
||||
errors.CheckError(err)
|
||||
tlsClientConfig.KeyData = data
|
||||
}
|
||||
|
||||
clst := argoappv1.Cluster{
|
||||
Server: conf.Host,
|
||||
Name: name,
|
||||
Namespaces: namespaces,
|
||||
Config: argoappv1.ClusterConfig{
|
||||
BearerToken: managerBearerToken,
|
||||
TLSClientConfig: tlsClientConfig,
|
||||
AWSAuthConfig: awsAuthConf,
|
||||
},
|
||||
}
|
||||
|
||||
// Bearer token will preferentially be used for auth if present,
|
||||
// Even in presence of key/cert credentials
|
||||
// So set bearer token only if the key/cert data is absent
|
||||
if len(tlsClientConfig.CertData) == 0 || len(tlsClientConfig.KeyData) == 0 {
|
||||
clst.Config.BearerToken = managerBearerToken
|
||||
}
|
||||
|
||||
return &clst
|
||||
}
|
||||
|
||||
@@ -213,7 +233,7 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
clusters := make([]argoappv1.Cluster, 0)
|
||||
for _, clusterName := range args {
|
||||
clst, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: clusterName})
|
||||
@@ -282,7 +302,7 @@ func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
// clientset, err := kubernetes.NewForConfig(conf)
|
||||
// errors.CheckError(err)
|
||||
@@ -330,7 +350,7 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
Short: "List configured clusters",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
clusters, err := clusterIf.List(context.Background(), &clusterpkg.ClusterQuery{})
|
||||
errors.CheckError(err)
|
||||
switch output {
|
||||
@@ -362,7 +382,7 @@ func NewClusterRotateAuthCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
clusterQuery := clusterpkg.ClusterQuery{
|
||||
Server: args[0],
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
@@ -29,3 +32,52 @@ func Test_printClusterTable(t *testing.T) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func Test_newCluster(t *testing.T) {
|
||||
clusterWithData := newCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: false,
|
||||
ServerName: "test-endpoint.example.com",
|
||||
CAData: []byte("test-ca-data"),
|
||||
CertData: []byte("test-cert-data"),
|
||||
KeyData: []byte("test-key-data"),
|
||||
},
|
||||
Host: "test-endpoint.example.com",
|
||||
},
|
||||
"test-bearer-token",
|
||||
&v1alpha1.AWSAuthConfig{})
|
||||
|
||||
assert.Equal(t, "test-cert-data", string(clusterWithData.Config.CertData))
|
||||
assert.Equal(t, "test-key-data", string(clusterWithData.Config.KeyData))
|
||||
assert.Equal(t, "", clusterWithData.Config.BearerToken)
|
||||
|
||||
clusterWithFiles := newCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: false,
|
||||
ServerName: "test-endpoint.example.com",
|
||||
CAData: []byte("test-ca-data"),
|
||||
CertFile: "./testdata/test.cert.pem",
|
||||
KeyFile: "./testdata/test.key.pem",
|
||||
},
|
||||
Host: "test-endpoint.example.com",
|
||||
},
|
||||
"test-bearer-token",
|
||||
&v1alpha1.AWSAuthConfig{})
|
||||
|
||||
assert.True(t, strings.Contains(string(clusterWithFiles.Config.CertData), "test-cert-data"))
|
||||
assert.True(t, strings.Contains(string(clusterWithFiles.Config.KeyData), "test-key-data"))
|
||||
assert.Equal(t, "", clusterWithFiles.Config.BearerToken)
|
||||
|
||||
clusterWithBearerToken := newCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: false,
|
||||
ServerName: "test-endpoint.example.com",
|
||||
CAData: []byte("test-ca-data"),
|
||||
},
|
||||
Host: "test-endpoint.example.com",
|
||||
},
|
||||
"test-bearer-token",
|
||||
&v1alpha1.AWSAuthConfig{})
|
||||
|
||||
assert.Equal(t, "test-bearer-token", clusterWithBearerToken.Config.BearerToken)
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package commands
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
)
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -16,11 +18,9 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
sessionpkg "github.com/argoproj/argo-cd/pkg/apiclient/session"
|
||||
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
@@ -42,39 +42,48 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
Short: "Log in to Argo CD",
|
||||
Long: "Log in to Argo CD",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
var server string
|
||||
|
||||
if len(args) != 1 && !globalClientOpts.PortForward {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
server := args[0]
|
||||
tlsTestResult, err := grpc_util.TestTLS(server)
|
||||
errors.CheckError(err)
|
||||
if !tlsTestResult.TLS {
|
||||
if !globalClientOpts.PlainText {
|
||||
if !cli.AskToProceed("WARNING: server is not configured with TLS. Proceed (y/n)? ") {
|
||||
os.Exit(1)
|
||||
|
||||
if globalClientOpts.PortForward {
|
||||
server = "port-forward"
|
||||
} else {
|
||||
server = args[0]
|
||||
tlsTestResult, err := grpc_util.TestTLS(server)
|
||||
errors.CheckError(err)
|
||||
if !tlsTestResult.TLS {
|
||||
if !globalClientOpts.PlainText {
|
||||
if !cli.AskToProceed("WARNING: server is not configured with TLS. Proceed (y/n)? ") {
|
||||
os.Exit(1)
|
||||
}
|
||||
globalClientOpts.PlainText = true
|
||||
}
|
||||
globalClientOpts.PlainText = true
|
||||
}
|
||||
} else if tlsTestResult.InsecureErr != nil {
|
||||
if !globalClientOpts.Insecure {
|
||||
if !cli.AskToProceed(fmt.Sprintf("WARNING: server certificate had error: %s. Proceed insecurely (y/n)? ", tlsTestResult.InsecureErr)) {
|
||||
os.Exit(1)
|
||||
} else if tlsTestResult.InsecureErr != nil {
|
||||
if !globalClientOpts.Insecure {
|
||||
if !cli.AskToProceed(fmt.Sprintf("WARNING: server certificate had error: %s. Proceed insecurely (y/n)? ", tlsTestResult.InsecureErr)) {
|
||||
os.Exit(1)
|
||||
}
|
||||
globalClientOpts.Insecure = true
|
||||
}
|
||||
globalClientOpts.Insecure = true
|
||||
}
|
||||
}
|
||||
clientOpts := argocdclient.ClientOptions{
|
||||
ConfigPath: "",
|
||||
ServerAddr: server,
|
||||
Insecure: globalClientOpts.Insecure,
|
||||
PlainText: globalClientOpts.PlainText,
|
||||
GRPCWeb: globalClientOpts.GRPCWeb,
|
||||
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
|
||||
ConfigPath: "",
|
||||
ServerAddr: server,
|
||||
Insecure: globalClientOpts.Insecure,
|
||||
PlainText: globalClientOpts.PlainText,
|
||||
GRPCWeb: globalClientOpts.GRPCWeb,
|
||||
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
|
||||
PortForward: globalClientOpts.PortForward,
|
||||
PortForwardNamespace: globalClientOpts.PortForwardNamespace,
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(&clientOpts)
|
||||
setConn, setIf := acdClient.NewSettingsClientOrDie()
|
||||
defer util.Close(setConn)
|
||||
defer io.Close(setConn)
|
||||
|
||||
if ctxName == "" {
|
||||
ctxName = server
|
||||
@@ -105,7 +114,7 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
SkipClaimsValidation: true,
|
||||
}
|
||||
claims := jwt.MapClaims{}
|
||||
_, _, err = parser.ParseUnverified(tokenString, &claims)
|
||||
_, _, err := parser.ParseUnverified(tokenString, &claims)
|
||||
errors.CheckError(err)
|
||||
|
||||
fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims))
|
||||
@@ -290,7 +299,7 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
|
||||
func passwordLogin(acdClient argocdclient.Client, username, password string) string {
|
||||
username, password = cli.PromptCredentials(username, password)
|
||||
sessConn, sessionIf := acdClient.NewSessionClientOrDie()
|
||||
defer util.Close(sessConn)
|
||||
defer io.Close(sessConn)
|
||||
sessionRequest := sessionpkg.SessionCreateRequest{
|
||||
Username: username,
|
||||
Password: password,
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
)
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -20,11 +22,9 @@ import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
@@ -170,7 +170,7 @@ func NewProjectCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
}
|
||||
}
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
_, err := projIf.Create(context.Background(), &projectpkg.ProjectCreateRequest{Project: &proj, Upsert: upsert})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
@@ -200,7 +200,7 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
}
|
||||
projName := args[0]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -247,7 +247,7 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
server := args[1]
|
||||
namespace := args[2]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -279,7 +279,7 @@ func NewProjectRemoveDestinationCommand(clientOpts *argocdclient.ClientOptions)
|
||||
server := args[1]
|
||||
namespace := args[2]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -317,7 +317,7 @@ func NewProjectAddSourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
projName := args[0]
|
||||
url := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -352,7 +352,7 @@ func modifyClusterResourceCmd(cmdUse, cmdDesc string, clientOpts *argocdclient.C
|
||||
}
|
||||
projName, group, kind := args[0], args[1], args[2]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -379,7 +379,7 @@ func modifyNamespaceResourceCmd(cmdUse, cmdDesc string, clientOpts *argocdclient
|
||||
}
|
||||
projName, group, kind := args[0], args[1], args[2]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -515,7 +515,7 @@ func NewProjectRemoveSourceCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
projName := args[0]
|
||||
url := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -551,7 +551,7 @@ func NewProjectDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
for _, name := range args {
|
||||
_, err := projIf.Delete(context.Background(), &projectpkg.ProjectQuery{Name: name})
|
||||
errors.CheckError(err)
|
||||
@@ -588,7 +588,7 @@ func NewProjectListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
Short: "List projects",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
projects, err := projIf.List(context.Background(), &projectpkg.ProjectQuery{})
|
||||
errors.CheckError(err)
|
||||
switch output {
|
||||
@@ -714,7 +714,7 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
}
|
||||
projName := args[0]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
p, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -744,7 +744,7 @@ func NewProjectEditCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
}
|
||||
projName := args[0]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer argoio.Close(conn)
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
projData, err := json.Marshal(proj.Spec)
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
timeutil "github.com/argoproj/pkg/time"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -60,7 +60,7 @@ func NewProjectRoleAddPolicyCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
projName := args[0]
|
||||
roleName := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -95,7 +95,7 @@ func NewProjectRoleRemovePolicyCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
projName := args[0]
|
||||
roleName := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -140,7 +140,7 @@ func NewProjectRoleCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
projName := args[0]
|
||||
roleName := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -174,7 +174,7 @@ func NewProjectRoleDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
projName := args[0]
|
||||
roleName := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -211,7 +211,7 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
projName := args[0]
|
||||
roleName := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
duration, err := timeutil.ParseDuration(expiresIn)
|
||||
errors.CheckError(err)
|
||||
token, err := projIf.CreateToken(context.Background(), &projectpkg.ProjectTokenCreateRequest{Project: projName, Role: roleName, ExpiresIn: int64(duration.Seconds())})
|
||||
@@ -240,7 +240,7 @@ func NewProjectRoleDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
errors.CheckError(err)
|
||||
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
_, err = projIf.DeleteToken(context.Background(), &projectpkg.ProjectTokenDeleteRequest{Project: projName, Role: roleName, Iat: issuedAt})
|
||||
errors.CheckError(err)
|
||||
@@ -281,7 +281,7 @@ func NewProjectRoleListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
projName := args[0]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
project, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -315,7 +315,7 @@ func NewProjectRoleGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
projName := args[0]
|
||||
roleName := args[1]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -357,7 +357,7 @@ func NewProjectRoleAddGroupCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
}
|
||||
projName, roleName, groupName := args[0], args[1], args[2]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
updated, err := proj.AddGroupToRole(roleName, groupName)
|
||||
@@ -386,7 +386,7 @@ func NewProjectRoleRemoveGroupCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
}
|
||||
projName, roleName, groupName := args[0], args[1], args[2]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
updated, err := proj.RemoveGroupFromRole(roleName, groupName)
|
||||
|
||||
@@ -2,21 +2,19 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"strconv"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
)
|
||||
|
||||
// NewProjectWindowsCommand returns a new instance of the `argocd proj windows` command
|
||||
@@ -55,7 +53,7 @@ func NewProjectWindowsDisableManualSyncCommand(clientOpts *argocdclient.ClientOp
|
||||
errors.CheckError(err)
|
||||
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -90,7 +88,7 @@ func NewProjectWindowsEnableManualSyncCommand(clientOpts *argocdclient.ClientOpt
|
||||
errors.CheckError(err)
|
||||
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -129,7 +127,7 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
}
|
||||
projName := args[0]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -168,7 +166,7 @@ func NewProjectWindowsDeleteCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
errors.CheckError(err)
|
||||
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -207,7 +205,7 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
errors.CheckError(err)
|
||||
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
@@ -248,7 +246,7 @@ func NewProjectWindowsListCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
}
|
||||
projName := args[0]
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/coreos/go-oidc"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
)
|
||||
@@ -59,7 +59,7 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
} else {
|
||||
fmt.Println("Reinitiating SSO login")
|
||||
setConn, setIf := acdClient.NewSettingsClientOrDie()
|
||||
defer util.Close(setConn)
|
||||
defer argoio.Close(setConn)
|
||||
ctx := context.Background()
|
||||
httpClient, err := acdClient.HTTPClient()
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
)
|
||||
@@ -32,6 +32,7 @@ func NewRepoCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
}
|
||||
|
||||
command.AddCommand(NewRepoAddCommand(clientOpts))
|
||||
command.AddCommand(NewRepoGetCommand(clientOpts))
|
||||
command.AddCommand(NewRepoListCommand(clientOpts))
|
||||
command.AddCommand(NewRepoRemoveCommand(clientOpts))
|
||||
return command
|
||||
@@ -131,7 +132,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
}
|
||||
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
// If the user set a username, but didn't supply password via --password,
|
||||
// then we prompt for it
|
||||
@@ -195,7 +196,7 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
for _, repoURL := range args {
|
||||
_, err := repoIf.Delete(context.Background(), &repositorypkg.RepoQuery{Repo: repoURL})
|
||||
errors.CheckError(err)
|
||||
@@ -243,7 +244,7 @@ func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
Short: "List configured repositories",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
forceRefresh := false
|
||||
switch refresh {
|
||||
case "":
|
||||
@@ -273,3 +274,52 @@ func NewRepoListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status")
|
||||
return command
|
||||
}
|
||||
|
||||
// NewRepoGetCommand returns a new instance of an `argocd repo rm` command
|
||||
func NewRepoGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
output string
|
||||
refresh string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Get a configured repository by URL",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Repository URL
|
||||
repoURL := args[0]
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoClientOrDie()
|
||||
defer io.Close(conn)
|
||||
forceRefresh := false
|
||||
switch refresh {
|
||||
case "":
|
||||
case "hard":
|
||||
forceRefresh = true
|
||||
default:
|
||||
err := fmt.Errorf("--refresh must be one of: 'hard'")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
repo, err := repoIf.Get(context.Background(), &repositorypkg.RepoQuery{Repo: repoURL, ForceRefresh: forceRefresh})
|
||||
errors.CheckError(err)
|
||||
switch output {
|
||||
case "yaml", "json":
|
||||
err := PrintResource(repo, output)
|
||||
errors.CheckError(err)
|
||||
case "url":
|
||||
fmt.Println(repo.Repo)
|
||||
// wide is the default
|
||||
case "wide", "":
|
||||
printRepoTable(appsv1.Repositories{repo})
|
||||
default:
|
||||
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
|
||||
}
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: json|yaml|wide|url")
|
||||
command.Flags().StringVar(&refresh, "refresh", "", "Force a cache refresh on connection status")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -7,14 +7,14 @@ import (
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
repocredspkg "github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
)
|
||||
@@ -104,7 +104,7 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
|
||||
}
|
||||
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoCredsClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
// If the user set a username, but didn't supply password via --password,
|
||||
// then we prompt for it
|
||||
@@ -142,7 +142,7 @@ func NewRepoCredsRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoCredsClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
for _, repoURL := range args {
|
||||
_, err := repoIf.DeleteRepositoryCredentials(context.Background(), &repocredspkg.RepoCredsDeleteRequest{Url: repoURL})
|
||||
errors.CheckError(err)
|
||||
@@ -182,7 +182,7 @@ func NewRepoCredsListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
Short: "List configured repository credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
conn, repoIf := argocdclient.NewClientOrDie(clientOpts).NewRepoCredsClientOrDie()
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
repos, err := repoIf.ListRepositoryCredentials(context.Background(), &repocredspkg.RepoCredsQuery{})
|
||||
errors.CheckError(err)
|
||||
switch output {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
@@ -15,9 +15,13 @@ func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
}
|
||||
|
||||
var logLevel string
|
||||
var (
|
||||
logFormat string
|
||||
logLevel string
|
||||
)
|
||||
|
||||
func initConfig() {
|
||||
cli.SetLogFormat(logFormat)
|
||||
cli.SetLogLevel(logLevel)
|
||||
}
|
||||
|
||||
@@ -60,6 +64,7 @@ func NewCommand() *cobra.Command {
|
||||
command.PersistentFlags().StringVar(&clientOpts.AuthToken, "auth-token", config.GetFlag("auth-token", ""), "Authentication token")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.GRPCWeb, "grpc-web", config.GetBoolFlag("grpc-web"), "Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2.")
|
||||
command.PersistentFlags().StringVar(&clientOpts.GRPCWebRootPath, "grpc-web-root-path", config.GetFlag("grpc-web-root-path", ""), "Enables gRPC-web protocol. Useful if Argo CD server is behind proxy which does not support HTTP2. Set web root.")
|
||||
command.PersistentFlags().StringVar(&logFormat, "logformat", config.GetFlag("logformat", "text"), "Set the logging format. One of: text|json")
|
||||
command.PersistentFlags().StringVar(&logLevel, "loglevel", config.GetFlag("loglevel", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", []string{}, "Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.PortForward, "port-forward", config.GetBoolFlag("port-forward"), "Connect to a random argocd-server port using port forwarding")
|
||||
|
||||
3
cmd/argocd/commands/testdata/test.cert.pem
vendored
Normal file
3
cmd/argocd/commands/testdata/test.cert.pem
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
test-cert-data
|
||||
-----END CERTIFICATE-----
|
||||
3
cmd/argocd/commands/testdata/test.key.pem
vendored
Normal file
3
cmd/argocd/commands/testdata/test.key.pem
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
test-key-data
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -3,16 +3,17 @@ package commands
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
)
|
||||
|
||||
// NewVersionCmd returns a new `version` command to be used as a sub-command to root
|
||||
@@ -25,7 +26,7 @@ func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
|
||||
versionCmd := cobra.Command{
|
||||
Use: "version",
|
||||
Short: fmt.Sprintf("Print version information"),
|
||||
Short: "Print version information",
|
||||
Example: ` # Print the full version of client and server to stdout
|
||||
argocd version
|
||||
|
||||
@@ -39,44 +40,39 @@ func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
argocd version --short -o yaml
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
var (
|
||||
versionIf version.VersionServiceClient
|
||||
serverVers *version.VersionMessage
|
||||
conn io.Closer
|
||||
err error
|
||||
)
|
||||
if !client {
|
||||
// Get Server version
|
||||
conn, versionIf = argocdclient.NewClientOrDie(clientOpts).NewVersionClientOrDie()
|
||||
defer util.Close(conn)
|
||||
serverVers, err = versionIf.Version(context.Background(), &empty.Empty{})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
cv := common.GetVersion()
|
||||
|
||||
switch output {
|
||||
case "yaml", "json":
|
||||
clientVers := common.GetVersion()
|
||||
version := make(map[string]interface{})
|
||||
if !short {
|
||||
version["client"] = clientVers
|
||||
v := make(map[string]interface{})
|
||||
|
||||
if short {
|
||||
v["client"] = map[string]string{cliName: cv.Version}
|
||||
} else {
|
||||
version["client"] = map[string]string{cliName: clientVers.Version}
|
||||
v["client"] = cv
|
||||
}
|
||||
|
||||
if !client {
|
||||
if !short {
|
||||
version["server"] = serverVers
|
||||
sv := getServerVersion(clientOpts)
|
||||
|
||||
if short {
|
||||
v["server"] = map[string]string{"argocd-server": sv.Version}
|
||||
} else {
|
||||
version["server"] = map[string]string{"argocd-server": serverVers.Version}
|
||||
v["server"] = sv
|
||||
}
|
||||
}
|
||||
err := PrintResource(version, output)
|
||||
|
||||
err := PrintResource(v, output)
|
||||
errors.CheckError(err)
|
||||
case "short":
|
||||
printVersion(serverVers, client, true)
|
||||
case "wide", "":
|
||||
// we use value of short for backward compatibility
|
||||
printVersion(serverVers, client, short)
|
||||
case "wide", "short", "":
|
||||
printClientVersion(&cv, short || (output == "short"))
|
||||
|
||||
if !client {
|
||||
sv := getServerVersion(clientOpts)
|
||||
printServerVersion(sv, short || (output == "short"))
|
||||
}
|
||||
default:
|
||||
errors.CheckError(fmt.Errorf("unknown output format: %s", output))
|
||||
log.Fatalf("unknown output format: %s", output)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -86,38 +82,52 @@ func NewVersionCmd(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
return &versionCmd
|
||||
}
|
||||
|
||||
func printVersion(serverVers *version.VersionMessage, client bool, short bool) {
|
||||
version := common.GetVersion()
|
||||
func getServerVersion(options *argocdclient.ClientOptions) *version.VersionMessage {
|
||||
conn, versionIf := argocdclient.NewClientOrDie(options).NewVersionClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
|
||||
v, err := versionIf.Version(context.Background(), &empty.Empty{})
|
||||
errors.CheckError(err)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func printClientVersion(version *common.Version, short bool) {
|
||||
fmt.Printf("%s: %s\n", cliName, version)
|
||||
if !short {
|
||||
fmt.Printf(" BuildDate: %s\n", version.BuildDate)
|
||||
fmt.Printf(" GitCommit: %s\n", version.GitCommit)
|
||||
fmt.Printf(" GitTreeState: %s\n", version.GitTreeState)
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", version.GitTag)
|
||||
}
|
||||
fmt.Printf(" GoVersion: %s\n", version.GoVersion)
|
||||
fmt.Printf(" Compiler: %s\n", version.Compiler)
|
||||
fmt.Printf(" Platform: %s\n", version.Platform)
|
||||
}
|
||||
if client {
|
||||
|
||||
if short {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%s: %s\n", "argocd-server", serverVers.Version)
|
||||
if !short {
|
||||
fmt.Printf(" BuildDate: %s\n", serverVers.BuildDate)
|
||||
fmt.Printf(" GitCommit: %s\n", serverVers.GitCommit)
|
||||
fmt.Printf(" GitTreeState: %s\n", serverVers.GitTreeState)
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", serverVers.GitTag)
|
||||
}
|
||||
fmt.Printf(" GoVersion: %s\n", serverVers.GoVersion)
|
||||
fmt.Printf(" Compiler: %s\n", serverVers.Compiler)
|
||||
fmt.Printf(" Platform: %s\n", serverVers.Platform)
|
||||
fmt.Printf(" Ksonnet Version: %s\n", serverVers.KsonnetVersion)
|
||||
fmt.Printf(" Kustomize Version: %s\n", serverVers.KustomizeVersion)
|
||||
fmt.Printf(" Helm Version: %s\n", serverVers.HelmVersion)
|
||||
fmt.Printf(" Kubectl Version: %s\n", serverVers.KubectlVersion)
|
||||
fmt.Printf(" BuildDate: %s\n", version.BuildDate)
|
||||
fmt.Printf(" GitCommit: %s\n", version.GitCommit)
|
||||
fmt.Printf(" GitTreeState: %s\n", version.GitTreeState)
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", version.GitTag)
|
||||
}
|
||||
fmt.Printf(" GoVersion: %s\n", version.GoVersion)
|
||||
fmt.Printf(" Compiler: %s\n", version.Compiler)
|
||||
fmt.Printf(" Platform: %s\n", version.Platform)
|
||||
}
|
||||
|
||||
func printServerVersion(version *version.VersionMessage, short bool) {
|
||||
fmt.Printf("%s: %s\n", "argocd-server", version.Version)
|
||||
|
||||
if short {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" BuildDate: %s\n", version.BuildDate)
|
||||
fmt.Printf(" GitCommit: %s\n", version.GitCommit)
|
||||
fmt.Printf(" GitTreeState: %s\n", version.GitTreeState)
|
||||
if version.GitTag != "" {
|
||||
fmt.Printf(" GitTag: %s\n", version.GitTag)
|
||||
}
|
||||
fmt.Printf(" GoVersion: %s\n", version.GoVersion)
|
||||
fmt.Printf(" Compiler: %s\n", version.Compiler)
|
||||
fmt.Printf(" Platform: %s\n", version.Platform)
|
||||
fmt.Printf(" Ksonnet Version: %s\n", version.KsonnetVersion)
|
||||
fmt.Printf(" Kustomize Version: %s\n", version.KustomizeVersion)
|
||||
fmt.Printf(" Helm Version: %s\n", version.HelmVersion)
|
||||
fmt.Printf(" Kubectl Version: %s\n", version.KubectlVersion)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
|
||||
commands "github.com/argoproj/argo-cd/cmd/argocd/commands"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
|
||||
// load the gcp plugin (required to authenticate against GKE clusters).
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
|
||||
@@ -104,14 +104,7 @@ const (
|
||||
|
||||
// AnnotationCompareOptions is a comma-separated list of options for comparison
|
||||
AnnotationCompareOptions = "argocd.argoproj.io/compare-options"
|
||||
// AnnotationSyncOptions is a comma-separated list of options for syncing
|
||||
AnnotationSyncOptions = "argocd.argoproj.io/sync-options"
|
||||
// AnnotationSyncWave indicates which wave of the sync the resource or hook should be in
|
||||
AnnotationSyncWave = "argocd.argoproj.io/sync-wave"
|
||||
// AnnotationKeyHook contains the hook type of a resource
|
||||
AnnotationKeyHook = "argocd.argoproj.io/hook"
|
||||
// AnnotationKeyHookDeletePolicy is the policy of deleting a hook
|
||||
AnnotationKeyHookDeletePolicy = "argocd.argoproj.io/hook-delete-policy"
|
||||
|
||||
// AnnotationKeyRefresh is the annotation key which indicates that app needs to be refreshed. Removed by application controller after app is refreshed.
|
||||
// Might take values 'normal'/'hard'. Value 'hard' means manifest cache and target cluster state cache should be invalidated before refresh.
|
||||
AnnotationKeyRefresh = "argocd.argoproj.io/refresh"
|
||||
|
||||
@@ -12,6 +12,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/errors"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/semaphore"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -31,7 +37,6 @@ import (
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
statecache "github.com/argoproj/argo-cd/controller/cache"
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
@@ -39,12 +44,9 @@ import (
|
||||
"github.com/argoproj/argo-cd/pkg/client/informers/externalversions/application/v1alpha1"
|
||||
applisters "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/argo"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
settings_util "github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
@@ -171,7 +173,7 @@ func (ctrl *ApplicationController) GetMetricsServer() *metrics.MetricsServer {
|
||||
return ctrl.metricsServer
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) onKubectlRun(command string) (util.Closer, error) {
|
||||
func (ctrl *ApplicationController) onKubectlRun(command string) (io.Closer, error) {
|
||||
ctrl.metricsServer.IncKubectlExec(command)
|
||||
if ctrl.kubectlSemaphore != nil {
|
||||
if err := ctrl.kubectlSemaphore.Acquire(context.Background(), 1); err != nil {
|
||||
@@ -179,7 +181,7 @@ func (ctrl *ApplicationController) onKubectlRun(command string) (util.Closer, er
|
||||
}
|
||||
ctrl.metricsServer.IncKubectlExecPending(command)
|
||||
}
|
||||
return util.NewCloser(func() error {
|
||||
return io.NewCloser(func() error {
|
||||
if ctrl.kubectlSemaphore != nil {
|
||||
ctrl.kubectlSemaphore.Release(1)
|
||||
ctrl.metricsServer.DecKubectlExecPending(command)
|
||||
@@ -363,7 +365,11 @@ func (ctrl *ApplicationController) managedResources(comparisonResult *comparison
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resDiffPtr, err := diff.Diff(target, live, comparisonResult.diffNormalizer)
|
||||
compareOptions, err := ctrl.settingsMgr.GetResourceCompareOptions()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resDiffPtr, err := diff.Diff(target, live, comparisonResult.diffNormalizer, compareOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -389,11 +395,6 @@ func (ctrl *ApplicationController) managedResources(comparisonResult *comparison
|
||||
} else {
|
||||
item.TargetState = "null"
|
||||
}
|
||||
jsonDiff, err := resDiff.JSONFormat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.Diff = jsonDiff
|
||||
item.PredictedLiveState = string(resDiff.PredictedLive)
|
||||
item.NormalizedLiveState = string(resDiff.NormalizedLive)
|
||||
|
||||
@@ -413,6 +414,8 @@ func (ctrl *ApplicationController) Run(ctx context.Context, statusProcessors int
|
||||
go ctrl.appInformer.Run(ctx.Done())
|
||||
go ctrl.projInformer.Run(ctx.Done())
|
||||
|
||||
errors.CheckError(ctrl.stateCache.Init())
|
||||
|
||||
if !cache.WaitForCacheSync(ctx.Done(), ctrl.appInformer.HasSynced, ctrl.projInformer.HasSynced) {
|
||||
log.Error("Timed out waiting for caches to sync")
|
||||
return
|
||||
@@ -543,7 +546,7 @@ func (ctrl *ApplicationController) processAppComparisonTypeQueueItem() (processN
|
||||
return
|
||||
}
|
||||
|
||||
// shouldbeDeleted returns whether a given resource obj should be deleted on cascade delete of application app
|
||||
// shouldBeDeleted returns whether a given resource obj should be deleted on cascade delete of application app
|
||||
func (ctrl *ApplicationController) shouldBeDeleted(app *appv1.Application, obj *unstructured.Unstructured) bool {
|
||||
return !kube.IsCRD(obj) && !isSelfReferencedApp(app, kube.GetObjectRef(obj))
|
||||
}
|
||||
@@ -596,7 +599,7 @@ func (ctrl *ApplicationController) finalizeApplicationDeletion(app *appv1.Applic
|
||||
}
|
||||
config := metrics.AddMetricsTransportWrapper(ctrl.metricsServer, app, cluster.RESTConfig())
|
||||
|
||||
err = util.RunAllAsync(len(objs), func(i int) error {
|
||||
err = kube.RunAllAsync(len(objs), func(i int) error {
|
||||
obj := objs[i]
|
||||
return ctrl.kubectl.DeleteResource(config, obj.GroupVersionKind(), obj.GetName(), obj.GetNamespace(), false)
|
||||
})
|
||||
@@ -666,7 +669,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logCtx.Errorf("Recovered from panic: %+v\n%s", r, debug.Stack())
|
||||
state.Phase = appv1.OperationError
|
||||
state.Phase = synccommon.OperationError
|
||||
if rerr, ok := r.(error); ok {
|
||||
state.Message = rerr.Error()
|
||||
} else {
|
||||
@@ -693,20 +696,20 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
state = app.Status.OperationState.DeepCopy()
|
||||
logCtx.Infof("Resuming in-progress operation. phase: %s, message: %s", state.Phase, state.Message)
|
||||
} else {
|
||||
state = &appv1.OperationState{Phase: appv1.OperationRunning, Operation: *app.Operation, StartedAt: metav1.Now()}
|
||||
state = &appv1.OperationState{Phase: synccommon.OperationRunning, Operation: *app.Operation, StartedAt: metav1.Now()}
|
||||
ctrl.setOperationState(app, state)
|
||||
logCtx.Infof("Initialized new operation: %v", *app.Operation)
|
||||
}
|
||||
|
||||
ctrl.appStateManager.SyncAppState(app, state)
|
||||
|
||||
if state.Phase == appv1.OperationRunning {
|
||||
if state.Phase == synccommon.OperationRunning {
|
||||
// It's possible for an app to be terminated while we were operating on it. We do not want
|
||||
// to clobber the Terminated state with Running. Get the latest app state to check for this.
|
||||
freshApp, err := ctrl.applicationClientset.ArgoprojV1alpha1().Applications(ctrl.namespace).Get(app.ObjectMeta.Name, metav1.GetOptions{})
|
||||
if err == nil {
|
||||
if freshApp.Status.OperationState != nil && freshApp.Status.OperationState.Phase == appv1.OperationTerminating {
|
||||
state.Phase = appv1.OperationTerminating
|
||||
if freshApp.Status.OperationState != nil && freshApp.Status.OperationState.Phase == synccommon.OperationTerminating {
|
||||
state.Phase = synccommon.OperationTerminating
|
||||
state.Message = "operation is terminating"
|
||||
// after this, we will get requeued to the workqueue, but next time the
|
||||
// SyncAppState will operate in a Terminating phase, allowing the worker to perform
|
||||
@@ -729,7 +732,7 @@ func (ctrl *ApplicationController) processRequestedAppOperation(app *appv1.Appli
|
||||
}
|
||||
|
||||
func (ctrl *ApplicationController) setOperationState(app *appv1.Application, state *appv1.OperationState) {
|
||||
util.RetryUntilSucceed(func() error {
|
||||
kube.RetryUntilSucceed(func() error {
|
||||
if state.Phase == "" {
|
||||
// expose any bugs where we neglect to set phase
|
||||
panic("no phase was set")
|
||||
@@ -873,7 +876,7 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
|
||||
project, hasErrors := ctrl.refreshAppConditions(app)
|
||||
if hasErrors {
|
||||
app.Status.Sync.Status = appv1.SyncStatusCodeUnknown
|
||||
app.Status.Health.Status = appv1.HealthStatusUnknown
|
||||
app.Status.Health.Status = health.HealthStatusUnknown
|
||||
ctrl.persistAppStatus(origApp, &app.Status)
|
||||
return
|
||||
}
|
||||
@@ -962,7 +965,7 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
|
||||
reason = "spec.destination differs"
|
||||
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
|
||||
compareWith = level
|
||||
reason = fmt.Sprintf("controller refresh requested")
|
||||
reason = "controller refresh requested"
|
||||
}
|
||||
|
||||
if reason != "" {
|
||||
@@ -1155,7 +1158,7 @@ func (ctrl *ApplicationController) autoSync(app *appv1.Application, syncStatus *
|
||||
|
||||
// alreadyAttemptedSync returns whether or not the most recent sync was performed against the
|
||||
// commitSHA and with the same app source config which are currently set in the app
|
||||
func alreadyAttemptedSync(app *appv1.Application, commitSHA string) (bool, appv1.OperationPhase) {
|
||||
func alreadyAttemptedSync(app *appv1.Application, commitSHA string) (bool, synccommon.OperationPhase) {
|
||||
if app.Status.OperationState == nil || app.Status.OperationState.Operation.Sync == nil || app.Status.OperationState.SyncResult == nil {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/cache/mocks"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube/kubetest"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -25,12 +29,9 @@ import (
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
mockrepoclient "github.com/argoproj/argo-cd/reposerver/apiclient/mocks"
|
||||
mockreposerver "github.com/argoproj/argo-cd/reposerver/mocks"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
cacheutil "github.com/argoproj/argo-cd/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/kube/kubetest"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
@@ -57,7 +58,7 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
// Mock out call to GenerateManifest
|
||||
mockRepoClient := mockrepoclient.RepoServerServiceClient{}
|
||||
mockRepoClient.On("GenerateManifest", mock.Anything, mock.Anything).Return(data.manifestResponse, nil)
|
||||
mockRepoClientset := mockreposerver.Clientset{}
|
||||
mockRepoClientset := mockrepoclient.Clientset{}
|
||||
mockRepoClientset.On("NewRepoServerClient").Return(&fakeCloser{}, &mockRepoClient, nil)
|
||||
|
||||
secret := corev1.Secret{
|
||||
@@ -106,6 +107,9 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
defer cancelProj()
|
||||
cancelApp := test.StartInformer(ctrl.appInformer)
|
||||
defer cancelApp()
|
||||
clusterCacheMock := mocks.ClusterCache{}
|
||||
clusterCacheMock.On("IsNamespaced", mock.Anything).Return(true, nil)
|
||||
|
||||
mockStateCache := mockstatecache.LiveStateCache{}
|
||||
ctrl.appStateManager.(*appStateManager).liveStateCache = &mockStateCache
|
||||
ctrl.stateCache = &mockStateCache
|
||||
@@ -117,6 +121,7 @@ func newFakeController(data *fakeData) *ApplicationController {
|
||||
response[k] = v.ResourceNode
|
||||
}
|
||||
mockStateCache.On("GetNamespaceTopLevelResources", mock.Anything, mock.Anything).Return(response, nil)
|
||||
mockStateCache.On("GetClusterCache", mock.Anything).Return(&clusterCacheMock, nil)
|
||||
mockStateCache.On("IterateHierarchy", mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
||||
key := args[1].(kube.ResourceKey)
|
||||
action := args[2].(func(child argoappv1.ResourceNode, appName string))
|
||||
@@ -310,7 +315,7 @@ func TestSkipAutoSync(t *testing.T) {
|
||||
Operation: argoappv1.Operation{
|
||||
Sync: &argoappv1.SyncOperation{},
|
||||
},
|
||||
Phase: argoappv1.OperationFailed,
|
||||
Phase: synccommon.OperationFailed,
|
||||
SyncResult: &argoappv1.SyncOperationResult{
|
||||
Revision: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
||||
Source: *app.Spec.Source.DeepCopy(),
|
||||
@@ -367,7 +372,7 @@ func TestAutoSyncIndicateError(t *testing.T) {
|
||||
Source: app.Spec.Source.DeepCopy(),
|
||||
},
|
||||
},
|
||||
Phase: argoappv1.OperationFailed,
|
||||
Phase: synccommon.OperationFailed,
|
||||
SyncResult: &argoappv1.SyncOperationResult{
|
||||
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
Source: *app.Spec.Source.DeepCopy(),
|
||||
@@ -411,7 +416,7 @@ func TestAutoSyncParameterOverrides(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Phase: argoappv1.OperationFailed,
|
||||
Phase: synccommon.OperationFailed,
|
||||
SyncResult: &argoappv1.SyncOperationResult{
|
||||
Revision: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
},
|
||||
@@ -465,7 +470,7 @@ func TestFinalizeAppDeletion(t *testing.T) {
|
||||
assert.True(t, patched)
|
||||
}
|
||||
|
||||
// Ensure any stray resources irregulary labeled with instance label of app are not deleted upon deleting,
|
||||
// Ensure any stray resources irregularly labeled with instance label of app are not deleted upon deleting,
|
||||
// when app project restriction is in place
|
||||
{
|
||||
defaultProj := argoappv1.AppProject{
|
||||
@@ -666,7 +671,7 @@ func TestSetOperationStateOnDeletedApp(t *testing.T) {
|
||||
patched = true
|
||||
return true, nil, apierr.NewNotFound(schema.GroupResource{}, "my-app")
|
||||
})
|
||||
ctrl.setOperationState(newFakeApp(), &argoappv1.OperationState{Phase: argoappv1.OperationSucceeded})
|
||||
ctrl.setOperationState(newFakeApp(), &argoappv1.OperationState{Phase: synccommon.OperationSucceeded})
|
||||
assert.True(t, patched)
|
||||
}
|
||||
|
||||
|
||||
335
controller/cache/cache.go
vendored
335
controller/cache/cache.go
vendored
@@ -5,6 +5,9 @@ import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
clustercache "github.com/argoproj/gitops-engine/pkg/cache"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -15,23 +18,18 @@ import (
|
||||
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/lua"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
type cacheSettings struct {
|
||||
ResourceOverrides map[string]appv1.ResourceOverride
|
||||
AppInstanceLabelKey string
|
||||
ResourcesFilter *settings.ResourcesFilter
|
||||
}
|
||||
|
||||
type LiveStateCache interface {
|
||||
// Returns k8s server version
|
||||
GetVersionsInfo(serverURL string) (string, []metav1.APIGroup, error)
|
||||
// Returns true of given group kind is a namespaced resource
|
||||
IsNamespaced(server string, gk schema.GroupKind) (bool, error)
|
||||
// Returns synced cluster cache
|
||||
GetClusterCache(server string) (clustercache.ClusterCache, error)
|
||||
// Executes give callback against resource specified by the key and all its children
|
||||
IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error
|
||||
// Returns state of live nodes which correspond for target nodes of specified application.
|
||||
@@ -40,23 +38,21 @@ type LiveStateCache interface {
|
||||
GetNamespaceTopLevelResources(server string, namespace string) (map[kube.ResourceKey]appv1.ResourceNode, error)
|
||||
// Starts watching resources of each controlled cluster.
|
||||
Run(ctx context.Context) error
|
||||
// Invalidate invalidates the entire cluster state cache
|
||||
Invalidate()
|
||||
// Returns information about monitored clusters
|
||||
GetClustersInfo() []metrics.ClusterInfo
|
||||
GetClustersInfo() []clustercache.ClusterInfo
|
||||
// Init must be executed before cache can be used
|
||||
Init() error
|
||||
}
|
||||
|
||||
type ObjectUpdatedHandler = func(managedByApp map[string]bool, ref v1.ObjectReference)
|
||||
|
||||
func GetTargetObjKey(a *appv1.Application, un *unstructured.Unstructured, isNamespaced bool) kube.ResourceKey {
|
||||
key := kube.GetResourceKey(un)
|
||||
if !isNamespaced {
|
||||
key.Namespace = ""
|
||||
} else if isNamespaced && key.Namespace == "" {
|
||||
key.Namespace = a.Spec.Destination.Namespace
|
||||
}
|
||||
|
||||
return key
|
||||
type ResourceInfo struct {
|
||||
Info []appv1.InfoItem
|
||||
AppName string
|
||||
// networkingInfo are available only for known types involved into networking: Ingress, Service, Pod
|
||||
NetworkingInfo *appv1.ResourceNetworkingInfo
|
||||
Images []string
|
||||
Health *health.HealthStatus
|
||||
}
|
||||
|
||||
func NewLiveStateCache(
|
||||
@@ -68,29 +64,32 @@ func NewLiveStateCache(
|
||||
onObjectUpdated ObjectUpdatedHandler) LiveStateCache {
|
||||
|
||||
return &liveStateCache{
|
||||
appInformer: appInformer,
|
||||
db: db,
|
||||
clusters: make(map[string]*clusterInfo),
|
||||
lock: &sync.RWMutex{},
|
||||
onObjectUpdated: onObjectUpdated,
|
||||
kubectl: kubectl,
|
||||
settingsMgr: settingsMgr,
|
||||
metricsServer: metricsServer,
|
||||
cacheSettingsLock: &sync.Mutex{},
|
||||
appInformer: appInformer,
|
||||
db: db,
|
||||
clusters: make(map[string]clustercache.ClusterCache),
|
||||
onObjectUpdated: onObjectUpdated,
|
||||
kubectl: kubectl,
|
||||
settingsMgr: settingsMgr,
|
||||
metricsServer: metricsServer,
|
||||
}
|
||||
}
|
||||
|
||||
type cacheSettings struct {
|
||||
clusterSettings clustercache.Settings
|
||||
appInstanceLabelKey string
|
||||
}
|
||||
|
||||
type liveStateCache struct {
|
||||
db db.ArgoDB
|
||||
clusters map[string]*clusterInfo
|
||||
lock *sync.RWMutex
|
||||
appInformer cache.SharedIndexInformer
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
kubectl kube.Kubectl
|
||||
settingsMgr *settings.SettingsManager
|
||||
metricsServer *metrics.MetricsServer
|
||||
cacheSettingsLock *sync.Mutex
|
||||
cacheSettings *cacheSettings
|
||||
db db.ArgoDB
|
||||
appInformer cache.SharedIndexInformer
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
kubectl kube.Kubectl
|
||||
settingsMgr *settings.SettingsManager
|
||||
metricsServer *metrics.MetricsServer
|
||||
|
||||
clusters map[string]clustercache.ClusterCache
|
||||
cacheSettings cacheSettings
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) {
|
||||
@@ -106,72 +105,198 @@ func (c *liveStateCache) loadCacheSettings() (*cacheSettings, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cacheSettings{AppInstanceLabelKey: appInstanceLabelKey, ResourceOverrides: resourceOverrides, ResourcesFilter: resourcesFilter}, nil
|
||||
clusterSettings := clustercache.Settings{
|
||||
ResourceHealthOverride: lua.ResourceHealthOverrides(resourceOverrides),
|
||||
ResourcesFilter: resourcesFilter,
|
||||
}
|
||||
return &cacheSettings{clusterSettings, appInstanceLabelKey}, nil
|
||||
}
|
||||
|
||||
func (c *liveStateCache) getCluster(server string) (*clusterInfo, error) {
|
||||
func asResourceNode(r *clustercache.Resource) appv1.ResourceNode {
|
||||
gv, err := schema.ParseGroupVersion(r.Ref.APIVersion)
|
||||
if err != nil {
|
||||
gv = schema.GroupVersion{}
|
||||
}
|
||||
parentRefs := make([]appv1.ResourceRef, len(r.OwnerRefs))
|
||||
for _, ownerRef := range r.OwnerRefs {
|
||||
ownerGvk := schema.FromAPIVersionAndKind(ownerRef.APIVersion, ownerRef.Kind)
|
||||
ownerKey := kube.NewResourceKey(ownerGvk.Group, ownerRef.Kind, r.Ref.Namespace, ownerRef.Name)
|
||||
parentRefs[0] = appv1.ResourceRef{Name: ownerRef.Name, Kind: ownerKey.Kind, Namespace: r.Ref.Namespace, Group: ownerKey.Group, UID: string(ownerRef.UID)}
|
||||
}
|
||||
var resHealth *appv1.HealthStatus
|
||||
resourceInfo := resInfo(r)
|
||||
if resourceInfo.Health != nil {
|
||||
resHealth = &appv1.HealthStatus{Status: resourceInfo.Health.Status, Message: resourceInfo.Health.Message}
|
||||
}
|
||||
return appv1.ResourceNode{
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
UID: string(r.Ref.UID),
|
||||
Name: r.Ref.Name,
|
||||
Group: gv.Group,
|
||||
Version: gv.Version,
|
||||
Kind: r.Ref.Kind,
|
||||
Namespace: r.Ref.Namespace,
|
||||
},
|
||||
ParentRefs: parentRefs,
|
||||
Info: resourceInfo.Info,
|
||||
ResourceVersion: r.ResourceVersion,
|
||||
NetworkingInfo: resourceInfo.NetworkingInfo,
|
||||
Images: resourceInfo.Images,
|
||||
Health: resHealth,
|
||||
}
|
||||
}
|
||||
|
||||
func resInfo(r *clustercache.Resource) *ResourceInfo {
|
||||
info, ok := r.Info.(*ResourceInfo)
|
||||
if !ok || info == nil {
|
||||
info = &ResourceInfo{}
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func isRootAppNode(r *clustercache.Resource) bool {
|
||||
return resInfo(r).AppName != "" && len(r.OwnerRefs) == 0
|
||||
}
|
||||
|
||||
func getApp(r *clustercache.Resource, ns map[kube.ResourceKey]*clustercache.Resource) string {
|
||||
return getAppRecursive(r, ns, map[kube.ResourceKey]bool{})
|
||||
}
|
||||
|
||||
func ownerRefGV(ownerRef metav1.OwnerReference) schema.GroupVersion {
|
||||
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
|
||||
if err != nil {
|
||||
gv = schema.GroupVersion{}
|
||||
}
|
||||
return gv
|
||||
}
|
||||
|
||||
func getAppRecursive(r *clustercache.Resource, ns map[kube.ResourceKey]*clustercache.Resource, visited map[kube.ResourceKey]bool) string {
|
||||
if !visited[r.ResourceKey()] {
|
||||
visited[r.ResourceKey()] = true
|
||||
} else {
|
||||
log.Warnf("Circular dependency detected: %v.", visited)
|
||||
return resInfo(r).AppName
|
||||
}
|
||||
|
||||
if resInfo(r).AppName != "" {
|
||||
return resInfo(r).AppName
|
||||
}
|
||||
for _, ownerRef := range r.OwnerRefs {
|
||||
gv := ownerRefGV(ownerRef)
|
||||
if parent, ok := ns[kube.NewResourceKey(gv.Group, ownerRef.Kind, r.Ref.Namespace, ownerRef.Name)]; ok {
|
||||
app := getAppRecursive(parent, ns, visited)
|
||||
if app != "" {
|
||||
return app
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var (
|
||||
ignoredRefreshResources = map[string]bool{
|
||||
"/" + kube.EndpointsKind: true,
|
||||
}
|
||||
)
|
||||
|
||||
// skipAppRequeuing checks if the object is an API type which we want to skip requeuing against.
|
||||
// We ignore API types which have a high churn rate, and/or whose updates are irrelevant to the app
|
||||
func skipAppRequeuing(key kube.ResourceKey) bool {
|
||||
return ignoredRefreshResources[key.Group+"/"+key.Kind]
|
||||
}
|
||||
|
||||
func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, error) {
|
||||
c.lock.RLock()
|
||||
info, ok := c.clusters[server]
|
||||
clusterCache, ok := c.clusters[server]
|
||||
cacheSettings := c.cacheSettings
|
||||
c.lock.RUnlock()
|
||||
|
||||
if ok {
|
||||
return info, nil
|
||||
return clusterCache, nil
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
info, ok = c.clusters[server]
|
||||
clusterCache, ok = c.clusters[server]
|
||||
if ok {
|
||||
return info, nil
|
||||
return clusterCache, nil
|
||||
}
|
||||
|
||||
logCtx := log.WithField("server", server)
|
||||
logCtx.Info("initializing cluster")
|
||||
cluster, err := c.db.GetCluster(context.Background(), server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = &clusterInfo{
|
||||
apisMeta: make(map[schema.GroupKind]*apiMeta),
|
||||
lock: &sync.RWMutex{},
|
||||
nodes: make(map[kube.ResourceKey]*node),
|
||||
nsIndex: make(map[string]map[kube.ResourceKey]*node),
|
||||
onObjectUpdated: c.onObjectUpdated,
|
||||
kubectl: c.kubectl,
|
||||
cluster: cluster,
|
||||
syncTime: nil,
|
||||
log: logCtx,
|
||||
cacheSettingsSrc: c.getCacheSettings,
|
||||
onEventReceived: func(event watch.EventType, un *unstructured.Unstructured) {
|
||||
gvk := un.GroupVersionKind()
|
||||
c.metricsServer.IncClusterEventsCount(cluster.Server, gvk.Group, gvk.Kind)
|
||||
},
|
||||
metricsServer: c.metricsServer,
|
||||
}
|
||||
c.clusters[cluster.Server] = info
|
||||
|
||||
return info, nil
|
||||
clusterCache = clustercache.NewClusterCache(cluster.RESTConfig(),
|
||||
clustercache.SetSettings(cacheSettings.clusterSettings),
|
||||
clustercache.SetNamespaces(cluster.Namespaces),
|
||||
clustercache.SetPopulateResourceInfoHandler(func(un *unstructured.Unstructured, isRoot bool) (interface{}, bool) {
|
||||
res := &ResourceInfo{}
|
||||
populateNodeInfo(un, res)
|
||||
res.Health, _ = health.GetResourceHealth(un, cacheSettings.clusterSettings.ResourceHealthOverride)
|
||||
appName := kube.GetAppInstanceLabel(un, cacheSettings.appInstanceLabelKey)
|
||||
if isRoot && appName != "" {
|
||||
res.AppName = appName
|
||||
}
|
||||
|
||||
// edge case. we do not label CRDs, so they miss the tracking label we inject. But we still
|
||||
// want the full resource to be available in our cache (to diff), so we store all CRDs
|
||||
return res, res.AppName != "" || un.GroupVersionKind().Kind == kube.CustomResourceDefinitionKind
|
||||
}),
|
||||
)
|
||||
|
||||
_ = clusterCache.OnResourceUpdated(func(newRes *clustercache.Resource, oldRes *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
|
||||
toNotify := make(map[string]bool)
|
||||
var ref v1.ObjectReference
|
||||
if newRes != nil {
|
||||
ref = newRes.Ref
|
||||
} else {
|
||||
ref = oldRes.Ref
|
||||
}
|
||||
for _, r := range []*clustercache.Resource{newRes, oldRes} {
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
app := getApp(r, namespaceResources)
|
||||
if app == "" || skipAppRequeuing(r.ResourceKey()) {
|
||||
continue
|
||||
}
|
||||
toNotify[app] = isRootAppNode(r) || toNotify[app]
|
||||
}
|
||||
c.onObjectUpdated(toNotify, ref)
|
||||
})
|
||||
|
||||
_ = clusterCache.OnEvent(func(event watch.EventType, un *unstructured.Unstructured) {
|
||||
gvk := un.GroupVersionKind()
|
||||
c.metricsServer.IncClusterEventsCount(cluster.Server, gvk.Group, gvk.Kind)
|
||||
})
|
||||
|
||||
c.clusters[cluster.Server] = clusterCache
|
||||
|
||||
return clusterCache, nil
|
||||
}
|
||||
|
||||
func (c *liveStateCache) getSyncedCluster(server string) (*clusterInfo, error) {
|
||||
info, err := c.getCluster(server)
|
||||
func (c *liveStateCache) getSyncedCluster(server string) (clustercache.ClusterCache, error) {
|
||||
clusterCache, err := c.getCluster(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = info.ensureSynced()
|
||||
err = clusterCache.EnsureSynced()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
return clusterCache, nil
|
||||
}
|
||||
|
||||
func (c *liveStateCache) Invalidate() {
|
||||
func (c *liveStateCache) invalidate(cacheSettings cacheSettings) {
|
||||
log.Info("invalidating live state cache")
|
||||
c.lock.RLock()
|
||||
defer c.lock.RLock()
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.cacheSettings = cacheSettings
|
||||
for _, clust := range c.clusters {
|
||||
clust.invalidate()
|
||||
clust.Invalidate(clustercache.SetSettings(cacheSettings.clusterSettings))
|
||||
}
|
||||
log.Info("live state cache invalidated")
|
||||
}
|
||||
@@ -181,7 +306,7 @@ func (c *liveStateCache) IsNamespaced(server string, gk schema.GroupKind) (bool,
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return clusterInfo.isNamespaced(gk), nil
|
||||
return clusterInfo.IsNamespaced(gk)
|
||||
}
|
||||
|
||||
func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) error {
|
||||
@@ -189,7 +314,9 @@ func (c *liveStateCache) IterateHierarchy(server string, key kube.ResourceKey, a
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusterInfo.iterateHierarchy(key, action)
|
||||
clusterInfo.IterateHierarchy(key, func(resource *clustercache.Resource, namespaceResources map[kube.ResourceKey]*clustercache.Resource) {
|
||||
action(asResourceNode(resource), getApp(resource, namespaceResources))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -198,7 +325,12 @@ func (c *liveStateCache) GetNamespaceTopLevelResources(server string, namespace
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterInfo.getNamespaceTopLevelResources(namespace), nil
|
||||
resources := clusterInfo.GetNamespaceTopLevelResources(namespace)
|
||||
res := make(map[kube.ResourceKey]appv1.ResourceNode)
|
||||
for k, r := range resources {
|
||||
res[k] = asResourceNode(r)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
|
||||
@@ -206,7 +338,9 @@ func (c *liveStateCache) GetManagedLiveObjs(a *appv1.Application, targetObjs []*
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterInfo.getManagedLiveObjs(a, targetObjs)
|
||||
return clusterInfo.GetManagedLiveObjs(targetObjs, func(r *clustercache.Resource) bool {
|
||||
return resInfo(r).AppName == a.Name
|
||||
})
|
||||
}
|
||||
|
||||
func (c *liveStateCache) GetVersionsInfo(serverURL string) (string, []metav1.APIGroup, error) {
|
||||
@@ -214,7 +348,7 @@ func (c *liveStateCache) GetVersionsInfo(serverURL string) (string, []metav1.API
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return clusterInfo.serverVersion, clusterInfo.apiGroups, nil
|
||||
return clusterInfo.GetServerVersion(), clusterInfo.GetAPIGroups(), nil
|
||||
}
|
||||
|
||||
func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
||||
@@ -226,10 +360,6 @@ func isClusterHasApps(apps []interface{}, cluster *appv1.Cluster) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *liveStateCache) getCacheSettings() *cacheSettings {
|
||||
return c.cacheSettings
|
||||
}
|
||||
|
||||
func (c *liveStateCache) watchSettings(ctx context.Context) {
|
||||
updateCh := make(chan *settings.ArgoCDSettings, 1)
|
||||
c.settingsMgr.Subscribe(updateCh)
|
||||
@@ -244,15 +374,15 @@ func (c *liveStateCache) watchSettings(ctx context.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
c.cacheSettingsLock.Lock()
|
||||
c.lock.Lock()
|
||||
needInvalidate := false
|
||||
if !reflect.DeepEqual(c.cacheSettings, nextCacheSettings) {
|
||||
c.cacheSettings = nextCacheSettings
|
||||
if !reflect.DeepEqual(c.cacheSettings, *nextCacheSettings) {
|
||||
c.cacheSettings = *nextCacheSettings
|
||||
needInvalidate = true
|
||||
}
|
||||
c.cacheSettingsLock.Unlock()
|
||||
c.lock.Unlock()
|
||||
if needInvalidate {
|
||||
c.Invalidate()
|
||||
c.invalidate(*nextCacheSettings)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
done = true
|
||||
@@ -263,28 +393,30 @@ func (c *liveStateCache) watchSettings(ctx context.Context) {
|
||||
close(updateCh)
|
||||
}
|
||||
|
||||
// Run watches for resource changes annotated with application label on all registered clusters and schedule corresponding app refresh.
|
||||
func (c *liveStateCache) Run(ctx context.Context) error {
|
||||
func (c *liveStateCache) Init() error {
|
||||
cacheSettings, err := c.loadCacheSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.cacheSettings = cacheSettings
|
||||
c.cacheSettings = *cacheSettings
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run watches for resource changes annotated with application label on all registered clusters and schedule corresponding app refresh.
|
||||
func (c *liveStateCache) Run(ctx context.Context) error {
|
||||
go c.watchSettings(ctx)
|
||||
|
||||
util.RetryUntilSucceed(func() error {
|
||||
kube.RetryUntilSucceed(func() error {
|
||||
clusterEventCallback := func(event *db.ClusterEvent) {
|
||||
c.lock.Lock()
|
||||
cluster, ok := c.clusters[event.Cluster.Server]
|
||||
if ok {
|
||||
defer c.lock.Unlock()
|
||||
if event.Type == watch.Deleted {
|
||||
cluster.invalidate()
|
||||
cluster.Invalidate()
|
||||
delete(c.clusters, event.Cluster.Server)
|
||||
} else if event.Type == watch.Modified {
|
||||
cluster.cluster = event.Cluster
|
||||
cluster.invalidate()
|
||||
cluster.Invalidate(clustercache.SetConfig(event.Cluster.RESTConfig()))
|
||||
}
|
||||
} else {
|
||||
c.lock.Unlock()
|
||||
@@ -299,18 +431,23 @@ func (c *liveStateCache) Run(ctx context.Context) error {
|
||||
|
||||
return c.db.WatchClusters(ctx, clusterEventCallback)
|
||||
|
||||
}, "watch clusters", ctx, clusterRetryTimeout)
|
||||
}, "watch clusters", ctx, clustercache.ClusterRetryTimeout)
|
||||
|
||||
<-ctx.Done()
|
||||
c.invalidate(c.cacheSettings)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *liveStateCache) GetClustersInfo() []metrics.ClusterInfo {
|
||||
func (c *liveStateCache) GetClustersInfo() []clustercache.ClusterInfo {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
res := make([]metrics.ClusterInfo, 0)
|
||||
res := make([]clustercache.ClusterInfo, 0)
|
||||
for _, info := range c.clusters {
|
||||
res = append(res, info.getClusterInfo())
|
||||
res = append(res, info.GetClusterInfo())
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (c *liveStateCache) GetClusterCache(server string) (clustercache.ClusterCache, error) {
|
||||
return c.getSyncedCluster(server)
|
||||
}
|
||||
|
||||
26
controller/cache/cache_test.go
vendored
26
controller/cache/cache_test.go
vendored
@@ -1,26 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetServerVersion(t *testing.T) {
|
||||
now := time.Now()
|
||||
cache := &liveStateCache{
|
||||
lock: &sync.RWMutex{},
|
||||
clusters: map[string]*clusterInfo{
|
||||
"http://localhost": {
|
||||
syncTime: &now,
|
||||
lock: &sync.RWMutex{},
|
||||
serverVersion: "123",
|
||||
},
|
||||
}}
|
||||
|
||||
version, _, err := cache.GetVersionsInfo("http://localhost")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "123", version)
|
||||
}
|
||||
683
controller/cache/cluster.go
vendored
683
controller/cache/cluster.go
vendored
@@ -1,683 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/pager"
|
||||
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/health"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
)
|
||||
|
||||
const (
|
||||
clusterSyncTimeout = 24 * time.Hour
|
||||
clusterRetryTimeout = 10 * time.Second
|
||||
watchResourcesRetryTimeout = 1 * time.Second
|
||||
)
|
||||
|
||||
type apiMeta struct {
|
||||
namespaced bool
|
||||
resourceVersion string
|
||||
watchCancel context.CancelFunc
|
||||
}
|
||||
|
||||
type clusterInfo struct {
|
||||
syncTime *time.Time
|
||||
syncError error
|
||||
apisMeta map[schema.GroupKind]*apiMeta
|
||||
serverVersion string
|
||||
apiGroups []metav1.APIGroup
|
||||
// namespacedResources is a simple map which indicates a groupKind is namespaced
|
||||
namespacedResources map[schema.GroupKind]bool
|
||||
|
||||
// lock is a rw lock which protects the fields of clusterInfo
|
||||
lock *sync.RWMutex
|
||||
nodes map[kube.ResourceKey]*node
|
||||
nsIndex map[string]map[kube.ResourceKey]*node
|
||||
|
||||
onObjectUpdated ObjectUpdatedHandler
|
||||
onEventReceived func(event watch.EventType, un *unstructured.Unstructured)
|
||||
kubectl kube.Kubectl
|
||||
cluster *appv1.Cluster
|
||||
log *log.Entry
|
||||
cacheSettingsSrc func() *cacheSettings
|
||||
metricsServer *metrics.MetricsServer
|
||||
}
|
||||
|
||||
func (c *clusterInfo) replaceResourceCache(gk schema.GroupKind, resourceVersion string, objs []unstructured.Unstructured, ns string) {
|
||||
info, ok := c.apisMeta[gk]
|
||||
if ok {
|
||||
objByKey := make(map[kube.ResourceKey]*unstructured.Unstructured)
|
||||
for i := range objs {
|
||||
objByKey[kube.GetResourceKey(&objs[i])] = &objs[i]
|
||||
}
|
||||
|
||||
// update existing nodes
|
||||
for i := range objs {
|
||||
obj := &objs[i]
|
||||
key := kube.GetResourceKey(&objs[i])
|
||||
existingNode, exists := c.nodes[key]
|
||||
c.onNodeUpdated(exists, existingNode, obj)
|
||||
}
|
||||
|
||||
// remove existing nodes that a no longer exist
|
||||
for key, existingNode := range c.nodes {
|
||||
if key.Kind != gk.Kind || key.Group != gk.Group || ns != "" && key.Namespace != ns {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := objByKey[key]; !ok {
|
||||
c.onNodeRemoved(key, existingNode)
|
||||
}
|
||||
}
|
||||
info.resourceVersion = resourceVersion
|
||||
}
|
||||
}
|
||||
|
||||
func isServiceAccountTokenSecret(un *unstructured.Unstructured) (bool, metav1.OwnerReference) {
|
||||
ref := metav1.OwnerReference{
|
||||
APIVersion: "v1",
|
||||
Kind: kube.ServiceAccountKind,
|
||||
}
|
||||
if un.GetKind() != kube.SecretKind || un.GroupVersionKind().Group != "" {
|
||||
return false, ref
|
||||
}
|
||||
|
||||
if typeVal, ok, err := unstructured.NestedString(un.Object, "type"); !ok || err != nil || typeVal != "kubernetes.io/service-account-token" {
|
||||
return false, ref
|
||||
}
|
||||
|
||||
annotations := un.GetAnnotations()
|
||||
if annotations == nil {
|
||||
return false, ref
|
||||
}
|
||||
|
||||
id, okId := annotations["kubernetes.io/service-account.uid"]
|
||||
name, okName := annotations["kubernetes.io/service-account.name"]
|
||||
if okId && okName {
|
||||
ref.Name = name
|
||||
ref.UID = types.UID(id)
|
||||
}
|
||||
return ref.Name != "" && ref.UID != "", ref
|
||||
}
|
||||
|
||||
func (c *clusterInfo) createObjInfo(un *unstructured.Unstructured, appInstanceLabel string) *node {
|
||||
ownerRefs := un.GetOwnerReferences()
|
||||
gvk := un.GroupVersionKind()
|
||||
// Special case for endpoint. Remove after https://github.com/kubernetes/kubernetes/issues/28483 is fixed
|
||||
if gvk.Group == "" && gvk.Kind == kube.EndpointsKind && len(un.GetOwnerReferences()) == 0 {
|
||||
ownerRefs = append(ownerRefs, metav1.OwnerReference{
|
||||
Name: un.GetName(),
|
||||
Kind: kube.ServiceKind,
|
||||
APIVersion: "v1",
|
||||
})
|
||||
}
|
||||
|
||||
// Special case for Operator Lifecycle Manager ClusterServiceVersion:
|
||||
if un.GroupVersionKind().Group == "operators.coreos.com" && un.GetKind() == "ClusterServiceVersion" {
|
||||
if un.GetAnnotations()["olm.operatorGroup"] != "" {
|
||||
ownerRefs = append(ownerRefs, metav1.OwnerReference{
|
||||
Name: un.GetAnnotations()["olm.operatorGroup"],
|
||||
Kind: "OperatorGroup",
|
||||
APIVersion: "operators.coreos.com/v1",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// edge case. Consider auto-created service account tokens as a child of service account objects
|
||||
if yes, ref := isServiceAccountTokenSecret(un); yes {
|
||||
ownerRefs = append(ownerRefs, ref)
|
||||
}
|
||||
|
||||
nodeInfo := &node{
|
||||
resourceVersion: un.GetResourceVersion(),
|
||||
ref: kube.GetObjectRef(un),
|
||||
ownerRefs: ownerRefs,
|
||||
}
|
||||
|
||||
populateNodeInfo(un, nodeInfo)
|
||||
appName := kube.GetAppInstanceLabel(un, appInstanceLabel)
|
||||
if len(ownerRefs) == 0 && appName != "" {
|
||||
nodeInfo.appName = appName
|
||||
nodeInfo.resource = un
|
||||
} else {
|
||||
// edge case. we do not label CRDs, so they miss the tracking label we inject. But we still
|
||||
// want the full resource to be available in our cache (to diff), so we store all CRDs
|
||||
switch gvk.Kind {
|
||||
case kube.CustomResourceDefinitionKind:
|
||||
nodeInfo.resource = un
|
||||
}
|
||||
}
|
||||
|
||||
nodeInfo.health, _ = health.GetResourceHealth(un, c.cacheSettingsSrc().ResourceOverrides)
|
||||
return nodeInfo
|
||||
}
|
||||
|
||||
func (c *clusterInfo) setNode(n *node) {
|
||||
key := n.resourceKey()
|
||||
c.nodes[key] = n
|
||||
ns, ok := c.nsIndex[key.Namespace]
|
||||
if !ok {
|
||||
ns = make(map[kube.ResourceKey]*node)
|
||||
c.nsIndex[key.Namespace] = ns
|
||||
}
|
||||
ns[key] = n
|
||||
}
|
||||
|
||||
func (c *clusterInfo) removeNode(key kube.ResourceKey) {
|
||||
delete(c.nodes, key)
|
||||
if ns, ok := c.nsIndex[key.Namespace]; ok {
|
||||
delete(ns, key)
|
||||
if len(ns) == 0 {
|
||||
delete(c.nsIndex, key.Namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clusterInfo) invalidate() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.syncTime = nil
|
||||
for i := range c.apisMeta {
|
||||
c.apisMeta[i].watchCancel()
|
||||
}
|
||||
c.apisMeta = nil
|
||||
c.namespacedResources = nil
|
||||
c.log.Warnf("invalidated cluster")
|
||||
}
|
||||
|
||||
func (c *clusterInfo) synced() bool {
|
||||
syncTime := c.syncTime
|
||||
if syncTime == nil {
|
||||
return false
|
||||
}
|
||||
if c.syncError != nil {
|
||||
return time.Now().Before(syncTime.Add(clusterRetryTimeout))
|
||||
}
|
||||
return time.Now().Before(syncTime.Add(clusterSyncTimeout))
|
||||
}
|
||||
|
||||
func (c *clusterInfo) stopWatching(gk schema.GroupKind, ns string) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if info, ok := c.apisMeta[gk]; ok {
|
||||
info.watchCancel()
|
||||
delete(c.apisMeta, gk)
|
||||
c.replaceResourceCache(gk, "", []unstructured.Unstructured{}, ns)
|
||||
c.log.Warnf("Stop watching: %s not found", gk)
|
||||
}
|
||||
}
|
||||
|
||||
// startMissingWatches lists supported cluster resources and start watching for changes unless watch is already running
|
||||
func (c *clusterInfo) startMissingWatches() error {
|
||||
config := c.cluster.RESTConfig()
|
||||
|
||||
apis, err := c.kubectl.GetAPIResources(config, c.cacheSettingsSrc().ResourcesFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := c.kubectl.NewDynamicClient(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
namespacedResources := make(map[schema.GroupKind]bool)
|
||||
for i := range apis {
|
||||
api := apis[i]
|
||||
namespacedResources[api.GroupKind] = api.Meta.Namespaced
|
||||
if _, ok := c.apisMeta[api.GroupKind]; !ok {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
info := &apiMeta{namespaced: api.Meta.Namespaced, watchCancel: cancel}
|
||||
c.apisMeta[api.GroupKind] = info
|
||||
|
||||
err = c.processApi(client, api, func(resClient dynamic.ResourceInterface, ns string) error {
|
||||
go c.watchEvents(ctx, api, info, resClient, ns)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
c.namespacedResources = namespacedResources
|
||||
return nil
|
||||
}
|
||||
|
||||
func runSynced(lock sync.Locker, action func() error) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
return action()
|
||||
}
|
||||
|
||||
func (c *clusterInfo) watchEvents(ctx context.Context, api kube.APIResourceInfo, info *apiMeta, resClient dynamic.ResourceInterface, ns string) {
|
||||
util.RetryUntilSucceed(func() (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("Recovered from panic: %+v\n%s", r, debug.Stack())
|
||||
}
|
||||
}()
|
||||
|
||||
err = runSynced(c.lock, func() error {
|
||||
if info.resourceVersion == "" {
|
||||
listPager := pager.New(func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
|
||||
res, err := resClient.List(opts)
|
||||
if err == nil {
|
||||
info.resourceVersion = res.GetResourceVersion()
|
||||
}
|
||||
return res, err
|
||||
})
|
||||
var items []unstructured.Unstructured
|
||||
err = listPager.EachListItem(ctx, metav1.ListOptions{}, func(obj runtime.Object) error {
|
||||
if un, ok := obj.(*unstructured.Unstructured); !ok {
|
||||
return fmt.Errorf("object %s/%s has an unexpected type", un.GroupVersionKind().String(), un.GetName())
|
||||
} else {
|
||||
items = append(items, *un)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load initial state of resource %s: %v", api.GroupKind.String(), err)
|
||||
}
|
||||
c.replaceResourceCache(api.GroupKind, info.resourceVersion, items, ns)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := resClient.Watch(metav1.ListOptions{ResourceVersion: info.resourceVersion})
|
||||
if errors.IsNotFound(err) {
|
||||
c.stopWatching(api.GroupKind, ns)
|
||||
return nil
|
||||
}
|
||||
if errors.IsGone(err) {
|
||||
info.resourceVersion = ""
|
||||
c.log.Warnf("Resource version of %s is too old", api.GroupKind)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case event, ok := <-w.ResultChan():
|
||||
if ok {
|
||||
obj := event.Object.(*unstructured.Unstructured)
|
||||
info.resourceVersion = obj.GetResourceVersion()
|
||||
c.processEvent(event.Type, obj)
|
||||
if kube.IsCRD(obj) {
|
||||
if event.Type == watch.Deleted {
|
||||
group, groupOk, groupErr := unstructured.NestedString(obj.Object, "spec", "group")
|
||||
kind, kindOk, kindErr := unstructured.NestedString(obj.Object, "spec", "names", "kind")
|
||||
|
||||
if groupOk && groupErr == nil && kindOk && kindErr == nil {
|
||||
gk := schema.GroupKind{Group: group, Kind: kind}
|
||||
c.stopWatching(gk, ns)
|
||||
}
|
||||
} else {
|
||||
err = runSynced(c.lock, func() error {
|
||||
return c.startMissingWatches()
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
c.log.Warnf("Failed to start missing watch: %v", err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Watch %s on %s has closed", api.GroupKind, c.cluster.Server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, fmt.Sprintf("watch %s on %s", api.GroupKind, c.cluster.Server), ctx, watchResourcesRetryTimeout)
|
||||
}
|
||||
|
||||
func (c *clusterInfo) processApi(client dynamic.Interface, api kube.APIResourceInfo, callback func(resClient dynamic.ResourceInterface, ns string) error) error {
|
||||
resClient := client.Resource(api.GroupVersionResource)
|
||||
if len(c.cluster.Namespaces) == 0 {
|
||||
return callback(resClient, "")
|
||||
}
|
||||
|
||||
if !api.Meta.Namespaced {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, ns := range c.cluster.Namespaces {
|
||||
err := callback(resClient.Namespace(ns), ns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clusterInfo) sync() (err error) {
|
||||
|
||||
c.log.Info("Start syncing cluster")
|
||||
|
||||
for i := range c.apisMeta {
|
||||
c.apisMeta[i].watchCancel()
|
||||
}
|
||||
c.apisMeta = make(map[schema.GroupKind]*apiMeta)
|
||||
c.nodes = make(map[kube.ResourceKey]*node)
|
||||
c.namespacedResources = make(map[schema.GroupKind]bool)
|
||||
config := c.cluster.RESTConfig()
|
||||
version, err := c.kubectl.GetServerVersion(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.serverVersion = version
|
||||
groups, err := c.kubectl.GetAPIGroups(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.apiGroups = groups
|
||||
|
||||
apis, err := c.kubectl.GetAPIResources(config, c.cacheSettingsSrc().ResourcesFilter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := c.kubectl.NewDynamicClient(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lock := sync.Mutex{}
|
||||
err = util.RunAllAsync(len(apis), func(i int) error {
|
||||
api := apis[i]
|
||||
|
||||
lock.Lock()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
info := &apiMeta{namespaced: api.Meta.Namespaced, watchCancel: cancel}
|
||||
c.apisMeta[api.GroupKind] = info
|
||||
c.namespacedResources[api.GroupKind] = api.Meta.Namespaced
|
||||
lock.Unlock()
|
||||
|
||||
return c.processApi(client, api, func(resClient dynamic.ResourceInterface, ns string) error {
|
||||
|
||||
listPager := pager.New(func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
|
||||
res, err := resClient.List(opts)
|
||||
if err == nil {
|
||||
lock.Lock()
|
||||
info.resourceVersion = res.GetResourceVersion()
|
||||
lock.Unlock()
|
||||
}
|
||||
return res, err
|
||||
})
|
||||
|
||||
err = listPager.EachListItem(context.Background(), metav1.ListOptions{}, func(obj runtime.Object) error {
|
||||
if un, ok := obj.(*unstructured.Unstructured); !ok {
|
||||
return fmt.Errorf("object %s/%s has an unexpected type", un.GroupVersionKind().String(), un.GetName())
|
||||
} else {
|
||||
lock.Lock()
|
||||
c.setNode(c.createObjInfo(un, c.cacheSettingsSrc().AppInstanceLabelKey))
|
||||
lock.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load initial state of resource %s: %v", api.GroupKind.String(), err)
|
||||
}
|
||||
|
||||
go c.watchEvents(ctx, api, info, resClient, ns)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Failed to sync cluster %s: %v", c.cluster.Server, err)
|
||||
return err
|
||||
}
|
||||
|
||||
c.log.Info("Cluster successfully synced")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clusterInfo) ensureSynced() error {
|
||||
// first check if cluster is synced *without lock*
|
||||
if c.synced() {
|
||||
return c.syncError
|
||||
}
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
// before doing any work, check once again now that we have the lock, to see if it got
|
||||
// synced between the first check and now
|
||||
if c.synced() {
|
||||
return c.syncError
|
||||
}
|
||||
err := c.sync()
|
||||
syncTime := time.Now()
|
||||
c.syncTime = &syncTime
|
||||
c.syncError = err
|
||||
return c.syncError
|
||||
}
|
||||
|
||||
func (c *clusterInfo) getNamespaceTopLevelResources(namespace string) map[kube.ResourceKey]appv1.ResourceNode {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
nodes := make(map[kube.ResourceKey]appv1.ResourceNode)
|
||||
for _, node := range c.nsIndex[namespace] {
|
||||
if len(node.ownerRefs) == 0 {
|
||||
nodes[node.resourceKey()] = node.asResourceNode()
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (c *clusterInfo) iterateHierarchy(key kube.ResourceKey, action func(child appv1.ResourceNode, appName string)) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if objInfo, ok := c.nodes[key]; ok {
|
||||
nsNodes := c.nsIndex[key.Namespace]
|
||||
action(objInfo.asResourceNode(), objInfo.getApp(nsNodes))
|
||||
childrenByUID := make(map[types.UID][]*node)
|
||||
for _, child := range nsNodes {
|
||||
if objInfo.isParentOf(child) {
|
||||
childrenByUID[child.ref.UID] = append(childrenByUID[child.ref.UID], child)
|
||||
}
|
||||
}
|
||||
// make sure children has no duplicates
|
||||
for _, children := range childrenByUID {
|
||||
if len(children) > 0 {
|
||||
// The object might have multiple children with the same UID (e.g. replicaset from apps and extensions group). It is ok to pick any object but we need to make sure
|
||||
// we pick the same child after every refresh.
|
||||
sort.Slice(children, func(i, j int) bool {
|
||||
key1 := children[i].resourceKey()
|
||||
key2 := children[j].resourceKey()
|
||||
return strings.Compare(key1.String(), key2.String()) < 0
|
||||
})
|
||||
child := children[0]
|
||||
action(child.asResourceNode(), child.getApp(nsNodes))
|
||||
child.iterateChildren(nsNodes, map[kube.ResourceKey]bool{objInfo.resourceKey(): true}, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clusterInfo) isNamespaced(gk schema.GroupKind) bool {
|
||||
// this is safe to access without a lock since we always replace the entire map instead of mutating keys
|
||||
if isNamespaced, ok := c.namespacedResources[gk]; ok {
|
||||
return isNamespaced
|
||||
}
|
||||
log.Warnf("group/kind %s scope is unknown (known objects: %d). assuming namespaced object", gk, len(c.namespacedResources))
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *clusterInfo) getManagedLiveObjs(a *appv1.Application, targetObjs []*unstructured.Unstructured) (map[kube.ResourceKey]*unstructured.Unstructured, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
|
||||
managedObjs := make(map[kube.ResourceKey]*unstructured.Unstructured)
|
||||
// iterate all objects in live state cache to find ones associated with app
|
||||
for key, o := range c.nodes {
|
||||
if o.appName == a.Name && o.resource != nil && len(o.ownerRefs) == 0 {
|
||||
managedObjs[key] = o.resource
|
||||
}
|
||||
}
|
||||
config := metrics.AddMetricsTransportWrapper(c.metricsServer, a, c.cluster.RESTConfig())
|
||||
// iterate target objects and identify ones that already exist in the cluster,
|
||||
// but are simply missing our label
|
||||
lock := &sync.Mutex{}
|
||||
err := util.RunAllAsync(len(targetObjs), func(i int) error {
|
||||
targetObj := targetObjs[i]
|
||||
key := GetTargetObjKey(a, targetObj, c.isNamespaced(targetObj.GroupVersionKind().GroupKind()))
|
||||
lock.Lock()
|
||||
managedObj := managedObjs[key]
|
||||
lock.Unlock()
|
||||
|
||||
if managedObj == nil {
|
||||
if existingObj, exists := c.nodes[key]; exists {
|
||||
if existingObj.resource != nil {
|
||||
managedObj = existingObj.resource
|
||||
} else {
|
||||
var err error
|
||||
managedObj, err = c.kubectl.GetResource(config, targetObj.GroupVersionKind(), existingObj.ref.Name, existingObj.ref.Namespace)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if _, watched := c.apisMeta[key.GroupKind()]; !watched {
|
||||
var err error
|
||||
managedObj, err = c.kubectl.GetResource(config, targetObj.GroupVersionKind(), targetObj.GetName(), targetObj.GetNamespace())
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if managedObj != nil {
|
||||
converted, err := c.kubectl.ConvertToVersion(managedObj, targetObj.GroupVersionKind().Group, targetObj.GroupVersionKind().Version)
|
||||
if err != nil {
|
||||
// fallback to loading resource from kubernetes if conversion fails
|
||||
log.Warnf("Failed to convert resource: %v", err)
|
||||
managedObj, err = c.kubectl.GetResource(config, targetObj.GroupVersionKind(), managedObj.GetName(), managedObj.GetNamespace())
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
managedObj = converted
|
||||
}
|
||||
lock.Lock()
|
||||
managedObjs[key] = managedObj
|
||||
lock.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return managedObjs, nil
|
||||
}
|
||||
|
||||
func (c *clusterInfo) processEvent(event watch.EventType, un *unstructured.Unstructured) {
|
||||
if c.onEventReceived != nil {
|
||||
c.onEventReceived(event, un)
|
||||
}
|
||||
key := kube.GetResourceKey(un)
|
||||
if event == watch.Modified && skipAppRequeing(key) {
|
||||
return
|
||||
}
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
existingNode, exists := c.nodes[key]
|
||||
if event == watch.Deleted {
|
||||
if exists {
|
||||
c.onNodeRemoved(key, existingNode)
|
||||
}
|
||||
} else if event != watch.Deleted {
|
||||
c.onNodeUpdated(exists, existingNode, un)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *clusterInfo) onNodeUpdated(exists bool, existingNode *node, un *unstructured.Unstructured) {
|
||||
nodes := make([]*node, 0)
|
||||
if exists {
|
||||
nodes = append(nodes, existingNode)
|
||||
}
|
||||
newObj := c.createObjInfo(un, c.cacheSettingsSrc().AppInstanceLabelKey)
|
||||
c.setNode(newObj)
|
||||
nodes = append(nodes, newObj)
|
||||
toNotify := make(map[string]bool)
|
||||
for i := range nodes {
|
||||
n := nodes[i]
|
||||
if ns, ok := c.nsIndex[n.ref.Namespace]; ok {
|
||||
app := n.getApp(ns)
|
||||
if app == "" {
|
||||
continue
|
||||
}
|
||||
toNotify[app] = n.isRootAppNode() || toNotify[app]
|
||||
}
|
||||
}
|
||||
c.onObjectUpdated(toNotify, newObj.ref)
|
||||
}
|
||||
|
||||
func (c *clusterInfo) onNodeRemoved(key kube.ResourceKey, n *node) {
|
||||
appName := n.appName
|
||||
if ns, ok := c.nsIndex[key.Namespace]; ok {
|
||||
appName = n.getApp(ns)
|
||||
}
|
||||
|
||||
c.removeNode(key)
|
||||
managedByApp := make(map[string]bool)
|
||||
if appName != "" {
|
||||
managedByApp[appName] = n.isRootAppNode()
|
||||
}
|
||||
c.onObjectUpdated(managedByApp, n.ref)
|
||||
}
|
||||
|
||||
var (
|
||||
ignoredRefreshResources = map[string]bool{
|
||||
"/" + kube.EndpointsKind: true,
|
||||
}
|
||||
)
|
||||
|
||||
func (c *clusterInfo) getClusterInfo() metrics.ClusterInfo {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
return metrics.ClusterInfo{
|
||||
APIsCount: len(c.apisMeta),
|
||||
K8SVersion: c.serverVersion,
|
||||
ResourcesCount: len(c.nodes),
|
||||
Server: c.cluster.Server,
|
||||
LastCacheSyncTime: c.syncTime,
|
||||
}
|
||||
}
|
||||
|
||||
// skipAppRequeing checks if the object is an API type which we want to skip requeuing against.
|
||||
// We ignore API types which have a high churn rate, and/or whose updates are irrelevant to the app
|
||||
func skipAppRequeing(key kube.ResourceKey) bool {
|
||||
return ignoredRefreshResources[key.Group+"/"+key.Kind]
|
||||
}
|
||||
560
controller/cache/cluster_test.go
vendored
560
controller/cache/cluster_test.go
vendored
@@ -1,560 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "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/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/kube/kubetest"
|
||||
)
|
||||
|
||||
func strToUnstructured(jsonStr string) *unstructured.Unstructured {
|
||||
obj := make(map[string]interface{})
|
||||
err := yaml.Unmarshal([]byte(jsonStr), &obj)
|
||||
errors.CheckError(err)
|
||||
return &unstructured.Unstructured{Object: obj}
|
||||
}
|
||||
|
||||
func mustToUnstructured(obj interface{}) *unstructured.Unstructured {
|
||||
un, err := kube.ToUnstructured(obj)
|
||||
errors.CheckError(err)
|
||||
return un
|
||||
}
|
||||
|
||||
var (
|
||||
testPod = strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
uid: "1"
|
||||
name: helm-guestbook-pod
|
||||
namespace: default
|
||||
ownerReferences:
|
||||
- apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
name: helm-guestbook-rs
|
||||
uid: "2"
|
||||
resourceVersion: "123"`)
|
||||
|
||||
testRS = strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
uid: "2"
|
||||
name: helm-guestbook-rs
|
||||
namespace: default
|
||||
annotations:
|
||||
deployment.kubernetes.io/revision: "2"
|
||||
ownerReferences:
|
||||
- apiVersion: apps/v1beta1
|
||||
kind: Deployment
|
||||
name: helm-guestbook
|
||||
uid: "3"
|
||||
resourceVersion: "123"`)
|
||||
|
||||
testDeploy = strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: helm-guestbook
|
||||
uid: "3"
|
||||
name: helm-guestbook
|
||||
namespace: default
|
||||
resourceVersion: "123"`)
|
||||
|
||||
testService = strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: helm-guestbook
|
||||
namespace: default
|
||||
resourceVersion: "123"
|
||||
uid: "4"
|
||||
spec:
|
||||
selector:
|
||||
app: guestbook
|
||||
type: LoadBalancer
|
||||
status:
|
||||
loadBalancer:
|
||||
ingress:
|
||||
- hostname: localhost`)
|
||||
|
||||
testIngress = strToUnstructured(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: helm-guestbook
|
||||
namespace: default
|
||||
uid: "4"
|
||||
spec:
|
||||
backend:
|
||||
serviceName: not-found-service
|
||||
servicePort: 443
|
||||
rules:
|
||||
- host: helm-guestbook.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: helm-guestbook
|
||||
servicePort: 443
|
||||
path: /
|
||||
- backend:
|
||||
serviceName: helm-guestbook
|
||||
servicePort: https
|
||||
path: /
|
||||
status:
|
||||
loadBalancer:
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
)
|
||||
|
||||
func newCluster(objs ...*unstructured.Unstructured) *clusterInfo {
|
||||
runtimeObjs := make([]runtime.Object, len(objs))
|
||||
for i := range objs {
|
||||
runtimeObjs[i] = objs[i]
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
client := fake.NewSimpleDynamicClient(scheme, runtimeObjs...)
|
||||
|
||||
apiResources := []kube.APIResourceInfo{{
|
||||
GroupKind: schema.GroupKind{Group: "", Kind: "Pod"},
|
||||
GroupVersionResource: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
|
||||
Meta: metav1.APIResource{Namespaced: true},
|
||||
}, {
|
||||
GroupKind: schema.GroupKind{Group: "apps", Kind: "ReplicaSet"},
|
||||
GroupVersionResource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "replicasets"},
|
||||
Meta: metav1.APIResource{Namespaced: true},
|
||||
}, {
|
||||
GroupKind: schema.GroupKind{Group: "apps", Kind: "Deployment"},
|
||||
GroupVersionResource: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
Meta: metav1.APIResource{Namespaced: true},
|
||||
}}
|
||||
|
||||
return newClusterExt(&kubetest.MockKubectlCmd{APIResources: apiResources, DynamicClient: client})
|
||||
}
|
||||
|
||||
func newClusterExt(kubectl kube.Kubectl) *clusterInfo {
|
||||
return &clusterInfo{
|
||||
lock: &sync.RWMutex{},
|
||||
nodes: make(map[kube.ResourceKey]*node),
|
||||
onObjectUpdated: func(managedByApp map[string]bool, reference corev1.ObjectReference) {},
|
||||
kubectl: kubectl,
|
||||
nsIndex: make(map[string]map[kube.ResourceKey]*node),
|
||||
cluster: &appv1.Cluster{},
|
||||
syncTime: nil,
|
||||
apisMeta: make(map[schema.GroupKind]*apiMeta),
|
||||
log: log.WithField("cluster", "test"),
|
||||
cacheSettingsSrc: func() *cacheSettings {
|
||||
return &cacheSettings{AppInstanceLabelKey: common.LabelKeyAppInstance}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getChildren(cluster *clusterInfo, un *unstructured.Unstructured) []appv1.ResourceNode {
|
||||
hierarchy := make([]appv1.ResourceNode, 0)
|
||||
cluster.iterateHierarchy(kube.GetResourceKey(un), func(child appv1.ResourceNode, app string) {
|
||||
hierarchy = append(hierarchy, child)
|
||||
})
|
||||
return hierarchy[1:]
|
||||
}
|
||||
|
||||
func TestEnsureSynced(t *testing.T) {
|
||||
obj1 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook1", "namespace": "default1"}
|
||||
`)
|
||||
obj2 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook2", "namespace": "default2"}
|
||||
`)
|
||||
|
||||
cluster := newCluster(obj1, obj2)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Len(t, cluster.nodes, 2)
|
||||
var names []string
|
||||
for k := range cluster.nodes {
|
||||
names = append(names, k.Name)
|
||||
}
|
||||
assert.ElementsMatch(t, []string{"helm-guestbook1", "helm-guestbook2"}, names)
|
||||
}
|
||||
|
||||
func TestEnsureSyncedSingleNamespace(t *testing.T) {
|
||||
obj1 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook1", "namespace": "default1"}
|
||||
`)
|
||||
obj2 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook2", "namespace": "default2"}
|
||||
`)
|
||||
|
||||
cluster := newCluster(obj1, obj2)
|
||||
cluster.cluster.Namespaces = []string{"default1"}
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Len(t, cluster.nodes, 1)
|
||||
var names []string
|
||||
for k := range cluster.nodes {
|
||||
names = append(names, k.Name)
|
||||
}
|
||||
assert.ElementsMatch(t, []string{"helm-guestbook1"}, names)
|
||||
}
|
||||
|
||||
func TestGetNamespaceResources(t *testing.T) {
|
||||
defaultNamespaceTopLevel1 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook1", "namespace": "default"}
|
||||
`)
|
||||
defaultNamespaceTopLevel2 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook2", "namespace": "default"}
|
||||
`)
|
||||
kubesystemNamespaceTopLevel2 := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata: {"name": "helm-guestbook3", "namespace": "kube-system"}
|
||||
`)
|
||||
|
||||
cluster := newCluster(defaultNamespaceTopLevel1, defaultNamespaceTopLevel2, kubesystemNamespaceTopLevel2)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
resources := cluster.getNamespaceTopLevelResources("default")
|
||||
assert.Len(t, resources, 2)
|
||||
assert.Equal(t, resources[kube.GetResourceKey(defaultNamespaceTopLevel1)].Name, "helm-guestbook1")
|
||||
assert.Equal(t, resources[kube.GetResourceKey(defaultNamespaceTopLevel2)].Name, "helm-guestbook2")
|
||||
|
||||
resources = cluster.getNamespaceTopLevelResources("kube-system")
|
||||
assert.Len(t, resources, 1)
|
||||
assert.Equal(t, resources[kube.GetResourceKey(kubesystemNamespaceTopLevel2)].Name, "helm-guestbook3")
|
||||
}
|
||||
|
||||
func TestGetChildren(t *testing.T) {
|
||||
cluster := newCluster(testPod, testRS, testDeploy)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
rsChildren := getChildren(cluster, testRS)
|
||||
assert.Equal(t, []appv1.ResourceNode{{
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
Kind: "Pod",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-pod",
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
UID: "1",
|
||||
},
|
||||
ParentRefs: []appv1.ResourceRef{{
|
||||
Group: "apps",
|
||||
Version: "",
|
||||
Kind: "ReplicaSet",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-rs",
|
||||
UID: "2",
|
||||
}},
|
||||
Health: &appv1.HealthStatus{Status: appv1.HealthStatusUnknown},
|
||||
NetworkingInfo: &appv1.ResourceNetworkingInfo{Labels: testPod.GetLabels()},
|
||||
ResourceVersion: "123",
|
||||
Info: []appv1.InfoItem{{Name: "Containers", Value: "0/0"}},
|
||||
}}, rsChildren)
|
||||
deployChildren := getChildren(cluster, testDeploy)
|
||||
|
||||
assert.Equal(t, append([]appv1.ResourceNode{{
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
Kind: "ReplicaSet",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-rs",
|
||||
Group: "apps",
|
||||
Version: "v1",
|
||||
UID: "2",
|
||||
},
|
||||
ResourceVersion: "123",
|
||||
Health: &appv1.HealthStatus{Status: appv1.HealthStatusHealthy},
|
||||
Info: []appv1.InfoItem{{Name: "Revision", Value: "Rev:2"}},
|
||||
ParentRefs: []appv1.ResourceRef{{Group: "apps", Version: "", Kind: "Deployment", Namespace: "default", Name: "helm-guestbook", UID: "3"}},
|
||||
}}, rsChildren...), deployChildren)
|
||||
}
|
||||
|
||||
func TestGetManagedLiveObjs(t *testing.T) {
|
||||
cluster := newCluster(testPod, testRS, testDeploy)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
targetDeploy := strToUnstructured(`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: helm-guestbook
|
||||
labels:
|
||||
app: helm-guestbook`)
|
||||
|
||||
managedObjs, err := cluster.getManagedLiveObjs(&appv1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "helm-guestbook"},
|
||||
Spec: appv1.ApplicationSpec{
|
||||
Destination: appv1.ApplicationDestination{
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
}, []*unstructured.Unstructured{targetDeploy})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, managedObjs, map[kube.ResourceKey]*unstructured.Unstructured{
|
||||
kube.NewResourceKey("apps", "Deployment", "default", "helm-guestbook"): testDeploy,
|
||||
})
|
||||
}
|
||||
|
||||
func TestChildDeletedEvent(t *testing.T) {
|
||||
cluster := newCluster(testPod, testRS, testDeploy)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
cluster.processEvent(watch.Deleted, testPod)
|
||||
|
||||
rsChildren := getChildren(cluster, testRS)
|
||||
assert.Equal(t, []appv1.ResourceNode{}, rsChildren)
|
||||
}
|
||||
|
||||
func TestProcessNewChildEvent(t *testing.T) {
|
||||
cluster := newCluster(testPod, testRS, testDeploy)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
newPod := strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
uid: "4"
|
||||
name: helm-guestbook-pod2
|
||||
namespace: default
|
||||
ownerReferences:
|
||||
- apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
name: helm-guestbook-rs
|
||||
uid: "2"
|
||||
resourceVersion: "123"`)
|
||||
|
||||
cluster.processEvent(watch.Added, newPod)
|
||||
|
||||
rsChildren := getChildren(cluster, testRS)
|
||||
sort.Slice(rsChildren, func(i, j int) bool {
|
||||
return strings.Compare(rsChildren[i].Name, rsChildren[j].Name) < 0
|
||||
})
|
||||
assert.Equal(t, []appv1.ResourceNode{{
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
Kind: "Pod",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-pod",
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
UID: "1",
|
||||
},
|
||||
Info: []appv1.InfoItem{{Name: "Containers", Value: "0/0"}},
|
||||
Health: &appv1.HealthStatus{Status: appv1.HealthStatusUnknown},
|
||||
NetworkingInfo: &appv1.ResourceNetworkingInfo{Labels: testPod.GetLabels()},
|
||||
ParentRefs: []appv1.ResourceRef{{
|
||||
Group: "apps",
|
||||
Version: "",
|
||||
Kind: "ReplicaSet",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-rs",
|
||||
UID: "2",
|
||||
}},
|
||||
ResourceVersion: "123",
|
||||
}, {
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
Kind: "Pod",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-pod2",
|
||||
Group: "",
|
||||
Version: "v1",
|
||||
UID: "4",
|
||||
},
|
||||
NetworkingInfo: &appv1.ResourceNetworkingInfo{Labels: testPod.GetLabels()},
|
||||
Info: []appv1.InfoItem{{Name: "Containers", Value: "0/0"}},
|
||||
Health: &appv1.HealthStatus{Status: appv1.HealthStatusUnknown},
|
||||
ParentRefs: []appv1.ResourceRef{{
|
||||
Group: "apps",
|
||||
Version: "",
|
||||
Kind: "ReplicaSet",
|
||||
Namespace: "default",
|
||||
Name: "helm-guestbook-rs",
|
||||
UID: "2",
|
||||
}},
|
||||
ResourceVersion: "123",
|
||||
}}, rsChildren)
|
||||
}
|
||||
|
||||
func TestUpdateResourceTags(t *testing.T) {
|
||||
pod := &corev1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "default"},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{{
|
||||
Name: "test",
|
||||
Image: "test",
|
||||
}},
|
||||
},
|
||||
}
|
||||
cluster := newCluster(mustToUnstructured(pod))
|
||||
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
podNode := cluster.nodes[kube.GetResourceKey(mustToUnstructured(pod))]
|
||||
|
||||
assert.NotNil(t, podNode)
|
||||
assert.Equal(t, []appv1.InfoItem{{Name: "Containers", Value: "0/1"}}, podNode.info)
|
||||
|
||||
pod.Status = corev1.PodStatus{
|
||||
ContainerStatuses: []corev1.ContainerStatus{{
|
||||
State: corev1.ContainerState{
|
||||
Terminated: &corev1.ContainerStateTerminated{
|
||||
ExitCode: -1,
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
cluster.processEvent(watch.Modified, mustToUnstructured(pod))
|
||||
|
||||
podNode = cluster.nodes[kube.GetResourceKey(mustToUnstructured(pod))]
|
||||
|
||||
assert.NotNil(t, podNode)
|
||||
assert.Equal(t, []appv1.InfoItem{{Name: "Status Reason", Value: "ExitCode:-1"}, {Name: "Containers", Value: "0/1"}}, podNode.info)
|
||||
}
|
||||
|
||||
func TestUpdateAppResource(t *testing.T) {
|
||||
updatesReceived := make([]string, 0)
|
||||
cluster := newCluster(testPod, testRS, testDeploy)
|
||||
cluster.onObjectUpdated = func(managedByApp map[string]bool, _ corev1.ObjectReference) {
|
||||
for appName, fullRefresh := range managedByApp {
|
||||
updatesReceived = append(updatesReceived, fmt.Sprintf("%s: %v", appName, fullRefresh))
|
||||
}
|
||||
}
|
||||
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
cluster.processEvent(watch.Modified, mustToUnstructured(testPod))
|
||||
|
||||
assert.Contains(t, updatesReceived, "helm-guestbook: false")
|
||||
}
|
||||
|
||||
func TestCircularReference(t *testing.T) {
|
||||
dep := testDeploy.DeepCopy()
|
||||
dep.SetOwnerReferences([]metav1.OwnerReference{{
|
||||
Name: testPod.GetName(),
|
||||
Kind: testPod.GetKind(),
|
||||
APIVersion: testPod.GetAPIVersion(),
|
||||
}})
|
||||
cluster := newCluster(testPod, testRS, dep)
|
||||
err := cluster.ensureSynced()
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
children := getChildren(cluster, dep)
|
||||
assert.Len(t, children, 2)
|
||||
|
||||
node := cluster.nodes[kube.GetResourceKey(dep)]
|
||||
assert.NotNil(t, node)
|
||||
app := node.getApp(cluster.nodes)
|
||||
assert.Equal(t, "", app)
|
||||
}
|
||||
|
||||
func TestWatchCacheUpdated(t *testing.T) {
|
||||
removed := testPod.DeepCopy()
|
||||
removed.SetName(testPod.GetName() + "-removed-pod")
|
||||
|
||||
updated := testPod.DeepCopy()
|
||||
updated.SetName(testPod.GetName() + "-updated-pod")
|
||||
updated.SetResourceVersion("updated-pod-version")
|
||||
|
||||
cluster := newCluster(removed, updated)
|
||||
err := cluster.ensureSynced()
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
added := testPod.DeepCopy()
|
||||
added.SetName(testPod.GetName() + "-new-pod")
|
||||
|
||||
podGroupKind := testPod.GroupVersionKind().GroupKind()
|
||||
|
||||
cluster.lock.Lock()
|
||||
cluster.replaceResourceCache(podGroupKind, "updated-list-version", []unstructured.Unstructured{*updated, *added}, "")
|
||||
|
||||
_, ok := cluster.nodes[kube.GetResourceKey(removed)]
|
||||
assert.False(t, ok)
|
||||
|
||||
updatedNode, ok := cluster.nodes[kube.GetResourceKey(updated)]
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, updatedNode.resourceVersion, "updated-pod-version")
|
||||
|
||||
_, ok = cluster.nodes[kube.GetResourceKey(added)]
|
||||
assert.True(t, ok)
|
||||
cluster.lock.Unlock()
|
||||
}
|
||||
|
||||
func TestNamespaceModeReplace(t *testing.T) {
|
||||
ns1Pod := testPod.DeepCopy()
|
||||
ns1Pod.SetNamespace("ns1")
|
||||
ns1Pod.SetName("pod1")
|
||||
|
||||
ns2Pod := testPod.DeepCopy()
|
||||
ns2Pod.SetNamespace("ns2")
|
||||
podGroupKind := testPod.GroupVersionKind().GroupKind()
|
||||
|
||||
cluster := newCluster(ns1Pod, ns2Pod)
|
||||
err := cluster.ensureSynced()
|
||||
assert.Nil(t, err)
|
||||
|
||||
cluster.replaceResourceCache(podGroupKind, "", nil, "ns1")
|
||||
|
||||
_, ok := cluster.nodes[kube.GetResourceKey(ns1Pod)]
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = cluster.nodes[kube.GetResourceKey(ns2Pod)]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func TestGetDuplicatedChildren(t *testing.T) {
|
||||
extensionsRS := testRS.DeepCopy()
|
||||
extensionsRS.SetGroupVersionKind(schema.GroupVersionKind{Group: "extensions", Kind: kube.ReplicaSetKind, Version: "v1beta1"})
|
||||
cluster := newCluster(testDeploy, testRS, extensionsRS)
|
||||
err := cluster.ensureSynced()
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Get children multiple times to make sure the right child is picked up every time.
|
||||
for i := 0; i < 5; i++ {
|
||||
children := getChildren(cluster, testDeploy)
|
||||
assert.Len(t, children, 1)
|
||||
assert.Equal(t, "apps", children[0].Group)
|
||||
assert.Equal(t, kube.ReplicaSetKind, children[0].Kind)
|
||||
assert.Equal(t, testRS.GetName(), children[0].Name)
|
||||
}
|
||||
}
|
||||
37
controller/cache/info.go
vendored
37
controller/cache/info.go
vendored
@@ -3,38 +3,37 @@ package cache
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/text"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
k8snode "k8s.io/kubernetes/pkg/util/node"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/resource"
|
||||
)
|
||||
|
||||
func populateNodeInfo(un *unstructured.Unstructured, node *node) {
|
||||
|
||||
func populateNodeInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
gvk := un.GroupVersionKind()
|
||||
revision := resource.GetRevision(un)
|
||||
if revision > 0 {
|
||||
node.info = append(node.info, v1alpha1.InfoItem{Name: "Revision", Value: fmt.Sprintf("Rev:%v", revision)})
|
||||
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Revision", Value: fmt.Sprintf("Rev:%v", revision)})
|
||||
}
|
||||
switch gvk.Group {
|
||||
case "":
|
||||
switch gvk.Kind {
|
||||
case kube.PodKind:
|
||||
populatePodInfo(un, node)
|
||||
populatePodInfo(un, res)
|
||||
return
|
||||
case kube.ServiceKind:
|
||||
populateServiceInfo(un, node)
|
||||
populateServiceInfo(un, res)
|
||||
return
|
||||
}
|
||||
case "extensions", "networking.k8s.io":
|
||||
switch gvk.Kind {
|
||||
case kube.IngressKind:
|
||||
populateIngressInfo(un, node)
|
||||
populateIngressInfo(un, res)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -58,16 +57,16 @@ func getIngress(un *unstructured.Unstructured) []v1.LoadBalancerIngress {
|
||||
return res
|
||||
}
|
||||
|
||||
func populateServiceInfo(un *unstructured.Unstructured, node *node) {
|
||||
func populateServiceInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
targetLabels, _, _ := unstructured.NestedStringMap(un.Object, "spec", "selector")
|
||||
ingress := make([]v1.LoadBalancerIngress, 0)
|
||||
if serviceType, ok, err := unstructured.NestedString(un.Object, "spec", "type"); ok && err == nil && serviceType == string(v1.ServiceTypeLoadBalancer) {
|
||||
ingress = getIngress(un)
|
||||
}
|
||||
node.networkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress}
|
||||
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetLabels: targetLabels, Ingress: ingress}
|
||||
}
|
||||
|
||||
func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||
func populateIngressInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
ingress := getIngress(un)
|
||||
targetsMap := make(map[v1alpha1.ResourceRef]bool)
|
||||
if backend, ok, err := unstructured.NestedMap(un.Object, "spec", "backend"); ok && err == nil {
|
||||
@@ -88,7 +87,7 @@ func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||
host := rule["host"]
|
||||
if host == nil || host == "" {
|
||||
for i := range ingress {
|
||||
host = util.FirstNonEmpty(ingress[i].Hostname, ingress[i].IP)
|
||||
host = text.FirstNonEmpty(ingress[i].Hostname, ingress[i].IP)
|
||||
if host != "" {
|
||||
break
|
||||
}
|
||||
@@ -155,10 +154,10 @@ func populateIngressInfo(un *unstructured.Unstructured, node *node) {
|
||||
for url := range urlsSet {
|
||||
urls = append(urls, url)
|
||||
}
|
||||
node.networkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, Ingress: ingress, ExternalURLs: urls}
|
||||
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{TargetRefs: targets, Ingress: ingress, ExternalURLs: urls}
|
||||
}
|
||||
|
||||
func populatePodInfo(un *unstructured.Unstructured, node *node) {
|
||||
func populatePodInfo(un *unstructured.Unstructured, res *ResourceInfo) {
|
||||
pod := v1.Pod{}
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &pod)
|
||||
if err != nil {
|
||||
@@ -181,9 +180,9 @@ func populatePodInfo(un *unstructured.Unstructured, node *node) {
|
||||
imagesSet[container.Image] = true
|
||||
}
|
||||
|
||||
node.images = nil
|
||||
res.Images = nil
|
||||
for image := range imagesSet {
|
||||
node.images = append(node.images, image)
|
||||
res.Images = append(res.Images, image)
|
||||
}
|
||||
|
||||
initializing := false
|
||||
@@ -250,8 +249,8 @@ func populatePodInfo(un *unstructured.Unstructured, node *node) {
|
||||
}
|
||||
|
||||
if reason != "" {
|
||||
node.info = append(node.info, v1alpha1.InfoItem{Name: "Status Reason", Value: reason})
|
||||
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Status Reason", Value: reason})
|
||||
}
|
||||
node.info = append(node.info, v1alpha1.InfoItem{Name: "Containers", Value: fmt.Sprintf("%d/%d", readyContainers, totalContainers)})
|
||||
node.networkingInfo = &v1alpha1.ResourceNetworkingInfo{Labels: un.GetLabels()}
|
||||
res.Info = append(res.Info, v1alpha1.InfoItem{Name: "Containers", Value: fmt.Sprintf("%d/%d", readyContainers, totalContainers)})
|
||||
res.NetworkingInfo = &v1alpha1.ResourceNetworkingInfo{Labels: un.GetLabels()}
|
||||
}
|
||||
|
||||
120
controller/cache/info_test.go
vendored
120
controller/cache/info_test.go
vendored
@@ -5,12 +5,68 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/argoproj/pkg/errors"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
)
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
func strToUnstructured(jsonStr string) *unstructured.Unstructured {
|
||||
obj := make(map[string]interface{})
|
||||
err := yaml.Unmarshal([]byte(jsonStr), &obj)
|
||||
errors.CheckError(err)
|
||||
return &unstructured.Unstructured{Object: obj}
|
||||
}
|
||||
|
||||
var (
|
||||
testService = strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: helm-guestbook
|
||||
namespace: default
|
||||
resourceVersion: "123"
|
||||
uid: "4"
|
||||
spec:
|
||||
selector:
|
||||
app: guestbook
|
||||
type: LoadBalancer
|
||||
status:
|
||||
loadBalancer:
|
||||
ingress:
|
||||
- hostname: localhost`)
|
||||
|
||||
testIngress = strToUnstructured(`
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: helm-guestbook
|
||||
namespace: default
|
||||
uid: "4"
|
||||
spec:
|
||||
backend:
|
||||
serviceName: not-found-service
|
||||
servicePort: 443
|
||||
rules:
|
||||
- host: helm-guestbook.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: helm-guestbook
|
||||
servicePort: 443
|
||||
path: /
|
||||
- backend:
|
||||
serviceName: helm-guestbook
|
||||
servicePort: https
|
||||
path: /
|
||||
status:
|
||||
loadBalancer:
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
)
|
||||
|
||||
func TestGetPodInfo(t *testing.T) {
|
||||
@@ -31,29 +87,29 @@ func TestGetPodInfo(t *testing.T) {
|
||||
containers:
|
||||
- image: bar`)
|
||||
|
||||
node := &node{}
|
||||
populateNodeInfo(pod, node)
|
||||
assert.Equal(t, []v1alpha1.InfoItem{{Name: "Containers", Value: "0/1"}}, node.info)
|
||||
assert.Equal(t, []string{"bar"}, node.images)
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{Labels: map[string]string{"app": "guestbook"}}, node.networkingInfo)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(pod, info)
|
||||
assert.Equal(t, []v1alpha1.InfoItem{{Name: "Containers", Value: "0/1"}}, info.Info)
|
||||
assert.Equal(t, []string{"bar"}, info.Images)
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{Labels: map[string]string{"app": "guestbook"}}, info.NetworkingInfo)
|
||||
}
|
||||
|
||||
func TestGetServiceInfo(t *testing.T) {
|
||||
node := &node{}
|
||||
populateNodeInfo(testService, node)
|
||||
assert.Equal(t, 0, len(node.info))
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testService, info)
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
TargetLabels: map[string]string{"app": "guestbook"},
|
||||
Ingress: []v1.LoadBalancerIngress{{Hostname: "localhost"}},
|
||||
}, node.networkingInfo)
|
||||
}, info.NetworkingInfo)
|
||||
}
|
||||
|
||||
func TestGetIngressInfo(t *testing.T) {
|
||||
node := &node{}
|
||||
populateNodeInfo(testIngress, node)
|
||||
assert.Equal(t, 0, len(node.info))
|
||||
sort.Slice(node.networkingInfo.TargetRefs, func(i, j int) bool {
|
||||
return strings.Compare(node.networkingInfo.TargetRefs[j].Name, node.networkingInfo.TargetRefs[i].Name) < 0
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(testIngress, info)
|
||||
assert.Equal(t, 0, len(info.Info))
|
||||
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
|
||||
return strings.Compare(info.NetworkingInfo.TargetRefs[j].Name, info.NetworkingInfo.TargetRefs[i].Name) < 0
|
||||
})
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
|
||||
@@ -69,7 +125,7 @@ func TestGetIngressInfo(t *testing.T) {
|
||||
Name: "helm-guestbook",
|
||||
}},
|
||||
ExternalURLs: []string{"https://helm-guestbook.com/"},
|
||||
}, node.networkingInfo)
|
||||
}, info.NetworkingInfo)
|
||||
}
|
||||
|
||||
func TestGetIngressInfoNoHost(t *testing.T) {
|
||||
@@ -92,8 +148,8 @@ func TestGetIngressInfoNoHost(t *testing.T) {
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
node := &node{}
|
||||
populateNodeInfo(ingress, node)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
|
||||
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
|
||||
Ingress: []v1.LoadBalancerIngress{{IP: "107.178.210.11"}},
|
||||
@@ -104,7 +160,7 @@ func TestGetIngressInfoNoHost(t *testing.T) {
|
||||
Name: "helm-guestbook",
|
||||
}},
|
||||
ExternalURLs: []string{"https://107.178.210.11/"},
|
||||
}, node.networkingInfo)
|
||||
}, info.NetworkingInfo)
|
||||
}
|
||||
func TestExternalUrlWithSubPath(t *testing.T) {
|
||||
ingress := strToUnstructured(`
|
||||
@@ -126,11 +182,11 @@ func TestExternalUrlWithSubPath(t *testing.T) {
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
node := &node{}
|
||||
populateNodeInfo(ingress, node)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
|
||||
expectedExternalUrls := []string{"https://107.178.210.11/my/sub/path/"}
|
||||
assert.Equal(t, expectedExternalUrls, node.networkingInfo.ExternalURLs)
|
||||
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
|
||||
}
|
||||
func TestExternalUrlWithMultipleSubPaths(t *testing.T) {
|
||||
ingress := strToUnstructured(`
|
||||
@@ -160,11 +216,11 @@ func TestExternalUrlWithMultipleSubPaths(t *testing.T) {
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
node := &node{}
|
||||
populateNodeInfo(ingress, node)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
|
||||
expectedExternalUrls := []string{"https://helm-guestbook.com/my/sub/path/", "https://helm-guestbook.com/my/sub/path/2", "https://helm-guestbook.com"}
|
||||
actualURLs := node.networkingInfo.ExternalURLs
|
||||
actualURLs := info.NetworkingInfo.ExternalURLs
|
||||
sort.Strings(expectedExternalUrls)
|
||||
sort.Strings(actualURLs)
|
||||
assert.Equal(t, expectedExternalUrls, actualURLs)
|
||||
@@ -188,11 +244,11 @@ func TestExternalUrlWithNoSubPath(t *testing.T) {
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
node := &node{}
|
||||
populateNodeInfo(ingress, node)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
|
||||
expectedExternalUrls := []string{"https://107.178.210.11"}
|
||||
assert.Equal(t, expectedExternalUrls, node.networkingInfo.ExternalURLs)
|
||||
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
|
||||
}
|
||||
|
||||
func TestExternalUrlWithNetworkingApi(t *testing.T) {
|
||||
@@ -214,9 +270,9 @@ func TestExternalUrlWithNetworkingApi(t *testing.T) {
|
||||
ingress:
|
||||
- ip: 107.178.210.11`)
|
||||
|
||||
node := &node{}
|
||||
populateNodeInfo(ingress, node)
|
||||
info := &ResourceInfo{}
|
||||
populateNodeInfo(ingress, info)
|
||||
|
||||
expectedExternalUrls := []string{"https://107.178.210.11"}
|
||||
assert.Equal(t, expectedExternalUrls, node.networkingInfo.ExternalURLs)
|
||||
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
|
||||
}
|
||||
|
||||
51
controller/cache/mocks/LiveStateCache.go
vendored
51
controller/cache/mocks/LiveStateCache.go
vendored
@@ -5,8 +5,9 @@ package mocks
|
||||
import (
|
||||
context "context"
|
||||
|
||||
metrics "github.com/argoproj/argo-cd/controller/metrics"
|
||||
kube "github.com/argoproj/argo-cd/util/kube"
|
||||
cache "github.com/argoproj/gitops-engine/pkg/cache"
|
||||
|
||||
kube "github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
@@ -24,16 +25,39 @@ type LiveStateCache struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetClusterCache provides a mock function with given fields: server
|
||||
func (_m *LiveStateCache) GetClusterCache(server string) (cache.ClusterCache, error) {
|
||||
ret := _m.Called(server)
|
||||
|
||||
var r0 cache.ClusterCache
|
||||
if rf, ok := ret.Get(0).(func(string) cache.ClusterCache); ok {
|
||||
r0 = rf(server)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(cache.ClusterCache)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(server)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetClustersInfo provides a mock function with given fields:
|
||||
func (_m *LiveStateCache) GetClustersInfo() []metrics.ClusterInfo {
|
||||
func (_m *LiveStateCache) GetClustersInfo() []cache.ClusterInfo {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 []metrics.ClusterInfo
|
||||
if rf, ok := ret.Get(0).(func() []metrics.ClusterInfo); ok {
|
||||
var r0 []cache.ClusterInfo
|
||||
if rf, ok := ret.Get(0).(func() []cache.ClusterInfo); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]metrics.ClusterInfo)
|
||||
r0 = ret.Get(0).([]cache.ClusterInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +140,18 @@ func (_m *LiveStateCache) GetVersionsInfo(serverURL string) (string, []v1.APIGro
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// Invalidate provides a mock function with given fields:
|
||||
func (_m *LiveStateCache) Invalidate() {
|
||||
_m.Called()
|
||||
// Init provides a mock function with given fields:
|
||||
func (_m *LiveStateCache) Init() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// IsNamespaced provides a mock function with given fields: server, gk
|
||||
|
||||
142
controller/cache/node.go
vendored
142
controller/cache/node.go
vendored
@@ -1,142 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
|
||||
v1 "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/schema"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
resourceVersion string
|
||||
ref v1.ObjectReference
|
||||
ownerRefs []metav1.OwnerReference
|
||||
info []appv1.InfoItem
|
||||
appName string
|
||||
// available only for root application nodes
|
||||
resource *unstructured.Unstructured
|
||||
// networkingInfo are available only for known types involved into networking: Ingress, Service, Pod
|
||||
networkingInfo *appv1.ResourceNetworkingInfo
|
||||
images []string
|
||||
health *appv1.HealthStatus
|
||||
}
|
||||
|
||||
func (n *node) isRootAppNode() bool {
|
||||
return n.appName != "" && len(n.ownerRefs) == 0
|
||||
}
|
||||
|
||||
func (n *node) resourceKey() kube.ResourceKey {
|
||||
return kube.NewResourceKey(n.ref.GroupVersionKind().Group, n.ref.Kind, n.ref.Namespace, n.ref.Name)
|
||||
}
|
||||
|
||||
func (n *node) isParentOf(child *node) bool {
|
||||
for i, ownerRef := range child.ownerRefs {
|
||||
|
||||
// backfill UID of inferred owner child references
|
||||
if ownerRef.UID == "" && n.ref.Kind == ownerRef.Kind && n.ref.APIVersion == ownerRef.APIVersion && n.ref.Name == ownerRef.Name {
|
||||
ownerRef.UID = n.ref.UID
|
||||
child.ownerRefs[i] = ownerRef
|
||||
return true
|
||||
}
|
||||
|
||||
if n.ref.UID == ownerRef.UID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func ownerRefGV(ownerRef metav1.OwnerReference) schema.GroupVersion {
|
||||
gv, err := schema.ParseGroupVersion(ownerRef.APIVersion)
|
||||
if err != nil {
|
||||
gv = schema.GroupVersion{}
|
||||
}
|
||||
return gv
|
||||
}
|
||||
|
||||
func (n *node) getApp(ns map[kube.ResourceKey]*node) string {
|
||||
return n.getAppRecursive(ns, map[kube.ResourceKey]bool{})
|
||||
}
|
||||
|
||||
func (n *node) getAppRecursive(ns map[kube.ResourceKey]*node, visited map[kube.ResourceKey]bool) string {
|
||||
if !visited[n.resourceKey()] {
|
||||
visited[n.resourceKey()] = true
|
||||
} else {
|
||||
log.Warnf("Circular dependency detected: %v.", visited)
|
||||
return n.appName
|
||||
}
|
||||
|
||||
if n.appName != "" {
|
||||
return n.appName
|
||||
}
|
||||
for _, ownerRef := range n.ownerRefs {
|
||||
gv := ownerRefGV(ownerRef)
|
||||
if parent, ok := ns[kube.NewResourceKey(gv.Group, ownerRef.Kind, n.ref.Namespace, ownerRef.Name)]; ok {
|
||||
app := parent.getAppRecursive(ns, visited)
|
||||
if app != "" {
|
||||
return app
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func newResourceKeySet(set map[kube.ResourceKey]bool, keys ...kube.ResourceKey) map[kube.ResourceKey]bool {
|
||||
newSet := make(map[kube.ResourceKey]bool)
|
||||
for k, v := range set {
|
||||
newSet[k] = v
|
||||
}
|
||||
for i := range keys {
|
||||
newSet[keys[i]] = true
|
||||
}
|
||||
return newSet
|
||||
}
|
||||
|
||||
func (n *node) asResourceNode() appv1.ResourceNode {
|
||||
gv, err := schema.ParseGroupVersion(n.ref.APIVersion)
|
||||
if err != nil {
|
||||
gv = schema.GroupVersion{}
|
||||
}
|
||||
parentRefs := make([]appv1.ResourceRef, len(n.ownerRefs))
|
||||
for _, ownerRef := range n.ownerRefs {
|
||||
ownerGvk := schema.FromAPIVersionAndKind(ownerRef.APIVersion, ownerRef.Kind)
|
||||
ownerKey := kube.NewResourceKey(ownerGvk.Group, ownerRef.Kind, n.ref.Namespace, ownerRef.Name)
|
||||
parentRefs[0] = appv1.ResourceRef{Name: ownerRef.Name, Kind: ownerKey.Kind, Namespace: n.ref.Namespace, Group: ownerKey.Group, UID: string(ownerRef.UID)}
|
||||
}
|
||||
return appv1.ResourceNode{
|
||||
ResourceRef: appv1.ResourceRef{
|
||||
UID: string(n.ref.UID),
|
||||
Name: n.ref.Name,
|
||||
Group: gv.Group,
|
||||
Version: gv.Version,
|
||||
Kind: n.ref.Kind,
|
||||
Namespace: n.ref.Namespace,
|
||||
},
|
||||
ParentRefs: parentRefs,
|
||||
Info: n.info,
|
||||
ResourceVersion: n.resourceVersion,
|
||||
NetworkingInfo: n.networkingInfo,
|
||||
Images: n.images,
|
||||
Health: n.health,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) iterateChildren(ns map[kube.ResourceKey]*node, parents map[kube.ResourceKey]bool, action func(child appv1.ResourceNode, appName string)) {
|
||||
for childKey, child := range ns {
|
||||
if n.isParentOf(ns[childKey]) {
|
||||
if parents[childKey] {
|
||||
key := n.resourceKey()
|
||||
log.Warnf("Circular dependency detected. %s is child and parent of %s", childKey.String(), key.String())
|
||||
} else {
|
||||
action(child.asResourceNode(), child.getApp(ns))
|
||||
child.iterateChildren(ns, newResourceKeySet(parents, n.resourceKey()), action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
83
controller/cache/node_test.go
vendored
83
controller/cache/node_test.go
vendored
@@ -1,83 +0,0 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var c = &clusterInfo{cacheSettingsSrc: func() *cacheSettings {
|
||||
return &cacheSettings{AppInstanceLabelKey: common.LabelKeyAppInstance}
|
||||
}}
|
||||
|
||||
func TestIsParentOf(t *testing.T) {
|
||||
child := c.createObjInfo(testPod, "")
|
||||
parent := c.createObjInfo(testRS, "")
|
||||
grandParent := c.createObjInfo(testDeploy, "")
|
||||
|
||||
assert.True(t, parent.isParentOf(child))
|
||||
assert.False(t, grandParent.isParentOf(child))
|
||||
}
|
||||
|
||||
func TestIsParentOfSameKindDifferentGroupAndUID(t *testing.T) {
|
||||
rs := testRS.DeepCopy()
|
||||
rs.SetAPIVersion("somecrd.io/v1")
|
||||
rs.SetUID("123")
|
||||
child := c.createObjInfo(testPod, "")
|
||||
invalidParent := c.createObjInfo(rs, "")
|
||||
|
||||
assert.False(t, invalidParent.isParentOf(child))
|
||||
}
|
||||
|
||||
func TestIsServiceParentOfEndPointWithTheSameName(t *testing.T) {
|
||||
nonMatchingNameEndPoint := c.createObjInfo(strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: not-matching-name
|
||||
namespace: default
|
||||
`), "")
|
||||
|
||||
matchingNameEndPoint := c.createObjInfo(strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Endpoints
|
||||
metadata:
|
||||
name: helm-guestbook
|
||||
namespace: default
|
||||
`), "")
|
||||
|
||||
parent := c.createObjInfo(testService, "")
|
||||
|
||||
assert.True(t, parent.isParentOf(matchingNameEndPoint))
|
||||
assert.Equal(t, parent.ref.UID, matchingNameEndPoint.ownerRefs[0].UID)
|
||||
assert.False(t, parent.isParentOf(nonMatchingNameEndPoint))
|
||||
}
|
||||
|
||||
func TestIsServiceAccoountParentOfSecret(t *testing.T) {
|
||||
serviceAccount := c.createObjInfo(strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
uid: '123'
|
||||
secrets:
|
||||
- name: default-token-123
|
||||
`), "")
|
||||
tokenSecret := c.createObjInfo(strToUnstructured(`
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: default
|
||||
kubernetes.io/service-account.uid: '123'
|
||||
name: default-token-123
|
||||
namespace: default
|
||||
uid: '345'
|
||||
type: kubernetes.io/service-account-token
|
||||
`), "")
|
||||
|
||||
assert.True(t, serviceAccount.isParentOf(tokenSecret))
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/cache"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
@@ -41,25 +43,19 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
type ClusterInfo struct {
|
||||
Server string
|
||||
K8SVersion string
|
||||
ResourcesCount int
|
||||
APIsCount int
|
||||
LastCacheSyncTime *time.Time
|
||||
}
|
||||
|
||||
type HasClustersInfo interface {
|
||||
GetClustersInfo() []ClusterInfo
|
||||
GetClustersInfo() []cache.ClusterInfo
|
||||
}
|
||||
|
||||
type clusterCollector struct {
|
||||
infoSource HasClustersInfo
|
||||
info []ClusterInfo
|
||||
info []cache.ClusterInfo
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (c *clusterCollector) Run(ctx context.Context) {
|
||||
// FIXME: complains about SA1015
|
||||
// nolint:staticcheck
|
||||
tick := time.Tick(metricsCollectionInterval)
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -27,6 +28,7 @@ type MetricsServer struct {
|
||||
clusterEventsCounter *prometheus.CounterVec
|
||||
redisRequestCounter *prometheus.CounterVec
|
||||
reconcileHistogram *prometheus.HistogramVec
|
||||
redisRequestHistogram *prometheus.HistogramVec
|
||||
registry *prometheus.Registry
|
||||
}
|
||||
|
||||
@@ -118,6 +120,15 @@ var (
|
||||
},
|
||||
[]string{"initiator", "failed"},
|
||||
)
|
||||
|
||||
redisRequestHistogram = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "argocd_redis_request_duration",
|
||||
Help: "Redis requests duration.",
|
||||
Buckets: []float64{0.01, 0.05, 0.10, 0.25, .5, 1},
|
||||
},
|
||||
[]string{"initiator"},
|
||||
)
|
||||
)
|
||||
|
||||
// NewMetricsServer returns a new prometheus server which collects application metrics
|
||||
@@ -139,6 +150,7 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, health
|
||||
registry.MustRegister(reconcileHistogram)
|
||||
registry.MustRegister(clusterEventsCounter)
|
||||
registry.MustRegister(redisRequestCounter)
|
||||
registry.MustRegister(redisRequestHistogram)
|
||||
|
||||
return &MetricsServer{
|
||||
registry: registry,
|
||||
@@ -153,6 +165,7 @@ func NewMetricsServer(addr string, appLister applister.ApplicationLister, health
|
||||
reconcileHistogram: reconcileHistogram,
|
||||
clusterEventsCounter: clusterEventsCounter,
|
||||
redisRequestCounter: redisRequestCounter,
|
||||
redisRequestHistogram: redisRequestHistogram,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +218,11 @@ func (m *MetricsServer) IncRedisRequest(failed bool) {
|
||||
m.redisRequestCounter.WithLabelValues("argocd-application-controller", strconv.FormatBool(failed)).Inc()
|
||||
}
|
||||
|
||||
// ObserveRedisRequestDuration observes redis request duration
|
||||
func (m *MetricsServer) ObserveRedisRequestDuration(duration time.Duration) {
|
||||
m.redisRequestHistogram.WithLabelValues("argocd-application-controller").Observe(duration.Seconds())
|
||||
}
|
||||
|
||||
// IncReconcile increments the reconcile counter for an application
|
||||
func (m *MetricsServer) IncReconcile(app *argoappv1.Application, duration time.Duration) {
|
||||
m.reconcileHistogram.WithLabelValues(app.Namespace, app.Spec.Destination.Server).Observe(duration.Seconds())
|
||||
@@ -276,10 +294,10 @@ func collectApps(ch chan<- prometheus.Metric, app *argoappv1.Application) {
|
||||
}
|
||||
healthStatus := app.Status.Health.Status
|
||||
if healthStatus == "" {
|
||||
healthStatus = argoappv1.HealthStatusUnknown
|
||||
healthStatus = health.HealthStatusUnknown
|
||||
}
|
||||
|
||||
addGauge(descAppInfo, 1, git.NormalizeGitURL(app.Spec.Source.RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), healthStatus, operation)
|
||||
addGauge(descAppInfo, 1, git.NormalizeGitURL(app.Spec.Source.RepoURL), app.Spec.Destination.Server, app.Spec.Destination.Namespace, string(syncStatus), string(healthStatus), operation)
|
||||
|
||||
// Deprecated controller metrics
|
||||
if os.Getenv(EnvVarLegacyControllerMetrics) == "true" {
|
||||
@@ -289,11 +307,12 @@ func collectApps(ch chan<- prometheus.Metric, app *argoappv1.Application) {
|
||||
addGauge(descAppSyncStatusCode, boolFloat64(syncStatus == argoappv1.SyncStatusCodeOutOfSync), string(argoappv1.SyncStatusCodeOutOfSync))
|
||||
addGauge(descAppSyncStatusCode, boolFloat64(syncStatus == argoappv1.SyncStatusCodeUnknown || syncStatus == ""), string(argoappv1.SyncStatusCodeUnknown))
|
||||
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == argoappv1.HealthStatusUnknown || healthStatus == ""), argoappv1.HealthStatusUnknown)
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == argoappv1.HealthStatusProgressing), argoappv1.HealthStatusProgressing)
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == argoappv1.HealthStatusSuspended), argoappv1.HealthStatusSuspended)
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == argoappv1.HealthStatusHealthy), argoappv1.HealthStatusHealthy)
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == argoappv1.HealthStatusDegraded), argoappv1.HealthStatusDegraded)
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == argoappv1.HealthStatusMissing), argoappv1.HealthStatusMissing)
|
||||
healthStatus := app.Status.Health.Status
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == health.HealthStatusUnknown || healthStatus == ""), string(health.HealthStatusUnknown))
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == health.HealthStatusProgressing), string(health.HealthStatusProgressing))
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == health.HealthStatusSuspended), string(health.HealthStatusSuspended))
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == health.HealthStatusHealthy), string(health.HealthStatusHealthy))
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == health.HealthStatusDegraded), string(health.HealthStatusDegraded))
|
||||
addGauge(descAppHealthStatus, boolFloat64(healthStatus == health.HealthStatusMissing), string(health.HealthStatusMissing))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -227,11 +228,11 @@ argocd_app_sync_total{dest_server="https://localhost:6443",name="my-app",namespa
|
||||
`
|
||||
|
||||
fakeApp := newFakeApp(fakeApp)
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: argoappv1.OperationRunning})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: argoappv1.OperationFailed})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: argoappv1.OperationError})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: argoappv1.OperationSucceeded})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: argoappv1.OperationSucceeded})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationRunning})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationFailed})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationError})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded})
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: common.OperationSucceeded})
|
||||
|
||||
req, err := http.NewRequest("GET", "/metrics", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -6,8 +6,15 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync"
|
||||
hookutil "github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
resourceutil "github.com/argoproj/gitops-engine/pkg/sync/resource"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/io"
|
||||
kubeutil "github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/yudai/gojsondiff"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -21,19 +28,20 @@ import (
|
||||
appv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/argo"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/diff"
|
||||
"github.com/argoproj/argo-cd/util/health"
|
||||
hookutil "github.com/argoproj/argo-cd/util/hook"
|
||||
kubeutil "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/resource"
|
||||
"github.com/argoproj/argo-cd/util/resource/ignore"
|
||||
argohealth "github.com/argoproj/argo-cd/util/health"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"github.com/argoproj/argo-cd/util/stats"
|
||||
)
|
||||
|
||||
type resourceInfoProviderStub struct {
|
||||
}
|
||||
|
||||
func (r *resourceInfoProviderStub) IsNamespaced(_ schema.GroupKind) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type managedResource struct {
|
||||
Target *unstructured.Unstructured
|
||||
Live *unstructured.Unstructured
|
||||
@@ -54,10 +62,6 @@ func GetLiveObjs(res []managedResource) []*unstructured.Unstructured {
|
||||
return objs
|
||||
}
|
||||
|
||||
type ResourceInfoProvider interface {
|
||||
IsNamespaced(server string, gk schema.GroupKind) (bool, error)
|
||||
}
|
||||
|
||||
// AppStateManager defines methods which allow to compare application spec and actual application state.
|
||||
type AppStateManager interface {
|
||||
CompareAppState(app *v1alpha1.Application, project *appv1.AppProject, revision string, source v1alpha1.ApplicationSource, noCache bool, localObjects []string) *comparisonResult
|
||||
@@ -65,27 +69,17 @@ type AppStateManager interface {
|
||||
}
|
||||
|
||||
type comparisonResult struct {
|
||||
syncStatus *v1alpha1.SyncStatus
|
||||
healthStatus *v1alpha1.HealthStatus
|
||||
resources []v1alpha1.ResourceStatus
|
||||
managedResources []managedResource
|
||||
hooks []*unstructured.Unstructured
|
||||
diffNormalizer diff.Normalizer
|
||||
appSourceType v1alpha1.ApplicationSourceType
|
||||
syncStatus *v1alpha1.SyncStatus
|
||||
healthStatus *v1alpha1.HealthStatus
|
||||
resources []v1alpha1.ResourceStatus
|
||||
managedResources []managedResource
|
||||
reconciliationResult sync.ReconciliationResult
|
||||
diffNormalizer diff.Normalizer
|
||||
appSourceType v1alpha1.ApplicationSourceType
|
||||
// timings maps phases of comparison to the duration it took to complete (for statistical purposes)
|
||||
timings map[string]time.Duration
|
||||
}
|
||||
|
||||
func (cr *comparisonResult) targetObjs() []*unstructured.Unstructured {
|
||||
objs := cr.hooks
|
||||
for _, r := range cr.managedResources {
|
||||
if r.Target != nil {
|
||||
objs = append(objs, r.Target)
|
||||
}
|
||||
}
|
||||
return objs
|
||||
}
|
||||
|
||||
// appStateManager allows to compare applications to git
|
||||
type appStateManager struct {
|
||||
metricsServer *metrics.MetricsServer
|
||||
@@ -99,23 +93,23 @@ type appStateManager struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, appLabelKey, revision string, noCache bool) ([]*unstructured.Unstructured, []*unstructured.Unstructured, *apiclient.ManifestResponse, error) {
|
||||
func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1.ApplicationSource, appLabelKey, revision string, noCache bool) ([]*unstructured.Unstructured, *apiclient.ManifestResponse, error) {
|
||||
ts := stats.NewTimingStats()
|
||||
helmRepos, err := m.db.ListHelmRepositories(context.Background())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("helm_ms")
|
||||
repo, err := m.db.GetRepository(context.Background(), source.RepoURL)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("repo_ms")
|
||||
conn, repoClient, err := m.repoClientset.NewRepoServerClient()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer util.Close(conn)
|
||||
defer io.Close(conn)
|
||||
|
||||
if revision == "" {
|
||||
revision = source.TargetRevision
|
||||
@@ -123,7 +117,7 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
|
||||
plugins, err := m.settingsMgr.GetConfigManagementPlugins()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("plugins_ms")
|
||||
tools := make([]*appv1.ConfigManagementPlugin, len(plugins))
|
||||
@@ -133,16 +127,16 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
|
||||
kustomizeSettings, err := m.settingsMgr.GetKustomizeSettings()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
kustomizeOptions, err := kustomizeSettings.GetOptions(app.Spec.Source)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("build_options_ms")
|
||||
serverVersion, apiGroups, err := m.liveStateCache.GetVersionsInfo(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("version_ms")
|
||||
manifestInfo, err := repoClient.GenerateManifest(context.Background(), &apiclient.ManifestRequest{
|
||||
@@ -160,13 +154,14 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
ApiVersions: argo.APIGroupsToVersions(apiGroups),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
ts.AddCheckpoint("manifests_ms")
|
||||
targetObjs, hooks, err := unmarshalManifests(manifestInfo.Manifests)
|
||||
targetObjs, err := unmarshalManifests(manifestInfo.Manifests)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ts.AddCheckpoint("unmarshal_ms")
|
||||
logCtx := log.WithField("application", app.Name)
|
||||
for k, v := range ts.Timings() {
|
||||
@@ -174,49 +169,43 @@ func (m *appStateManager) getRepoObjs(app *v1alpha1.Application, source v1alpha1
|
||||
}
|
||||
logCtx = logCtx.WithField("time_ms", time.Since(ts.StartTime).Milliseconds())
|
||||
logCtx.Info("getRepoObjs stats")
|
||||
return targetObjs, hooks, manifestInfo, nil
|
||||
return targetObjs, manifestInfo, nil
|
||||
}
|
||||
|
||||
func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, []*unstructured.Unstructured, error) {
|
||||
func unmarshalManifests(manifests []string) ([]*unstructured.Unstructured, error) {
|
||||
targetObjs := make([]*unstructured.Unstructured, 0)
|
||||
hooks := make([]*unstructured.Unstructured, 0)
|
||||
for _, manifest := range manifests {
|
||||
obj, err := v1alpha1.UnmarshalToUnstructured(manifest)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if obj == nil || ignore.Ignore(obj) {
|
||||
continue
|
||||
}
|
||||
if hookutil.IsHook(obj) {
|
||||
hooks = append(hooks, obj)
|
||||
} else {
|
||||
targetObjs = append(targetObjs, obj)
|
||||
return nil, err
|
||||
}
|
||||
targetObjs = append(targetObjs, obj)
|
||||
}
|
||||
return targetObjs, hooks, nil
|
||||
return targetObjs, nil
|
||||
}
|
||||
|
||||
func DeduplicateTargetObjects(
|
||||
server string,
|
||||
namespace string,
|
||||
objs []*unstructured.Unstructured,
|
||||
infoProvider ResourceInfoProvider,
|
||||
infoProvider kubeutil.ResourceInfoProvider,
|
||||
) ([]*unstructured.Unstructured, []v1alpha1.ApplicationCondition, error) {
|
||||
|
||||
targetByKey := make(map[kubeutil.ResourceKey][]*unstructured.Unstructured)
|
||||
for i := range objs {
|
||||
obj := objs[i]
|
||||
isNamespaced, err := infoProvider.IsNamespaced(server, obj.GroupVersionKind().GroupKind())
|
||||
if err != nil {
|
||||
return objs, nil, err
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
isNamespaced := kubeutil.IsNamespacedOrUnknown(infoProvider, obj.GroupVersionKind().GroupKind())
|
||||
if !isNamespaced {
|
||||
obj.SetNamespace("")
|
||||
} else if obj.GetNamespace() == "" {
|
||||
obj.SetNamespace(namespace)
|
||||
}
|
||||
key := kubeutil.GetResourceKey(obj)
|
||||
if key.Name == "" && obj.GetGenerateName() != "" {
|
||||
key.Name = fmt.Sprintf("%s%d", obj.GetGenerateName(), i)
|
||||
}
|
||||
targetByKey[key] = append(targetByKey[key], obj)
|
||||
}
|
||||
conditions := make([]v1alpha1.ApplicationCondition, 0)
|
||||
@@ -236,42 +225,6 @@ func DeduplicateTargetObjects(
|
||||
return result, conditions, nil
|
||||
}
|
||||
|
||||
// dedupLiveResources handles removes live resource duplicates with the same UID. Duplicates are created in a separate resource groups.
|
||||
// E.g. apps/Deployment produces duplicate in extensions/Deployment, authorization.openshift.io/ClusterRole produces duplicate in rbac.authorization.k8s.io/ClusterRole etc.
|
||||
// The method removes such duplicates unless it was defined in git ( exists in target resources list ). At least one duplicate stays.
|
||||
// If non of duplicates are in git at random one stays
|
||||
func dedupLiveResources(targetObjs []*unstructured.Unstructured, liveObjsByKey map[kubeutil.ResourceKey]*unstructured.Unstructured) {
|
||||
targetObjByKey := make(map[kubeutil.ResourceKey]*unstructured.Unstructured)
|
||||
for i := range targetObjs {
|
||||
targetObjByKey[kubeutil.GetResourceKey(targetObjs[i])] = targetObjs[i]
|
||||
}
|
||||
liveObjsById := make(map[types.UID][]*unstructured.Unstructured)
|
||||
for k := range liveObjsByKey {
|
||||
obj := liveObjsByKey[k]
|
||||
if obj != nil {
|
||||
liveObjsById[obj.GetUID()] = append(liveObjsById[obj.GetUID()], obj)
|
||||
}
|
||||
}
|
||||
for id := range liveObjsById {
|
||||
objs := liveObjsById[id]
|
||||
|
||||
if len(objs) > 1 {
|
||||
duplicatesLeft := len(objs)
|
||||
for i := range objs {
|
||||
obj := objs[i]
|
||||
resourceKey := kubeutil.GetResourceKey(obj)
|
||||
if _, ok := targetObjByKey[resourceKey]; !ok {
|
||||
delete(liveObjsByKey, resourceKey)
|
||||
duplicatesLeft--
|
||||
if duplicatesLeft == 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *appStateManager) getComparisonSettings(app *appv1.Application) (string, map[string]v1alpha1.ResourceOverride, diff.Normalizer, *settings.ResourcesFilter, error) {
|
||||
resourceOverrides, err := m.settingsMgr.GetResourceOverrides()
|
||||
if err != nil {
|
||||
@@ -307,7 +260,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
ComparedTo: appv1.ComparedTo{Source: source, Destination: app.Spec.Destination},
|
||||
Status: appv1.SyncStatusCodeUnknown,
|
||||
},
|
||||
healthStatus: &appv1.HealthStatus{Status: appv1.HealthStatusUnknown},
|
||||
healthStatus: &appv1.HealthStatus{Status: health.HealthStatusUnknown},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,19 +272,18 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
logCtx.Infof("Comparing app state (cluster: %s, namespace: %s)", app.Spec.Destination.Server, app.Spec.Destination.Namespace)
|
||||
|
||||
var targetObjs []*unstructured.Unstructured
|
||||
var hooks []*unstructured.Unstructured
|
||||
var manifestInfo *apiclient.ManifestResponse
|
||||
now := metav1.Now()
|
||||
|
||||
if len(localManifests) == 0 {
|
||||
targetObjs, hooks, manifestInfo, err = m.getRepoObjs(app, source, appLabelKey, revision, noCache)
|
||||
targetObjs, manifestInfo, err = m.getRepoObjs(app, source, appLabelKey, revision, noCache)
|
||||
if err != nil {
|
||||
targetObjs = make([]*unstructured.Unstructured, 0)
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
failedToLoadObjs = true
|
||||
}
|
||||
} else {
|
||||
targetObjs, hooks, err = unmarshalManifests(localManifests)
|
||||
targetObjs, err = unmarshalManifests(localManifests)
|
||||
if err != nil {
|
||||
targetObjs = make([]*unstructured.Unstructured, 0)
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
@@ -341,7 +293,12 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
ts.AddCheckpoint("git_ms")
|
||||
|
||||
targetObjs, dedupConditions, err := DeduplicateTargetObjects(app.Spec.Destination.Server, app.Spec.Destination.Namespace, targetObjs, m.liveStateCache)
|
||||
var infoProvider kubeutil.ResourceInfoProvider
|
||||
infoProvider, err = m.liveStateCache.GetClusterCache(app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
infoProvider = &resourceInfoProviderStub{}
|
||||
}
|
||||
targetObjs, dedupConditions, err := DeduplicateTargetObjects(app.Spec.Destination.Namespace, targetObjs, infoProvider)
|
||||
if err != nil {
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
}
|
||||
@@ -366,7 +323,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
conditions = append(conditions, v1alpha1.ApplicationCondition{Type: v1alpha1.ApplicationConditionComparisonError, Message: err.Error(), LastTransitionTime: &now})
|
||||
failedToLoadObjs = true
|
||||
}
|
||||
dedupLiveResources(targetObjs, liveObjByKey)
|
||||
logCtx.Debugf("Retrieved lived manifests")
|
||||
|
||||
// filter out all resources which are not permitted in the application project
|
||||
for k, v := range liveObjByKey {
|
||||
if !project.IsLiveResourcePermitted(v, app.Spec.Destination.Server) {
|
||||
@@ -387,33 +345,18 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
}
|
||||
|
||||
managedLiveObj := make([]*unstructured.Unstructured, len(targetObjs))
|
||||
for i, obj := range targetObjs {
|
||||
gvk := obj.GroupVersionKind()
|
||||
ns := util.FirstNonEmpty(obj.GetNamespace(), app.Spec.Destination.Namespace)
|
||||
if namespaced, err := m.liveStateCache.IsNamespaced(app.Spec.Destination.Server, obj.GroupVersionKind().GroupKind()); err == nil && !namespaced {
|
||||
ns = ""
|
||||
}
|
||||
key := kubeutil.NewResourceKey(gvk.Group, gvk.Kind, ns, obj.GetName())
|
||||
if liveObj, ok := liveObjByKey[key]; ok {
|
||||
managedLiveObj[i] = liveObj
|
||||
delete(liveObjByKey, key)
|
||||
} else {
|
||||
managedLiveObj[i] = nil
|
||||
}
|
||||
}
|
||||
reconciliation := sync.Reconcile(targetObjs, liveObjByKey, app.Spec.Destination.Namespace, infoProvider)
|
||||
ts.AddCheckpoint("live_ms")
|
||||
|
||||
// Everything remaining in liveObjByKey are "extra" resources that aren't tracked in git.
|
||||
// The following adds all the extras to the managedLiveObj list and backfills the targetObj
|
||||
// list with nils, so that the lists are of equal lengths for comparison purposes.
|
||||
for _, obj := range liveObjByKey {
|
||||
targetObjs = append(targetObjs, nil)
|
||||
managedLiveObj = append(managedLiveObj, obj)
|
||||
compareOptions, err := m.settingsMgr.GetResourceCompareOptions()
|
||||
if err != nil {
|
||||
log.Warnf("Could not get compare options from ConfigMap (assuming defaults): %v", err)
|
||||
compareOptions = diff.GetDefaultDiffOptions()
|
||||
}
|
||||
|
||||
logCtx.Debugf("built managed objects list")
|
||||
// Do the actual comparison
|
||||
diffResults, err := diff.DiffArray(targetObjs, managedLiveObj, diffNormalizer)
|
||||
diffResults, err := diff.DiffArray(reconciliation.Target, reconciliation.Live, diffNormalizer, compareOptions)
|
||||
if err != nil {
|
||||
diffResults = &diff.DiffResultList{}
|
||||
failedToLoadObjs = true
|
||||
@@ -422,10 +365,10 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
ts.AddCheckpoint("diff_ms")
|
||||
|
||||
syncCode := v1alpha1.SyncStatusCodeSynced
|
||||
managedResources := make([]managedResource, len(targetObjs))
|
||||
resourceSummaries := make([]v1alpha1.ResourceStatus, len(targetObjs))
|
||||
for i, targetObj := range targetObjs {
|
||||
liveObj := managedLiveObj[i]
|
||||
managedResources := make([]managedResource, len(reconciliation.Target))
|
||||
resourceSummaries := make([]v1alpha1.ResourceStatus, len(reconciliation.Target))
|
||||
for i, targetObj := range reconciliation.Target {
|
||||
liveObj := reconciliation.Live[i]
|
||||
obj := liveObj
|
||||
if obj == nil {
|
||||
obj = targetObj
|
||||
@@ -449,12 +392,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
if i < len(diffResults.Diffs) {
|
||||
diffResult = diffResults.Diffs[i]
|
||||
} else {
|
||||
diffResult = diff.DiffResult{
|
||||
Diff: gojsondiff.New().CompareObjects(map[string]interface{}{}, map[string]interface{}{}),
|
||||
Modified: false,
|
||||
NormalizedLive: []byte("{}"),
|
||||
PredictedLive: []byte("{}"),
|
||||
}
|
||||
diffResult = diff.DiffResult{Modified: false, NormalizedLive: []byte("{}"), PredictedLive: []byte("{}")}
|
||||
}
|
||||
if resState.Hook || ignore.Ignore(obj) {
|
||||
// For resource hooks, don't store sync status, and do not affect overall sync status
|
||||
@@ -466,7 +404,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
resState.Status = v1alpha1.SyncStatusCodeOutOfSync
|
||||
// we ignore the status if the obj needs pruning AND we have the annotation
|
||||
needsPruning := targetObj == nil && liveObj != nil
|
||||
if !(needsPruning && resource.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreExtraneous")) {
|
||||
if !(needsPruning && resourceutil.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreExtraneous")) {
|
||||
syncCode = v1alpha1.SyncStatusCodeOutOfSync
|
||||
}
|
||||
} else {
|
||||
@@ -511,7 +449,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
ts.AddCheckpoint("sync_ms")
|
||||
|
||||
healthStatus, err := health.SetApplicationHealth(resourceSummaries, GetLiveObjs(managedResources), resourceOverrides, func(obj *unstructured.Unstructured) bool {
|
||||
healthStatus, err := argohealth.SetApplicationHealth(resourceSummaries, GetLiveObjs(managedResources), resourceOverrides, func(obj *unstructured.Unstructured) bool {
|
||||
return !isSelfReferencedApp(app, kubeutil.GetObjectRef(obj))
|
||||
})
|
||||
|
||||
@@ -520,12 +458,12 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, project *ap
|
||||
}
|
||||
|
||||
compRes := comparisonResult{
|
||||
syncStatus: &syncStatus,
|
||||
healthStatus: healthStatus,
|
||||
resources: resourceSummaries,
|
||||
managedResources: managedResources,
|
||||
hooks: hooks,
|
||||
diffNormalizer: diffNormalizer,
|
||||
syncStatus: &syncStatus,
|
||||
healthStatus: healthStatus,
|
||||
resources: resourceSummaries,
|
||||
managedResources: managedResources,
|
||||
reconciliationResult: reconciliation,
|
||||
diffNormalizer: diffNormalizer,
|
||||
}
|
||||
if manifestInfo != nil {
|
||||
compRes.appSourceType = v1alpha1.ApplicationSourceType(manifestInfo.SourceType)
|
||||
@@ -567,7 +505,7 @@ func (m *appStateManager) persistRevisionHistory(app *v1alpha1.Application, revi
|
||||
return err
|
||||
}
|
||||
|
||||
// NewAppStateManager creates new instance of Ksonnet app comparator
|
||||
// NewAppStateManager creates new instance of AppStateManager
|
||||
func NewAppStateManager(
|
||||
db db.ArgoDB,
|
||||
appclientset appclientset.Interface,
|
||||
|
||||
@@ -4,6 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
synccommon "github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
. "github.com/argoproj/gitops-engine/pkg/utils/testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -14,7 +18,6 @@ import (
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
)
|
||||
|
||||
// TestCompareAppStateEmpty tests comparison when both git and live have no objects
|
||||
@@ -45,7 +48,7 @@ func TestCompareAppStateMissing(t *testing.T) {
|
||||
data := fakeData{
|
||||
apps: []runtime.Object{app},
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{test.PodManifest},
|
||||
Manifests: []string{PodManifest},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
@@ -64,7 +67,7 @@ func TestCompareAppStateMissing(t *testing.T) {
|
||||
|
||||
// TestCompareAppStateExtra tests when there is an extra object in live but not defined in git
|
||||
func TestCompareAppStateExtra(t *testing.T) {
|
||||
pod := test.NewPod()
|
||||
pod := NewPod()
|
||||
pod.SetNamespace(test.FakeDestNamespace)
|
||||
app := newFakeApp()
|
||||
key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name}
|
||||
@@ -91,8 +94,8 @@ func TestCompareAppStateExtra(t *testing.T) {
|
||||
// TestCompareAppStateHook checks that hooks are detected during manifest generation, and not
|
||||
// considered as part of resources when assessing Synced status
|
||||
func TestCompareAppStateHook(t *testing.T) {
|
||||
pod := test.NewPod()
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"})
|
||||
pod := NewPod()
|
||||
pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
|
||||
podBytes, _ := json.Marshal(pod)
|
||||
app := newFakeApp()
|
||||
data := fakeData{
|
||||
@@ -111,13 +114,13 @@ func TestCompareAppStateHook(t *testing.T) {
|
||||
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
|
||||
assert.Equal(t, 0, len(compRes.resources))
|
||||
assert.Equal(t, 0, len(compRes.managedResources))
|
||||
assert.Equal(t, 1, len(compRes.hooks))
|
||||
assert.Equal(t, 1, len(compRes.reconciliationResult.Hooks))
|
||||
assert.Equal(t, 0, len(app.Status.Conditions))
|
||||
}
|
||||
|
||||
// checks that ignore resources are detected, but excluded from status
|
||||
func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
|
||||
pod := test.NewPod()
|
||||
pod := NewPod()
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreExtraneous"})
|
||||
app := newFakeApp()
|
||||
data := fakeData{
|
||||
@@ -143,8 +146,8 @@ func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) {
|
||||
|
||||
// TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git
|
||||
func TestCompareAppStateExtraHook(t *testing.T) {
|
||||
pod := test.NewPod()
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"})
|
||||
pod := NewPod()
|
||||
pod.SetAnnotations(map[string]string{synccommon.AnnotationKeyHook: "PreSync"})
|
||||
pod.SetNamespace(test.FakeDestNamespace)
|
||||
app := newFakeApp()
|
||||
key := kube.ResourceKey{Group: "", Kind: "Pod", Namespace: test.FakeDestNamespace, Name: app.Name}
|
||||
@@ -166,7 +169,7 @@ func TestCompareAppStateExtraHook(t *testing.T) {
|
||||
assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status)
|
||||
assert.Equal(t, 1, len(compRes.resources))
|
||||
assert.Equal(t, 1, len(compRes.managedResources))
|
||||
assert.Equal(t, 0, len(compRes.hooks))
|
||||
assert.Equal(t, 0, len(compRes.reconciliationResult.Hooks))
|
||||
assert.Equal(t, 0, len(app.Status.Conditions))
|
||||
}
|
||||
|
||||
@@ -177,16 +180,22 @@ func toJSON(t *testing.T, obj *unstructured.Unstructured) string {
|
||||
}
|
||||
|
||||
func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
|
||||
obj1 := test.NewPod()
|
||||
obj1 := NewPod()
|
||||
obj1.SetNamespace(test.FakeDestNamespace)
|
||||
obj2 := test.NewPod()
|
||||
obj3 := test.NewPod()
|
||||
obj2 := NewPod()
|
||||
obj3 := NewPod()
|
||||
obj3.SetNamespace("kube-system")
|
||||
obj4 := NewPod()
|
||||
obj4.SetGenerateName("my-pod")
|
||||
obj4.SetName("")
|
||||
obj5 := NewPod()
|
||||
obj5.SetName("")
|
||||
obj5.SetGenerateName("my-pod")
|
||||
|
||||
app := newFakeApp()
|
||||
data := fakeData{
|
||||
manifestResponse: &apiclient.ManifestResponse{
|
||||
Manifests: []string{toJSON(t, obj1), toJSON(t, obj2), toJSON(t, obj3)},
|
||||
Manifests: []string{toJSON(t, obj1), toJSON(t, obj2), toJSON(t, obj3), toJSON(t, obj4), toJSON(t, obj5)},
|
||||
Namespace: test.FakeDestNamespace,
|
||||
Server: test.FakeClusterURL,
|
||||
Revision: "abc123",
|
||||
@@ -204,7 +213,7 @@ func TestCompareAppStateDuplicatedNamespacedResources(t *testing.T) {
|
||||
assert.NotNil(t, app.Status.Conditions[0].LastTransitionTime)
|
||||
assert.Equal(t, argoappv1.ApplicationConditionRepeatedResourceWarning, app.Status.Conditions[0].Type)
|
||||
assert.Equal(t, "Resource /Pod/fake-dest-ns/my-pod appeared 2 times among application resources.", app.Status.Conditions[0].Message)
|
||||
assert.Equal(t, 2, len(compRes.resources))
|
||||
assert.Equal(t, 4, len(compRes.resources))
|
||||
}
|
||||
|
||||
var defaultProj = argoappv1.AppProject{
|
||||
@@ -250,7 +259,7 @@ func TestSetHealth(t *testing.T) {
|
||||
|
||||
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
|
||||
|
||||
assert.Equal(t, compRes.healthStatus.Status, argoappv1.HealthStatusHealthy)
|
||||
assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy)
|
||||
}
|
||||
|
||||
func TestSetHealthSelfReferencedApp(t *testing.T) {
|
||||
@@ -282,7 +291,7 @@ func TestSetHealthSelfReferencedApp(t *testing.T) {
|
||||
|
||||
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
|
||||
|
||||
assert.Equal(t, compRes.healthStatus.Status, argoappv1.HealthStatusHealthy)
|
||||
assert.Equal(t, compRes.healthStatus.Status, health.HealthStatusHealthy)
|
||||
}
|
||||
|
||||
func TestSetManagedResourcesWithOrphanedResources(t *testing.T) {
|
||||
@@ -352,7 +361,7 @@ func TestReturnUnknownComparisonStateOnSettingLoadError(t *testing.T) {
|
||||
|
||||
compRes := ctrl.appStateManager.CompareAppState(app, &defaultProj, "", app.Spec.Source, false, nil)
|
||||
|
||||
assert.Equal(t, argoappv1.HealthStatusUnknown, compRes.healthStatus.Status)
|
||||
assert.Equal(t, health.HealthStatusUnknown, compRes.healthStatus.Status)
|
||||
assert.Equal(t, argoappv1.SyncStatusCodeUnknown, compRes.syncStatus.Status)
|
||||
}
|
||||
|
||||
@@ -385,13 +394,6 @@ func TestSetManagedResourcesKnownOrphanedResourceExceptions(t *testing.T) {
|
||||
assert.Equal(t, "guestbook", tree.OrphanedNodes[0].Name)
|
||||
}
|
||||
|
||||
func Test_comparisonResult_obs(t *testing.T) {
|
||||
assert.Len(t, (&comparisonResult{}).targetObjs(), 0)
|
||||
assert.Len(t, (&comparisonResult{managedResources: []managedResource{{}}}).targetObjs(), 0)
|
||||
assert.Len(t, (&comparisonResult{managedResources: []managedResource{{Target: test.NewPod()}}}).targetObjs(), 1)
|
||||
assert.Len(t, (&comparisonResult{hooks: []*unstructured.Unstructured{{}}}).targetObjs(), 1)
|
||||
}
|
||||
|
||||
func Test_appStateManager_persistRevisionHistory(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
ctrl := newFakeController(&fakeData{
|
||||
|
||||
@@ -3,66 +3,27 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/sync"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
listersv1alpha1 "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/argo"
|
||||
"github.com/argoproj/argo-cd/util/health"
|
||||
"github.com/argoproj/argo-cd/util/hook"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/lua"
|
||||
"github.com/argoproj/argo-cd/util/rand"
|
||||
"github.com/argoproj/argo-cd/util/resource"
|
||||
)
|
||||
|
||||
const (
|
||||
crdReadinessTimeout = time.Duration(3) * time.Second
|
||||
)
|
||||
|
||||
var syncIdPrefix uint64 = 0
|
||||
|
||||
type syncContext struct {
|
||||
resourceOverrides map[string]v1alpha1.ResourceOverride
|
||||
appName string
|
||||
proj *v1alpha1.AppProject
|
||||
compareResult *comparisonResult
|
||||
config *rest.Config
|
||||
rawConfig *rest.Config
|
||||
dynamicIf dynamic.Interface
|
||||
disco discovery.DiscoveryInterface
|
||||
extensionsclientset *clientset.Clientset
|
||||
kubectl kube.Kubectl
|
||||
namespace string
|
||||
server string
|
||||
syncOp *v1alpha1.SyncOperation
|
||||
syncRes *v1alpha1.SyncOperationResult
|
||||
syncResources []v1alpha1.SyncOperationResource
|
||||
opState *v1alpha1.OperationState
|
||||
log *log.Entry
|
||||
// lock to protect concurrent updates of the result list
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha1.OperationState) {
|
||||
// Sync requests might be requested with ambiguous revisions (e.g. master, HEAD, v1.2.3).
|
||||
// This can change meaning when resuming operations (e.g a hook sync). After calculating a
|
||||
@@ -72,11 +33,10 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
var revision string
|
||||
var syncOp v1alpha1.SyncOperation
|
||||
var syncRes *v1alpha1.SyncOperationResult
|
||||
var syncResources []v1alpha1.SyncOperationResource
|
||||
var source v1alpha1.ApplicationSource
|
||||
|
||||
if state.Operation.Sync == nil {
|
||||
state.Phase = v1alpha1.OperationFailed
|
||||
state.Phase = common.OperationFailed
|
||||
state.Message = "Invalid operation request: no operation specified"
|
||||
return
|
||||
}
|
||||
@@ -88,7 +48,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
// rollback case
|
||||
source = *state.Operation.Sync.Source
|
||||
}
|
||||
syncResources = syncOp.Resources
|
||||
|
||||
if state.SyncResult != nil {
|
||||
syncRes = state.SyncResult
|
||||
revision = state.SyncResult.Revision
|
||||
@@ -110,7 +70,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
|
||||
proj, err := argo.GetAppProject(&app.Spec, listersv1alpha1.NewAppProjectLister(m.projInformer.GetIndexer()), m.namespace)
|
||||
if err != nil {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to load application project: %v", err)
|
||||
return
|
||||
}
|
||||
@@ -122,7 +82,7 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
v1alpha1.ApplicationConditionComparisonError: true,
|
||||
v1alpha1.ApplicationConditionInvalidSpecError: true,
|
||||
}); len(errConditions) > 0 {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Phase = common.OperationError
|
||||
state.Message = argo.FormatAppConditions(errConditions)
|
||||
return
|
||||
}
|
||||
@@ -133,703 +93,95 @@ func (m *appStateManager) SyncAppState(app *v1alpha1.Application, state *v1alpha
|
||||
|
||||
clst, err := m.db.GetCluster(context.Background(), app.Spec.Destination.Server)
|
||||
if err != nil {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Phase = common.OperationError
|
||||
state.Message = err.Error()
|
||||
return
|
||||
}
|
||||
|
||||
rawConfig := clst.RawRestConfig()
|
||||
restConfig := metrics.AddMetricsTransportWrapper(m.metricsServer, app, clst.RESTConfig())
|
||||
dynamicIf, err := dynamic.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to initialize dynamic client: %v", err)
|
||||
return
|
||||
}
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(restConfig)
|
||||
if err != nil {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to initialize discovery client: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
extensionsclientset, err := clientset.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to initialize extensions client: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
resourceOverrides, err := m.settingsMgr.GetResourceOverrides()
|
||||
if err != nil {
|
||||
state.Phase = v1alpha1.OperationError
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("Failed to load resource overrides: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
atomic.AddUint64(&syncIdPrefix, 1)
|
||||
syncId := fmt.Sprintf("%05d-%s", syncIdPrefix, rand.RandString(5))
|
||||
syncCtx := syncContext{
|
||||
resourceOverrides: resourceOverrides,
|
||||
appName: app.Name,
|
||||
proj: proj,
|
||||
compareResult: compareResult,
|
||||
config: restConfig,
|
||||
rawConfig: clst.RawRestConfig(),
|
||||
dynamicIf: dynamicIf,
|
||||
disco: disco,
|
||||
extensionsclientset: extensionsclientset,
|
||||
kubectl: m.kubectl,
|
||||
namespace: app.Spec.Destination.Namespace,
|
||||
server: app.Spec.Destination.Server,
|
||||
syncOp: &syncOp,
|
||||
syncRes: syncRes,
|
||||
syncResources: syncResources,
|
||||
opState: state,
|
||||
log: log.WithFields(log.Fields{"application": app.Name, "syncId": syncId}),
|
||||
|
||||
logEntry := log.WithFields(log.Fields{"application": app.Name, "syncId": syncId})
|
||||
initialResourcesRes := make([]common.ResourceSyncResult, 0)
|
||||
for i, res := range syncRes.Resources {
|
||||
key := kube.ResourceKey{Group: res.Group, Kind: res.Kind, Namespace: res.Namespace, Name: res.Name}
|
||||
initialResourcesRes = append(initialResourcesRes, common.ResourceSyncResult{
|
||||
ResourceKey: key,
|
||||
Message: res.Message,
|
||||
Status: res.Status,
|
||||
HookPhase: res.HookPhase,
|
||||
HookType: res.HookType,
|
||||
SyncPhase: res.SyncPhase,
|
||||
Version: res.Version,
|
||||
Order: i + 1,
|
||||
})
|
||||
}
|
||||
syncCtx, err := sync.NewSyncContext(compareResult.syncStatus.Revision, compareResult.reconciliationResult, restConfig, rawConfig, m.kubectl, app.Spec.Destination.Namespace, logEntry,
|
||||
sync.WithHealthOverride(lua.ResourceHealthOverrides(resourceOverrides)),
|
||||
sync.WithPermissionValidator(func(un *unstructured.Unstructured, res *v1.APIResource) error {
|
||||
if !proj.IsGroupKindPermitted(un.GroupVersionKind().GroupKind(), res.Namespaced) {
|
||||
return fmt.Errorf("Resource %s:%s is not permitted in project %s.", un.GroupVersionKind().Group, un.GroupVersionKind().Kind, proj.Name)
|
||||
}
|
||||
if res.Namespaced && !proj.IsDestinationPermitted(v1alpha1.ApplicationDestination{Namespace: un.GetNamespace(), Server: app.Spec.Destination.Server}) {
|
||||
return fmt.Errorf("namespace %v is not permitted in project '%s'", un.GetNamespace(), proj.Name)
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
sync.WithOperationSettings(syncOp.DryRun, syncOp.Prune, syncOp.SyncStrategy.Force(), syncOp.IsApplyStrategy() || len(syncOp.Resources) > 0),
|
||||
sync.WithInitialState(state.Phase, state.Message, initialResourcesRes),
|
||||
sync.WithResourcesFilter(func(key kube.ResourceKey, target *unstructured.Unstructured, live *unstructured.Unstructured) bool {
|
||||
return len(syncOp.Resources) == 0 || argo.ContainsSyncResource(key.Name, schema.GroupVersionKind{Kind: key.Kind, Group: key.Group}, syncOp.Resources)
|
||||
}),
|
||||
sync.WithManifestValidation(!syncOp.SyncOptions.HasOption("Validate=false")),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("failed to record sync to history: %v", err)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
if state.Phase == v1alpha1.OperationTerminating {
|
||||
syncCtx.terminate()
|
||||
if state.Phase == common.OperationTerminating {
|
||||
syncCtx.Terminate()
|
||||
} else {
|
||||
syncCtx.sync()
|
||||
syncCtx.Sync()
|
||||
}
|
||||
var resState []common.ResourceSyncResult
|
||||
state.Phase, state.Message, resState = syncCtx.GetState()
|
||||
state.SyncResult.Resources = nil
|
||||
for _, res := range resState {
|
||||
state.SyncResult.Resources = append(state.SyncResult.Resources, &v1alpha1.ResourceResult{
|
||||
HookType: res.HookType,
|
||||
Group: res.ResourceKey.Group,
|
||||
Kind: res.ResourceKey.Kind,
|
||||
Namespace: res.ResourceKey.Namespace,
|
||||
Name: res.ResourceKey.Name,
|
||||
Version: res.Version,
|
||||
SyncPhase: res.SyncPhase,
|
||||
HookPhase: res.HookPhase,
|
||||
Status: res.Status,
|
||||
Message: res.Message,
|
||||
})
|
||||
}
|
||||
|
||||
syncCtx.log.WithField("duration", time.Since(start)).Info("sync/terminate complete")
|
||||
logEntry.WithField("duration", time.Since(start)).Info("sync/terminate complete")
|
||||
|
||||
if !syncOp.DryRun && !syncCtx.isSelectiveSync() && syncCtx.opState.Phase.Successful() {
|
||||
if !syncOp.DryRun && len(syncOp.Resources) == 0 && state.Phase.Successful() {
|
||||
err := m.persistRevisionHistory(app, compareResult.syncStatus.Revision, source)
|
||||
if err != nil {
|
||||
syncCtx.setOperationPhase(v1alpha1.OperationError, fmt.Sprintf("failed to record sync to history: %v", err))
|
||||
state.Phase = common.OperationError
|
||||
state.Message = fmt.Sprintf("failed to record sync to history: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sync has performs the actual apply or hook based sync
|
||||
func (sc *syncContext) sync() {
|
||||
sc.log.WithFields(log.Fields{"isSelectiveSync": sc.isSelectiveSync(), "skipHooks": sc.skipHooks(), "started": sc.started()}).Info("syncing")
|
||||
tasks, ok := sc.getSyncTasks()
|
||||
if !ok {
|
||||
sc.setOperationPhase(v1alpha1.OperationFailed, "one or more synchronization tasks are not valid")
|
||||
return
|
||||
}
|
||||
|
||||
sc.log.WithFields(log.Fields{"tasks": tasks, "isSelectiveSync": sc.isSelectiveSync()}).Info("tasks")
|
||||
|
||||
// Perform a `kubectl apply --dry-run` against all the manifests. This will detect most (but
|
||||
// not all) validation issues with the user's manifests (e.g. will detect syntax issues, but
|
||||
// will not not detect if they are mutating immutable fields). If anything fails, we will refuse
|
||||
// to perform the sync. we only wish to do this once per operation, performing additional dry-runs
|
||||
// is harmless, but redundant. The indicator we use to detect if we have already performed
|
||||
// the dry-run for this operation, is if the resource or hook list is empty.
|
||||
if !sc.started() {
|
||||
sc.log.Debug("dry-run")
|
||||
if sc.runTasks(tasks, true) == failed {
|
||||
sc.setOperationPhase(v1alpha1.OperationFailed, "one or more objects failed to apply (dry run)")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// update status of any tasks that are running, note that this must exclude pruning tasks
|
||||
for _, task := range tasks.Filter(func(t *syncTask) bool {
|
||||
// just occasionally, you can be running yet not have a live resource
|
||||
return t.running() && t.liveObj != nil
|
||||
}) {
|
||||
if task.isHook() {
|
||||
// update the hook's result
|
||||
operationState, message, err := sc.getOperationPhase(task.liveObj)
|
||||
if err != nil {
|
||||
sc.setResourceResult(task, "", v1alpha1.OperationError, fmt.Sprintf("failed to get resource health: %v", err))
|
||||
} else {
|
||||
sc.setResourceResult(task, "", operationState, message)
|
||||
|
||||
// maybe delete the hook
|
||||
if task.needsDeleting() {
|
||||
err := sc.deleteResource(task)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
sc.setResourceResult(task, "", v1alpha1.OperationError, fmt.Sprintf("failed to delete resource: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// this must be calculated on the live object
|
||||
healthStatus, err := health.GetResourceHealth(task.liveObj, sc.resourceOverrides)
|
||||
if err == nil {
|
||||
log.WithFields(log.Fields{"task": task, "healthStatus": healthStatus}).Debug("attempting to update health of running task")
|
||||
if healthStatus == nil {
|
||||
// some objects (e.g. secret) do not have health, and they automatically success
|
||||
sc.setResourceResult(task, task.syncStatus, v1alpha1.OperationSucceeded, task.message)
|
||||
} else {
|
||||
switch healthStatus.Status {
|
||||
case v1alpha1.HealthStatusHealthy:
|
||||
sc.setResourceResult(task, task.syncStatus, v1alpha1.OperationSucceeded, healthStatus.Message)
|
||||
case v1alpha1.HealthStatusDegraded:
|
||||
sc.setResourceResult(task, task.syncStatus, v1alpha1.OperationFailed, healthStatus.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (a) we are multi-step and we have any running tasks,
|
||||
// or (b) there are any running hooks,
|
||||
// then wait...
|
||||
multiStep := tasks.multiStep()
|
||||
if tasks.Any(func(t *syncTask) bool { return (multiStep || t.isHook()) && t.running() }) {
|
||||
sc.setOperationPhase(v1alpha1.OperationRunning, "one or more tasks are running")
|
||||
return
|
||||
}
|
||||
|
||||
// syncFailTasks only run during failure, so separate them from regular tasks
|
||||
syncFailTasks, tasks := tasks.Split(func(t *syncTask) bool { return t.phase == v1alpha1.SyncPhaseSyncFail })
|
||||
|
||||
// if there are any completed but unsuccessful tasks, sync is a failure.
|
||||
if tasks.Any(func(t *syncTask) bool { return t.completed() && !t.successful() }) {
|
||||
sc.setOperationFailed(syncFailTasks, "one or more synchronization tasks completed unsuccessfully")
|
||||
return
|
||||
}
|
||||
|
||||
sc.log.WithFields(log.Fields{"tasks": tasks}).Debug("filtering out non-pending tasks")
|
||||
// remove tasks that are completed, we can assume that there are no running tasks
|
||||
tasks = tasks.Filter(func(t *syncTask) bool { return t.pending() })
|
||||
|
||||
// If no sync tasks were generated (e.g., in case all application manifests have been removed),
|
||||
// the sync operation is successful.
|
||||
if len(tasks) == 0 {
|
||||
sc.setOperationPhase(v1alpha1.OperationSucceeded, "successfully synced (no more tasks)")
|
||||
return
|
||||
}
|
||||
|
||||
// remove any tasks not in this wave
|
||||
phase := tasks.phase()
|
||||
wave := tasks.wave()
|
||||
|
||||
// if it is the last phase/wave and the only remaining tasks are non-hooks, the we are successful
|
||||
// EVEN if those objects subsequently degraded
|
||||
// This handles the common case where neither hooks or waves are used and a sync equates to simply an (asynchronous) kubectl apply of manifests, which succeeds immediately.
|
||||
complete := !tasks.Any(func(t *syncTask) bool { return t.phase != phase || wave != t.wave() || t.isHook() })
|
||||
|
||||
sc.log.WithFields(log.Fields{"phase": phase, "wave": wave, "tasks": tasks, "syncFailTasks": syncFailTasks}).Debug("filtering tasks in correct phase and wave")
|
||||
tasks = tasks.Filter(func(t *syncTask) bool { return t.phase == phase && t.wave() == wave })
|
||||
|
||||
sc.setOperationPhase(v1alpha1.OperationRunning, "one or more tasks are running")
|
||||
|
||||
sc.log.WithFields(log.Fields{"tasks": tasks}).Debug("wet-run")
|
||||
runState := sc.runTasks(tasks, false)
|
||||
switch runState {
|
||||
case failed:
|
||||
sc.setOperationFailed(syncFailTasks, "one or more objects failed to apply")
|
||||
case successful:
|
||||
if complete {
|
||||
sc.setOperationPhase(v1alpha1.OperationSucceeded, "successfully synced (all tasks run)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *syncContext) setOperationFailed(syncFailTasks syncTasks, message string) {
|
||||
if len(syncFailTasks) > 0 {
|
||||
// if all the failure hooks are completed, don't run them again, and mark the sync as failed
|
||||
if syncFailTasks.All(func(task *syncTask) bool { return task.completed() }) {
|
||||
sc.setOperationPhase(v1alpha1.OperationFailed, message)
|
||||
return
|
||||
}
|
||||
// otherwise, we need to start the failure hooks, and then return without setting
|
||||
// the phase, so we make sure we have at least one more sync
|
||||
sc.log.WithFields(log.Fields{"syncFailTasks": syncFailTasks}).Debug("running sync fail tasks")
|
||||
if sc.runTasks(syncFailTasks, false) == failed {
|
||||
sc.setOperationPhase(v1alpha1.OperationFailed, message)
|
||||
}
|
||||
} else {
|
||||
sc.setOperationPhase(v1alpha1.OperationFailed, message)
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *syncContext) started() bool {
|
||||
return len(sc.syncRes.Resources) > 0
|
||||
}
|
||||
|
||||
func (sc *syncContext) isSelectiveSync() bool {
|
||||
// we've selected no resources
|
||||
if sc.syncResources == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// map both lists into string
|
||||
var a []string
|
||||
for _, r := range sc.compareResult.resources {
|
||||
if !r.Hook {
|
||||
a = append(a, fmt.Sprintf("%s:%s:%s", r.Group, r.Kind, r.Name))
|
||||
}
|
||||
}
|
||||
sort.Strings(a)
|
||||
|
||||
var b []string
|
||||
for _, r := range sc.syncResources {
|
||||
b = append(b, fmt.Sprintf("%s:%s:%s", r.Group, r.Kind, r.Name))
|
||||
}
|
||||
sort.Strings(b)
|
||||
|
||||
return !reflect.DeepEqual(a, b)
|
||||
}
|
||||
|
||||
// this essentially enforces the old "apply" behaviour
|
||||
func (sc *syncContext) skipHooks() bool {
|
||||
// All objects passed a `kubectl apply --dry-run`, so we are now ready to actually perform the sync.
|
||||
// default sync strategy to hook if no strategy
|
||||
return sc.syncOp.IsApplyStrategy() || sc.isSelectiveSync()
|
||||
}
|
||||
|
||||
func (sc *syncContext) containsResource(resourceState managedResource) bool {
|
||||
return !sc.isSelectiveSync() ||
|
||||
(resourceState.Live != nil && argo.ContainsSyncResource(resourceState.Live.GetName(), resourceState.Live.GroupVersionKind(), sc.syncResources)) ||
|
||||
(resourceState.Target != nil && argo.ContainsSyncResource(resourceState.Target.GetName(), resourceState.Target.GroupVersionKind(), sc.syncResources))
|
||||
}
|
||||
|
||||
// generates the list of sync tasks we will be performing during this sync.
|
||||
func (sc *syncContext) getSyncTasks() (_ syncTasks, successful bool) {
|
||||
resourceTasks := syncTasks{}
|
||||
successful = true
|
||||
|
||||
for _, resource := range sc.compareResult.managedResources {
|
||||
if !sc.containsResource(resource) {
|
||||
sc.log.WithFields(log.Fields{"group": resource.Group, "kind": resource.Kind, "name": resource.Name}).
|
||||
Debug("skipping")
|
||||
continue
|
||||
}
|
||||
|
||||
obj := obj(resource.Target, resource.Live)
|
||||
|
||||
// this creates garbage tasks
|
||||
if hook.IsHook(obj) {
|
||||
sc.log.WithFields(log.Fields{"group": obj.GroupVersionKind().Group, "kind": obj.GetKind(), "namespace": obj.GetNamespace(), "name": obj.GetName()}).
|
||||
Debug("skipping hook")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, phase := range syncPhases(obj) {
|
||||
resourceTasks = append(resourceTasks, &syncTask{phase: phase, targetObj: resource.Target, liveObj: resource.Live})
|
||||
}
|
||||
}
|
||||
|
||||
sc.log.WithFields(log.Fields{"resourceTasks": resourceTasks}).Debug("tasks from managed resources")
|
||||
|
||||
hookTasks := syncTasks{}
|
||||
if !sc.skipHooks() {
|
||||
for _, obj := range sc.compareResult.hooks {
|
||||
for _, phase := range syncPhases(obj) {
|
||||
// Hook resources names are deterministic, whether they are defined by the user (metadata.name),
|
||||
// or formulated at the time of the operation (metadata.generateName). If user specifies
|
||||
// metadata.generateName, then we will generate a formulated metadata.name before submission.
|
||||
targetObj := obj.DeepCopy()
|
||||
if targetObj.GetName() == "" {
|
||||
var syncRevision string
|
||||
if len(sc.syncRes.Revision) >= 8 {
|
||||
syncRevision = sc.syncRes.Revision[0:7]
|
||||
} else {
|
||||
syncRevision = sc.syncRes.Revision
|
||||
}
|
||||
postfix := strings.ToLower(fmt.Sprintf("%s-%s-%d", syncRevision, phase, sc.opState.StartedAt.UTC().Unix()))
|
||||
generateName := obj.GetGenerateName()
|
||||
targetObj.SetName(fmt.Sprintf("%s%s", generateName, postfix))
|
||||
}
|
||||
|
||||
hookTasks = append(hookTasks, &syncTask{phase: phase, targetObj: targetObj})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sc.log.WithFields(log.Fields{"hookTasks": hookTasks}).Debug("tasks from hooks")
|
||||
|
||||
tasks := resourceTasks
|
||||
tasks = append(tasks, hookTasks...)
|
||||
|
||||
// enrich target objects with the namespace
|
||||
for _, task := range tasks {
|
||||
if task.targetObj == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if task.targetObj.GetNamespace() == "" {
|
||||
// If target object's namespace is empty, we set namespace in the object. We do
|
||||
// this even though it might be a cluster-scoped resource. This prevents any
|
||||
// possibility of the resource from unintentionally becoming created in the
|
||||
// namespace during the `kubectl apply`
|
||||
task.targetObj = task.targetObj.DeepCopy()
|
||||
task.targetObj.SetNamespace(sc.namespace)
|
||||
}
|
||||
}
|
||||
|
||||
// enrich task with live obj
|
||||
for _, task := range tasks {
|
||||
if task.targetObj == nil || task.liveObj != nil {
|
||||
continue
|
||||
}
|
||||
task.liveObj = sc.liveObj(task.targetObj)
|
||||
}
|
||||
|
||||
// enrich tasks with the result
|
||||
for _, task := range tasks {
|
||||
_, result := sc.syncRes.Resources.Find(task.group(), task.kind(), task.namespace(), task.name(), task.phase)
|
||||
if result != nil {
|
||||
task.syncStatus = result.Status
|
||||
task.operationState = result.HookPhase
|
||||
task.message = result.Message
|
||||
}
|
||||
}
|
||||
|
||||
// check permissions
|
||||
for _, task := range tasks {
|
||||
serverRes, err := kube.ServerResourceForGroupVersionKind(sc.disco, task.groupVersionKind())
|
||||
if err != nil {
|
||||
// Special case for custom resources: if CRD is not yet known by the K8s API server,
|
||||
// and the CRD is part of this sync or the resource is annotated with SkipDryRunOnMissingResource=true,
|
||||
// then skip verification during `kubectl apply --dry-run` since we expect the CRD
|
||||
// to be created during app synchronization.
|
||||
skipDryRunOnMissingResource := resource.HasAnnotationOption(task.targetObj, common.AnnotationSyncOptions, "SkipDryRunOnMissingResource=true")
|
||||
if apierr.IsNotFound(err) && (skipDryRunOnMissingResource || sc.hasCRDOfGroupKind(task.group(), task.kind())) {
|
||||
sc.log.WithFields(log.Fields{"task": task}).Debug("skip dry-run for custom resource")
|
||||
task.skipDryRun = true
|
||||
} else {
|
||||
sc.setResourceResult(task, v1alpha1.ResultCodeSyncFailed, "", err.Error())
|
||||
successful = false
|
||||
}
|
||||
} else {
|
||||
if !sc.proj.IsGroupKindPermitted(schema.GroupKind{Group: task.group(), Kind: task.kind()}, serverRes.Namespaced) {
|
||||
sc.setResourceResult(task, v1alpha1.ResultCodeSyncFailed, "", fmt.Sprintf("Resource %s:%s is not permitted in project %s.", task.group(), task.kind(), sc.proj.Name))
|
||||
successful = false
|
||||
}
|
||||
if serverRes.Namespaced && !sc.proj.IsDestinationPermitted(v1alpha1.ApplicationDestination{Namespace: task.namespace(), Server: sc.server}) {
|
||||
sc.setResourceResult(task, v1alpha1.ResultCodeSyncFailed, "", fmt.Sprintf("namespace %v is not permitted in project '%s'", task.namespace(), sc.proj.Name))
|
||||
successful = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(tasks)
|
||||
|
||||
return tasks, successful
|
||||
}
|
||||
|
||||
func obj(a, b *unstructured.Unstructured) *unstructured.Unstructured {
|
||||
if a != nil {
|
||||
return a
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *syncContext) liveObj(obj *unstructured.Unstructured) *unstructured.Unstructured {
|
||||
for _, resource := range sc.compareResult.managedResources {
|
||||
if resource.Group == obj.GroupVersionKind().Group &&
|
||||
resource.Kind == obj.GetKind() &&
|
||||
// cluster scoped objects will not have a namespace, even if the user has defined it
|
||||
(resource.Namespace == "" || resource.Namespace == obj.GetNamespace()) &&
|
||||
resource.Name == obj.GetName() {
|
||||
return resource.Live
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sc *syncContext) setOperationPhase(phase v1alpha1.OperationPhase, message string) {
|
||||
if sc.opState.Phase != phase || sc.opState.Message != message {
|
||||
sc.log.Infof("Updating operation state. phase: %s -> %s, message: '%s' -> '%s'", sc.opState.Phase, phase, sc.opState.Message, message)
|
||||
}
|
||||
sc.opState.Phase = phase
|
||||
sc.opState.Message = message
|
||||
}
|
||||
|
||||
// ensureCRDReady waits until specified CRD is ready (established condition is true). Method is best effort - it does not fail even if CRD is not ready without timeout.
|
||||
func (sc *syncContext) ensureCRDReady(name string) {
|
||||
_ = wait.PollImmediate(time.Duration(100)*time.Millisecond, crdReadinessTimeout, func() (bool, error) {
|
||||
crd, err := sc.extensionsclientset.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, condition := range crd.Status.Conditions {
|
||||
if condition.Type == v1beta1.Established {
|
||||
return condition.Status == v1beta1.ConditionTrue, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
}
|
||||
|
||||
// applyObject performs a `kubectl apply` of a single resource
|
||||
func (sc *syncContext) applyObject(targetObj *unstructured.Unstructured, dryRun, force, validate bool) (v1alpha1.ResultCode, string) {
|
||||
message, err := sc.kubectl.ApplyResource(sc.rawConfig, targetObj, targetObj.GetNamespace(), dryRun, force, validate)
|
||||
if err != nil {
|
||||
return v1alpha1.ResultCodeSyncFailed, err.Error()
|
||||
}
|
||||
if kube.IsCRD(targetObj) && !dryRun {
|
||||
sc.ensureCRDReady(targetObj.GetName())
|
||||
}
|
||||
return v1alpha1.ResultCodeSynced, message
|
||||
}
|
||||
|
||||
// pruneObject deletes the object if both prune is true and dryRun is false. Otherwise appropriate message
|
||||
func (sc *syncContext) pruneObject(liveObj *unstructured.Unstructured, prune, dryRun bool) (v1alpha1.ResultCode, string) {
|
||||
if resource.HasAnnotationOption(liveObj, common.AnnotationSyncOptions, "Prune=false") {
|
||||
return v1alpha1.ResultCodePruneSkipped, "ignored (no prune)"
|
||||
} else if !prune {
|
||||
return v1alpha1.ResultCodePruneSkipped, "ignored (requires pruning)"
|
||||
} else {
|
||||
if dryRun {
|
||||
return v1alpha1.ResultCodePruned, "pruned (dry run)"
|
||||
} else {
|
||||
// Skip deletion if object is already marked for deletion, so we don't cause a resource update hotloop
|
||||
deletionTimestamp := liveObj.GetDeletionTimestamp()
|
||||
if deletionTimestamp == nil || deletionTimestamp.IsZero() {
|
||||
err := sc.kubectl.DeleteResource(sc.config, liveObj.GroupVersionKind(), liveObj.GetName(), liveObj.GetNamespace(), false)
|
||||
if err != nil {
|
||||
return v1alpha1.ResultCodeSyncFailed, err.Error()
|
||||
}
|
||||
}
|
||||
return v1alpha1.ResultCodePruned, "pruned"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *syncContext) hasCRDOfGroupKind(group string, kind string) bool {
|
||||
for _, obj := range sc.compareResult.targetObjs() {
|
||||
if kube.IsCRD(obj) {
|
||||
crdGroup, ok, err := unstructured.NestedString(obj.Object, "spec", "group")
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
crdKind, ok, err := unstructured.NestedString(obj.Object, "spec", "names", "kind")
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
if group == crdGroup && crdKind == kind {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// terminate looks for any running jobs/workflow hooks and deletes the resource
|
||||
func (sc *syncContext) terminate() {
|
||||
terminateSuccessful := true
|
||||
sc.log.Debug("terminating")
|
||||
tasks, _ := sc.getSyncTasks()
|
||||
for _, task := range tasks {
|
||||
if !task.isHook() || task.liveObj == nil {
|
||||
continue
|
||||
}
|
||||
phase, msg, err := sc.getOperationPhase(task.liveObj)
|
||||
if err != nil {
|
||||
sc.setOperationPhase(v1alpha1.OperationError, fmt.Sprintf("Failed to get hook health: %v", err))
|
||||
return
|
||||
}
|
||||
if phase == v1alpha1.OperationRunning {
|
||||
err := sc.deleteResource(task)
|
||||
if err != nil {
|
||||
sc.setResourceResult(task, "", v1alpha1.OperationFailed, fmt.Sprintf("Failed to delete: %v", err))
|
||||
terminateSuccessful = false
|
||||
} else {
|
||||
sc.setResourceResult(task, "", v1alpha1.OperationSucceeded, fmt.Sprintf("Deleted"))
|
||||
}
|
||||
} else {
|
||||
sc.setResourceResult(task, "", phase, msg)
|
||||
}
|
||||
}
|
||||
if terminateSuccessful {
|
||||
sc.setOperationPhase(v1alpha1.OperationFailed, "Operation terminated")
|
||||
} else {
|
||||
sc.setOperationPhase(v1alpha1.OperationError, "Operation termination had errors")
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *syncContext) deleteResource(task *syncTask) error {
|
||||
sc.log.WithFields(log.Fields{"task": task}).Debug("deleting resource")
|
||||
resIf, err := sc.getResourceIf(task)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
propagationPolicy := metav1.DeletePropagationForeground
|
||||
return resIf.Delete(task.name(), &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy})
|
||||
}
|
||||
|
||||
func (sc *syncContext) getResourceIf(task *syncTask) (dynamic.ResourceInterface, error) {
|
||||
apiResource, err := kube.ServerResourceForGroupVersionKind(sc.disco, task.groupVersionKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := kube.ToGroupVersionResource(task.groupVersionKind().GroupVersion().String(), apiResource)
|
||||
resIf := kube.ToResourceInterface(sc.dynamicIf, apiResource, res, task.namespace())
|
||||
return resIf, err
|
||||
}
|
||||
|
||||
var operationPhases = map[v1alpha1.ResultCode]v1alpha1.OperationPhase{
|
||||
v1alpha1.ResultCodeSynced: v1alpha1.OperationRunning,
|
||||
v1alpha1.ResultCodeSyncFailed: v1alpha1.OperationFailed,
|
||||
v1alpha1.ResultCodePruned: v1alpha1.OperationSucceeded,
|
||||
v1alpha1.ResultCodePruneSkipped: v1alpha1.OperationSucceeded,
|
||||
}
|
||||
|
||||
// tri-state
|
||||
type runState = int
|
||||
|
||||
const (
|
||||
successful = iota
|
||||
pending
|
||||
failed
|
||||
)
|
||||
|
||||
func (sc *syncContext) runTasks(tasks syncTasks, dryRun bool) runState {
|
||||
|
||||
dryRun = dryRun || sc.syncOp.DryRun
|
||||
|
||||
sc.log.WithFields(log.Fields{"numTasks": len(tasks), "dryRun": dryRun}).Debug("running tasks")
|
||||
|
||||
runState := successful
|
||||
var createTasks syncTasks
|
||||
var pruneTasks syncTasks
|
||||
|
||||
for _, task := range tasks {
|
||||
if task.isPrune() {
|
||||
pruneTasks = append(pruneTasks, task)
|
||||
} else {
|
||||
createTasks = append(createTasks, task)
|
||||
}
|
||||
}
|
||||
// prune first
|
||||
{
|
||||
var wg sync.WaitGroup
|
||||
for _, task := range pruneTasks {
|
||||
wg.Add(1)
|
||||
go func(t *syncTask) {
|
||||
defer wg.Done()
|
||||
logCtx := sc.log.WithFields(log.Fields{"dryRun": dryRun, "task": t})
|
||||
logCtx.Debug("pruning")
|
||||
result, message := sc.pruneObject(t.liveObj, sc.syncOp.Prune, dryRun)
|
||||
if result == v1alpha1.ResultCodeSyncFailed {
|
||||
runState = failed
|
||||
logCtx.WithField("message", message).Info("pruning failed")
|
||||
}
|
||||
if !dryRun || sc.syncOp.DryRun || result == v1alpha1.ResultCodeSyncFailed {
|
||||
sc.setResourceResult(t, result, operationPhases[result], message)
|
||||
}
|
||||
}(task)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// delete anything that need deleting
|
||||
if runState == successful && createTasks.Any(func(t *syncTask) bool { return t.needsDeleting() }) {
|
||||
var wg sync.WaitGroup
|
||||
for _, task := range createTasks.Filter(func(t *syncTask) bool { return t.needsDeleting() }) {
|
||||
wg.Add(1)
|
||||
go func(t *syncTask) {
|
||||
defer wg.Done()
|
||||
sc.log.WithFields(log.Fields{"dryRun": dryRun, "task": t}).Debug("deleting")
|
||||
if !dryRun {
|
||||
err := sc.deleteResource(t)
|
||||
if err != nil {
|
||||
// it is possible to get a race condition here, such that the resource does not exist when
|
||||
// delete is requested, we treat this as a nop
|
||||
if !apierr.IsNotFound(err) {
|
||||
runState = failed
|
||||
sc.setResourceResult(t, "", v1alpha1.OperationError, fmt.Sprintf("failed to delete resource: %v", err))
|
||||
}
|
||||
} else {
|
||||
// if there is anything that needs deleting, we are at best now in pending and
|
||||
// want to return and wait for sync to be invoked again
|
||||
runState = pending
|
||||
}
|
||||
}
|
||||
}(task)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
// finally create resources
|
||||
if runState == successful {
|
||||
processCreateTasks := func(tasks syncTasks) {
|
||||
var createWg sync.WaitGroup
|
||||
for _, task := range tasks {
|
||||
if dryRun && task.skipDryRun {
|
||||
continue
|
||||
}
|
||||
createWg.Add(1)
|
||||
go func(t *syncTask) {
|
||||
defer createWg.Done()
|
||||
logCtx := sc.log.WithFields(log.Fields{"dryRun": dryRun, "task": t})
|
||||
logCtx.Debug("applying")
|
||||
validate := !(sc.syncOp.SyncOptions.HasOption("Validate=false") || resource.HasAnnotationOption(t.targetObj, common.AnnotationSyncOptions, "Validate=false"))
|
||||
result, message := sc.applyObject(t.targetObj, dryRun, sc.syncOp.SyncStrategy.Force(), validate)
|
||||
if result == v1alpha1.ResultCodeSyncFailed {
|
||||
logCtx.WithField("message", message).Info("apply failed")
|
||||
runState = failed
|
||||
}
|
||||
if !dryRun || sc.syncOp.DryRun || result == v1alpha1.ResultCodeSyncFailed {
|
||||
sc.setResourceResult(t, result, operationPhases[result], message)
|
||||
}
|
||||
}(task)
|
||||
}
|
||||
createWg.Wait()
|
||||
}
|
||||
|
||||
var tasksGroup syncTasks
|
||||
for _, task := range createTasks {
|
||||
//Only wait if the type of the next task is different than the previous type
|
||||
if len(tasksGroup) > 0 && tasksGroup[0].targetObj.GetKind() != task.kind() {
|
||||
processCreateTasks(tasksGroup)
|
||||
tasksGroup = syncTasks{task}
|
||||
} else {
|
||||
tasksGroup = append(tasksGroup, task)
|
||||
}
|
||||
}
|
||||
if len(tasksGroup) > 0 {
|
||||
processCreateTasks(tasksGroup)
|
||||
}
|
||||
}
|
||||
return runState
|
||||
}
|
||||
|
||||
// setResourceResult sets a resource details in the SyncResult.Resources list
|
||||
func (sc *syncContext) setResourceResult(task *syncTask, syncStatus v1alpha1.ResultCode, operationState v1alpha1.OperationPhase, message string) {
|
||||
|
||||
task.syncStatus = syncStatus
|
||||
task.operationState = operationState
|
||||
// we always want to keep the latest message
|
||||
if message != "" {
|
||||
task.message = message
|
||||
}
|
||||
|
||||
sc.lock.Lock()
|
||||
defer sc.lock.Unlock()
|
||||
i, existing := sc.syncRes.Resources.Find(task.group(), task.kind(), task.namespace(), task.name(), task.phase)
|
||||
|
||||
res := v1alpha1.ResourceResult{
|
||||
Group: task.group(),
|
||||
Version: task.version(),
|
||||
Kind: task.kind(),
|
||||
Namespace: task.namespace(),
|
||||
Name: task.name(),
|
||||
Status: task.syncStatus,
|
||||
Message: task.message,
|
||||
HookType: task.hookType(),
|
||||
HookPhase: task.operationState,
|
||||
SyncPhase: task.phase,
|
||||
}
|
||||
|
||||
logCtx := sc.log.WithFields(log.Fields{"namespace": task.namespace(), "kind": task.kind(), "name": task.name(), "phase": task.phase})
|
||||
|
||||
if existing != nil {
|
||||
// update existing value
|
||||
if res.Status != existing.Status || res.HookPhase != existing.HookPhase || res.Message != existing.Message {
|
||||
logCtx.Infof("updating resource result, status: '%s' -> '%s', phase '%s' -> '%s', message '%s' -> '%s'",
|
||||
existing.Status, res.Status,
|
||||
existing.HookPhase, res.HookPhase,
|
||||
existing.Message, res.Message)
|
||||
}
|
||||
sc.syncRes.Resources[i] = &res
|
||||
} else {
|
||||
logCtx.Infof("adding resource result, status: '%s', phase: '%s', message: '%s'", res.Status, res.HookPhase, res.Message)
|
||||
sc.syncRes.Resources = append(sc.syncRes.Resources, &res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/health"
|
||||
)
|
||||
|
||||
// getOperationPhase returns a hook status from an _live_ unstructured object
|
||||
func (sc *syncContext) getOperationPhase(hook *unstructured.Unstructured) (v1alpha1.OperationPhase, string, error) {
|
||||
phase := v1alpha1.OperationSucceeded
|
||||
message := fmt.Sprintf("%s created", hook.GetName())
|
||||
|
||||
resHealth, err := health.GetResourceHealth(hook, sc.resourceOverrides)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if resHealth != nil {
|
||||
switch resHealth.Status {
|
||||
case v1alpha1.HealthStatusUnknown, v1alpha1.HealthStatusDegraded:
|
||||
phase = v1alpha1.OperationFailed
|
||||
message = resHealth.Message
|
||||
case v1alpha1.HealthStatusProgressing, v1alpha1.HealthStatusSuspended:
|
||||
phase = v1alpha1.OperationRunning
|
||||
message = resHealth.Message
|
||||
case v1alpha1.HealthStatusHealthy:
|
||||
phase = v1alpha1.OperationSucceeded
|
||||
message = resHealth.Message
|
||||
}
|
||||
}
|
||||
return phase, message, nil
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/hook"
|
||||
)
|
||||
|
||||
func syncPhases(obj *unstructured.Unstructured) []v1alpha1.SyncPhase {
|
||||
if hook.Skip(obj) {
|
||||
return nil
|
||||
} else if hook.IsHook(obj) {
|
||||
phasesMap := make(map[v1alpha1.SyncPhase]bool)
|
||||
for _, hookType := range hook.Types(obj) {
|
||||
switch hookType {
|
||||
case v1alpha1.HookTypePreSync, v1alpha1.HookTypeSync, v1alpha1.HookTypePostSync, v1alpha1.HookTypeSyncFail:
|
||||
phasesMap[v1alpha1.SyncPhase(hookType)] = true
|
||||
}
|
||||
}
|
||||
var phases []v1alpha1.SyncPhase
|
||||
for phase := range phasesMap {
|
||||
phases = append(phases, phase)
|
||||
}
|
||||
return phases
|
||||
} else {
|
||||
return []v1alpha1.SyncPhase{v1alpha1.SyncPhaseSync}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
)
|
||||
|
||||
func TestSyncPhaseNone(t *testing.T) {
|
||||
assert.Equal(t, []SyncPhase{SyncPhaseSync}, syncPhases(&unstructured.Unstructured{}))
|
||||
}
|
||||
|
||||
func TestSyncPhasePreSync(t *testing.T) {
|
||||
assert.Equal(t, []SyncPhase{SyncPhasePreSync}, syncPhases(pod("PreSync")))
|
||||
}
|
||||
|
||||
func TestSyncPhaseSync(t *testing.T) {
|
||||
assert.Equal(t, []SyncPhase{SyncPhaseSync}, syncPhases(pod("Sync")))
|
||||
}
|
||||
|
||||
func TestSyncPhaseSkip(t *testing.T) {
|
||||
assert.Nil(t, syncPhases(pod("Skip")))
|
||||
}
|
||||
|
||||
// garbage hooks are still hooks, but have no phases, because some user spelled something wrong
|
||||
func TestSyncPhaseGarbage(t *testing.T) {
|
||||
assert.Nil(t, syncPhases(pod("Garbage")))
|
||||
}
|
||||
|
||||
func TestSyncPhasePost(t *testing.T) {
|
||||
assert.Equal(t, []SyncPhase{SyncPhasePostSync}, syncPhases(pod("PostSync")))
|
||||
}
|
||||
|
||||
func TestSyncPhaseFail(t *testing.T) {
|
||||
assert.Equal(t, []SyncPhase{SyncPhaseSyncFail}, syncPhases(pod("SyncFail")))
|
||||
}
|
||||
|
||||
func TestSyncPhaseTwoPhases(t *testing.T) {
|
||||
assert.ElementsMatch(t, []SyncPhase{SyncPhasePreSync, SyncPhasePostSync}, syncPhases(pod("PreSync,PostSync")))
|
||||
}
|
||||
|
||||
func TestSyncDuplicatedPhases(t *testing.T) {
|
||||
assert.ElementsMatch(t, []SyncPhase{SyncPhasePreSync}, syncPhases(pod("PreSync,PreSync")))
|
||||
assert.ElementsMatch(t, []SyncPhase{SyncPhasePreSync}, syncPhases(podWithHelmHook("pre-install,pre-upgrade")))
|
||||
}
|
||||
|
||||
func pod(hookType string) *unstructured.Unstructured {
|
||||
return test.Annotate(test.NewPod(), "argocd.argoproj.io/hook", hookType)
|
||||
}
|
||||
|
||||
func podWithHelmHook(hookType string) *unstructured.Unstructured {
|
||||
return test.Annotate(test.NewPod(), "helm.sh/hook", hookType)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/hook"
|
||||
"github.com/argoproj/argo-cd/util/resource/syncwaves"
|
||||
)
|
||||
|
||||
// syncTask holds the live and target object. At least one should be non-nil. A targetObj of nil
|
||||
// indicates the live object needs to be pruned. A liveObj of nil indicates the object has yet to
|
||||
// be deployed
|
||||
type syncTask struct {
|
||||
phase v1alpha1.SyncPhase
|
||||
liveObj *unstructured.Unstructured
|
||||
targetObj *unstructured.Unstructured
|
||||
skipDryRun bool
|
||||
syncStatus v1alpha1.ResultCode
|
||||
operationState v1alpha1.OperationPhase
|
||||
message string
|
||||
}
|
||||
|
||||
func ternary(val bool, a, b string) string {
|
||||
if val {
|
||||
return a
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
func (t *syncTask) String() string {
|
||||
return fmt.Sprintf("%s/%d %s %s/%s:%s/%s %s->%s (%s,%s,%s)",
|
||||
t.phase, t.wave(),
|
||||
ternary(t.isHook(), "hook", "resource"), t.group(), t.kind(), t.namespace(), t.name(),
|
||||
ternary(t.liveObj != nil, "obj", "nil"), ternary(t.targetObj != nil, "obj", "nil"),
|
||||
t.syncStatus, t.operationState, t.message,
|
||||
)
|
||||
}
|
||||
|
||||
func (t *syncTask) isPrune() bool {
|
||||
return t.targetObj == nil
|
||||
}
|
||||
|
||||
// return the target object (if this exists) otherwise the live object
|
||||
// some caution - often you explicitly want the live object not the target object
|
||||
func (t *syncTask) obj() *unstructured.Unstructured {
|
||||
return obj(t.targetObj, t.liveObj)
|
||||
}
|
||||
|
||||
func (t *syncTask) wave() int {
|
||||
return syncwaves.Wave(t.obj())
|
||||
}
|
||||
|
||||
func (t *syncTask) isHook() bool {
|
||||
return hook.IsHook(t.obj())
|
||||
}
|
||||
|
||||
func (t *syncTask) group() string {
|
||||
return t.groupVersionKind().Group
|
||||
}
|
||||
func (t *syncTask) kind() string {
|
||||
return t.groupVersionKind().Kind
|
||||
}
|
||||
|
||||
func (t *syncTask) version() string {
|
||||
return t.groupVersionKind().Version
|
||||
}
|
||||
|
||||
func (t *syncTask) groupVersionKind() schema.GroupVersionKind {
|
||||
return t.obj().GroupVersionKind()
|
||||
}
|
||||
|
||||
func (t *syncTask) name() string {
|
||||
return t.obj().GetName()
|
||||
}
|
||||
|
||||
func (t *syncTask) namespace() string {
|
||||
return t.obj().GetNamespace()
|
||||
}
|
||||
|
||||
func (t *syncTask) pending() bool {
|
||||
return t.operationState == ""
|
||||
}
|
||||
|
||||
func (t *syncTask) running() bool {
|
||||
return t.operationState.Running()
|
||||
}
|
||||
|
||||
func (t *syncTask) completed() bool {
|
||||
return t.operationState.Completed()
|
||||
}
|
||||
|
||||
func (t *syncTask) successful() bool {
|
||||
return t.operationState.Successful()
|
||||
}
|
||||
|
||||
func (t *syncTask) failed() bool {
|
||||
return t.operationState.Failed()
|
||||
}
|
||||
|
||||
func (t *syncTask) hookType() v1alpha1.HookType {
|
||||
if t.isHook() {
|
||||
return v1alpha1.HookType(t.phase)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (t *syncTask) hasHookDeletePolicy(policy v1alpha1.HookDeletePolicy) bool {
|
||||
// cannot have a policy if it is not a hook, it is meaningless
|
||||
if !t.isHook() {
|
||||
return false
|
||||
}
|
||||
for _, p := range hook.DeletePolicies(t.obj()) {
|
||||
if p == policy {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *syncTask) needsDeleting() bool {
|
||||
return t.liveObj != nil && (t.pending() && t.hasHookDeletePolicy(v1alpha1.HookDeletePolicyBeforeHookCreation) ||
|
||||
t.successful() && t.hasHookDeletePolicy(v1alpha1.HookDeletePolicyHookSucceeded) ||
|
||||
t.failed() && t.hasHookDeletePolicy(v1alpha1.HookDeletePolicyHookFailed))
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
. "github.com/argoproj/argo-cd/test"
|
||||
)
|
||||
|
||||
func Test_syncTask_hookType(t *testing.T) {
|
||||
type fields struct {
|
||||
phase SyncPhase
|
||||
liveObj *unstructured.Unstructured
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want HookType
|
||||
}{
|
||||
{"Empty", fields{SyncPhaseSync, NewPod()}, ""},
|
||||
{"PreSyncHook", fields{SyncPhasePreSync, NewHook(HookTypePreSync)}, HookTypePreSync},
|
||||
{"SyncHook", fields{SyncPhaseSync, NewHook(HookTypeSync)}, HookTypeSync},
|
||||
{"PostSyncHook", fields{SyncPhasePostSync, NewHook(HookTypePostSync)}, HookTypePostSync},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
task := &syncTask{
|
||||
phase: tt.fields.phase,
|
||||
liveObj: tt.fields.liveObj,
|
||||
}
|
||||
hookType := task.hookType()
|
||||
assert.EqualValues(t, tt.want, hookType)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_syncTask_hasHookDeletePolicy(t *testing.T) {
|
||||
assert.False(t, (&syncTask{targetObj: NewPod()}).hasHookDeletePolicy(HookDeletePolicyBeforeHookCreation))
|
||||
assert.False(t, (&syncTask{targetObj: NewPod()}).hasHookDeletePolicy(HookDeletePolicyHookSucceeded))
|
||||
assert.False(t, (&syncTask{targetObj: NewPod()}).hasHookDeletePolicy(HookDeletePolicyHookFailed))
|
||||
// must be hook
|
||||
assert.False(t, (&syncTask{targetObj: Annotate(NewPod(), "argocd.argoproj.io/hook-delete-policy", "BeforeHookCreation")}).hasHookDeletePolicy(HookDeletePolicyBeforeHookCreation))
|
||||
assert.True(t, (&syncTask{targetObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "BeforeHookCreation")}).hasHookDeletePolicy(HookDeletePolicyBeforeHookCreation))
|
||||
assert.True(t, (&syncTask{targetObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "HookSucceeded")}).hasHookDeletePolicy(HookDeletePolicyHookSucceeded))
|
||||
assert.True(t, (&syncTask{targetObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "HookFailed")}).hasHookDeletePolicy(HookDeletePolicyHookFailed))
|
||||
}
|
||||
|
||||
func Test_syncTask_needsDeleting(t *testing.T) {
|
||||
assert.False(t, (&syncTask{liveObj: NewPod()}).needsDeleting())
|
||||
// must be hook
|
||||
assert.False(t, (&syncTask{liveObj: Annotate(NewPod(), "argocd.argoproj.io/hook-delete-policy", "BeforeHookCreation")}).needsDeleting())
|
||||
// no need to delete if no live obj
|
||||
assert.False(t, (&syncTask{targetObj: Annotate(Annotate(NewPod(), "argoocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "BeforeHookCreation")}).needsDeleting())
|
||||
assert.True(t, (&syncTask{liveObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "BeforeHookCreation")}).needsDeleting())
|
||||
assert.True(t, (&syncTask{liveObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "BeforeHookCreation")}).needsDeleting())
|
||||
assert.True(t, (&syncTask{operationState: OperationSucceeded, liveObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "HookSucceeded")}).needsDeleting())
|
||||
assert.True(t, (&syncTask{operationState: OperationFailed, liveObj: Annotate(Annotate(NewPod(), "argocd.argoproj.io/hook", "Sync"), "argocd.argoproj.io/hook-delete-policy", "HookFailed")}).needsDeleting())
|
||||
}
|
||||
|
||||
func Test_syncTask_wave(t *testing.T) {
|
||||
assert.Equal(t, 0, (&syncTask{targetObj: NewPod()}).wave())
|
||||
assert.Equal(t, 1, (&syncTask{targetObj: Annotate(NewPod(), "argocd.argoproj.io/sync-wave", "1")}).wave())
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
// kindOrder represents the correct order of Kubernetes resources within a manifest
|
||||
var syncPhaseOrder = map[v1alpha1.SyncPhase]int{
|
||||
v1alpha1.SyncPhasePreSync: -1,
|
||||
v1alpha1.SyncPhaseSync: 0,
|
||||
v1alpha1.SyncPhasePostSync: 1,
|
||||
v1alpha1.SyncPhaseSyncFail: 2,
|
||||
}
|
||||
|
||||
// kindOrder represents the correct order of Kubernetes resources within a manifest
|
||||
// https://github.com/helm/helm/blob/master/pkg/tiller/kind_sorter.go
|
||||
var kindOrder = map[string]int{}
|
||||
|
||||
func init() {
|
||||
kinds := []string{
|
||||
"Namespace",
|
||||
"ResourceQuota",
|
||||
"LimitRange",
|
||||
"PodSecurityPolicy",
|
||||
"PodDisruptionBudget",
|
||||
"Secret",
|
||||
"ConfigMap",
|
||||
"StorageClass",
|
||||
"PersistentVolume",
|
||||
"PersistentVolumeClaim",
|
||||
"ServiceAccount",
|
||||
"CustomResourceDefinition",
|
||||
"ClusterRole",
|
||||
"ClusterRoleBinding",
|
||||
"Role",
|
||||
"RoleBinding",
|
||||
"Service",
|
||||
"DaemonSet",
|
||||
"Pod",
|
||||
"ReplicationController",
|
||||
"ReplicaSet",
|
||||
"Deployment",
|
||||
"StatefulSet",
|
||||
"Job",
|
||||
"CronJob",
|
||||
"Ingress",
|
||||
"APIService",
|
||||
}
|
||||
for i, kind := range kinds {
|
||||
// make sure none of the above entries are zero, we need that for custom resources
|
||||
kindOrder[kind] = i - len(kinds)
|
||||
}
|
||||
}
|
||||
|
||||
type syncTasks []*syncTask
|
||||
|
||||
func (s syncTasks) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s syncTasks) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// order is
|
||||
// 1. phase
|
||||
// 2. wave
|
||||
// 3. kind
|
||||
// 4. name
|
||||
func (s syncTasks) Less(i, j int) bool {
|
||||
|
||||
tA := s[i]
|
||||
tB := s[j]
|
||||
|
||||
d := syncPhaseOrder[tA.phase] - syncPhaseOrder[tB.phase]
|
||||
if d != 0 {
|
||||
return d < 0
|
||||
}
|
||||
|
||||
d = tA.wave() - tB.wave()
|
||||
if d != 0 {
|
||||
return d < 0
|
||||
}
|
||||
|
||||
a := tA.obj()
|
||||
b := tB.obj()
|
||||
|
||||
// we take advantage of the fact that if the kind is not in the kindOrder map,
|
||||
// then it will return the default int value of zero, which is the highest value
|
||||
d = kindOrder[a.GetKind()] - kindOrder[b.GetKind()]
|
||||
if d != 0 {
|
||||
return d < 0
|
||||
}
|
||||
|
||||
return a.GetName() < b.GetName()
|
||||
}
|
||||
|
||||
func (s syncTasks) Filter(predicate func(task *syncTask) bool) (tasks syncTasks) {
|
||||
for _, task := range s {
|
||||
if predicate(task) {
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
return tasks
|
||||
}
|
||||
|
||||
func (s syncTasks) Split(predicate func(task *syncTask) bool) (trueTasks, falseTasks syncTasks) {
|
||||
for _, task := range s {
|
||||
if predicate(task) {
|
||||
trueTasks = append(trueTasks, task)
|
||||
} else {
|
||||
falseTasks = append(falseTasks, task)
|
||||
}
|
||||
}
|
||||
return trueTasks, falseTasks
|
||||
}
|
||||
|
||||
func (s syncTasks) All(predicate func(task *syncTask) bool) bool {
|
||||
for _, task := range s {
|
||||
if !predicate(task) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s syncTasks) Any(predicate func(task *syncTask) bool) bool {
|
||||
for _, task := range s {
|
||||
if predicate(task) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s syncTasks) Find(predicate func(task *syncTask) bool) *syncTask {
|
||||
for _, task := range s {
|
||||
if predicate(task) {
|
||||
return task
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s syncTasks) String() string {
|
||||
var values []string
|
||||
for _, task := range s {
|
||||
values = append(values, task.String())
|
||||
}
|
||||
return "[" + strings.Join(values, ", ") + "]"
|
||||
}
|
||||
|
||||
func (s syncTasks) phase() v1alpha1.SyncPhase {
|
||||
if len(s) > 0 {
|
||||
return s[0].phase
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s syncTasks) wave() int {
|
||||
if len(s) > 0 {
|
||||
return s[0].wave()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s syncTasks) lastPhase() v1alpha1.SyncPhase {
|
||||
if len(s) > 0 {
|
||||
return s[len(s)-1].phase
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s syncTasks) lastWave() int {
|
||||
if len(s) > 0 {
|
||||
return s[len(s)-1].wave()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s syncTasks) multiStep() bool {
|
||||
return s.wave() != s.lastWave() || s.phase() != s.lastPhase()
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
. "github.com/argoproj/argo-cd/test"
|
||||
)
|
||||
|
||||
func Test_syncTasks_kindOrder(t *testing.T) {
|
||||
assert.Equal(t, -27, kindOrder["Namespace"])
|
||||
assert.Equal(t, -1, kindOrder["APIService"])
|
||||
assert.Equal(t, 0, kindOrder["MyCRD"])
|
||||
}
|
||||
|
||||
func TestSortSyncTask(t *testing.T) {
|
||||
sort.Sort(unsortedTasks)
|
||||
assert.Equal(t, sortedTasks, unsortedTasks)
|
||||
}
|
||||
|
||||
func TestAnySyncTasks(t *testing.T) {
|
||||
res := unsortedTasks.Any(func(task *syncTask) bool {
|
||||
return task.name() == "a"
|
||||
})
|
||||
assert.True(t, res)
|
||||
|
||||
res = unsortedTasks.Any(func(task *syncTask) bool {
|
||||
return task.name() == "does-not-exist"
|
||||
})
|
||||
assert.False(t, res)
|
||||
|
||||
}
|
||||
|
||||
func TestAllSyncTasks(t *testing.T) {
|
||||
res := unsortedTasks.All(func(task *syncTask) bool {
|
||||
return task.name() != ""
|
||||
})
|
||||
assert.False(t, res)
|
||||
|
||||
res = unsortedTasks.All(func(task *syncTask) bool {
|
||||
return task.name() == "a"
|
||||
})
|
||||
assert.False(t, res)
|
||||
}
|
||||
|
||||
func TestSplitSyncTasks(t *testing.T) {
|
||||
named, unnamed := sortedTasks.Split(func(task *syncTask) bool {
|
||||
return task.name() != ""
|
||||
})
|
||||
assert.Equal(t, named, namedObjTasks)
|
||||
assert.Equal(t, unnamed, unnamedTasks)
|
||||
}
|
||||
|
||||
var unsortedTasks = syncTasks{
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "Pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "Service",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "PersistentVolume",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
phase: SyncPhaseSyncFail, targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"argocd.argoproj.io/sync-wave": "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"argocd.argoproj.io/sync-wave": "-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
phase: SyncPhasePreSync,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
phase: SyncPhasePostSync, targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "ConfigMap",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var sortedTasks = syncTasks{
|
||||
{
|
||||
phase: SyncPhasePreSync,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"argocd.argoproj.io/sync-wave": "-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "ConfigMap",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "PersistentVolume",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "Service",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "Pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"argocd.argoproj.io/sync-wave": "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
phase: SyncPhasePostSync,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
phase: SyncPhaseSyncFail,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
}
|
||||
|
||||
var namedObjTasks = syncTasks{
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "a",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "b",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var unnamedTasks = syncTasks{
|
||||
{
|
||||
phase: SyncPhasePreSync,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"argocd.argoproj.io/sync-wave": "-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "ConfigMap",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "PersistentVolume",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "Service",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
"kind": "Pod",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"GroupVersion": apiv1.SchemeGroupVersion.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
"argocd.argoproj.io/sync-wave": "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
phase: SyncPhasePostSync,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
{
|
||||
phase: SyncPhaseSyncFail,
|
||||
targetObj: &unstructured.Unstructured{},
|
||||
},
|
||||
}
|
||||
|
||||
func Test_syncTasks_Filter(t *testing.T) {
|
||||
tasks := syncTasks{{phase: SyncPhaseSync}, {phase: SyncPhasePostSync}}
|
||||
|
||||
assert.Equal(t, syncTasks{{phase: SyncPhaseSync}}, tasks.Filter(func(t *syncTask) bool {
|
||||
return t.phase == SyncPhaseSync
|
||||
}))
|
||||
}
|
||||
|
||||
func TestSyncNamespaceAgainstCRD(t *testing.T) {
|
||||
crd := &syncTask{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "Workflow",
|
||||
},
|
||||
}}
|
||||
namespace := &syncTask{
|
||||
targetObj: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"kind": "Namespace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
unsorted := syncTasks{crd, namespace}
|
||||
sort.Sort(unsorted)
|
||||
|
||||
assert.Equal(t, syncTasks{namespace, crd}, unsorted)
|
||||
}
|
||||
|
||||
func Test_syncTasks_multiStep(t *testing.T) {
|
||||
t.Run("Single", func(t *testing.T) {
|
||||
tasks := syncTasks{{liveObj: Annotate(NewPod(), common.AnnotationSyncWave, "-1"), phase: SyncPhaseSync}}
|
||||
assert.Equal(t, SyncPhaseSync, tasks.phase())
|
||||
assert.Equal(t, -1, tasks.wave())
|
||||
assert.Equal(t, SyncPhaseSync, tasks.lastPhase())
|
||||
assert.Equal(t, -1, tasks.lastWave())
|
||||
assert.False(t, tasks.multiStep())
|
||||
})
|
||||
t.Run("Double", func(t *testing.T) {
|
||||
tasks := syncTasks{
|
||||
{liveObj: Annotate(NewPod(), common.AnnotationSyncWave, "-1"), phase: SyncPhasePreSync},
|
||||
{liveObj: Annotate(NewPod(), common.AnnotationSyncWave, "1"), phase: SyncPhasePostSync},
|
||||
}
|
||||
assert.Equal(t, SyncPhasePreSync, tasks.phase())
|
||||
assert.Equal(t, -1, tasks.wave())
|
||||
assert.Equal(t, SyncPhasePostSync, tasks.lastPhase())
|
||||
assert.Equal(t, 1, tasks.lastWave())
|
||||
assert.True(t, tasks.multiStep())
|
||||
})
|
||||
}
|
||||
@@ -1,578 +1,19 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/stretchr/testify/assert"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
fakedisco "k8s.io/client-go/discovery/fake"
|
||||
"k8s.io/client-go/dynamic/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
testcore "k8s.io/client-go/testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/kube/kubetest"
|
||||
)
|
||||
|
||||
func newTestSyncCtx(resources ...*v1.APIResourceList) *syncContext {
|
||||
fakeDisco := &fakedisco.FakeDiscovery{Fake: &testcore.Fake{}}
|
||||
fakeDisco.Resources = append(resources,
|
||||
&v1.APIResourceList{
|
||||
GroupVersion: "v1",
|
||||
APIResources: []v1.APIResource{
|
||||
{Kind: "Pod", Group: "", Version: "v1", Namespaced: true},
|
||||
{Kind: "Service", Group: "", Version: "v1", Namespaced: true},
|
||||
},
|
||||
},
|
||||
&v1.APIResourceList{
|
||||
GroupVersion: "apps/v1",
|
||||
APIResources: []v1.APIResource{
|
||||
{Kind: "Deployment", Group: "apps", Version: "v1", Namespaced: true},
|
||||
},
|
||||
})
|
||||
sc := syncContext{
|
||||
config: &rest.Config{},
|
||||
rawConfig: &rest.Config{},
|
||||
namespace: test.FakeArgoCDNamespace,
|
||||
server: test.FakeClusterURL,
|
||||
syncRes: &v1alpha1.SyncOperationResult{
|
||||
Revision: "FooBarBaz",
|
||||
},
|
||||
syncOp: &v1alpha1.SyncOperation{
|
||||
Prune: true,
|
||||
SyncStrategy: &v1alpha1.SyncStrategy{
|
||||
Apply: &v1alpha1.SyncStrategyApply{},
|
||||
},
|
||||
},
|
||||
proj: &v1alpha1.AppProject{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
},
|
||||
Spec: v1alpha1.AppProjectSpec{
|
||||
Destinations: []v1alpha1.ApplicationDestination{{
|
||||
Server: test.FakeClusterURL,
|
||||
Namespace: test.FakeArgoCDNamespace,
|
||||
}},
|
||||
ClusterResourceWhitelist: []v1.GroupKind{
|
||||
{Group: "*", Kind: "*"},
|
||||
},
|
||||
},
|
||||
},
|
||||
opState: &v1alpha1.OperationState{},
|
||||
disco: fakeDisco,
|
||||
log: log.WithFields(log.Fields{"application": "fake-app"}),
|
||||
}
|
||||
sc.kubectl = &kubetest.MockKubectlCmd{}
|
||||
return &sc
|
||||
}
|
||||
|
||||
func newManagedResource(live *unstructured.Unstructured) managedResource {
|
||||
return managedResource{
|
||||
Live: live,
|
||||
Group: live.GroupVersionKind().Group,
|
||||
Version: live.GroupVersionKind().Version,
|
||||
Kind: live.GroupVersionKind().Kind,
|
||||
Namespace: live.GetNamespace(),
|
||||
Name: live.GetName(),
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncNotPermittedNamespace(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
targetPod := test.NewPod()
|
||||
targetPod.SetNamespace("kube-system")
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: nil,
|
||||
Target: targetPod,
|
||||
}, {
|
||||
Live: nil,
|
||||
Target: test.NewService(),
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Equal(t, v1alpha1.OperationFailed, syncCtx.opState.Phase)
|
||||
assert.Contains(t, syncCtx.syncRes.Resources[0].Message, "not permitted in project")
|
||||
}
|
||||
|
||||
func TestSyncCreateInSortedOrder(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: nil,
|
||||
Target: test.NewPod(),
|
||||
}, {
|
||||
Live: nil,
|
||||
Target: test.NewService(),
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase)
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 2)
|
||||
for i := range syncCtx.syncRes.Resources {
|
||||
result := syncCtx.syncRes.Resources[i]
|
||||
if result.Kind == "Pod" {
|
||||
assert.Equal(t, v1alpha1.ResultCodeSynced, result.Status)
|
||||
assert.Equal(t, "", result.Message)
|
||||
} else if result.Kind == "Service" {
|
||||
assert.Equal(t, "", result.Message)
|
||||
} else {
|
||||
t.Error("Resource isn't a pod or a service")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncCreateNotWhitelistedClusterResources(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx(&v1.APIResourceList{
|
||||
GroupVersion: v1alpha1.SchemeGroupVersion.String(),
|
||||
APIResources: []v1.APIResource{
|
||||
{Name: "workflows", Namespaced: false, Kind: "Workflow", Group: "argoproj.io"},
|
||||
{Name: "application", Namespaced: false, Kind: "Application", Group: "argoproj.io"},
|
||||
},
|
||||
}, &v1.APIResourceList{
|
||||
GroupVersion: "rbac.authorization.k8s.io/v1",
|
||||
APIResources: []v1.APIResource{
|
||||
{Name: "clusterroles", Namespaced: false, Kind: "ClusterRole", Group: "rbac.authorization.k8s.io"},
|
||||
},
|
||||
})
|
||||
|
||||
syncCtx.proj.Spec.ClusterResourceWhitelist = []v1.GroupKind{
|
||||
{Group: "argoproj.io", Kind: "*"},
|
||||
}
|
||||
|
||||
syncCtx.kubectl = &kubetest.MockKubectlCmd{}
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: nil,
|
||||
Target: kube.MustToUnstructured(&rbacv1.ClusterRole{
|
||||
TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "argo-ui-cluster-role"}}),
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
result := syncCtx.syncRes.Resources[0]
|
||||
assert.Equal(t, v1alpha1.ResultCodeSyncFailed, result.Status)
|
||||
assert.Contains(t, result.Message, "not permitted in project")
|
||||
}
|
||||
|
||||
func TestSyncBlacklistedNamespacedResources(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
|
||||
syncCtx.proj.Spec.NamespaceResourceBlacklist = []v1.GroupKind{
|
||||
{Group: "*", Kind: "Deployment"},
|
||||
}
|
||||
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: nil,
|
||||
Target: test.NewDeployment(),
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
result := syncCtx.syncRes.Resources[0]
|
||||
assert.Equal(t, v1alpha1.ResultCodeSyncFailed, result.Status)
|
||||
assert.Contains(t, result.Message, "not permitted in project")
|
||||
}
|
||||
|
||||
func TestSyncSuccessfully(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod := test.NewPod()
|
||||
pod.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: nil,
|
||||
Target: test.NewService(),
|
||||
}, {
|
||||
Live: pod,
|
||||
Target: nil,
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase)
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 2)
|
||||
for i := range syncCtx.syncRes.Resources {
|
||||
result := syncCtx.syncRes.Resources[i]
|
||||
if result.Kind == "Pod" {
|
||||
assert.Equal(t, v1alpha1.ResultCodePruned, result.Status)
|
||||
assert.Equal(t, "pruned", result.Message)
|
||||
} else if result.Kind == "Service" {
|
||||
assert.Equal(t, v1alpha1.ResultCodeSynced, result.Status)
|
||||
assert.Equal(t, "", result.Message)
|
||||
} else {
|
||||
t.Error("Resource isn't a pod or a service")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncDeleteSuccessfully(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
svc := test.NewService()
|
||||
svc.SetNamespace(test.FakeArgoCDNamespace)
|
||||
pod := test.NewPod()
|
||||
pod.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: svc,
|
||||
Target: nil,
|
||||
}, {
|
||||
Live: pod,
|
||||
Target: nil,
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase)
|
||||
for i := range syncCtx.syncRes.Resources {
|
||||
result := syncCtx.syncRes.Resources[i]
|
||||
if result.Kind == "Pod" {
|
||||
assert.Equal(t, v1alpha1.ResultCodePruned, result.Status)
|
||||
assert.Equal(t, "pruned", result.Message)
|
||||
} else if result.Kind == "Service" {
|
||||
assert.Equal(t, v1alpha1.ResultCodePruned, result.Status)
|
||||
assert.Equal(t, "pruned", result.Message)
|
||||
} else {
|
||||
t.Error("Resource isn't a pod or a service")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSyncCreateFailure(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
testSvc := test.NewService()
|
||||
syncCtx.kubectl = &kubetest.MockKubectlCmd{
|
||||
Commands: map[string]kubetest.KubectlOutput{
|
||||
testSvc.GetName(): {
|
||||
Output: "",
|
||||
Err: fmt.Errorf("foo"),
|
||||
},
|
||||
},
|
||||
}
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: nil,
|
||||
Target: testSvc,
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
result := syncCtx.syncRes.Resources[0]
|
||||
assert.Equal(t, v1alpha1.ResultCodeSyncFailed, result.Status)
|
||||
assert.Equal(t, "foo", result.Message)
|
||||
}
|
||||
|
||||
func TestSyncPruneFailure(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.kubectl = &kubetest.MockKubectlCmd{
|
||||
Commands: map[string]kubetest.KubectlOutput{
|
||||
"test-service": {
|
||||
Output: "",
|
||||
Err: fmt.Errorf("foo"),
|
||||
},
|
||||
},
|
||||
}
|
||||
testSvc := test.NewService()
|
||||
testSvc.SetName("test-service")
|
||||
testSvc.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{
|
||||
Live: testSvc,
|
||||
Target: nil,
|
||||
}},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Equal(t, v1alpha1.OperationFailed, syncCtx.opState.Phase)
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
result := syncCtx.syncRes.Resources[0]
|
||||
assert.Equal(t, v1alpha1.ResultCodeSyncFailed, result.Status)
|
||||
assert.Equal(t, "foo", result.Message)
|
||||
}
|
||||
|
||||
func TestDontSyncOrPruneHooks(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
targetPod := test.NewPod()
|
||||
targetPod.SetName("dont-create-me")
|
||||
targetPod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"})
|
||||
liveSvc := test.NewService()
|
||||
liveSvc.SetName("dont-prune-me")
|
||||
liveSvc.SetNamespace(test.FakeArgoCDNamespace)
|
||||
liveSvc.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"})
|
||||
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
hooks: []*unstructured.Unstructured{targetPod, liveSvc},
|
||||
}
|
||||
syncCtx.sync()
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 0)
|
||||
syncCtx.sync()
|
||||
assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase)
|
||||
}
|
||||
|
||||
// make sure that we do not prune resources with Prune=false
|
||||
func TestDontPrunePruneFalse(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod := test.NewPod()
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationSyncOptions: "Prune=false"})
|
||||
pod.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Live: pod}}}
|
||||
|
||||
syncCtx.sync()
|
||||
|
||||
assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase)
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
assert.Equal(t, v1alpha1.ResultCodePruneSkipped, syncCtx.syncRes.Resources[0].Status)
|
||||
assert.Equal(t, "ignored (no prune)", syncCtx.syncRes.Resources[0].Message)
|
||||
|
||||
syncCtx.sync()
|
||||
|
||||
assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase)
|
||||
}
|
||||
|
||||
// make sure Validate=false means we don't validate
|
||||
func TestSyncOptionValidate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
annotationVal string
|
||||
want bool
|
||||
}{
|
||||
{"Empty", "", true},
|
||||
{"True", "Validate=true", true},
|
||||
{"False", "Validate=false", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod := test.NewPod()
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationSyncOptions: tt.annotationVal})
|
||||
pod.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Target: pod, Live: pod}}}
|
||||
|
||||
syncCtx.sync()
|
||||
|
||||
kubectl, _ := syncCtx.kubectl.(*kubetest.MockKubectlCmd)
|
||||
assert.Equal(t, tt.want, kubectl.LastValidate)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// make sure Validate means we don't validate
|
||||
func TestSyncValidate(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod := test.NewPod()
|
||||
pod.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Target: pod, Live: pod}}}
|
||||
syncCtx.syncOp.SyncOptions = SyncOptions{"Validate=false"}
|
||||
|
||||
syncCtx.sync()
|
||||
|
||||
kubectl := syncCtx.kubectl.(*kubetest.MockKubectlCmd)
|
||||
assert.False(t, kubectl.LastValidate)
|
||||
}
|
||||
|
||||
func TestSelectiveSyncOnly(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod1 := test.NewPod()
|
||||
pod1.SetName("pod-1")
|
||||
pod2 := test.NewPod()
|
||||
pod2.SetName("pod-2")
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{Target: pod1}},
|
||||
}
|
||||
syncCtx.syncResources = []v1alpha1.SyncOperationResource{{Kind: "Pod", Name: "pod-1"}}
|
||||
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
assert.True(t, successful)
|
||||
assert.Len(t, tasks, 1)
|
||||
assert.Equal(t, "pod-1", tasks[0].name())
|
||||
}
|
||||
|
||||
func TestUnnamedHooksGetUniqueNames(t *testing.T) {
|
||||
t.Run("Truncated revision", func(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
pod := test.NewPod()
|
||||
pod.SetName("")
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync,PostSync"})
|
||||
syncCtx.compareResult = &comparisonResult{hooks: []*unstructured.Unstructured{pod}}
|
||||
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
assert.True(t, successful)
|
||||
assert.Len(t, tasks, 2)
|
||||
assert.Contains(t, tasks[0].name(), "foobarb-presync-")
|
||||
assert.Contains(t, tasks[1].name(), "foobarb-postsync-")
|
||||
assert.Equal(t, "", pod.GetName())
|
||||
})
|
||||
|
||||
t.Run("Short revision", func(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
pod := test.NewPod()
|
||||
pod.SetName("")
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync,PostSync"})
|
||||
syncCtx.compareResult = &comparisonResult{hooks: []*unstructured.Unstructured{pod}}
|
||||
syncCtx.syncRes.Revision = "foobar"
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
assert.True(t, successful)
|
||||
assert.Len(t, tasks, 2)
|
||||
assert.Contains(t, tasks[0].name(), "foobar-presync-")
|
||||
assert.Contains(t, tasks[1].name(), "foobar-postsync-")
|
||||
assert.Equal(t, "", pod.GetName())
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestManagedResourceAreNotNamed(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod := test.NewPod()
|
||||
pod.SetName("")
|
||||
syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Target: pod}}}
|
||||
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
assert.True(t, successful)
|
||||
assert.Len(t, tasks, 1)
|
||||
assert.Equal(t, "", tasks[0].name())
|
||||
assert.Equal(t, "", pod.GetName())
|
||||
}
|
||||
|
||||
func TestDeDupingTasks(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
pod := test.NewPod()
|
||||
pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "Sync"})
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{Target: pod}},
|
||||
hooks: []*unstructured.Unstructured{pod},
|
||||
}
|
||||
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
assert.True(t, successful)
|
||||
assert.Len(t, tasks, 1)
|
||||
}
|
||||
|
||||
func TestObjectsGetANamespace(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
pod := test.NewPod()
|
||||
syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Target: pod}}}
|
||||
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
assert.True(t, successful)
|
||||
assert.Len(t, tasks, 1)
|
||||
assert.Equal(t, test.FakeArgoCDNamespace, tasks[0].namespace())
|
||||
assert.Equal(t, "", pod.GetNamespace())
|
||||
}
|
||||
|
||||
func TestSyncCustomResources(t *testing.T) {
|
||||
type fields struct {
|
||||
skipDryRunAnnotationPresent bool
|
||||
crdAlreadyPresent bool
|
||||
crdInSameSync bool
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
wantDryRun bool
|
||||
wantSuccess bool
|
||||
}{
|
||||
|
||||
{"unknown crd", fields{
|
||||
skipDryRunAnnotationPresent: false, crdAlreadyPresent: false, crdInSameSync: false,
|
||||
}, true, false},
|
||||
{"crd present in same sync", fields{
|
||||
skipDryRunAnnotationPresent: false, crdAlreadyPresent: false, crdInSameSync: true,
|
||||
}, false, true},
|
||||
{"crd is already present in cluster", fields{
|
||||
skipDryRunAnnotationPresent: false, crdAlreadyPresent: true, crdInSameSync: false,
|
||||
}, true, true},
|
||||
{"crd is already present in cluster, skip dry run annotated", fields{
|
||||
skipDryRunAnnotationPresent: true, crdAlreadyPresent: true, crdInSameSync: false,
|
||||
}, true, true},
|
||||
{"unknown crd, skip dry run annotated", fields{
|
||||
skipDryRunAnnotationPresent: true, crdAlreadyPresent: false, crdInSameSync: false,
|
||||
}, false, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
knownCustomResourceTypes := []v1.APIResource{}
|
||||
if tt.fields.crdAlreadyPresent {
|
||||
knownCustomResourceTypes = append(knownCustomResourceTypes, v1.APIResource{Kind: "TestCrd", Group: "argoproj.io", Version: "v1", Namespaced: true})
|
||||
}
|
||||
|
||||
syncCtx := newTestSyncCtx(
|
||||
&v1.APIResourceList{
|
||||
GroupVersion: "argoproj.io/v1",
|
||||
APIResources: knownCustomResourceTypes,
|
||||
},
|
||||
&v1.APIResourceList{
|
||||
GroupVersion: "apiextensions.k8s.io/v1beta1",
|
||||
APIResources: []v1.APIResource{
|
||||
{Kind: "CustomResourceDefinition", Group: "apiextensions.k8s.io", Version: "v1beta1", Namespaced: true},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
cr := test.Unstructured(`
|
||||
{
|
||||
"apiVersion": "argoproj.io/v1",
|
||||
"kind": "TestCrd",
|
||||
"metadata": {
|
||||
"name": "my-resource"
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
if tt.fields.skipDryRunAnnotationPresent {
|
||||
cr.SetAnnotations(map[string]string{common.AnnotationSyncOptions: "SkipDryRunOnMissingResource=true"})
|
||||
}
|
||||
|
||||
resources := []managedResource{{Target: cr}}
|
||||
if tt.fields.crdInSameSync {
|
||||
resources = append(resources, managedResource{Target: test.NewCRD()})
|
||||
}
|
||||
|
||||
syncCtx.compareResult = &comparisonResult{managedResources: resources}
|
||||
|
||||
tasks, successful := syncCtx.getSyncTasks()
|
||||
|
||||
if successful != tt.wantSuccess {
|
||||
t.Errorf("successful = %v, want: %v", successful, tt.wantSuccess)
|
||||
return
|
||||
}
|
||||
|
||||
skipDryRun := false
|
||||
for _, task := range tasks {
|
||||
if task.targetObj.GetKind() == cr.GetKind() {
|
||||
skipDryRun = task.skipDryRun
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if tt.wantDryRun != !skipDryRun {
|
||||
t.Errorf("dryRun = %v, want: %v", !skipDryRun, tt.wantDryRun)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPersistRevisionHistory(t *testing.T) {
|
||||
app := newFakeApp()
|
||||
app.Status.OperationState = nil
|
||||
@@ -659,171 +100,3 @@ func TestPersistRevisionHistoryRollback(t *testing.T) {
|
||||
assert.Equal(t, source, updatedApp.Status.History[0].Source)
|
||||
assert.Equal(t, "abc123", updatedApp.Status.History[0].Revision)
|
||||
}
|
||||
|
||||
func TestSyncFailureHookWithSuccessfulSync(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{Target: test.NewPod()}},
|
||||
hooks: []*unstructured.Unstructured{test.NewHook(HookTypeSyncFail)},
|
||||
}
|
||||
|
||||
syncCtx.sync()
|
||||
|
||||
assert.Equal(t, OperationSucceeded, syncCtx.opState.Phase)
|
||||
// only one result, we did not run the failure failureHook
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
}
|
||||
|
||||
func TestSyncFailureHookWithFailedSync(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
pod := test.NewPod()
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{Target: pod}},
|
||||
hooks: []*unstructured.Unstructured{test.NewHook(HookTypeSyncFail)},
|
||||
}
|
||||
syncCtx.kubectl = &kubetest.MockKubectlCmd{
|
||||
Commands: map[string]kubetest.KubectlOutput{pod.GetName(): {Err: fmt.Errorf("")}},
|
||||
}
|
||||
|
||||
syncCtx.sync()
|
||||
syncCtx.sync()
|
||||
|
||||
assert.Equal(t, OperationFailed, syncCtx.opState.Phase)
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 2)
|
||||
}
|
||||
|
||||
func TestBeforeHookCreation(t *testing.T) {
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
hook := test.Annotate(test.Annotate(test.NewPod(), common.AnnotationKeyHook, "Sync"), common.AnnotationKeyHookDeletePolicy, "BeforeHookCreation")
|
||||
hook.SetNamespace(test.FakeArgoCDNamespace)
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{newManagedResource(hook)},
|
||||
hooks: []*unstructured.Unstructured{hook},
|
||||
}
|
||||
syncCtx.dynamicIf = fake.NewSimpleDynamicClient(runtime.NewScheme())
|
||||
|
||||
syncCtx.sync()
|
||||
assert.Len(t, syncCtx.syncRes.Resources, 1)
|
||||
assert.Empty(t, syncCtx.syncRes.Resources[0].Message)
|
||||
}
|
||||
|
||||
func TestRunSyncFailHooksFailed(t *testing.T) {
|
||||
// Tests that other SyncFail Hooks run even if one of them fail.
|
||||
|
||||
syncCtx := newTestSyncCtx()
|
||||
syncCtx.syncOp.SyncStrategy.Apply = nil
|
||||
pod := test.NewPod()
|
||||
successfulSyncFailHook := test.NewHook(HookTypeSyncFail)
|
||||
successfulSyncFailHook.SetName("successful-sync-fail-hook")
|
||||
failedSyncFailHook := test.NewHook(HookTypeSyncFail)
|
||||
failedSyncFailHook.SetName("failed-sync-fail-hook")
|
||||
syncCtx.compareResult = &comparisonResult{
|
||||
managedResources: []managedResource{{Target: pod}},
|
||||
hooks: []*unstructured.Unstructured{successfulSyncFailHook, failedSyncFailHook},
|
||||
}
|
||||
|
||||
syncCtx.kubectl = &kubetest.MockKubectlCmd{
|
||||
Commands: map[string]kubetest.KubectlOutput{
|
||||
// Fail operation
|
||||
pod.GetName(): {Err: fmt.Errorf("")},
|
||||
// Fail a single SyncFail hook
|
||||
failedSyncFailHook.GetName(): {Err: fmt.Errorf("")}},
|
||||
}
|
||||
|
||||
syncCtx.sync()
|
||||
syncCtx.sync()
|
||||
|
||||
fmt.Println(syncCtx.syncRes.Resources)
|
||||
fmt.Println(syncCtx.opState.Phase)
|
||||
// Operation as a whole should fail
|
||||
assert.Equal(t, OperationFailed, syncCtx.opState.Phase)
|
||||
// failedSyncFailHook should fail
|
||||
assert.Equal(t, OperationFailed, syncCtx.syncRes.Resources[1].HookPhase)
|
||||
assert.Equal(t, ResultCodeSyncFailed, syncCtx.syncRes.Resources[1].Status)
|
||||
// successfulSyncFailHook should be synced running (it is an nginx pod)
|
||||
assert.Equal(t, OperationRunning, syncCtx.syncRes.Resources[2].HookPhase)
|
||||
assert.Equal(t, ResultCodeSynced, syncCtx.syncRes.Resources[2].Status)
|
||||
}
|
||||
|
||||
func Test_syncContext_isSelectiveSync(t *testing.T) {
|
||||
type fields struct {
|
||||
compareResult *comparisonResult
|
||||
syncResources []SyncOperationResource
|
||||
}
|
||||
oneSyncResource := []SyncOperationResource{{}}
|
||||
oneResource := func(group, kind, name string, hook bool) *comparisonResult {
|
||||
return &comparisonResult{resources: []v1alpha1.ResourceStatus{{Group: group, Kind: kind, Name: name, Hook: hook}}}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want bool
|
||||
}{
|
||||
{"Empty", fields{}, false},
|
||||
{"OneCompareResult", fields{oneResource("", "", "", false), []SyncOperationResource{}}, true},
|
||||
{"OneSyncResource", fields{&comparisonResult{}, oneSyncResource}, true},
|
||||
{"Equal", fields{oneResource("", "", "", false), oneSyncResource}, false},
|
||||
{"EqualOutOfOrder", fields{&comparisonResult{resources: []v1alpha1.ResourceStatus{{Group: "a"}, {Group: "b"}}}, []SyncOperationResource{{Group: "b"}, {Group: "a"}}}, false},
|
||||
{"KindDifferent", fields{oneResource("foo", "", "", false), oneSyncResource}, true},
|
||||
{"GroupDifferent", fields{oneResource("", "foo", "", false), oneSyncResource}, true},
|
||||
{"NameDifferent", fields{oneResource("", "", "foo", false), oneSyncResource}, true},
|
||||
{"HookIgnored", fields{oneResource("", "", "", true), []SyncOperationResource{}}, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sc := &syncContext{
|
||||
compareResult: tt.fields.compareResult,
|
||||
syncResources: tt.fields.syncResources,
|
||||
}
|
||||
if got := sc.isSelectiveSync(); got != tt.want {
|
||||
t.Errorf("syncContext.isSelectiveSync() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_syncContext_liveObj(t *testing.T) {
|
||||
type fields struct {
|
||||
compareResult *comparisonResult
|
||||
}
|
||||
type args struct {
|
||||
obj *unstructured.Unstructured
|
||||
}
|
||||
obj := test.NewPod()
|
||||
obj.SetNamespace("my-ns")
|
||||
|
||||
found := test.NewPod()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *unstructured.Unstructured
|
||||
}{
|
||||
{"None", fields{compareResult: &comparisonResult{managedResources: []managedResource{}}}, args{obj: &unstructured.Unstructured{}}, nil},
|
||||
{"Found", fields{compareResult: &comparisonResult{managedResources: []managedResource{{Group: obj.GroupVersionKind().Group, Kind: obj.GetKind(), Namespace: obj.GetNamespace(), Name: obj.GetName(), Live: found}}}}, args{obj: obj}, found},
|
||||
{"EmptyNamespace", fields{compareResult: &comparisonResult{managedResources: []managedResource{{Group: obj.GroupVersionKind().Group, Kind: obj.GetKind(), Name: obj.GetName(), Live: found}}}}, args{obj: obj}, found},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sc := &syncContext{
|
||||
compareResult: tt.fields.compareResult,
|
||||
}
|
||||
if got := sc.liveObj(tt.args.obj); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("syncContext.liveObj() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_syncContext_hasCRDOfGroupKind(t *testing.T) {
|
||||
// target
|
||||
assert.False(t, (&syncContext{compareResult: &comparisonResult{managedResources: []managedResource{{Target: test.NewCRD()}}}}).hasCRDOfGroupKind("", ""))
|
||||
assert.True(t, (&syncContext{compareResult: &comparisonResult{managedResources: []managedResource{{Target: test.NewCRD()}}}}).hasCRDOfGroupKind("argoproj.io", "TestCrd"))
|
||||
// hook
|
||||
assert.False(t, (&syncContext{compareResult: &comparisonResult{hooks: []*unstructured.Unstructured{test.NewCRD()}}}).hasCRDOfGroupKind("", ""))
|
||||
assert.True(t, (&syncContext{compareResult: &comparisonResult{hooks: []*unstructured.Unstructured{test.NewCRD()}}}).hasCRDOfGroupKind("argoproj.io", "TestCrd"))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
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 obvous which bugs are important. Which bugs should be fixed
|
||||
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?
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ If you want to to submit a PR, please read this document carefully, as it contai
|
||||
|
||||
As is the case with the development process, this document is under constant change. If you notice any error, or if you think this document is out-of-date, or if you think it is missing something: Feel free to submit a PR or submit a bug to our GitHub issue tracker.
|
||||
|
||||
If you need guidance with submitting a PR, or have any other questions regarding development of ArgoCD, do not hestitate to [join our Slack](https://argoproj.github.io/community/join-slack) and get in touch with us in the `#argo-dev` channel!
|
||||
If you need guidance with submitting a PR, or have any other questions regarding development of ArgoCD, do not hesitate to [join our Slack](https://argoproj.github.io/community/join-slack) and get in touch with us in the `#argo-dev` channel!
|
||||
|
||||
## Before you start
|
||||
|
||||
@@ -23,7 +23,7 @@ The Docker version must be fairly recent, and support multi-stage builds. You sh
|
||||
|
||||
* Obviously, you will need a `git` client for pulling source code and pushing back your changes.
|
||||
|
||||
* Last but not least, you will need a Go SDK and related tools (such as GNU `make`) installed and working on your development environment.
|
||||
* Last but not least, you will need a Go SDK and related tools (such as GNU `make`) installed and working on your development environment. The minimum required Go version for building ArgoCD is **v1.14.0**.
|
||||
|
||||
* We will assume that your Go workspace is at `~/go`
|
||||
|
||||
@@ -34,7 +34,7 @@ The Docker version must be fairly recent, and support multi-stage builds. You sh
|
||||
|
||||
When you submit a PR against ArgoCD's GitHub repository, a couple of CI checks will be run automatically to ensure your changes will build fine and meet certain quality standards. Your contribution needs to pass those checks in order to be merged into the repository.
|
||||
|
||||
In general, it might be benefical to only submit a PR for an existing issue. Especially for larger changes, an Enhancement Proposal should exist before.
|
||||
In general, it might be beneficial to only submit a PR for an existing issue. Especially for larger changes, an Enhancement Proposal should exist before.
|
||||
|
||||
!!!note
|
||||
|
||||
@@ -46,7 +46,7 @@ The following read will help you to submit a PR that meets the standards of our
|
||||
|
||||
### Title of the PR
|
||||
|
||||
Please use a meaningful and consise title for your PR. This will help us to pick PRs for review quickly, and the PR title will also end up in the Changelog.
|
||||
Please use a meaningful and concise title for your PR. This will help us to pick PRs for review quickly, and the PR title will also end up in the Changelog.
|
||||
|
||||
We use the [Semantic PR title checker](https://github.com/zeke/semantic-pull-requests) to categorize your PR into one of the following categories:
|
||||
|
||||
@@ -162,12 +162,15 @@ When you have developed and possibly manually tested the code you want to contri
|
||||
|
||||
### Pull in all build dependencies
|
||||
|
||||
As build dependencies change over time, you have to synchronize your development environment with the current specification. In order to pull in all required depencies, issue:
|
||||
As build dependencies change over time, you have to synchronize your development environment with the current specification. In order to pull in all required dependencies, issue:
|
||||
|
||||
* `make dep`
|
||||
* `make dep-ensure`
|
||||
* `make dep-ui`
|
||||
|
||||
ArgoCD recently migrated to Go modules. Usually, dependencies will be downloaded on build time, but the Makefile provides two targets to download and vendor all dependencies:
|
||||
|
||||
* `make mod-download` will download all required Go modules and
|
||||
* `make mod-vendor` will vendor those dependencies into the ArgoCD source tree
|
||||
|
||||
### Generate API glue code and other assets
|
||||
|
||||
ArgoCD relies on Google's [Protocol Buffers](https://developers.google.com/protocol-buffers) for its API, and this makes heavy use of auto-generated glue code and stubs. Whenever you touched parts of the API code, you must re-generate the auto generated code.
|
||||
@@ -213,7 +216,7 @@ For development, you can either use the fully virtualized toolchain provided as
|
||||
!!!note
|
||||
The installations instructions are valid for Linux hosts only. Mac instructions will follow shortly.
|
||||
|
||||
For installing the tools required to build and test ArgoCD on your local system, we provide convinient installer scripts. By default, they will install binaries to `/usr/local/bin` on your system, which might require `root` privileges.
|
||||
For installing the tools required to build and test ArgoCD on your local system, we provide convenient installer scripts. By default, they will install binaries to `/usr/local/bin` on your system, which might require `root` privileges.
|
||||
|
||||
You can change the target location by setting the `BIN` environment before running the installer scripts. For example, you can install the binaries into `~/go/bin` (which should then be the first component in your `PATH` environment, i.e. `export PATH=~/go/bin:$PATH`):
|
||||
|
||||
@@ -230,8 +233,8 @@ Additionally, you have to install at least the following tools via your OS's pac
|
||||
|
||||
You need to pull in all required Go dependencies. To do so, run
|
||||
|
||||
* `make dep-ensure-local`
|
||||
* `make dep-local`
|
||||
* `make mod-download-local`
|
||||
* `make mod-vendor-local`
|
||||
|
||||
### Test your build toolchain
|
||||
|
||||
|
||||
@@ -1,5 +1,155 @@
|
||||
# Releasing
|
||||
|
||||
## Automated release procedure
|
||||
|
||||
Starting from `release-1.6` branch, ArgoCD can be released in automatic fashion
|
||||
using GitHub actions. The release process takes about 20 minutes, sometimes a
|
||||
little less, depending on the performance of GitHub actions runners.
|
||||
|
||||
The target release branch must already exist in GitHub repository. If you for
|
||||
example want to create a release `v1.7.0`, the corresponding release branch
|
||||
`release-1.7` needs to exist, otherwise the release cannot be build. Also,
|
||||
the trigger tag should always be created in the release branch, checked out
|
||||
in your local repository clone.
|
||||
|
||||
Before triggering the release automation, the `CHANGELOG.md` should be updated
|
||||
with the latest information, and this change should be commited and pushed to
|
||||
the GitHub repository to the release branch. Afterwards, the automation can be
|
||||
triggered.
|
||||
|
||||
**Manual steps before release creation:**
|
||||
|
||||
* Update `CHANGELOG.md` with changes for this release
|
||||
* Commit & push changes to `CHANGELOG.md`
|
||||
* Prepare release notes (save to some file, or copy from Changelog)
|
||||
|
||||
**The automation will perform the following steps:**
|
||||
|
||||
* Update `VERSION` file in release branch
|
||||
* Update manifests with image tags of new version in release branch
|
||||
* Build the Docker image and push to Docker Hub
|
||||
* Create release tag in the GitHub repository
|
||||
* Create GitHub release and attach the required assets to it (CLI binaries, ...)
|
||||
|
||||
Finally, it will the remove trigger tag from repository again.
|
||||
|
||||
Automation supports both, GA and pre-releases. The automation is triggered by
|
||||
pushing a tag to the repository. The tag must be in one of the following formats
|
||||
to trigger the GH workflow:
|
||||
|
||||
* GA: `release-v<MAJOR>.<MINOR>.<PATCH>`
|
||||
* Pre-release: `release-v<MAJOR>.<MINOR>.<PATCH>-rc<RC#>`
|
||||
|
||||
The tag must be an annotated tag, and it must contain the release notes in the
|
||||
commit message. Please note that Markdown uses `#` character for formatting, but
|
||||
Git uses it as comment char. To solve this, temporarily switch Git comment char
|
||||
to something else, the `;` character is recommended.
|
||||
|
||||
For example, considering you have configured the Git remote for repository to
|
||||
`github.com/argoproj/argo-cd` to be named `upstream` and are in your locally
|
||||
checked out repo:
|
||||
|
||||
```shell
|
||||
git config core.commentChar ';'
|
||||
git tag -a -F /path/to/release-notes.txt release-v1.6.0-rc2
|
||||
git push upstream release-v1.6.0-rc2
|
||||
git tag -d release-v1.6.0-rc2
|
||||
git config core.commentChar '#'
|
||||
|
||||
```
|
||||
|
||||
For convenience, there is a shell script in the tree that ensures all the
|
||||
pre-requisites are met and that the trigger is well-formed before pushing
|
||||
it to the GitHub repo.
|
||||
|
||||
In summary, the modifications it does are:
|
||||
|
||||
* Create annotated trigger tag in your local repository
|
||||
* Push tag to GitHub repository to trigger workflow
|
||||
* Remove trigger tag from your local repository
|
||||
|
||||
The script can be found at `hacks/trigger-release.sh` and is used as follows:
|
||||
|
||||
```shell
|
||||
./hacks/trigger-release.sh <version> <remote name> [<release notes path>]
|
||||
```
|
||||
|
||||
The `<version>` identifier needs to be specified **without** the `release-`
|
||||
prefix, so just specify it as `v1.6.0-rc2` for example. The `<remote name>`
|
||||
specifies the name of the remote used to push to the GitHub repository.
|
||||
|
||||
If you omit the `<release notes path>`, an editor will pop-up asking you to
|
||||
enter the tag's annotation so you can paste the release notes, save and exit.
|
||||
It will also take care of temporarily configuring the `core.commentChar` and
|
||||
setting it back to its original state.
|
||||
|
||||
!!!note
|
||||
It is strongly recommended to use this script to trigger the workflow
|
||||
instead of manually pushing a tag to the repository.
|
||||
|
||||
Once the trigger tag is pushed to the repo, the GitHub workflow will start
|
||||
execution. You can follow its progress under `Actions` tab, the name of the
|
||||
action is `Create release`. Don't get confused by the name of the running
|
||||
workflow, it will be the commit message of the latest commit to `master`
|
||||
branch, this is a limitation of GH actions.
|
||||
|
||||
The workflow performs necessary checks so that the release can be sucessfully
|
||||
build before the build actually starts. It will error when one of the
|
||||
prerequisites is not met, or if the release cannot be build (i.e. already
|
||||
exists, release notes invalid, etc etc). You can see a summary of what has
|
||||
failed in the job's overview page, and more detailed errors in the output
|
||||
of the step that has failed.
|
||||
|
||||
!!!note
|
||||
You cannot perform more than one release on the same release branch at the
|
||||
same time. For example, both `v1.6.0` and `v1.6.1` would operate on the
|
||||
`release-1.6` branch. If you submit `v1.6.1` while `v1.6.0` is still
|
||||
executing, the release automation will not execute. You have to either
|
||||
cancel `v1.6.0` before submitting `v1.6.1` or wait until it has finished.
|
||||
You can execute releases on different release branches simultaneously, for
|
||||
example `v1.6.0` and `v1.7.0-rc1`, without problems.
|
||||
|
||||
### Verifying automated release
|
||||
|
||||
After the automatic release creation has finished, you should perform manual
|
||||
checks to see if the release came out correctly:
|
||||
|
||||
* Check status & output of the GitHub action
|
||||
* Check [https://github.com/argoproj/argo-cd/releases](https://github.com/argoproj/argo-cd/releases)
|
||||
to see if release has been correctly created, and if all required assets
|
||||
are attached.
|
||||
* Check whether the image has been published on DockerHub correctly
|
||||
|
||||
### If something went wrong
|
||||
|
||||
If something went wrong, damage should be limited. Depending on the steps that
|
||||
have been performed, you will need to manually clean up.
|
||||
|
||||
* Delete release tag (i.e. `v1.6.0-rc2`) created on GitHub repository. This
|
||||
will immediately set release (if created) to `draft` status, invisible for
|
||||
general public.
|
||||
* Delete the draft release (if created) from `Releases` page on GitHub
|
||||
* If Docker image has been pushed to DockerHub, delete it
|
||||
* If commits have been performed to the release branch, revert them. Paths that could have been commited to are:
|
||||
* `VERSION`
|
||||
* `manifests/*`
|
||||
|
||||
### Post-process manual steps
|
||||
|
||||
For now, the only manual steps left are to
|
||||
|
||||
* update brew formulae for ArgoCD CLI on Mac if release is GA
|
||||
* update stable tag in GitHub repository to point to new release (if appropriate)
|
||||
|
||||
These will be automated as well in the future.
|
||||
|
||||
## Manual releasing
|
||||
|
||||
Automatic release process does not interfere with manual release process, since
|
||||
the trigger tag does not match a normal release tag. If you prefer to perform,
|
||||
manual release or if automatic release is for some reason broken, these are the
|
||||
steps:
|
||||
|
||||
Make sure you are logged into Docker Hub:
|
||||
|
||||
```bash
|
||||
@@ -42,18 +192,14 @@ git push $REPO $BRANCH
|
||||
git push $REPO $VERSION
|
||||
```
|
||||
|
||||
If GA, update `stable` tag:
|
||||
|
||||
```bash
|
||||
git tag stable --force && git push $REPO stable --force
|
||||
```
|
||||
|
||||
Update [Github releases](https://github.com/argoproj/argo-cd/releases) with:
|
||||
|
||||
* Getting started (copy from previous release)
|
||||
* Changelog
|
||||
* Binaries (e.g. `dist/argocd-darwin-amd64`).
|
||||
|
||||
## Update brew formulae (manual)
|
||||
|
||||
If GA, update Brew formula:
|
||||
|
||||
```bash
|
||||
@@ -64,7 +210,15 @@ git commit -am "Update argocd to $VERSION"
|
||||
git push
|
||||
```
|
||||
|
||||
### Verify
|
||||
## Update stable tag (manual)
|
||||
|
||||
If GA, update `stable` tag:
|
||||
|
||||
```bash
|
||||
git tag stable --force && git push $REPO stable --force
|
||||
```
|
||||
|
||||
## Verify release
|
||||
|
||||
Locally:
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
During development, it might be viable to run ArgoCD outside of a Kubernetes cluster. This will greatly speed up development, as you don't have to constantly build, push and install new ArgoCD Docker images with your latest changes.
|
||||
|
||||
You will still need a working Kubernetes cluster, as described in the [contributing.md](Contribution Guide), where ArgoCD will store all of its resources.
|
||||
You will still need a working Kubernetes cluster, as described in the [Contribution Guide](contributing.md), where ArgoCD will store all of its resources.
|
||||
|
||||
If you followed the [contributing.md](Contribution Guide) in setting up your toolchain, you can run ArgoCD locally with these simple steps:
|
||||
If you followed the [Contribution Guide](contributing.md) in setting up your toolchain, you can run ArgoCD locally with these simple steps:
|
||||
|
||||
### Scale down any ArgoCD instance in your cluster
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ This will create a new namespace, `argocd`, where Argo CD services and applicati
|
||||
On GKE, you will need grant your account the ability to create new cluster roles:
|
||||
|
||||
```bash
|
||||
kubectl create clusterrolebinding YOURNAME-cluster-admin-binding --clusterrole=cluster-admin --user=YOUREMAIL@gmail.com
|
||||
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user="$(gcloud config get-value account)"
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -9,7 +9,7 @@ metadata:
|
||||
type: Opaque
|
||||
data:
|
||||
# TLS certificate and private key for API server (required).
|
||||
# Autogenerated with a self-signed ceritificate when keys are missing or invalid.
|
||||
# Autogenerated with a self-signed certificate when keys are missing or invalid.
|
||||
tls.crt:
|
||||
tls.key:
|
||||
|
||||
|
||||
@@ -4,15 +4,20 @@ Argo CD applications, projects and settings can be defined declaratively using K
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Name | Kind | Description |
|
||||
|------|------|-------------|
|
||||
| [`argocd-cm.yaml`](argocd-cm.yaml) | ConfigMap | General Argo CD configuration |
|
||||
| [`argocd-secret.yaml`](argocd-secret.yaml) | Secret | Password, Certificates, Signing Key |
|
||||
| [`argocd-rbac-cm.yaml`](argocd-rbac-cm.yaml) | ConfigMap | RBAC Configuration |
|
||||
| [`argocd-tls-certs-cm.yaml`](argocd-tls-certs-cm.yaml) | ConfigMap | Custom TLS certificates for connecting Git repositories via HTTPS (v1.2 and later) |
|
||||
| [`argocd-ssh-known-hosts-cm.yaml`](argocd-ssh-known-hosts-cm.yaml) | ConfigMap | SSH known hosts data for connecting Git repositories via SSH (v1.2 and later) |
|
||||
| [`application.yaml`](application.yaml) | Application | Example application spec |
|
||||
| [`project.yaml`](project.yaml) | AppProject | Example project spec |
|
||||
| File Name | Resource Name | Kind | Description |
|
||||
|-----------|---------------|------|-------------|
|
||||
| [`argocd-cm.yaml`](argocd-cm.yaml) | argocd-cm | ConfigMap | General Argo CD configuration |
|
||||
| [`argocd-secret.yaml`](argocd-secret.yaml) | argocd-secret | Secret | Password, Certificates, Signing Key |
|
||||
| [`argocd-rbac-cm.yaml`](argocd-rbac-cm.yaml) | argocd-rbac-cm | ConfigMap | RBAC Configuration |
|
||||
| [`argocd-tls-certs-cm.yaml`](argocd-tls-certs-cm.yaml) | argocd-tls-certs-cm | ConfigMap | Custom TLS certificates for connecting Git repositories via HTTPS (v1.2 and later) |
|
||||
| [`argocd-ssh-known-hosts-cm.yaml`](argocd-ssh-known-hosts-cm.yaml) | argocd-ssh-known-hosts-cm | ConfigMap | SSH known hosts data for connecting Git repositories via SSH (v1.2 and later) |
|
||||
| [`application.yaml`](application.yaml) | - | Application | Example application spec |
|
||||
| [`project.yaml`](project.yaml) | - | AppProject | Example project spec |
|
||||
|
||||
All resources, including `Application` and `AppProject` specs, have to be installed in the ArgoCD namespace (by default `argocd`). Also, ConfigMap and Secret resources need to be named as shown in the table above. For `Application` and `AppProject` resources, the name of the resource equals the name of the application or project within ArgoCD. This also means that application and project names are unique within the same ArgoCD installation - you cannot i.e. have the same application name for two different applications.
|
||||
|
||||
!!!warning "A note about ConfigMap resources"
|
||||
Be sure to annotate your ConfigMap resources using the label `app.kubernetes.io/part-of: argocd`, otherwise ArgoCD will not be able to use them.
|
||||
|
||||
## Applications
|
||||
|
||||
@@ -101,7 +106,7 @@ spec:
|
||||
- group: ''
|
||||
kind: NetworkPolicy
|
||||
# Deny all namespaced-scoped resources from being created, except for Deployment and StatefulSet
|
||||
namespaceResourceWhilelist:
|
||||
namespaceResourceWhitelist:
|
||||
- group: 'apps'
|
||||
kind: Deployment
|
||||
- group: 'apps'
|
||||
@@ -442,7 +447,7 @@ tlsClientConfig:
|
||||
# PEM-encoded bytes (typically read from a client certificate key file).
|
||||
keyData: string
|
||||
# ServerName is passed to the server for SNI and is used in the client to check server
|
||||
# ceritificates against. If ServerName is empty, the hostname used to contact the
|
||||
# certificates against. If ServerName is empty, the hostname used to contact the
|
||||
# server is used.
|
||||
serverName: string
|
||||
```
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Overview
|
||||
Argo CD provides built-in health assessment for several standard Kubernetes types, which is then
|
||||
surfaced to the overall Application health status as a whole. The following checks are made for
|
||||
specific types of kuberentes resources:
|
||||
specific types of kubernetes resources:
|
||||
|
||||
### Deployment, ReplicaSet, StatefulSet DaemonSet
|
||||
* Observed generation is equal to desired generation.
|
||||
|
||||
@@ -246,6 +246,11 @@ http {
|
||||
}
|
||||
}
|
||||
```
|
||||
Flag ```--grpc-web-root-path ``` is used to provide a non-root path (e.g. /argo-cd)
|
||||
|
||||
```shell
|
||||
$ argocd login <host>:<port> --grpc-web-root-path /argo-cd
|
||||
```
|
||||
|
||||
## UI Base Path
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ spec:
|
||||
kind: NetworkPolicy
|
||||
|
||||
# Deny all namespaced-scoped resources from being created, except for Deployment and StatefulSet
|
||||
namespaceResourceWhilelist:
|
||||
namespaceResourceWhitelist:
|
||||
- group: 'apps'
|
||||
kind: Deployment
|
||||
- group: 'apps'
|
||||
|
||||
@@ -4,6 +4,7 @@ Argo CD is un-opinionated about how secrets are managed. There's many ways to do
|
||||
|
||||
* [Bitnami Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
|
||||
* [Godaddy Kubernetes External Secrets](https://github.com/godaddy/kubernetes-external-secrets)
|
||||
* [External Secrets Operator](https://github.com/ContainerSolutions/externalsecret-operator)
|
||||
* [Hashicorp Vault](https://www.vaultproject.io)
|
||||
* [Banzai Cloud Bank-Vaults](https://github.com/banzaicloud/bank-vaults)
|
||||
* [Helm Secrets](https://github.com/futuresimple/helm-secrets)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Security
|
||||
|
||||
Argo CD has undergone rigourous internal security reviews and penetration testing to satisfy [PCI
|
||||
Argo CD has undergone rigorous internal security reviews and penetration testing to satisfy [PCI
|
||||
compliance](https://www.pcisecuritystandards.org) requirements. The following are some security
|
||||
topics and implementation details of Argo CD.
|
||||
|
||||
@@ -17,7 +17,7 @@ in one of the following ways:
|
||||
|
||||
2. For Single Sign-On users, the user completes an OAuth2 login flow to the configured OIDC identity
|
||||
provider (either delegated through the bundled Dex provider, or directly to a self-managed OIDC
|
||||
provider). This JWT is signed & issued by the IDP, and expiration and revokation is handled by
|
||||
provider). This JWT is signed & issued by the IDP, and expiration and revocation is handled by
|
||||
the provider. Dex tokens expire after 24 hours.
|
||||
|
||||
3. Automation tokens are generated for a project using the `/api/v1/projects/{project}/roles/{role}/token`
|
||||
@@ -60,7 +60,7 @@ The information is used to reconstruct a REST config and kubeconfig to the clust
|
||||
services.
|
||||
|
||||
To rotate the bearer token used by Argo CD, the token can be deleted (e.g. using kubectl) which
|
||||
causes kuberentes to generate a new secret with a new bearer token. The new token can be re-inputted
|
||||
causes kubernetes to generate a new secret with a new bearer token. The new token can be re-inputted
|
||||
to Argo CD by re-running `argocd cluster add`. Run the following commands against the *_managed_*
|
||||
cluster:
|
||||
|
||||
@@ -83,7 +83,7 @@ argocd cluster rm https://your-kubernetes-cluster-addr
|
||||
<!-- markdownlint-disable MD027 -->
|
||||
> NOTE: for AWS EKS clusters, [aws-iam-authenticator](https://github.com/kubernetes-sigs/aws-iam-authenticator)
|
||||
is used to authenticate to the external cluster, which uses IAM roles in lieu of locally stored
|
||||
tokens, so token rotation is not needed, and revokation is handled through IAM.
|
||||
tokens, so token rotation is not needed, and revocation is handled through IAM.
|
||||
<!-- markdownlint-enable MD027 -->
|
||||
|
||||
## Cluster RBAC
|
||||
|
||||
@@ -57,7 +57,14 @@ applied modifications.
|
||||
|
||||
```bash
|
||||
docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd:<version> \
|
||||
argocd-util settings resource-overrides action /tmp/deploy.yaml restart --argocd-cm-path /private/tmp/argocd-cm.yaml
|
||||
argocd-util settings resource-overrides run-action /tmp/deploy.yaml restart --argocd-cm-path /private/tmp/argocd-cm.yaml
|
||||
```
|
||||
|
||||
The following `argocd-util` command lists actions available for a given resource using Lua script configured in the specified ConfigMap.
|
||||
|
||||
```bash
|
||||
docker run --rm -it -w /src -v $(pwd):/src argoproj/argocd:<version> \
|
||||
argocd-util settings resource-overrides list-actions /tmp/deploy.yaml --argocd-cm-path /private/tmp/argocd-cm.yaml
|
||||
```
|
||||
|
||||
## Cluster credentials
|
||||
|
||||
@@ -9,7 +9,15 @@ using `ARGOCD_LEGACY_CONTROLLER_METRICS=true` environment variable. The legacy e
|
||||
|
||||
## Redis HA Proxy
|
||||
|
||||
High-availability (HA) Argo CD manifests now bundles Redis in HA Proxy in front of it. During the upgrade Redis requests
|
||||
might fail you might see intermittent login failures.
|
||||
!!! warning
|
||||
Manual intervention might be required to complete the upgrade.
|
||||
|
||||
High-availability (HA) Argo CD manifests now bundles Redis in HA Proxy in front of it. Following issue have been
|
||||
observed during the upgrade:
|
||||
|
||||
* you might see intermittent login failures;
|
||||
* after upgrade is completed ha proxy might be unable to access redis server
|
||||
(see [argo-cd#3547](https://github.com/argoproj/argo-cd/issues/3547), [DandyDeveloper/charts#26](https://github.com/DandyDeveloper/charts/issues/26)).
|
||||
As workaround "restart" `argocd-redis-ha-haproxy` Deployment and `argocd-redis-ha-server` StatefulSet.
|
||||
|
||||
From here on you can follow the [regular upgrade process](./overview.md).
|
||||
@@ -41,7 +41,7 @@ data:
|
||||
- openid
|
||||
- profile
|
||||
- email
|
||||
# not strictly nesscessary - but good practice:
|
||||
# not strictly necessary - but good practice:
|
||||
- 'http://your.domain/groups'
|
||||
...
|
||||
```
|
||||
|
||||
@@ -5,7 +5,7 @@ for initial configuration and then switch to local users or configure SSO integr
|
||||
|
||||
## Local users/accounts (v1.5)
|
||||
|
||||
The local users/accounts feature serving to main use-cases:
|
||||
The local users/accounts feature serves two main use-cases:
|
||||
|
||||
* Auth tokens for Argo CD management automation. It is possible to configure an API account with limited permissions and generate an authentication token.
|
||||
Such token can be used to automatically create applications, projects etc.
|
||||
@@ -42,6 +42,7 @@ data:
|
||||
```
|
||||
|
||||
Each user might have two capabilities:
|
||||
|
||||
* apiKey - allows generating authentication tokens for API access
|
||||
* login - allows to login using UI
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ A working Single Sign-On configuration using Okta via at least two methods was a
|
||||
|
||||
1. Create a new SAML application in Okta UI.
|
||||
* 
|
||||
I've disabled `App Visibility` because Dex doesn't support Provider-initated login flows.
|
||||
I've disabled `App Visibility` because Dex doesn't support Provider-initiated login flows.
|
||||
* 
|
||||
1. Click `View setup instructions` after creating the application in Okta.
|
||||
* 
|
||||
@@ -50,7 +50,7 @@ dex.config: |
|
||||
## OIDC (without Dex)
|
||||
|
||||
!!! warning "Do you want groups for RBAC later?"
|
||||
If you want `groups` scope returned from Okta you need to unforunately contact support to enable [API Access Management with Okta](https://developer.okta.com/docs/concepts/api-access-management/) or [_just use SAML above!_](#saml-with-dex)
|
||||
If you want `groups` scope returned from Okta you need to unfortunately contact support to enable [API Access Management with Okta](https://developer.okta.com/docs/concepts/api-access-management/) or [_just use SAML above!_](#saml-with-dex)
|
||||
|
||||
Next you may need the API Access Management feature, which the support team can enable for your OktaPreview domain for testing, to enable "custom scopes" and a separate endpoint to use instead of the "public" `/oauth2/v1/authorize` API Access Management endpoint. This might be a paid feature if you want OIDC unfortunately. The free alternative I found was SAML.
|
||||
|
||||
|
||||
@@ -6,3 +6,160 @@
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<div style="text-align:center"><img src="../../../assets/argo.png" /></div>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
# Integrating OneLogin and ArgoCD
|
||||
|
||||
These instructions will take you through the entire process of getting your ArgoCD application authenticating with OneLogin. You will create a custom OIDC application within OneLogin and configure ArgoCD to use OneLogin for authentication, using UserRoles set in OneLogin to determine privileges in Argo.
|
||||
|
||||
## Creating and Configuring OneLogin App
|
||||
|
||||
For your ArgoCD application to communicate with OneLogin, you will first need to create and configure the OIDC application on the OneLogin side.
|
||||
|
||||
### Create OIDC Application
|
||||
|
||||
To create the application, do the following:
|
||||
|
||||
1. Navigate to your OneLogin portal, then Administration > Applications.
|
||||
2. Click "Add App".
|
||||
3. Search for "OpenID Connect" in the search field.
|
||||
4. Select the "OpenId Connect (OIDC)" app to create.
|
||||
5. Update the "Display Name" field (could be something like "ArgoCD (Production)".
|
||||
6. Click "Save".
|
||||
|
||||
### Configuring OIDC Application Settings
|
||||
|
||||
Now that the application is created, you can configure the settings of the app.
|
||||
|
||||
#### Configuration Tab
|
||||
|
||||
Update the "Configuration" settings as follows:
|
||||
|
||||
1. Select the "Configuration" tab on the left.
|
||||
2. Set the "Login Url" field to https://argocd.myproject.com/auth/login, replacing the hostname with your own.
|
||||
3. Set the "Redirect Url" field to https://argocd.myproject.com/auth/callback, replacing the hostname with your own.
|
||||
4. Click "Save".
|
||||
|
||||
!!! note "OneLogin may not let you save any other fields until the above fields are set."
|
||||
|
||||
#### Info Tab
|
||||
|
||||
You can update the "Display Name", "Description", "Notes", or the display images that appear in the OneLogin portal here.
|
||||
|
||||
#### Parameters Tab
|
||||
|
||||
This tab controls what information is sent to Argo in the token. By default it will contain a Groups field and "Credentials are" is set to "Configured by admin". Leave "Credentials are" as the default.
|
||||
|
||||
How the Value of the Groups field is configured will vary based on your needs, but to use OneLogin User roles for ArgoCD privileges, configure the Value of the Groups field with the following:
|
||||
|
||||
1. Click "Groups". A modal appears.
|
||||
2. Set the "Default if no value selected" field to "User Roles".
|
||||
3. Set the transform field (below it) to "Semicolon Delimited Input".
|
||||
4. Click "Save".
|
||||
|
||||
When a user attempts to login to Argo with OneLogin, the User roles in OneLogin, say, Manager, ProductTeam, and TestEngineering, will be included in the Groups field in the token. These are the values needed for Argo to assign permissions.
|
||||
|
||||
The groups field in the token will look similar to the following:
|
||||
|
||||
```
|
||||
"groups": [
|
||||
"Manager",
|
||||
"ProductTeam",
|
||||
"TestEngineering",
|
||||
],
|
||||
```
|
||||
|
||||
#### Rules Tab
|
||||
|
||||
To get up and running, you do not need to make modifications to any settings here.
|
||||
|
||||
#### SSO Tab
|
||||
|
||||
This tab contains much of the information needed to be placed into your ArgoCD configuration file (API endpoints, client ID, client secret).
|
||||
|
||||
Confirm "Application Type" is set to "Web".
|
||||
|
||||
Confirm "Token Endpoint" is set to "Basic".
|
||||
|
||||
#### Access Tab
|
||||
|
||||
This tab controls who can see this application in the OneLogin portal.
|
||||
|
||||
Select the roles you wish to have access to this application and click "Save".
|
||||
|
||||
#### Users Tab
|
||||
|
||||
This tab shows you the individual users that have access to this application (usually the ones that have roles specified in the Access Tab).
|
||||
|
||||
To get up and running, you do not need to make modifications to any settings here.
|
||||
|
||||
#### Privileges Tab
|
||||
|
||||
This tab shows which OneLogin users can configure this app.
|
||||
|
||||
To get up and running, you do not need to make modifications to any settings here.
|
||||
|
||||
## Updating OIDC configuration in ArgoCD
|
||||
|
||||
Now that the OIDC application is configured in OneLogin, you can update Argo configuration to communicate with OneLogin, as well as control permissions for those users that authenticate via OneLogin.
|
||||
|
||||
### Tell Argo where OneLogin is
|
||||
|
||||
Argo needs to have its config map (argocd-cm) updated in order to communicate with OneLogin. Consider the following yaml:
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
data:
|
||||
url: https://<argocd.myproject.com>
|
||||
oidc.config: |
|
||||
name: OneLogin
|
||||
issuer: https://openid-connect.onelogin.com/oidc
|
||||
clientID: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaaaaaaaa
|
||||
# This is a base64 encoded value!
|
||||
clientSecret: YWFiYmNjZGQ=
|
||||
|
||||
# Optional set of OIDC scopes to request. If omitted, defaults to: ["openid", "profile", "email", "groups"]
|
||||
requestedScopes: ["openid", "profile", "email", "groups"]
|
||||
```
|
||||
|
||||
The "url" key should have a value of the hostname of your Argo project.
|
||||
|
||||
The "clientID" is taken from the SSO tab of the OneLogin application.
|
||||
|
||||
The “issuer” is taken from the SSO tab of the OneLogin application. It is one of the issuer api endpoints.
|
||||
|
||||
The "clientSecret" value should be a base64 encoded version of the client secret located in the SSO tab of the OneLogin application. To generate this value, you can take the value of the client secret in OneLogin, and pipe it into base64 in a Linux/Unix terminal like so:
|
||||
|
||||
```
|
||||
$ echo -n "aabbccdd" | base64
|
||||
YWFiYmNjZGQ=
|
||||
```
|
||||
|
||||
!!! note "If you get an `invalid_client` error when trying the authenticate with OneLogin, there is a possibility that your client secret was not [correctly] base64 encoded.""
|
||||
|
||||
### Configure Permissions for OneLogin Auth'd Users
|
||||
|
||||
Permissions in ArgoCD can be configured by using the OneLogin role names that are passed in the Groups field in the token. Consider the following yaml in argocd-rbac-cm.yaml:
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-rbac-cm
|
||||
namespace: argocd
|
||||
data:
|
||||
policy.default: role:readonly
|
||||
policy.csv: |
|
||||
p, role:org-admin, applications, *, */*, allow
|
||||
p, role:org-admin, clusters, get, *, allow
|
||||
p, role:org-admin, repositories, get, *, allow
|
||||
p, role:org-admin, repositories, create, *, allow
|
||||
p, role:org-admin, repositories, update, *, allow
|
||||
p, role:org-admin, repositories, delete, *, allow
|
||||
|
||||
g, TestEngineering, role:org-admin
|
||||
```
|
||||
|
||||
In OneLogin, a user with user role "TestEngineering" will receive ArgoCD admin privileges when they log in to Argo via OneLogin. All other users will receive the readonly role. The key takeaway here is that "TestEngineering" is passed via the Group field in the token (which is specified in the Parameters tab in OneLogin).
|
||||
|
||||
@@ -16,7 +16,7 @@ no fix yet.
|
||||
|2020-04-14|[CVE-2020-5260](https://nvd.nist.gov/vuln/detail/CVE-2020-5260)|Possible Git credential leak|Critical|all|v1.4.3,v1.5.2|
|
||||
|2020-04-08|[CVE-2020-11576](https://nvd.nist.gov/vuln/detail/CVE-2020-11576)|User Enumeration|Medium|v1.5.0|v1.5.1|
|
||||
|2020-04-08|[CVE-2020-8826](https://nvd.nist.gov/vuln/detail/CVE-2020-8826)|Session-fixation|High|all|n/a|
|
||||
|2020-04-08|[CVE-2020-8827](https://nvd.nist.gov/vuln/detail/CVE-2020-8827)|Insufficient anti-automation/anti-brute force|High|all|n/a|
|
||||
|2020-04-08|[CVE-2020-8827](https://nvd.nist.gov/vuln/detail/CVE-2020-8827)|Insufficient anti-automation/anti-brute force|High|all <= 1.5.3|v1.5.3|
|
||||
|2020-04-08|[CVE-2020-8828](https://nvd.nist.gov/vuln/detail/CVE-2020-8828)|Insecure default administrative password|High|all|n/a|
|
||||
|2020-04-08|[CVE-2018-21034](https://nvd.nist.gov/vuln/detail/CVE-2018-21034)|Sensitive Information Disclosure|Medium|all <= v1.5.0|v1.5.0|
|
||||
|
||||
@@ -111,13 +111,13 @@ or at least changed to a more secure password.
|
||||
|
||||
**Details:**
|
||||
|
||||
Argo CD does not enforce rate-limiting or other anti-automation mechanisms which would mitigate admin password brute force.
|
||||
|
||||
We are considering some simple options for rate-limiting.
|
||||
ArgoCD before v1.5.3 does not enforce rate-limiting or other anti-automation mechanisms which would mitigate admin password brute force.
|
||||
|
||||
**Mitigation and/or workaround:**
|
||||
|
||||
As a workaround for mitigation until a fix is available, we recommend to disable local users and use SSO instead.
|
||||
Rate-limiting and anti-automation mechanisms for local user accounts have been introduced with ArgoCD v1.5.3.
|
||||
|
||||
As a workaround for mitigation if you cannot upgrade ArgoCD to v1.5.3 yet, we recommend to disable local users and use SSO instead.
|
||||
|
||||
### CVE-2020-8826 - Session-fixation
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Before enabling feature you might consider disabling warning. In this case appli
|
||||
|
||||
## Exceptions
|
||||
|
||||
Not every resource in the Kuberenetes cluster is controlled by the end user. Following resources are never considered as orphaned:
|
||||
Not every resource in the Kubernetes cluster is controlled by the end user. Following resources are never considered as orphaned:
|
||||
|
||||
* Namespaced resources blacklisted in the project. Usually, such resources are managed by cluster administrators and not supposed to be modified by namespace user.
|
||||
* `ServiceAccount` with name `default` ( and corresponding auto-generated `ServiceAccountToken` ).
|
||||
|
||||
@@ -32,7 +32,7 @@ or UI:
|
||||
|
||||

|
||||
|
||||
*Note: username in screenshot is for illustration purposes only , we have no relationship to this GitHub account should it exists*
|
||||
*Note: username in screenshot is for illustration purposes only , we have no relationship to this GitHub account should it exist.*
|
||||
|
||||
1. Click `Connect` to test the connection and have the repository added
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: slack-notification
|
||||
image: appropriate/curl
|
||||
image: curlimages/curl
|
||||
command:
|
||||
- "curl"
|
||||
- "-X"
|
||||
@@ -120,7 +120,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: slack-notification
|
||||
image: appropriate/curl
|
||||
image: curlimages/curl
|
||||
command:
|
||||
- "curl"
|
||||
- "-X"
|
||||
|
||||
@@ -36,7 +36,7 @@ metadata:
|
||||
argocd.argoproj.io/sync-options: Validate=false
|
||||
```
|
||||
|
||||
If you want to exclude a whole class of objects globally, consider setting `resource.customizations` in [system level configuation](../user-guide/diffing.md#system-level-configuration).
|
||||
If you want to exclude a whole class of objects globally, consider setting `resource.customizations` in [system level configuration](../user-guide/diffing.md#system-level-configuration).
|
||||
|
||||
## Skip Dry Run for new custom resources types
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Tracking and Deployment Strategies
|
||||
|
||||
An Argo CD application spec provides several different ways of track Kubernetes resource manifests
|
||||
An Argo CD application spec provides several different ways of tracking Kubernetes resource manifests.
|
||||
|
||||
In all tracking strategies, the app has the option to sync automatically. If [auto-sync](auto_sync.md)
|
||||
is configured, the new resources manifests will be applied automatically -- as soon as a difference
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// CheckError is a convenience function to exit if an error is non-nil and exit if it was
|
||||
func CheckError(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// panics if there is an error.
|
||||
// This returns the first value so you can use it if you cast it:
|
||||
// text := FailOrErr(Foo)).(string)
|
||||
func FailOnErr(v interface{}, err error) interface{} {
|
||||
CheckError(err)
|
||||
return v
|
||||
}
|
||||
@@ -3146,7 +3146,7 @@
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Git Requets (ls-remote)",
|
||||
"title": "Git Requests (ls-remote)",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 2,
|
||||
|
||||
115
go.mod
Normal file
115
go.mod
Normal file
@@ -0,0 +1,115 @@
|
||||
module github.com/argoproj/argo-cd
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
bou.ke/monkey v1.0.1
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||
github.com/argoproj/gitops-engine v0.1.3
|
||||
github.com/argoproj/pkg v0.0.0-20200319004004-f46beff7cd54
|
||||
github.com/bsm/redislock v0.4.3
|
||||
github.com/casbin/casbin v1.9.1
|
||||
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/docker/docker v1.6.0-rc5 // indirect
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-openapi/loads v0.19.2
|
||||
github.com/go-openapi/runtime v0.19.0
|
||||
github.com/go-openapi/spec v0.19.2
|
||||
github.com/go-redis/cache v6.3.5+incompatible
|
||||
github.com/go-redis/redis v6.15.6+incompatible
|
||||
github.com/gobuffalo/packr v1.11.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/golang/protobuf v1.3.1
|
||||
github.com/gomodule/redigo v2.0.0+incompatible // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/google/go-jsonnet v0.16.0
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/googleapis/gnostic v0.1.0 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.2
|
||||
github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d // indirect
|
||||
github.com/prometheus/client_golang v0.9.2
|
||||
github.com/robfig/cron v1.1.0
|
||||
github.com/rs/cors v1.6.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c
|
||||
github.com/soheilhy/cmux v0.1.4
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/vmihailenco/msgpack v3.3.1+incompatible
|
||||
github.com/yuin/gopher-lua v0.0.0-20190115140932-732aa6820ec4
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
gonum.org/v1/gonum v0.0.0-20190621125449-90b715451587 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873
|
||||
google.golang.org/grpc v1.23.0
|
||||
gopkg.in/go-playground/webhooks.v5 v5.11.0
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
k8s.io/api v0.16.6
|
||||
k8s.io/apiextensions-apiserver v0.16.6
|
||||
k8s.io/apimachinery v0.16.6
|
||||
k8s.io/client-go v11.0.1-0.20190816222228-6d55c1b1f1ca+incompatible
|
||||
k8s.io/code-generator v0.16.6
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
||||
k8s.io/kubectl v0.16.6
|
||||
k8s.io/kubernetes v1.16.6
|
||||
k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6
|
||||
layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/golang/protobuf => github.com/golang/protobuf v1.2.0
|
||||
github.com/grpc-ecosystem/grpc-gateway => github.com/grpc-ecosystem/grpc-gateway v1.3.1
|
||||
github.com/improbable-eng/grpc-web => github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a
|
||||
|
||||
google.golang.org/grpc => google.golang.org/grpc v1.15.0
|
||||
|
||||
k8s.io/api => k8s.io/api v0.16.6
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.16.6
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.16.6
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.16.6
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.16.6
|
||||
k8s.io/client-go => k8s.io/client-go v0.16.6
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.16.6
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.16.6
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.16.6
|
||||
k8s.io/component-base => k8s.io/component-base v0.16.6
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.16.6
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.16.6
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.16.6
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.16.6
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.16.6
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.16.6
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.16.6
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.16.6
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.16.6
|
||||
k8s.io/metrics => k8s.io/metrics v0.16.6
|
||||
k8s.io/node-api => k8s.io/node-api v0.16.6
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.16.6
|
||||
k8s.io/sample-cli-plugin => k8s.io/sample-cli-plugin v0.16.6
|
||||
k8s.io/sample-controller => k8s.io/sample-controller v0.16.6
|
||||
)
|
||||
848
go.sum
Normal file
848
go.sum
Normal file
@@ -0,0 +1,848 @@
|
||||
bitbucket.org/bertimus9/systemstat v0.0.0-20180207000608-0eeff89b0690/go.mod h1:Ulb78X89vxKYgdL24HMTiXYHlyHEvruOj1ZPlqeNEZM=
|
||||
bou.ke/monkey v1.0.1 h1:zEMLInw9xvNakzUUPjfS4Ds6jYPqCFx3m7bRmG5NH2U=
|
||||
bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/hcsshim v0.0.0-20190417211021-672e52e9209d/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d h1:WtAMR0fPCOfK7TPGZ8ZpLLY18HRvL7XJ3xcs0wnREgo=
|
||||
github.com/TomOnTime/utfutil v0.0.0-20180511104225-09c41003ee1d/go.mod h1:WML6KOYjeU8N6YyusMjj2qRvaPNUEvrQvaxuFcMRFJY=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
|
||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
|
||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/argoproj/gitops-engine v0.1.3 h1:eQp1bfqaeaATcu4XErlxNb6aVsN4rC7suL/Fqx/9E+k=
|
||||
github.com/argoproj/gitops-engine v0.1.3/go.mod h1:UmBGlQLT/MPNiMmbnouZRWhkk3slPuozMsENdXMkIMs=
|
||||
github.com/argoproj/pkg v0.0.0-20200102163130-2dd1f3f6b4de/go.mod h1:2EZ44RG/CcgtPTwrRR0apOc7oU6UIw8GjCUJWZ8X3bM=
|
||||
github.com/argoproj/pkg v0.0.0-20200319004004-f46beff7cd54 h1:hDn02iEkh5EUl4TJfOo6AI9uSgh0vt/qh66ODuQl/YE=
|
||||
github.com/argoproj/pkg v0.0.0-20200319004004-f46beff7cd54/go.mod h1:2EZ44RG/CcgtPTwrRR0apOc7oU6UIw8GjCUJWZ8X3bM=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM=
|
||||
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/bazelbuild/bazel-gazelle v0.18.2/go.mod h1:D0ehMSbS+vesFsLGiD6JXu3mVEzOlfUl8wNnq+x/9p0=
|
||||
github.com/bazelbuild/bazel-gazelle v0.19.1-0.20191105222053-70208cbdc798/go.mod h1:rPwzNHUqEzngx1iVBfO/2X2npKaT3tqPqqHW6rVsn/A=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20190731111112-f720930ceb60/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/bazelbuild/buildtools v0.0.0-20190917191645-69366ca98f89/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU=
|
||||
github.com/bazelbuild/rules_go v0.0.0-20190719190356-6dae44dc5cab/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/bsm/redislock v0.4.3 h1:TJ0RzHeSujLSuy4b33OWDknxAzKCdLdit0Hs9kOjElg=
|
||||
github.com/bsm/redislock v0.4.3/go.mod h1:mcygIsJknQThqWrlOgiPJ97CGmu3aAdQabg1ZIxT1BA=
|
||||
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
|
||||
github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM=
|
||||
github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cespare/prettybench v0.0.0-20150116022406-03b8cfe5406c/go.mod h1:Xe6ZsFhtM8HrDku0pxJ3/Lr51rwykrzgFwpmTzleatY=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 h1:HD4PLRzjuCVW79mQ0/pdsalOLHJ+FaEoqJLxfltpb2U=
|
||||
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho=
|
||||
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180726162950-56268a613adf/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/clusterhq/flocker-go v0.0.0-20160920122132-2b8b7259d313/go.mod h1:P1wt9Z3DP8O6W3rvwCt0REIlshg1InHImaLW0t3ObY0=
|
||||
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
|
||||
github.com/container-storage-interface/spec v1.1.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
|
||||
github.com/containerd/console v0.0.0-20170925154832-84eeaae905fa/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/containerd v1.0.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coredns/corefile-migration v1.0.2/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/rkt v1.30.0/go.mod h1:O634mlH6U7qk87poQifK6M2rsFNt+FyUTWNMnP1hF1U=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v1.6.0-rc5 h1:8dnqiCOcZf2QXwR4LNnG7AK9hXeeT6adGmtjicsVswc=
|
||||
github.com/docker/docker v1.6.0-rc5/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/libnetwork v0.0.0-20180830151422-a9cd636e3789/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
|
||||
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
|
||||
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.19.2 h1:ophLETFestFZHk3ji7niPEL4d466QjW+0Tdg5VyDq7E=
|
||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2 h1:rf5ArTHmIJxyV5Oiks+Su0mUens1+AjpkPoWr5xFRcI=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0 h1:sU6pp4dSV2sGlNKKyHxZzi1m1kG4WnYtWcJ+HYbygjE=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0 h1:0Dn9qy1G9+UJfRU7TR8bmdGxb4uifB7HNrJjOnV0yPk=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2 h1:ky5l57HjyVRrsJfd2+Ro5Z9PjGuKbsmftwyMtk8H7js=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-redis/cache v6.3.5+incompatible h1:4OUyoXXYRRQ6tKA4ue3TlPUkBzk3occzjtXBZBxCzgs=
|
||||
github.com/go-redis/cache v6.3.5+incompatible/go.mod h1:XNnMdvlNjcZvHjsscEozHAeOeSE5riG9Fj54meG4WT4=
|
||||
github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg=
|
||||
github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
||||
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
|
||||
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
|
||||
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
|
||||
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
|
||||
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
|
||||
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
|
||||
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
|
||||
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
|
||||
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
||||
github.com/gobuffalo/packr v1.11.0 h1:lxysfHcxVCWGNMHzKABP7ZEL3A7iIVYfkev/D7AR0aM=
|
||||
github.com/gobuffalo/packr v1.11.0/go.mod h1:rYwMLC6NXbAbkKb+9j3NTKbxSswkKLlelZYccr4HYVw=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2 h1:BbwX8wsMRDZRdNYxAna+4ls3wvMKJyn4PT6Zk1CPxP4=
|
||||
github.com/gogits/go-gogs-client v0.0.0-20190616193657-5a05380e4bc2/go.mod h1:cY2AIrMgHm6oOHmR7jY+9TtjzSjQ3iG7tURJG3Y6XH0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
|
||||
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
|
||||
github.com/golangci/go-tools v0.0.0-20190318055746-e32c54105b7c/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM=
|
||||
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
|
||||
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
|
||||
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||
github.com/golangci/golangci-lint v1.18.0/go.mod h1:kaqo8l0OZKYPtjNmG4z4HrWLgcYNIJ9B9q3LWri9uLg=
|
||||
github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU=
|
||||
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
|
||||
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
||||
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
||||
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
|
||||
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
|
||||
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
|
||||
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
|
||||
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/cadvisor v0.34.0/go.mod h1:1nql6U13uTHaLYB8rLS5x9IJc2qT6Xd/Tr1sTX6NE48=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-jsonnet v0.16.0 h1:Nb4EEOp+rdeGGyB1rQ5eisgSAqrTnhf9ip+X6lzZbY0=
|
||||
github.com/google/go-jsonnet v0.16.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
|
||||
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79 h1:lR9ssWAqp9qL0bALxqEEkuudiP1eweOdv9jsRK3e7lE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.3.1 h1:k2neygAEBYavP90THffKBVlkASdxu4XiI8cAWuL3MG0=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.3.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/heketi/heketi v9.0.1-0.20190917153846-c2e2a4ab7ab9+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o=
|
||||
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a h1:RweVA0vnEyStwtAelyGmnU8ENDnwd1Q7pQr7U3J/rXo=
|
||||
github.com/improbable-eng/grpc-web v0.0.0-20181111100011-16092bd1d58a/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
|
||||
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
|
||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/lpabon/godbc v0.1.1/go.mod h1:Jo9QV0cf3U6jZABgiJ2skINAXb9j8m51r07g4KI92ZA=
|
||||
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
|
||||
github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk=
|
||||
github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao=
|
||||
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
|
||||
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8 h1:A6SLdFpRzUUF5v9F/7T1fu3DERmOCgTwwP6x54eyFfU=
|
||||
github.com/malexdev/utfutil v0.0.0-20180510171754-00c8d4a8e7a8/go.mod h1:UtpLyb/EupVKXF/N0b4NRe1DNg+QYJsnsHQ038romhM=
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mesos/mesos-go v0.0.9/go.mod h1:kPYCMQ9gsOXVAle1OsoY4I1+9kPu8GHkf88aV59fDr4=
|
||||
github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
|
||||
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mindprince/gonvml v0.0.0-20171110221305-fee913ce8fb2/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY=
|
||||
github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/mrunalp/fileutils v0.0.0-20160930181131-4ee1cc9a8058/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mvdan/xurls v1.1.0/go.mod h1:tQlNn3BED8bE/15hnSL2HLkDeLWpNPAwtw7wkEq44oU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v1.0.0-rc2.0.20190611121236-6cc515888830/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d h1:7gXyC293Lsm2YWgQ+0uaAFFFDO82ruiQSwc3ua+Vtlc=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180306154005-525d0eb5f91d/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/ffjson v0.0.0-20180717144149-af8b230fcd20/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
||||
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/quobyte/api v0.1.2/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
|
||||
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto=
|
||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c h1:fyKiXKO1/I/B6Y2U8T7WdQGWzwehOuGIrljPtt7YTTI=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/thecodeteam/goscaleio v0.1.0/go.mod h1:68sdkZAsK8bvEwBlbQnlLS+xU+hvLYM/iQ8KXej1AwM=
|
||||
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/vishvananda/netlink v0.0.0-20171020171820-b2de5d10e38e/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20171111001504-be1fbeda1936/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
github.com/vmihailenco/msgpack v3.3.1+incompatible h1:ibe+d1lqocBmxbJ+gwcDO8LpAHFr3PGDYovoURuTVGk=
|
||||
github.com/vmihailenco/msgpack v3.3.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmware/govmomi v0.20.1/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yudai/gojsondiff v1.0.1-0.20180504020246-0525c875b75c h1:vGHScYm0uhmaxwGX38tj1TB1u1zVdO0vlgcz1fEVxc8=
|
||||
github.com/yudai/gojsondiff v1.0.1-0.20180504020246-0525c875b75c/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/yuin/gopher-lua v0.0.0-20190115140932-732aa6820ec4 h1:1yOVVSFiradDwXpgdkDjlGOcGJqcohH/W49Zn8Ywgco=
|
||||
github.com/yuin/gopher-lua v0.0.0-20190115140932-732aa6820ec4/go.mod h1:fFiAh+CowNFr0NK5VASokuwKwkbacRmHsVA7Yb1Tqac=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190927031335-2835ba2e683f/go.mod h1:fYw7AShPAhGMdXqA9gRadk/CcMsvLlClpE5oBwnS3dM=
|
||||
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
|
||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190122071731-054c452bb702/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190122202912-9c309ee22fab/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72 h1:bw9doJza/SFBEweII/rHQh338oozWyiFsBRHtrflcws=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/gonum v0.0.0-20190621125449-90b715451587 h1:3zGAyf1vSxRaoDJSCUXkvLLueYXyRYTuiUcxO+tURWY=
|
||||
gonum.org/v1/gonum v0.0.0-20190621125449-90b715451587/go.mod h1:03dgh78c4UvU1WksguQ/lvJQXbezKQGJSrwwRq5MraQ=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.6.1-0.20190607001116-5213b8090861/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.15.0 h1:Az/KuahOM4NAidTEuJCv/RonAA7rYsTPkqXVjr+8OOw=
|
||||
google.golang.org/grpc v1.15.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/go-playground/webhooks.v5 v5.11.0 h1:V3vej+ZXrVvO2EmBTKlhClEbpTqXH44K5OyLUMOkHMg=
|
||||
gopkg.in/go-playground/webhooks.v5 v5.11.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
|
||||
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/api v0.16.6 h1:FyTv/Z4RBlddLGJXRjGXE40QtYGHOjdZKRFSW5rU1oE=
|
||||
k8s.io/api v0.16.6/go.mod h1:naJcEPKsa3oqutLPPMxA2oLSqV4KxGDLU6IgkqHqgFE=
|
||||
k8s.io/apiextensions-apiserver v0.16.6 h1:GuzZMQ2t4vwvBSkprMsQhhlqyYZL2Lge+0+zQhDXV9I=
|
||||
k8s.io/apiextensions-apiserver v0.16.6/go.mod h1:WbwakFromAVhfvLITDk5nRf5UJJwazjeZRx+yKeDcY0=
|
||||
k8s.io/apimachinery v0.16.6 h1:A4RWH7F3jhkD6YyBpsm/2yXiFUEf9fiNqrz7bxZHPMc=
|
||||
k8s.io/apimachinery v0.16.6/go.mod h1:mhhO3hoLkWO+2eCvqjPtH2Ly92l9nJDwsswzWKpkN2w=
|
||||
k8s.io/apiserver v0.16.6 h1:7woiO69qcmhmTv2lsgAux2nb3bekuyogl3wzRI2HOBA=
|
||||
k8s.io/apiserver v0.16.6/go.mod h1:JaDblfPzg2nbxaA0H3PsMgO72QAx2rBoSYwxLEKu5RE=
|
||||
k8s.io/cli-runtime v0.16.6 h1:Z0X6rJanDHrpPtpJG3ObfRuPtlhvrJOJsfdqL+Y7bnw=
|
||||
k8s.io/cli-runtime v0.16.6/go.mod h1:8N6G/UJmYvLXzpD1kjpuss6mFUeez+eg6Nu15VtBHvM=
|
||||
k8s.io/client-go v0.16.6 h1:OR6ZaSlIn9dUdpiN4r5mAvMv5aCupUJUiDJZdrrvmhw=
|
||||
k8s.io/client-go v0.16.6/go.mod h1:xIQ44uaAH4SD1EHMtCHsB9By7D0qblbv1ADeGyXpZUQ=
|
||||
k8s.io/cloud-provider v0.16.6/go.mod h1:rTwoMb7ogSqEAZWev8ds88EApSPC6vVAikKgpvjOxpE=
|
||||
k8s.io/cluster-bootstrap v0.16.6/go.mod h1:cOnd4cgo8AthVSyH7rIWpUNUdJyuCthsZjA2MEsFipI=
|
||||
k8s.io/code-generator v0.16.6 h1:wykZVkEDQd/BcOZxcQMEW6uFgoM4ZmvjIhxkbmEJ3WA=
|
||||
k8s.io/code-generator v0.16.6/go.mod h1:2aiDuxDU7RQK2PVypXAXHo6+YwOlF33iezHQbSmKSA4=
|
||||
k8s.io/component-base v0.16.6 h1:1qIWVKni+gqRkN8vvaGYJk+R8tRtKDv0XvvDuYEBD1w=
|
||||
k8s.io/component-base v0.16.6/go.mod h1:8+4lrSEgLQ9wqOzHVYx4GLSCU6sus8wqg8bfaTdXTwg=
|
||||
k8s.io/cri-api v0.16.6/go.mod h1:W6aMMPN5fmxcRGaHnb6BEfoTeS82OsJcsUJyKf+EWYc=
|
||||
k8s.io/csi-translation-lib v0.16.6/go.mod h1:T/bEjsu1sQn2qVi9FzsPqjvT31mSqpThoFwtnj327jg=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/heapster v1.2.0-beta.1/go.mod h1:h1uhptVXMwC8xtZBYsPXKVi8fpdlYkTs6k949KozGrM=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-aggregator v0.16.6 h1:bNwY/t432BgxoL73jEMD5EdZQCUkqw5kwhQxFmxdNss=
|
||||
k8s.io/kube-aggregator v0.16.6/go.mod h1:lRjo9e3xeyF8tjkIKEX+pErNOdE4yTazx9VPO6zzdcw=
|
||||
k8s.io/kube-controller-manager v0.16.6/go.mod h1:7ovDaVMCHc4TBOQHzfb5w2XCib7rjx+QCMZTRVQteD4=
|
||||
k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
k8s.io/kube-proxy v0.16.6/go.mod h1:l7jgZcYyjERYxALU/EizkMx/JmIhN2Ff/f/aR/azFKg=
|
||||
k8s.io/kube-scheduler v0.16.6/go.mod h1:ohT2kmuQnNex0cDUYvXBAdMKHlneruoD4KOacEDpPq4=
|
||||
k8s.io/kubectl v0.16.6 h1:u7bQl9F78+7wYcvBSX7JfnIotLIQfAVpN1E11m2s+3w=
|
||||
k8s.io/kubectl v0.16.6/go.mod h1:ybKdxxoYuQLRqsmBFylvgyFPeVmmRYUbxk134JCiNoM=
|
||||
k8s.io/kubelet v0.16.6/go.mod h1:NAuB1uZwiOgUnJSgAnJIkWlueXFYkzxwv7xWEA/P35Y=
|
||||
k8s.io/kubernetes v1.16.6 h1:ZWSNwxZ1w/IPV7pYH9gohR7AhKmn1VoJ9fEKxmkkeh8=
|
||||
k8s.io/kubernetes v1.16.6/go.mod h1:rO6tSgbJjbo6lLkrq4jryUaXqZ2PdDJjzWXKZQmLfnQ=
|
||||
k8s.io/legacy-cloud-providers v0.16.6/go.mod h1:trzyJ8vT+vD+FEP4NHDbJvOXYtksUbpD7PfR6Iwnhxk=
|
||||
k8s.io/metrics v0.16.6/go.mod h1:de0nJbsn2wX/fapLW0Yi7k+GwXvEv4/g54agaDjzmQY=
|
||||
k8s.io/repo-infra v0.0.1-alpha.1/go.mod h1:wO1t9WaB99V80ljbeENTnayuEEwNZt7gECYh/CEyOJ8=
|
||||
k8s.io/sample-apiserver v0.16.6/go.mod h1:fyN8DaZXgtcQKCtb/x2mr4TDTUkaAdgWNU7BaLnlSqg=
|
||||
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6 h1:p0Ai3qVtkbCG/Af26dBmU0E1W58NID3hSSh7cMyylpM=
|
||||
k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427 h1:RZkKxMR3jbQxdCEcglq3j7wY3PRJIopAwBlx1RE71X0=
|
||||
layeh.com/gopher-json v0.0.0-20190114024228-97fed8db8427/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc=
|
||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
|
||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
extensionsobj "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -45,14 +45,16 @@ func getCustomResourceDefinitions() map[string]*extensionsobj.CustomResourceDefi
|
||||
|
||||
// We need to completely remove validation of problematic fields such as creationTimestamp,
|
||||
// which get marshalled to `null`, but are typed as as a `string` during Open API validation
|
||||
removeValidataion(un, "metadata.creationTimestamp")
|
||||
removeValidation(un, "metadata.creationTimestamp")
|
||||
|
||||
crd := toCRD(un)
|
||||
crd.Labels = map[string]string{
|
||||
"app.kubernetes.io/name": crd.Name,
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
}
|
||||
delete(crd.Annotations, "controller-gen.kubebuilder.io/version")
|
||||
crd.Spec.Scope = "Namespaced"
|
||||
crd.Spec.PreserveUnknownFields = nil
|
||||
crds[crd.Name] = crd
|
||||
}
|
||||
return crds
|
||||
@@ -65,7 +67,7 @@ func deleteFile(path string) {
|
||||
checkErr(os.Remove(path))
|
||||
}
|
||||
|
||||
func removeValidataion(un *unstructured.Unstructured, path string) {
|
||||
func removeValidation(un *unstructured.Unstructured, path string) {
|
||||
schemaPath := []string{"spec", "validation", "openAPIV3Schema"}
|
||||
for _, part := range strings.Split(path, ".") {
|
||||
schemaPath = append(schemaPath, "properties", part)
|
||||
|
||||
@@ -17,8 +17,13 @@ jq --version
|
||||
PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/..; pwd)
|
||||
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${PROJECT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
|
||||
PATH="${PROJECT_ROOT}/dist:${PATH}"
|
||||
MOD_ROOT=${GOPATH}/pkg/mod
|
||||
|
||||
# protbuf tooling required to build .proto files from go annotations from k8s-like api types
|
||||
. ${PROJECT_ROOT}/hack/versions.sh
|
||||
|
||||
export GO111MODULE=off
|
||||
|
||||
# protobuf tooling required to build .proto files from go annotations from k8s-like api types
|
||||
go build -i -o dist/go-to-protobuf ./vendor/k8s.io/code-generator/cmd/go-to-protobuf
|
||||
go build -i -o dist/protoc-gen-gogo ./vendor/k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo
|
||||
|
||||
@@ -37,7 +42,8 @@ APIMACHINERY_PKGS=(
|
||||
k8s.io/apimachinery/pkg/apis/meta/v1
|
||||
k8s.io/api/core/v1
|
||||
)
|
||||
go-to-protobuf \
|
||||
|
||||
${PROJECT_ROOT}/dist/go-to-protobuf \
|
||||
--go-header-file=${PROJECT_ROOT}/hack/custom-boilerplate.go.txt \
|
||||
--packages=$(IFS=, ; echo "${PACKAGES[*]}") \
|
||||
--apimachinery-packages=$(IFS=, ; echo "${APIMACHINERY_PKGS[*]}") \
|
||||
@@ -65,7 +71,7 @@ go build -i -o dist/protoc-gen-swagger ./vendor/github.com/grpc-ecosystem/grpc-g
|
||||
# Generate server/<service>/(<service>.pb.go|<service>.pb.gw.go)
|
||||
PROTO_FILES=$(find $PROJECT_ROOT \( -name "*.proto" -and -path '*/server/*' -or -path '*/reposerver/*' -and -name "*.proto" \) | sort)
|
||||
for i in ${PROTO_FILES}; do
|
||||
GOOGLE_PROTO_API_PATH=${PROJECT_ROOT}/vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis
|
||||
GOOGLE_PROTO_API_PATH=${MOD_ROOT}/github.com/grpc-ecosystem/grpc-gateway@${grpc_gateway_version}/third_party/googleapis
|
||||
GOGO_PROTOBUF_PATH=${PROJECT_ROOT}/vendor/github.com/gogo/protobuf
|
||||
protoc \
|
||||
-I${PROJECT_ROOT} \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user