From 4dfedc777f5e4ad29f61a5d40eaa0dbe00cdbfeb Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 12 Mar 2020 10:47:23 -0700 Subject: [PATCH] Add e2e framework and first e2e test case: dynamic stack --- .circleci/config.yml | 110 ++- Makefile | 1 + go.mod | 35 +- go.sum | 554 +++++++++-- pkg/client/clientset/versioned/clientset.go | 7 + scripts/ci_e2e_test.sh | 154 ++++ scripts/lib/cluster.sh | 115 +++ scripts/lib/ecr.sh | 79 ++ scripts/update-codegen.sh | 2 +- test/e2e/fishapp/dynamic_stack.go | 865 ++++++++++++++++++ test/e2e/fishapp/dynamic_stack_test.go | 111 +++ test/e2e/fishapp/fishapp_suite_test.go | 13 + test/e2e/fishapp/shared/manifest_builder.go | 275 ++++++ test/e2e/framework/collection/map.go | 11 + test/e2e/framework/framework.go | 78 ++ test/e2e/framework/helm/constants.go | 11 + test/e2e/framework/helm/release.go | 106 +++ test/e2e/framework/k8s/name.go | 13 + test/e2e/framework/k8s/patch.go | 37 + test/e2e/framework/k8s/port_forward.go | 33 + test/e2e/framework/options.go | 52 ++ .../framework/resource/deployment/manager.go | 61 ++ test/e2e/framework/resource/mesh/manager.go | 44 + .../framework/resource/namespace/manager.go | 72 ++ .../framework/resource/virtualnode/manager.go | 60 ++ .../resource/virtualservice/manager.go | 69 ++ test/e2e/framework/utils/log.go | 25 + test/e2e/framework/utils/name.go | 18 + test/e2e/framework/utils/poll.go | 8 + 29 files changed, 2927 insertions(+), 92 deletions(-) create mode 100755 scripts/ci_e2e_test.sh create mode 100755 scripts/lib/cluster.sh create mode 100755 scripts/lib/ecr.sh create mode 100644 test/e2e/fishapp/dynamic_stack.go create mode 100644 test/e2e/fishapp/dynamic_stack_test.go create mode 100644 test/e2e/fishapp/fishapp_suite_test.go create mode 100644 test/e2e/fishapp/shared/manifest_builder.go create mode 100644 test/e2e/framework/collection/map.go create mode 100644 test/e2e/framework/framework.go create mode 100644 test/e2e/framework/helm/constants.go create mode 100644 test/e2e/framework/helm/release.go create mode 100644 test/e2e/framework/k8s/name.go create mode 100644 test/e2e/framework/k8s/patch.go create mode 100644 test/e2e/framework/k8s/port_forward.go create mode 100644 test/e2e/framework/options.go create mode 100644 test/e2e/framework/resource/deployment/manager.go create mode 100644 test/e2e/framework/resource/mesh/manager.go create mode 100644 test/e2e/framework/resource/namespace/manager.go create mode 100644 test/e2e/framework/resource/virtualnode/manager.go create mode 100644 test/e2e/framework/resource/virtualservice/manager.go create mode 100644 test/e2e/framework/utils/log.go create mode 100644 test/e2e/framework/utils/name.go create mode 100644 test/e2e/framework/utils/poll.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 2072d4b9e..943373b7a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,35 +1,63 @@ version: 2.1 + +orbs: + aws-cli: circleci/aws-cli@0.1.22 + k8s: circleci/kubernetes@0.11.0 + +commands: + checkout_and_cache_dependencies: + description: "checkout code and cache go dependencies" + steps: + - checkout + - restore_cache: + key: go-mod-{{ checksum "go.sum" }} + - run: + name: go mod download + command: go mod download + - save_cache: + key: go-mod-{{ checksum "go.sum" }} + paths: + - "/go/pkg/mod/" + run_e2e_test: + description: "build and run e2e test on EKS" + parameters: + k8s_version: + type: string + steps: + - checkout_and_cache_dependencies + - setup_remote_docker + - aws-cli/setup + - run: + name: Run e2e tests + command: CLUSTER_VERSION=<< parameters.k8s_version >> ./scripts/ci_e2e_test.sh + - store_artifacts: + path: /tmp/appmesh-e2e/clusters/ + jobs: - validate-yaml: + validate_yaml: docker: - image: circleci/golang:1.13 + working_directory: ~/build steps: - checkout - - run: - name: Install kubectl - command: sudo curl -L https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl -o /usr/local/bin/kubectl && sudo chmod +x /usr/local/bin/kubectl + - k8s/install-kubectl - run: name: Install kubeval command: | mkdir -p $HOME/tools && \ cd $HOME/tools && \ - curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz && sudo mv kubeval /bin/kubeval + curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz && \ + sudo mv kubeval /usr/local/bin/kubeval - run: name: Validate deploy yamls command: | kubeval --strict --ignore-missing-schemas ./deploy/all.yaml - build-test-run: + lint_build_unit_test: docker: - image: circleci/golang:1.13 working_directory: ~/build steps: - - checkout - - restore_cache: - keys: - - go-mod-{{ checksum "go.sum" }} - - run: - name: go mod download - command: go mod download + - checkout_and_cache_dependencies - run: name: Lint command: make go-fmt @@ -37,7 +65,7 @@ jobs: name: Build command: make linux - run: - name: Test + name: Unit test command: make test - run: name: Verify code gen @@ -45,22 +73,50 @@ jobs: - run: name: Run command: _output/bin/app-mesh-controller version - - save_cache: - key: go-mod-{{ checksum "go.sum" }} - paths: - - "/go/pkg/mod/" + e2e_test_114: + docker: + - image: circleci/golang:1.13 + working_directory: ~/build + steps: + - run_e2e_test: + k8s_version: "1.14" + e2e_test_113: + docker: + - image: circleci/golang:1.13 + working_directory: ~/build + steps: + - run_e2e_test: + k8s_version: "1.13" + e2e_test_112: + docker: + - image: circleci/golang:1.13 + working_directory: ~/build + steps: + - run_e2e_test: + k8s_version: "1.12" workflows: version: 2 - test: + check: jobs: - - validate-yaml: + - validate_yaml + - lint_build_unit_test + - hold: + type: approval + requires: + - validate_yaml + - lint_build_unit_test + - e2e_test_114: + requires: + - hold + nightly-test-run: + triggers: + - schedule: + cron: "0 0 * * *" filters: branches: - ignore: - - gh-pages - - build-test-run: - filters: - branches: - ignore: - - gh-pages + only: + - master + jobs: + - e2e_test_114 + - e2e_test_113 diff --git a/Makefile b/Makefile index ccc4bd6c7..d0a325f8a 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ mock-gen: ./scripts/mockgen.sh PACKAGES:=$(shell go list ./... | sed -n '1!p' | grep ${PKG}/pkg | grep -v ${PKG}/pkg/client) +.PHONY: test test: echo "mode: count" > coverage-all.out $(foreach pkg,$(PACKAGES), \ diff --git a/go.mod b/go.mod index e248b243d..b42803aa3 100644 --- a/go.mod +++ b/go.mod @@ -5,24 +5,33 @@ go 1.13 require ( github.com/aws/aws-sdk-go v1.29.13 github.com/deckarep/golang-set v1.7.1 - github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect + github.com/evanphx/json-patch v4.5.0+incompatible + github.com/goccy/go-yaml v1.4.3 // indirect github.com/googleapis/gnostic v0.2.0 // indirect - github.com/imdario/mergo v0.3.7 // indirect - github.com/prometheus/client_golang v0.9.2 - github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 - github.com/spf13/cobra v0.0.5 + github.com/mattn/go-colorable v0.1.6 // indirect + github.com/mikefarah/yq/v3 v3.0.0-20200304043226-a06320f13c07 // indirect + github.com/onsi/ginkgo v1.12.0 + github.com/onsi/gomega v1.7.1 + github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.0.0 + github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 + github.com/spf13/cobra v0.0.6 github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.3.2 - github.com/stretchr/objx v0.2.0 // indirect + github.com/spf13/viper v1.4.0 github.com/stretchr/testify v1.4.0 github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5 - golang.org/x/tools v0.0.0-20200212213342-7a21e308cf6c // indirect + golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85 // indirect + golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d // indirect + golang.org/x/tools v0.0.0-20200316212524-3e76bee198d8 // indirect + gonum.org/v1/gonum v0.7.0 gopkg.in/inf.v0 v0.9.1 // indirect - k8s.io/api v0.0.0-20191025225708-5524a3672fbb - k8s.io/apimachinery v0.0.0-20191025225532-af6325b3a843 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + helm.sh/helm/v3 v3.1.2 + k8s.io/api v0.17.2 + k8s.io/apimachinery v0.17.2 + k8s.io/cli-runtime v0.17.2 k8s.io/client-go v11.0.0+incompatible - k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b - k8s.io/gengo v0.0.0-20190822140433-26a664648505 // indirect + k8s.io/code-generator v0.17.2 k8s.io/klog v1.0.0 ) @@ -31,6 +40,6 @@ replace ( k8s.io/api => k8s.io/api v0.0.0-20191025225708-5524a3672fbb k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191025225532-af6325b3a843 k8s.io/client-go => k8s.io/client-go v0.0.0-20190620085101-78d2af792bab - k8s.io/code-generator => k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b + k8s.io/code-generator => k8s.io/code-generator v0.0.0-20191025225349-fb66f1f7eb3c k8s.io/utils => k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 ) diff --git a/go.sum b/go.sum index b9ace201a..9f519aede 100644 --- a/go.sum +++ b/go.sum @@ -1,99 +1,347 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +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= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+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 v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/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/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= +github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14= +github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8= +github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU= +github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 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/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/aws/aws-sdk-go v1.29.13 h1:Y77U33nj5ic5hVxE6Th4LhZaw2rSwl3mXIm9OdmIs+k= github.com/aws/aws-sdk-go v1.29.13/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2 h1:ForxmXkA6tPIvffbrDAcPUIB32QgXkt2XFj+F0UxetA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+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/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-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= +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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deislabs/oras v0.8.1 h1:If674KraJVpujYR00rzdi0QAmW4BxzMJPVAZJKuhQ0c= +github.com/deislabs/oras v0.8.1/go.mod h1:Mx0rMSbBNaNfY9hjpccEnxkOqJL6KGjtxNHPLC4G4As= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492 h1:FwssHbCDJD025h+BchanCwE1Q8fyMgqDr2mOQAWOLGw= +github.com/docker/cli v0.0.0-20200130152716-5d0cf8839492/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +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.4.2-0.20200203170920-46ec8731fbce h1:KXS1Jg+ddGcWA8e1N7cupxaHHZhit5rB9tfDU+mfjyY= +github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 h1:yWHOI+vFjEsAakUTSrtqc/SAHrhSkmn48pqjidZX3QA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= 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/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= 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/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/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/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +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/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +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/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/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +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/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= 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/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +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/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= 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/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +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/goccy/go-yaml v1.3.2/go.mod h1:PsEEJ29nIFZL07P/c8dv4P6rQkVFFXafQee85U+ERHA= +github.com/goccy/go-yaml v1.4.3/go.mod h1:PsEEJ29nIFZL07P/c8dv4P6rQkVFFXafQee85U+ERHA= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 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/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +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.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/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/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/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= 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 v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= +github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +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/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/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/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.4/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.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2 h1:UnlwIPBGaTZfPQ6T1IGzPI0EkYAQmT9fAEJ/poFC63o= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= 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/mikefarah/yq/v3 v3.0.0-20200304043226-a06320f13c07/go.mod h1:KKe/ghmcYhPxoXBI4CMKzZ7ZJR7XJ7I6/dOcSeDVGhI= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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 v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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= @@ -101,168 +349,367 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN 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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +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.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 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/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/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/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/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 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/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +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/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +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.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 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.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 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 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5 h1:Xim2mBRFdXzXmKRO8DJg/FJtn/8Fj9NOEpO6+WuMPmk= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vektra/mockery v0.0.0-20181123154057-e78b021dcbb5/go.mod h1:ppEjwdhyy7Y31EnHRDm1JkChoC7LXIJ7Ex0VYLWtZtQ= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/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= -golang.org/x/crypto v0.0.0-20181025213731-e84da0312774 h1:a4tQYYYuK9QdeO/+kEvNYyuR21S+7ve5EANok6hABhI= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85 h1:jqhIzSw5SQNkbu5hOGpgMHhkfXxrbsLJdkIRcX19gCY= +golang.org/x/exp v0.0.0-20200228211341-fcea875c7e85/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/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-20190313153728-d0100b6bd8b3/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.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20181201002055-351d144fa1fc/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190206173232-65e2d4e15006/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/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-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-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/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-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190215142949-d0b11bdaac8a/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-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-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44= +golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/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.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-20161028155119-f51c12702a4d h1:TnM+PKb3ylGmZvyPXmo9m/wktg7Jn/a/fNmr33HSj8g= golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/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-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-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-20181112210238-4b1f3b6b1646/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20200212213342-7a21e308cf6c h1:D2X+P0Z6ychko7xn2jvd38yxQfdU0eksO4AHfd8AWFI= -golang.org/x/tools v0.0.0-20200212213342-7a21e308cf6c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/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-20190617190820-da514acc4774/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200316212524-3e76bee198d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/gonum v0.7.0 h1:Hdks0L0hgznZLG9nzXb8vZ0rRvqNvAcgAp84y7Mwkgw= +gonum.org/v1/gonum v0.7.0/go.mod h1:L02bwd0sqlsvRv41G7wGWFCsVNZFv/k1xzGIxeANHGM= 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/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +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/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 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/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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +helm.sh/helm/v3 v3.1.2 h1:VpNzaNv2DX4aRnOCcV7v5Of+XT2SZrJ8iOQ25AGKOos= +helm.sh/helm/v3 v3.1.2/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.0.0-20191025225708-5524a3672fbb h1:XpnI7Pjlb0pMfI0hmHgWgaKqkp/JIL3JYYFN21AYCP8= k8s.io/api v0.0.0-20191025225708-5524a3672fbb/go.mod h1:NMIXwlJTrA+pXie6lv562GUPkluJ4oRGzQfqWBLaceY= +k8s.io/apiextensions-apiserver v0.17.2 h1:cP579D2hSZNuO/rZj9XFRzwJNYb41DbNANJb6Kolpss= +k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= k8s.io/apimachinery v0.0.0-20191025225532-af6325b3a843 h1:Ge6Np+ecN6pKYcAaFXR53I88+UL5ch+KpLxkKREpsJ4= k8s.io/apimachinery v0.0.0-20191025225532-af6325b3a843/go.mod h1:gA1T9z4LIup7PIegBwxkF2UYXUNVKhOAPvQWWnAc34k= +k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= +k8s.io/cli-runtime v0.17.2 h1:YH4txSplyGudvxjhAJeHEtXc7Tr/16clKGfN076ydGk= +k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= -k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b h1:p+PRuwXWwk5e+UYvicGiavEupapqM5NOxUl3y1GkD6c= -k8s.io/code-generator v0.0.0-20190612205613-18da4a14b22b/go.mod h1:G8bQwmHm2eafm5bgtX67XDZQ8CWKSGu9DekI+yN4Y5I= -k8s.io/gengo v0.0.0-20190116091435-f8a0810f38af/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/code-generator v0.0.0-20191025225349-fb66f1f7eb3c/go.mod h1:HtDEU3n5Xo1vbwjXWiJ/lFNb5r6BWBz6aZU1IZTr4eA= +k8s.io/component-base v0.17.2 h1:0XHf+cerTvL9I5Xwn9v+0jmqzGAZI7zNydv4tL6Cw6A= +k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= 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/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 v0.3.1/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-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= 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/kubectl v0.17.2 h1:QZR8Q6lWiVRjwKslekdbN5WPMp53dS/17j5e+oi5XVU= +k8s.io/kubectl v0.17.2/go.mod h1:y4rfLV0n6aPmvbRCqZQjvOp3ezxsFgpqL+zF5jH/lxk= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw= k8s.io/utils v0.0.0-20191010214722-8d271d903fe4 h1:Gi+/O1saihwDqnlmC8Vhv1M5Sp4+rbOmK9TbsLn8ZEA= k8s.io/utils v0.0.0-20191010214722-8d271d903fe4/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= @@ -270,6 +717,11 @@ 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= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +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-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index c61fc5eb3..0e96c89fc 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -15,6 +15,8 @@ package versioned import ( + "fmt" + appmeshv1beta1 "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/client/clientset/versioned/typed/appmesh/v1beta1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" @@ -47,9 +49,14 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface { } // NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. func NewForConfig(c *rest.Config) (*Clientset, error) { configShallowCopy := *c if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) } var cs Clientset diff --git a/scripts/ci_e2e_test.sh b/scripts/ci_e2e_test.sh new file mode 100755 index 000000000..b9593aeb1 --- /dev/null +++ b/scripts/ci_e2e_test.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env bash + +set -ueo pipefail + +# AWS environment +AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"us-west-2"} +AWS_K8S_TESTER_VERSION="v0.7.4" +source $(dirname "${BASH_SOURCE}")/lib/cluster.sh +source $(dirname "${BASH_SOURCE}")/lib/ecr.sh + +# Build environment +LOCAL_GIT_VERSION=$(git describe --tags --always --dirty) +IMAGE_TAG=$LOCAL_GIT_VERSION +IMAGE_REPO_APP_MESH_CONTROLLER="amazon/app-mesh-controller" + +# Cluster settings +CLUSTER_ID=${CLUSTER_ID:-$RANDOM} +CLUSTER_NAME=appmesh-e2e-$CLUSTER_ID +CLUSTER_TEST_DIR=/tmp/appmesh-e2e/clusters/${CLUSTER_NAME} +CLUSTER_CONFIG=${CLUSTER_CONFIG:-${CLUSTER_TEST_DIR}/${CLUSTER_NAME}.yaml} +CLUSTER_KUBECONFIG=${CLUSTER_KUBECONFIG:-${CLUSTER_TEST_DIR}/${CLUSTER_NAME}.kubeconfig} +CLUSTER_VERSION=${CLUSTER_VERSION:-"1.14"} +CLUSTER_INSTANCE_TYPE=m5.xlarge +CLUSTER_NODE_COUNT=4 + +####################################### +# Build and push appMesh controller image +# Globals: +# None +# Arguments: +# image_repo +# image_tag +# image_name +# +####################################### +build_push_controller_image() { + declare -r image_repo="$1" image_tag="$2" image_name="$3" + + ecr::ensure_repository "${image_repo}" + if [[ $? -ne 0 ]]; then + echo "Unable to ensure docker image repository" >&2 + return 1 + fi + + if [[ $(ecr::contains_image "$image_repo" "$image_tag") ]]; then + echo "docker image ${image_name} already exists in repository. Skipping image build..." + return 0 + fi + + echo "Building docker image" + if ! docker build -t "${image_name}" ./; then + echo "Unable to build docker image" >&2 + return 1 + fi + + echo "Pushing docker image ${image_name}" + if ! ecr::push_image "${image_name}"; then + echo "Unable to push docker image" >&2 + return 1 + fi + return 0 +} + +####################################### +# Setup test cluster +# Globals: +# AWS_K8S_TESTER_VERSION +# CLUSTER_CONFIG +# CLUSTER_KUBECONFIG +# CLUSTER_NAME +# CLUSTER_VERSION +# CLUSTER_INSTANCE_TYPE +# CLUSTER_NODE_COUNT +# Arguments: +# None +# +####################################### +setup_cluster() { + if ! cluster::init "${AWS_K8S_TESTER_VERSION}"; then + ehco "Unable to init aws-k8s-tester" >&2 + exit 1 + fi + + mkdir -p "${CLUSTER_TEST_DIR}" + if ! cluster::create "${CLUSTER_CONFIG}" "${CLUSTER_KUBECONFIG}" "${CLUSTER_NAME}" "${CLUSTER_VERSION}" "${CLUSTER_INSTANCE_TYPE}" "${CLUSTER_NODE_COUNT}"; then + echo "Unable to create cluster" >&2 + exit 1 + fi +} + +####################################### +# Cleanup test cluster +# Globals: +# CLUSTER_CONFIG +# Arguments: +# None +# +####################################### +cleanup_cluster() { + if ! cluster::delete "${CLUSTER_CONFIG}"; then + echo "Unable to delete cluster" >&2 + fi +} + +####################################### +# Test appMesh controller image +# Globals: +# CLUSTER_CONFIG +# CLUSTER_KUBECONFIG +# CLUSTER_NAME +# AWS_DEFAULT_REGION +# Arguments: +# image_name +# +####################################### +test_controller_image() { + declare -r image_name="$1" + + go get github.com/mikefarah/yq/v3 + go get github.com/onsi/ginkgo/ginkgo + + local vpc_id=$($(go env GOBIN)/yq read "${CLUSTER_CONFIG}" parameters.vpc-id) + $(go env GOBIN)/ginkgo -v -r test/e2e/ -- \ + --kubeconfig=${CLUSTER_KUBECONFIG} \ + --cluster-name=${CLUSTER_NAME} \ + --aws-region=${AWS_DEFAULT_REGION} \ + --aws-vpc-id=${vpc_id} \ + --controller-image=${image_name} +} + +####################################### +# Entry point +# Globals: +# IMAGE_REPO_APP_MESH_CONTROLLER +# IMAGE_TAG +# Arguments: +# None +# +####################################### +main() { + local image_name=$(ecr::name_image "${IMAGE_REPO_APP_MESH_CONTROLLER}" "${IMAGE_TAG}") + if [[ $? -ne 0 ]]; then + echo "Unable to name docker image" >&2 + return 1 + fi + build_push_controller_image "${IMAGE_REPO_APP_MESH_CONTROLLER}" "${IMAGE_TAG}" "${image_name}" + + echo $image_name + trap "cleanup_cluster" EXIT + setup_cluster + test_controller_image "${image_name}" +} + +main $@ \ No newline at end of file diff --git a/scripts/lib/cluster.sh b/scripts/lib/cluster.sh new file mode 100755 index 000000000..5ef7e082c --- /dev/null +++ b/scripts/lib/cluster.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +set -ueo pipefail + +AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"us-west-2"} +K8S_TESTER_BINARY=${K8S_TESTER_BINARY:-"/tmp/appmesh-e2e/bin/aws-k8s-tester"} + +####################################### +# Initialize cluster package +# Globals: +# K8S_TESTER_BINARY +# Arguments: +# tester_version the version of aws-k8s-tester +# +# sample: cluster::init v0.7.4 +####################################### +cluster::init() { + declare -r tester_version="$1" + + local tester_os_arch="$(go env GOOS)-$(go env GOARCH)" + #local tester_download_url="https://github.com/aws/aws-k8s-tester/releases/download/${tester_version}/aws-k8s-tester-${tester_version}-${tester_os_arch}" + + # TODO: wait gyuho's new v0.7.5 release + local test_download_url="https://gist.github.com/M00nF1sh/949f81ce06da034f7540818ef85f5106/raw/d177bc62155b445ce2ec65942dc6650fa6195dd5/aws-k8s-tester-de1e65c500d1-linux-amd64" + local tester_binary_dir=$(dirname $K8S_TESTER_BINARY) + + if [[ ! -d ${tester_binary_dir} ]]; then + echo "Creating aws-k8s-tester directory ${tester_binary_dir}" + + if ! mkdir -p "${tester_binary_dir}"; then + echo "Unable to create aws-k8s-tester directory ${tester_binary_dir}" >&2 + return 1 + fi + fi + + if [[ ! -f ${K8S_TESTER_BINARY} ]]; then + echo "Downloading aws-k8s-tester from ${tester_download_url} to ${K8S_TESTER_BINARY}" + + if ! curl -L -X GET ${tester_download_url} -o ${K8S_TESTER_BINARY}; then + echo "Unable to download aws-k8s-tester binary" >&2 + return 1 + fi + if ! chmod u+x ${K8S_TESTER_BINARY}; then + echo "Unable to add execute permission for aws-k8s-tester binary" >&2 + return 1 + fi + fi + + return 0 +} + +####################################### +# Create k8s cluster. +# Globals: +# AWS_DEFAULT_REGION +# K8S_TESTER_BINARY +# Arguments: +# cluster_config +# kubeconfig +# cluster_name +# k8s_version +# node_instance_type +# node_count +# +# sample: cluster::create appmesh-e2e.yaml appmesh-e2e.kubeconfig appmesh-e2e 1.15 m5.xlarge 4 +####################################### +cluster::create() { + declare -r cluster_config="$1" kubeconfig="$2" cluster_name="$3" k8s_version="$4" node_instance_type="$5" node_count="$6" + + K8S_TESTER_BINARY="/Users/yyyng/workplace/AppMesh/aws-k8s-tester/bin/aws-k8s-tester-de1e65c500d1-darwin-amd64" + echo "Creating cluster config for ${cluster_config}" + if ! AWS_K8S_TESTER_EKS_REGION=${AWS_DEFAULT_REGION} \ + AWS_K8S_TESTER_EKS_KUBECONFIG_PATH=${kubeconfig} \ + AWS_K8S_TESTER_EKS_PARAMETERS_VERSION=${k8s_version} \ + AWS_K8S_TESTER_EKS_PARAMETERS_ENCRYPTION_CMK_CREATE=false \ + AWS_K8S_TESTER_EKS_ADD_ON_MANAGED_NODE_GROUPS_ENABLE=true \ + AWS_K8S_TESTER_EKS_ADD_ON_MANAGED_NODE_GROUPS_MNGS={\"${cluster_name}-mng\":{\"name\":\"${cluster_name}-mng\",\"tags\":{\"group\":\"aws-app-mesh-controller-for-k8s\"},\"ami-type\":\"AL2_x86_64\",\"asg-min-size\":${node_count},\"asg-max-size\":${node_count},\"asg-desired-capacity\":${node_count},\"instance-types\":[\"${node_instance_type}\"]}} \ + AWS_K8S_TESTER_EKS_ADD_ON_APP_MESH_ENABLE=true \ + ${K8S_TESTER_BINARY} eks create config \ + --path ${cluster_config}; then + echo "Unable to create cluster config for ${cluster_config}" + return 1 + fi + + cat ${cluster_config} + + echo "Creating cluster for ${cluster_config}" + if ! ${K8S_TESTER_BINARY} eks create cluster \ + --path ${cluster_config}; then + echo "Unable to create cluster for ${cluster_config}" + return 1 + fi + + return 0 +} + +####################################### +# Delete k8s cluster. +# Globals: +# AWS_DEFAULT_REGION +# K8S_TESTER_BINARY +# Arguments: +# cluster_config +# +# sample: cluster::delete appmesh-e2e.yaml +####################################### +cluster::delete() { + declare -r cluster_config="$1" + + echo "Deleting cluster for ${cluster_config}" + if ! ${K8S_TESTER_BINARY} eks delete cluster \ + --path ${cluster_config}; then + echo "Unable to delete cluster for ${cluster_config}" + fi +} \ No newline at end of file diff --git a/scripts/lib/ecr.sh b/scripts/lib/ecr.sh new file mode 100755 index 000000000..6ad95293e --- /dev/null +++ b/scripts/lib/ecr.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +set -ueo pipefail + +AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"us-west-2"} + +####################################### +# Create ECR repository if not exists +# Globals: +# AWS_DEFAULT_REGION +# Arguments: +# img_repo +# +# sample: ecr::ensure_repository amazon/app-mesh-controller +ecr::ensure_repository() { + declare -r img_repo="$1" + if ! aws ecr describe-repositories \ + --region=${AWS_DEFAULT_REGION} \ + --repository-names "${img_repo}" >/dev/null 2>&1; then + echo "creating ECR repo with name ${img_repo}" + aws ecr create-repository --region ${AWS_DEFAULT_REGION} --repository-name "${img_repo}" + fi +} + + +####################################### +# Generate docker image name for ecr +# Globals: +# AWS_DEFAULT_REGION +# Arguments: +# img_repo +# img_tag +# +# sample: ecr::name_image aws-alb-ingress-controlle v1.0.0 image_name +####################################### +ecr::name_image() { + declare -r img_repo="$1" img_tag="$2" + + local aws_account_id=$(aws sts get-caller-identity --region ${AWS_DEFAULT_REGION} --query Account --output text) + if [[ -z "$aws_account_id" ]]; then + echo "Unable to get AWS account ID" >&2 + return 1 + fi + + echo "$aws_account_id.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/$img_repo:$img_tag" +} + +####################################### +# Check whether docker image exists in ECR +# Globals: +# AWS_DEFAULT_REGION +# Arguments: +# img_repo +# img_tag +# +# sample: ecr::contains_image image_name +####################################### +ecr::contains_image() { + declare -r img_repo="$1" img_tag="$2" + + aws ecr describe-images --region=${AWS_DEFAULT_REGION} --repository-name "${img_repo}" --image-ids imageTag="${img_tag}" 2>/dev/null +} + +####################################### +# Push docker image to ECR +# Globals: +# AWS_DEFAULT_REGION +# Arguments: +# img_name +# +# sample: ecr::push_image image_name +####################################### +ecr::push_image() { + declare -r img_name="$1" + + eval $(aws ecr get-login --region ${AWS_DEFAULT_REGION} --no-include-email) + docker push "$img_name" +} + diff --git a/scripts/update-codegen.sh b/scripts/update-codegen.sh index 036f3a0cb..ae0eabcfb 100755 --- a/scripts/update-codegen.sh +++ b/scripts/update-codegen.sh @@ -11,7 +11,7 @@ APIS_PKG=${ROOT_PKG}/pkg/apis # Below code is copied from https://github.com/weaveworks/flagger/blob/master/hack/update-codegen.sh # Grab code-generator version from go.sum. -CODEGEN_VERSION=$(grep 'k8s.io/code-generator' go.sum | awk '{print $2}' | head -1) +CODEGEN_VERSION=$(grep 'k8s.io/code-generator' go.sum | awk '{print $2}' | awk -F / '{print $1}' | head -1) CODEGEN_PKG=$(echo `go env GOPATH`"/pkg/mod/k8s.io/code-generator@${CODEGEN_VERSION}") if [[ ! -d ${CODEGEN_PKG} ]]; then diff --git a/test/e2e/fishapp/dynamic_stack.go b/test/e2e/fishapp/dynamic_stack.go new file mode 100644 index 000000000..bdb2607c9 --- /dev/null +++ b/test/e2e/fishapp/dynamic_stack.go @@ -0,0 +1,865 @@ +package fishapp + +import ( + "context" + "encoding/json" + "fmt" + appmeshv1beta1 "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/apis/appmesh/v1beta1" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/fishapp/shared" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/collection" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/k8s" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/servicediscovery" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/pkg/errors" + "gonum.org/v1/gonum/stat" + "gonum.org/v1/gonum/stat/distuv" + "io/ioutil" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" + "net/http" + "net/url" + "sync" + "time" +) + +const ( + connectivityCheckRate = time.Second / 100 + connectivityCheckProxyPort = 8899 + connectivityCheckUniformDistributionSL = 0.001 // Significance level that traffic to targets are uniform distributed. + appMeshOperationRate = time.Second * 2 +) + +// A dynamic generated stack designed to test app mesh integration :D +// Suppose given configuration below: +// 5 VirtualServicesCount +// 10 VirtualNodesCount +// 2 RoutesCountPerVirtualRouter +// 2 TargetsCountPerRoute +// 4 BackendsCountPerVirtualNode +// We will generate virtual service configuration & virtual node configuration follows: +// =======virtual services ========= +// vs1 -> /path1 -> vn1(50) +// -> vn2(50) +// -> /path2 -> vn3(50) +// -> vn4(50) +// vs2 -> /path1 -> vn5(50) +// -> vn6(50) +// -> /path2 -> vn7(50) +// -> vn8(50) +// vs3 -> /path1 -> vn9(50) +// -> vn10(50) +// -> /path2 -> vn1(50) +// -> vn2(50) +// vs4 -> /path1 -> vn3(50) +// -> vn4(50) +// -> /path2 -> vn5(50) +// -> vn6(50) +// vs5 -> /path1 -> vn7(50) +// -> vn8(50) +// -> /path2 -> vn9(50) +// -> vn10(50) +// =======virtual nodes ========= +// vn1 -> vs1,vs2,vs3,vs4 +// vn2 -> vs5,vs1,vs2,vs3 +// vn3 -> vs4,vs5,vs1,vs2 +// ... +// +// then we validate each virtual node can access each virtual service at every path, and calculates the target distribution +type DynamicStack struct { + // service discovery type + ServiceDiscoveryType shared.ServiceDiscoveryType + + // number of virtual service + VirtualServicesCount int + + // number of virtual nodes count + VirtualNodesCount int + + // number of routes per virtual router + RoutesCountPerVirtualRouter int + + // number of targets per route + TargetsCountPerRoute int + + // number of backends per virtual node + BackendsCountPerVirtualNode int + + // number of replicas per virtual node + ReplicasPerVirtualNode int32 + + // how many time to check connectivity per URL + ConnectivityCheckPerURL int + + // ====== runtime variables ====== + mesh *appmeshv1beta1.Mesh + namespace *corev1.Namespace + cloudMapNamespace string + + createdNodeVNs []*appmeshv1beta1.VirtualNode + createdNodeDPs []*appsv1.Deployment + createdNodeSVCs []*corev1.Service + + createdServiceVSs []*appmeshv1beta1.VirtualService + createdServiceSVCs []*corev1.Service +} + +// one entry of connectivity check result. +type connectivityCheckEntry struct { + srcVirtualNode types.NamespacedName + srcPod types.NamespacedName + dstVirtualService types.NamespacedName + dstURL string + + retHTTPStatusCode int + retHTTPBody string + retErr error +} + +// expects the stack can be deployed to namespace successfully +func (s *DynamicStack) Deploy(ctx context.Context, f *framework.Framework) { + s.createMeshAndNamespace(ctx, f) + if s.ServiceDiscoveryType == shared.CloudMapServiceDiscovery { + s.createCloudMapNamespace(ctx, f) + } + mb := &shared.ManifestBuilder{ + MeshName: s.mesh.Name, + Namespace: s.namespace.Name, + ServiceDiscoveryType: s.ServiceDiscoveryType, + CloudMapNamespace: s.cloudMapNamespace, + } + s.createResourcesForNodes(ctx, f, mb) + s.createResourcesForServices(ctx, f, mb) + s.grantVirtualNodesBackendAccess(ctx, f) +} + +// expects the stack can be cleaned up from namespace successfully +func (s *DynamicStack) Cleanup(ctx context.Context, f *framework.Framework) { + var deletionErrors []error + if errs := s.revokeVirtualNodeBackendAccess(ctx, f); len(errs) != 0 { + deletionErrors = append(deletionErrors, errs...) + } + if errs := s.deleteResourcesForServices(ctx, f); len(errs) != 0 { + deletionErrors = append(deletionErrors, errs...) + } + if errs := s.deleteResourcesForNodes(ctx, f); len(errs) != 0 { + deletionErrors = append(deletionErrors, errs...) + } + if s.ServiceDiscoveryType == shared.CloudMapServiceDiscovery { + if errs := s.deleteCloudMapNamespace(ctx, f); len(errs) != 0 { + deletionErrors = append(deletionErrors, errs...) + } + } + if errs := s.deleteMeshAndNamespace(ctx, f); len(errs) != 0 { + deletionErrors = append(deletionErrors, errs...) + } + Expect(len(deletionErrors)).To(BeZero()) +} + +// Check connectivity and routing works correctly +func (s *DynamicStack) Check(ctx context.Context, f *framework.Framework) { + vsByName := make(map[string]*appmeshv1beta1.VirtualService) + for i := 0; i != s.VirtualServicesCount; i++ { + vs := s.createdServiceVSs[i] + vsByName[vs.Name] = vs + } + + var checkErrors []error + httpRetByPodRequest := make(map[string]map[string]map[string]int) // pod, url, serviceNode, count + for i := 0; i != s.VirtualNodesCount; i++ { + dp := s.createdNodeDPs[i] + vn := s.createdNodeVNs[i] + var vsList []*appmeshv1beta1.VirtualService + for _, backend := range vn.Spec.Backends { + vsList = append(vsList, vsByName[backend.VirtualService.VirtualServiceName]) + } + checkEntriesRet := s.checkDeploymentVirtualServiceConnectivity(ctx, f, vn, dp, vsList) + for _, checkEntry := range checkEntriesRet { + if checkEntry.retErr != nil { + checkErrors = append(checkErrors, errors.Wrapf(checkEntry.retErr, + "expect HTTP call from pod %s to %s succeed", + checkEntry.srcPod.String(), checkEntry.dstURL)) + continue + } + if checkEntry.retHTTPStatusCode != http.StatusOK { + checkErrors = append(checkErrors, errors.Errorf( + "expect HTTP call from pod %s to %s be to have status_code 200, got %v", + checkEntry.srcPod.String(), checkEntry.dstURL, checkEntry.retHTTPStatusCode)) + continue + } + podKey := checkEntry.srcPod.String() + if _, ok := httpRetByPodRequest[podKey]; !ok { + httpRetByPodRequest[podKey] = make(map[string]map[string]int) + } + if _, ok := httpRetByPodRequest[podKey][checkEntry.dstURL]; !ok { + httpRetByPodRequest[podKey][checkEntry.dstURL] = make(map[string]int) + } + httpRetByPodRequest[podKey][checkEntry.dstURL][checkEntry.retHTTPBody] += 1 + } + } + + prettyJSON, err := json.MarshalIndent(httpRetByPodRequest, "", " ") + Expect(err).NotTo(HaveOccurred()) + utils.Logf("%v", string(prettyJSON)) + + uniformDist := distuv.ChiSquared{K: float64(s.TargetsCountPerRoute - 1)} + var expectedHTTPRetCounts []float64 + for i := 0; i != s.TargetsCountPerRoute; i++ { + expectedHTTPRetCounts = append(expectedHTTPRetCounts, float64(s.ConnectivityCheckPerURL)/float64(s.TargetsCountPerRoute)) + } + for pod, httpRetCountMap := range httpRetByPodRequest { + for url, retCountMap := range httpRetCountMap { + var actualHTTPRetCounts []float64 + for _, count := range retCountMap { + actualHTTPRetCounts = append(actualHTTPRetCounts, float64(count)) + } + chiSqStatics := stat.ChiSquare(actualHTTPRetCounts, expectedHTTPRetCounts) + pv := 1 - uniformDist.CDF(chiSqStatics) + if pv < connectivityCheckUniformDistributionSL { + utils.Logf( + "expect HTTP call from pod %s to %s be uniform distributed under significance level %v, got pValue: %v", + pod, url, connectivityCheckUniformDistributionSL, pv) + } + } + } + + for _, err := range checkErrors { + utils.Logf("check error: %v", err) + } + + if s.ServiceDiscoveryType != shared.CloudMapServiceDiscovery { + Expect(len(checkErrors)).To(BeZero()) + } +} + +func (s *DynamicStack) createMeshAndNamespace(ctx context.Context, f *framework.Framework) { + By("create a mesh", func() { + meshName := fmt.Sprintf("%s-%s", f.Options.ClusterName, utils.RandomDNS1123Label(6)) + mesh, err := f.K8sMeshClient.AppmeshV1beta1().Meshes().Create(&appmeshv1beta1.Mesh{ + ObjectMeta: metav1.ObjectMeta{ + Name: meshName, + }, + Spec: appmeshv1beta1.MeshSpec{}, + }) + Expect(err).NotTo(HaveOccurred()) + s.mesh = mesh + }) + + By(fmt.Sprintf("wait for mesh %s become active", s.mesh.Name), func() { + mesh, err := f.MeshManager.WaitUntilMeshActive(ctx, s.mesh) + Expect(err).NotTo(HaveOccurred()) + s.mesh = mesh + }) + + By("allocates a namespace", func() { + namespace, err := f.NSManager.AllocateNamespace(ctx, "appmesh") + Expect(err).NotTo(HaveOccurred()) + s.namespace = namespace + }) + + By("label namespace with appMesh inject", func() { + namespace := s.namespace.DeepCopy() + namespace.Labels = collection.MergeStringMap(s.namespace.Labels, map[string]string{ + "appmesh.k8s.aws/sidecarInjectorWebhook": "enabled", + }) + patch, err := k8s.CreateStrategicTwoWayMergePatch(s.namespace, namespace, corev1.Namespace{}) + Expect(err).NotTo(HaveOccurred()) + namespace, err = f.K8sClient.CoreV1().Namespaces().Patch(namespace.Name, types.StrategicMergePatchType, patch) + Expect(err).NotTo(HaveOccurred()) + s.namespace = namespace + }) +} + +func (s *DynamicStack) deleteMeshAndNamespace(ctx context.Context, f *framework.Framework) []error { + var deletionErrors []error + if s.namespace != nil { + By(fmt.Sprintf("delete namespace: %s", s.namespace.Name), func() { + foregroundDeletion := metav1.DeletePropagationForeground + if err := f.K8sClient.CoreV1().Namespaces().Delete(s.namespace.Name, &metav1.DeleteOptions{ + GracePeriodSeconds: aws.Int64(0), + PropagationPolicy: &foregroundDeletion, + }); err != nil { + utils.Logf("failed to delete namespace: %s due to %v", s.namespace.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + if s.mesh != nil { + By(fmt.Sprintf("delete mesh %s", s.mesh.Name), func() { + foregroundDeletion := metav1.DeletePropagationForeground + if err := f.K8sMeshClient.AppmeshV1beta1().Meshes().Delete(s.mesh.Name, &metav1.DeleteOptions{ + GracePeriodSeconds: aws.Int64(0), + PropagationPolicy: &foregroundDeletion, + }); err != nil { + utils.Logf("failed to delete mesh: %s due to %v", s.mesh.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + return deletionErrors +} + +func (s *DynamicStack) createCloudMapNamespace(ctx context.Context, f *framework.Framework) { + cmNamespace := fmt.Sprintf("%s-%s", f.Options.ClusterName, utils.RandomDNS1123Label(6)) + By(fmt.Sprintf("create cloudMap namespace %s", cmNamespace), func() { + resp, err := f.SDClient.CreatePrivateDnsNamespaceWithContext(ctx, &servicediscovery.CreatePrivateDnsNamespaceInput{ + Name: aws.String(cmNamespace), + Vpc: aws.String(f.Options.AWSVPCID), + }) + Expect(err).NotTo(HaveOccurred()) + s.cloudMapNamespace = cmNamespace + utils.Logf("created cloudMap namespace %s with operationID %s", cmNamespace, aws.StringValue(resp.OperationId)) + }) +} + +func (s *DynamicStack) deleteCloudMapNamespace(ctx context.Context, f *framework.Framework) []error { + var deletionErrors []error + if s.cloudMapNamespace != "" { + By(fmt.Sprintf("delete cloudMap namespace %s", s.cloudMapNamespace), func() { + var cmNamespaceID string + f.SDClient.ListNamespacesPagesWithContext(ctx, &servicediscovery.ListNamespacesInput{}, func(output *servicediscovery.ListNamespacesOutput, b bool) bool { + for _, ns := range output.Namespaces { + if aws.StringValue(ns.Name) == s.cloudMapNamespace { + cmNamespaceID = aws.StringValue(ns.Id) + return true + } + } + return false + }) + if cmNamespaceID == "" { + err := errors.Errorf("cannot find cloudMap namespace with name %s", s.cloudMapNamespace) + utils.Logf("failed to delete cloudMap namespace: %s due to %v", s.cloudMapNamespace, err) + deletionErrors = append(deletionErrors, err) + return + } + + // hummm, let's fix the controller bug in test cases first xD: + // https://github.com/aws/aws-app-mesh-controller-for-k8s/issues/107 + // https://github.com/aws/aws-app-mesh-controller-for-k8s/issues/131 + By(fmt.Sprintf("[bug workaround] clean up resources in cloudMap namespace %s", s.cloudMapNamespace), func() { + // give controller a break to deregister instance xD + time.Sleep(1 * time.Minute) + var cmServiceIDs []string + f.SDClient.ListServicesPagesWithContext(ctx, &servicediscovery.ListServicesInput{ + Filters: []*servicediscovery.ServiceFilter{ + { + Condition: aws.String(servicediscovery.FilterConditionEq), + Name: aws.String("NAMESPACE_ID"), + Values: aws.StringSlice([]string{cmNamespaceID}), + }, + }, + }, func(output *servicediscovery.ListServicesOutput, b bool) bool { + for _, svc := range output.Services { + cmServiceIDs = append(cmServiceIDs, aws.StringValue(svc.Id)) + } + return false + }) + for _, cmServiceID := range cmServiceIDs { + var cmInstanceIDs []string + f.SDClient.ListInstancesPagesWithContext(ctx, &servicediscovery.ListInstancesInput{ + ServiceId: aws.String(cmServiceID), + }, func(output *servicediscovery.ListInstancesOutput, b bool) bool { + for _, ins := range output.Instances { + cmInstanceIDs = append(cmInstanceIDs, aws.StringValue(ins.Id)) + } + return false + }) + + for _, cmInstanceID := range cmInstanceIDs { + if _, err := f.SDClient.DeregisterInstanceWithContext(ctx, &servicediscovery.DeregisterInstanceInput{ + ServiceId: aws.String(cmServiceID), + InstanceId: aws.String(cmInstanceID), + }); err != nil { + utils.Logf("failed to deregister instance due to %v", err) + deletionErrors = append(deletionErrors, err) + } + } + time.Sleep(30 * time.Second) + + if _, err := f.SDClient.DeleteServiceWithContext(ctx, &servicediscovery.DeleteServiceInput{ + Id: aws.String(cmServiceID), + }); err != nil { + utils.Logf("failed to deregister instance due to %v", err) + deletionErrors = append(deletionErrors, err) + } + } + }) + time.Sleep(30 * time.Second) + if _, err := f.SDClient.DeleteNamespaceWithContext(ctx, &servicediscovery.DeleteNamespaceInput{ + Id: aws.String(cmNamespaceID), + }); err != nil { + utils.Logf("failed to delete cloudMap namespace: %s due to %v", s.cloudMapNamespace, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + return deletionErrors +} + +func (s *DynamicStack) createResourcesForNodes(ctx context.Context, f *framework.Framework, mb *shared.ManifestBuilder) { + throttle := time.Tick(appMeshOperationRate) + + By("create all resources for nodes", func() { + s.createdNodeVNs = make([]*appmeshv1beta1.VirtualNode, s.VirtualNodesCount) + s.createdNodeDPs = make([]*appsv1.Deployment, s.VirtualNodesCount) + s.createdNodeSVCs = make([]*corev1.Service, s.VirtualNodesCount) + + var err error + + for i := 0; i != s.VirtualNodesCount; i++ { + instanceName := fmt.Sprintf("node-%d", i) + By(fmt.Sprintf("create VirtualNode for node #%d", i), func() { + vn := mb.BuildNodeVirtualNode(instanceName, nil) + + <-throttle + vn, err = f.K8sMeshClient.AppmeshV1beta1().VirtualNodes(s.namespace.Name).Create(vn) + Expect(err).NotTo(HaveOccurred()) + s.createdNodeVNs[i] = vn + }) + + By(fmt.Sprintf("create Deployment for node #%d", i), func() { + dp := mb.BuildNodeDeployment(instanceName, s.ReplicasPerVirtualNode) + dp, err = f.K8sClient.AppsV1().Deployments(s.namespace.Name).Create(dp) + Expect(err).NotTo(HaveOccurred()) + s.createdNodeDPs[i] = dp + }) + + By(fmt.Sprintf("create Service for node #%d", i), func() { + svc := mb.BuildNodeService(instanceName) + svc, err := f.K8sClient.CoreV1().Services(s.namespace.Name).Create(svc) + Expect(err).NotTo(HaveOccurred()) + s.createdNodeSVCs[i] = svc + }) + } + + By("wait all VirtualNodes become active", func() { + var waitErrors []string + waitErrorsMutex := &sync.Mutex{} + var wg sync.WaitGroup + for i := 0; i != s.VirtualNodesCount; i++ { + wg.Add(1) + go func(nodeIndex int) { + defer wg.Done() + dp, err := f.VNManager.WaitUntilVirtualNodeActive(ctx, s.createdNodeVNs[nodeIndex]) + if err != nil { + waitErrorsMutex.Lock() + waitErrors = append(waitErrors, err.Error()) + waitErrorsMutex.Unlock() + return + } + s.createdNodeVNs[nodeIndex] = dp + }(i) + } + wg.Wait() + if len(waitErrors) != 0 { + utils.Failf("failed to wait all VirtualNodes become active due to errors: %v", waitErrors) + } + }) + + By("wait all deployments become ready", func() { + var waitErrors []string + waitErrorsMutex := &sync.Mutex{} + var wg sync.WaitGroup + for i := 0; i != s.VirtualNodesCount; i++ { + wg.Add(1) + go func(nodeIndex int) { + defer wg.Done() + dp, err := f.DPManager.WaitUntilDeploymentReady(ctx, s.createdNodeDPs[nodeIndex]) + if err != nil { + waitErrorsMutex.Lock() + waitErrors = append(waitErrors, err.Error()) + waitErrorsMutex.Unlock() + return + } + s.createdNodeDPs[nodeIndex] = dp + }(i) + } + wg.Wait() + if len(waitErrors) != 0 { + utils.Failf("failed to wait all deployments become ready due to errors: %v", waitErrors) + } + }) + }) +} + +func (s *DynamicStack) deleteResourcesForNodes(ctx context.Context, f *framework.Framework) []error { + throttle := time.Tick(appMeshOperationRate) + + var deletionErrors []error + By("delete all resources for nodes", func() { + for i, svc := range s.createdNodeSVCs { + if svc == nil { + continue + } + By(fmt.Sprintf("delete Service for node #%d", i), func() { + if err := f.K8sClient.CoreV1().Services(svc.Namespace).Delete(svc.Name, &metav1.DeleteOptions{}); err != nil { + utils.Logf("failed to delete Service: %s/%s due to %v", svc.Namespace, svc.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + for i, dp := range s.createdNodeDPs { + if dp == nil { + continue + } + By(fmt.Sprintf("delete Deployment for node #%d", i), func() { + foregroundDeletion := metav1.DeletePropagationForeground + if err := f.K8sClient.AppsV1().Deployments(dp.Namespace).Delete(dp.Name, &metav1.DeleteOptions{ + GracePeriodSeconds: aws.Int64(0), + PropagationPolicy: &foregroundDeletion, + }); err != nil { + utils.Logf("failed to delete Deployment: %s/%s due to %v", dp.Namespace, dp.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + for i, vn := range s.createdNodeVNs { + if vn == nil { + continue + } + By(fmt.Sprintf("delete VirtualNode for node #%d", i), func() { + <-throttle + if err := f.K8sMeshClient.AppmeshV1beta1().VirtualNodes(vn.Namespace).Delete(vn.Name, &metav1.DeleteOptions{}); err != nil { + utils.Logf("failed to delete VirtualNode: %s/%s due to %v", vn.Namespace, vn.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + + By("wait all deployments become deleted", func() { + var waitErrors []string + waitErrorsMutex := &sync.Mutex{} + var wg sync.WaitGroup + for i, dp := range s.createdNodeDPs { + if dp == nil { + continue + } + wg.Add(1) + go func(nodeIndex int) { + defer wg.Done() + if err := f.DPManager.WaitUntilDeploymentDeleted(ctx, s.createdNodeDPs[nodeIndex]); err != nil { + waitErrorsMutex.Lock() + waitErrors = append(waitErrors, err.Error()) + waitErrorsMutex.Unlock() + return + } + }(i) + } + wg.Wait() + if len(waitErrors) != 0 { + utils.Failf("failed to wait all deployments become deleted due to errors: %v", waitErrors) + } + }) + + By("wait all VirtualNodes become deleted", func() { + var waitErrors []string + waitErrorsMutex := &sync.Mutex{} + var wg sync.WaitGroup + for i, vn := range s.createdNodeVNs { + if vn == nil { + continue + } + wg.Add(1) + go func(nodeIndex int) { + defer wg.Done() + if err := f.VNManager.WaitUntilVirtualNodeDeleted(ctx, s.createdNodeVNs[nodeIndex]); err != nil { + waitErrorsMutex.Lock() + waitErrors = append(waitErrors, err.Error()) + waitErrorsMutex.Unlock() + return + } + }(i) + } + wg.Wait() + if len(waitErrors) != 0 { + utils.Failf("failed to wait all VirtualNodes become active due to errors: %v", waitErrors) + } + }) + }) + return deletionErrors +} + +func (s *DynamicStack) createResourcesForServices(ctx context.Context, f *framework.Framework, mb *shared.ManifestBuilder) { + throttle := time.Tick(appMeshOperationRate) + + By("create all resources for services", func() { + s.createdServiceVSs = make([]*appmeshv1beta1.VirtualService, s.VirtualServicesCount) + s.createdServiceSVCs = make([]*corev1.Service, s.VirtualServicesCount) + + var err error + nextVirtualNodeIndex := 0 + for i := 0; i != s.VirtualServicesCount; i++ { + instanceName := fmt.Sprintf("service-%d", i) + By(fmt.Sprintf("create VirtualService for service #%d", i), func() { + var routeCfgs []shared.RouteToWeightedVirtualNodes + for routeIndex := 0; routeIndex != s.RoutesCountPerVirtualRouter; routeIndex++ { + var weightedTargets []shared.WeightedVirtualNode + for targetIndex := 0; targetIndex != s.TargetsCountPerRoute; targetIndex++ { + weightedTargets = append(weightedTargets, shared.WeightedVirtualNode{ + VirtualNodeName: s.createdNodeVNs[nextVirtualNodeIndex].Name, + Weight: 1, + }) + nextVirtualNodeIndex = (nextVirtualNodeIndex + 1) % s.VirtualNodesCount + } + routeCfgs = append(routeCfgs, shared.RouteToWeightedVirtualNodes{ + Path: fmt.Sprintf("/path-%d", routeIndex), + WeightedTargets: weightedTargets, + }) + } + vs := mb.BuildServiceVirtualService(instanceName, routeCfgs) + <-throttle + vs, err := f.K8sMeshClient.AppmeshV1beta1().VirtualServices(s.namespace.Name).Create(vs) + Expect(err).NotTo(HaveOccurred()) + s.createdServiceVSs[i] = vs + }) + + By(fmt.Sprintf("create Service for service #%d", i), func() { + svc := mb.BuildServiceService(instanceName) + svc, err = f.K8sClient.CoreV1().Services(s.namespace.Name).Create(svc) + Expect(err).NotTo(HaveOccurred()) + s.createdServiceSVCs[i] = svc + }) + } + + By("wait all VirtualService become active", func() { + var waitErrors []string + waitErrorsMutex := &sync.Mutex{} + var wg sync.WaitGroup + for i := 0; i != s.VirtualServicesCount; i++ { + wg.Add(1) + go func(serviceIndex int) { + defer wg.Done() + vs, err := f.VSManager.WaitUntilVirtualServiceActive(ctx, s.createdServiceVSs[serviceIndex]) + if err != nil { + waitErrorsMutex.Lock() + waitErrors = append(waitErrors, err.Error()) + waitErrorsMutex.Unlock() + return + } + s.createdServiceVSs[serviceIndex] = vs + }(i) + } + wg.Wait() + if len(waitErrors) != 0 { + utils.Failf("failed to wait all VirtualService become active due to errors: %v", waitErrors) + } + }) + }) +} + +func (s *DynamicStack) deleteResourcesForServices(ctx context.Context, f *framework.Framework) []error { + throttle := time.Tick(appMeshOperationRate) + + var deletionErrors []error + By("delete all resources for services", func() { + for i, svc := range s.createdServiceSVCs { + if svc == nil { + continue + } + + By(fmt.Sprintf("delete Service for service #%d", i), func() { + if err := f.K8sClient.CoreV1().Services(svc.Namespace).Delete(svc.Name, &metav1.DeleteOptions{}); err != nil { + utils.Logf("failed to delete Service: %s/%s due to %v", svc.Namespace, svc.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + for i, vs := range s.createdServiceVSs { + if vs == nil { + continue + } + By(fmt.Sprintf("delete VirtualService for service #%d", i), func() { + <-throttle + if err := f.K8sMeshClient.AppmeshV1beta1().VirtualServices(vs.Namespace).Delete(vs.Name, &metav1.DeleteOptions{}); err != nil { + utils.Logf("failed to delete VirtualService: %s/%s due to %v", vs.Namespace, vs.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + + By("wait all VirtualService become deleted", func() { + var waitErrors []string + waitErrorsMutex := &sync.Mutex{} + var wg sync.WaitGroup + for i, vs := range s.createdServiceVSs { + if vs == nil { + continue + } + wg.Add(1) + go func(serviceIndex int) { + defer wg.Done() + if err := f.VSManager.WaitUntilVirtualServiceDeleted(ctx, s.createdServiceVSs[serviceIndex]); err != nil { + waitErrorsMutex.Lock() + waitErrors = append(waitErrors, err.Error()) + waitErrorsMutex.Unlock() + return + } + }(i) + } + wg.Wait() + if len(waitErrors) != 0 { + utils.Failf("failed to wait all VirtualService become deleted due to errors: %v", waitErrors) + } + }) + }) + return deletionErrors +} + +func (s *DynamicStack) grantVirtualNodesBackendAccess(ctx context.Context, f *framework.Framework) { + throttle := time.Tick(appMeshOperationRate) + + By("granting VirtualNodes backend access", func() { + nextVirtualServiceIndex := 0 + for i, vn := range s.createdNodeVNs { + if vn == nil { + continue + } + By(fmt.Sprintf("granting VirtualNode backend access for node #%d", i), func() { + var vnBackends []appmeshv1beta1.Backend + for backendIndex := 0; backendIndex != s.BackendsCountPerVirtualNode; backendIndex++ { + vnBackends = append(vnBackends, appmeshv1beta1.Backend{ + VirtualService: appmeshv1beta1.VirtualServiceBackend{ + VirtualServiceName: s.createdServiceVSs[nextVirtualServiceIndex].Name, + }, + }) + nextVirtualServiceIndex = (nextVirtualServiceIndex + 1) % s.VirtualServicesCount + } + + vnNew := vn.DeepCopy() + vnNew.Spec.Backends = vnBackends + patch, err := k8s.CreateJSONMergePatch(vn, vnNew, appmeshv1beta1.VirtualNode{}) + Expect(err).NotTo(HaveOccurred()) + <-throttle + vnNew, err = f.K8sMeshClient.AppmeshV1beta1().VirtualNodes(vnNew.Namespace).Patch(vnNew.Name, types.MergePatchType, patch) + Expect(err).NotTo(HaveOccurred()) + s.createdNodeVNs[i] = vnNew + }) + } + }) +} + +func (s *DynamicStack) revokeVirtualNodeBackendAccess(ctx context.Context, f *framework.Framework) []error { + throttle := time.Tick(appMeshOperationRate) + + var deletionErrors []error + By("revoking VirtualNodes backend access", func() { + for i, vn := range s.createdNodeVNs { + if vn == nil || len(vn.Spec.Backends) == 0 { + continue + } + By(fmt.Sprintf("revoking VirtualNode backend access for node #%d", i), func() { + vnNew := vn.DeepCopy() + vnNew.Spec.Backends = nil + patch, err := k8s.CreateJSONMergePatch(vn, vnNew, appmeshv1beta1.VirtualNode{}) + if err != nil { + utils.Logf("failed to revoke VirtualNode backend access : %s/%s due to %v", vn.Namespace, vn.Name, err) + deletionErrors = append(deletionErrors, err) + return + } + <-throttle + vnNew, err = f.K8sMeshClient.AppmeshV1beta1().VirtualNodes(vnNew.Namespace).Patch(vnNew.Name, types.MergePatchType, patch) + if err != nil { + utils.Logf("failed to revoke VirtualNode backend access : %s/%s due to %v", vn.Namespace, vn.Name, err) + deletionErrors = append(deletionErrors, err) + } + }) + } + }) + return deletionErrors +} + +func (s *DynamicStack) checkDeploymentVirtualServiceConnectivity(ctx context.Context, f *framework.Framework, + vn *appmeshv1beta1.VirtualNode, dp *appsv1.Deployment, vsList []*appmeshv1beta1.VirtualService) []connectivityCheckEntry { + sel := labels.Set(dp.Spec.Selector.MatchLabels) + opts := metav1.ListOptions{LabelSelector: sel.AsSelector().String()} + pods, err := f.K8sClient.CoreV1().Pods(dp.Namespace).List(opts) + Expect(err).NotTo(HaveOccurred()) + Expect(len(pods.Items)).NotTo(BeZero()) + + var checkEntriesRet []connectivityCheckEntry + for i := range pods.Items { + pod := pods.Items[i].DeepCopy() + By(fmt.Sprintf("check pod %s/%s connectivity to services", pod.Namespace, pod.Name), func() { + podConnectivityCheckResult := s.checkPodVirtualServiceConnectivity(ctx, f, vn, pod, vsList) + checkEntriesRet = append(checkEntriesRet, podConnectivityCheckResult...) + }) + } + return checkEntriesRet +} + +func (s *DynamicStack) checkPodVirtualServiceConnectivity(ctx context.Context, f *framework.Framework, + vn *appmeshv1beta1.VirtualNode, pod *corev1.Pod, vsList []*appmeshv1beta1.VirtualService) []connectivityCheckEntry { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + var checkEntries []connectivityCheckEntry + for _, vs := range vsList { + for _, route := range vs.Spec.Routes { + path := route.Http.Match.Prefix + checkEntries = append(checkEntries, connectivityCheckEntry{ + srcVirtualNode: k8s.NamespacedName(vn), + srcPod: k8s.NamespacedName(pod), + dstVirtualService: k8s.NamespacedName(vs), + dstURL: fmt.Sprintf("http://%s:%d%s", vs.Name, shared.AppContainerPort, path), + }) + } + } + + pfErrChan := make(chan error) + pfReadyChan := make(chan struct{}) + portForwarder, err := k8s.NewPortForwarder(ctx, f.RestCfg, pod, []string{fmt.Sprintf("%d:%d", connectivityCheckProxyPort, shared.HttpProxyContainerPort)}, pfReadyChan) + Expect(err).NotTo(HaveOccurred()) + go func() { + pfErrChan <- portForwarder.ForwardPorts() + }() + proxyURL, err := url.Parse(fmt.Sprintf("http://localhost:%d", connectivityCheckProxyPort)) + Expect(err).NotTo(HaveOccurred()) + proxyClient := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)}} + + checkEntriesRetChan := make(chan connectivityCheckEntry) + go func() { + var wg sync.WaitGroup + throttle := time.Tick(connectivityCheckRate) + for _, entry := range checkEntries { + for i := 0; i != s.ConnectivityCheckPerURL; i++ { + <-throttle + wg.Add(1) + go func(entry connectivityCheckEntry) { + defer wg.Done() + <-pfReadyChan + resp, err := proxyClient.Get(entry.dstURL) + if err != nil { + entry.retErr = err + checkEntriesRetChan <- entry + return + } + entry.retHTTPStatusCode = resp.StatusCode + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + entry.retErr = err + checkEntriesRetChan <- entry + return + } + entry.retHTTPBody = string(body) + checkEntriesRetChan <- entry + }(entry) + } + } + wg.Wait() + close(checkEntriesRetChan) + }() + + var checkEntriesRet []connectivityCheckEntry + for ret := range checkEntriesRetChan { + checkEntriesRet = append(checkEntriesRet, ret) + } + cancel() + Expect(<-pfErrChan).To(BeNil()) + return checkEntriesRet +} diff --git a/test/e2e/fishapp/dynamic_stack_test.go b/test/e2e/fishapp/dynamic_stack_test.go new file mode 100644 index 000000000..61463b754 --- /dev/null +++ b/test/e2e/fishapp/dynamic_stack_test.go @@ -0,0 +1,111 @@ +package fishapp_test + +import ( + "context" + "fmt" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/fishapp" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/fishapp/shared" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework" + . "github.com/onsi/ginkgo" + "time" +) + +var _ = Describe("tests by dynamic generated stack", func() { + var ( + ctx context.Context + f *framework.Framework + ) + + BeforeEach(func() { + ctx = context.Background() + f = framework.New(framework.GlobalOptions) + + if f.Options.ControllerImage != "" { + By("reset cluster with default controller", func() { + f.HelmManager.ResetAppMeshController() + }) + } + if f.Options.InjectorImage != "" { + By("reset cluster with default injector", func() { + f.HelmManager.ResetAppMeshInjector() + }) + } + }) + + Context("normal test dimensions", func() { + var stackPrototype fishapp.DynamicStack + var stacksPendingCleanUp []*fishapp.DynamicStack + + BeforeEach(func() { + stackPrototype = fishapp.DynamicStack{ + VirtualServicesCount: 5, + VirtualNodesCount: 5, + RoutesCountPerVirtualRouter: 2, + TargetsCountPerRoute: 4, + BackendsCountPerVirtualNode: 2, + ReplicasPerVirtualNode: 3, + ConnectivityCheckPerURL: 400, + } + stacksPendingCleanUp = nil + }) + + AfterEach(func() { + for _, stack := range stacksPendingCleanUp { + stack.Cleanup(ctx, f) + } + }) + + for _, sdType := range []shared.ServiceDiscoveryType{shared.DNSServiceDiscovery, shared.CloudMapServiceDiscovery} { + func(sdType shared.ServiceDiscoveryType) { + It(fmt.Sprintf("should behaves correctly with service discovery type %v", sdType), func() { + stackPrototype.ServiceDiscoveryType = sdType + stackDefault := stackPrototype + By("deploy stack into cluster with default controller/injector", func() { + stacksPendingCleanUp = append(stacksPendingCleanUp, &stackDefault) + stackDefault.Deploy(ctx, f) + }) + + By("sleep 1 minute to give controller/injector a break", func() { + time.Sleep(1 * time.Minute) + }) + By("check stack behavior on cluster with default controller/injector", func() { + stackDefault.Check(ctx, f) + }) + + if f.Options.ControllerImage != "" || f.Options.InjectorImage != "" { + if f.Options.ControllerImage != "" { + By("upgrade cluster into new controller", func() { + f.HelmManager.UpgradeAppMeshController(f.Options.ControllerImage) + }) + } + if f.Options.InjectorImage != "" { + By("upgrade cluster into new injector", func() { + f.HelmManager.UpgradeAppMeshInjector(f.Options.InjectorImage) + }) + } + + By("sleep 1 minute to give controller/injector a break", func() { + time.Sleep(1 * time.Minute) + }) + By("check stack behavior on cluster with upgraded controller/injector", func() { + stackDefault.Check(ctx, f) + }) + + stackNew := stackPrototype + By("deploy new stack into cluster with upgraded controller/injector", func() { + stacksPendingCleanUp = append(stacksPendingCleanUp, &stackNew) + stackNew.Deploy(ctx, f) + }) + + By("sleep 1 minute to give controller/injector a break", func() { + time.Sleep(1 * time.Minute) + }) + By("check new stack behavior on cluster with upgraded controller/injector", func() { + stackNew.Check(ctx, f) + }) + } + }) + }(sdType) + } + }) +}) diff --git a/test/e2e/fishapp/fishapp_suite_test.go b/test/e2e/fishapp/fishapp_suite_test.go new file mode 100644 index 000000000..3a705f984 --- /dev/null +++ b/test/e2e/fishapp/fishapp_suite_test.go @@ -0,0 +1,13 @@ +package fishapp_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestFishApp(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "FishApp Suite") +} diff --git a/test/e2e/fishapp/shared/manifest_builder.go b/test/e2e/fishapp/shared/manifest_builder.go new file mode 100644 index 000000000..ba54942c8 --- /dev/null +++ b/test/e2e/fishapp/shared/manifest_builder.go @@ -0,0 +1,275 @@ +package shared + +import ( + "fmt" + appmeshv1beta1 "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/apis/appmesh/v1beta1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +const ( + defaultAppImage = "970805265562.dkr.ecr.us-west-2.amazonaws.com/colorteller:latest" + defaultHTTPProxyImage = "abhinavsingh/proxy.py:latest" +) + +const ( + AppContainerPort = 9080 + HttpProxyContainerPort = 8899 +) + +type ServiceDiscoveryType string + +const ( + DNSServiceDiscovery ServiceDiscoveryType = "DNS" + CloudMapServiceDiscovery ServiceDiscoveryType = "CloudMap" +) + +type ManifestBuilder struct { + MeshName string + Namespace string + ServiceDiscoveryType ServiceDiscoveryType + + // required when serviceDiscoveryType == CloudMapServiceDiscovery + CloudMapNamespace string +} + +func (b *ManifestBuilder) BuildNodeDeployment(instanceName string, replicas int32) *appsv1.Deployment { + labels := b.buildNodeSelectors(instanceName) + dpName := instanceName + dp := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: b.Namespace, + Name: dpName, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: map[string]string{ + "appmesh.k8s.aws/mesh": b.MeshName, + "appmesh.k8s.aws/ports": fmt.Sprintf("%d", AppContainerPort), + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Image: defaultAppImage, + Ports: []corev1.ContainerPort{ + { + ContainerPort: AppContainerPort, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "SERVER_PORT", + Value: fmt.Sprintf("%d", AppContainerPort), + }, + { + Name: "COLOR", + Value: instanceName, + }, + }, + }, + { + Name: "http-proxy", + Image: defaultHTTPProxyImage, + Ports: []corev1.ContainerPort{ + { + ContainerPort: HttpProxyContainerPort, + }, + }, + Args: []string{ + "--hostname=0.0.0.0", + fmt.Sprintf("--port=%d", HttpProxyContainerPort), + }, + }, + }, + }, + }, + }, + } + return dp +} + +func (b *ManifestBuilder) BuildNodeService(instanceName string) *corev1.Service { + labels := b.buildNodeSelectors(instanceName) + svcName := b.buildNodeServiceName(instanceName) + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: b.Namespace, + Name: svcName, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Selector: labels, + Ports: []corev1.ServicePort{ + { + Port: AppContainerPort, + TargetPort: intstr.FromInt(AppContainerPort), + Protocol: corev1.ProtocolTCP, + }, + }, + }, + } + return svc +} + +func (b *ManifestBuilder) BuildNodeVirtualNode(instanceName string, backendVirtualServices []string) *appmeshv1beta1.VirtualNode { + vnName := instanceName + var sd *appmeshv1beta1.ServiceDiscovery + switch b.ServiceDiscoveryType { + case DNSServiceDiscovery: + sd = b.buildNodeDNSServiceDiscovery(instanceName) + case CloudMapServiceDiscovery: + sd = b.buildNodeCloudMapServiceDiscovery(instanceName) + } + var backends []appmeshv1beta1.Backend + for _, backendVS := range backendVirtualServices { + backends = append(backends, appmeshv1beta1.Backend{ + VirtualService: appmeshv1beta1.VirtualServiceBackend{ + VirtualServiceName: backendVS, + }, + }) + } + vn := &appmeshv1beta1.VirtualNode{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: b.Namespace, + Name: vnName, + }, + Spec: appmeshv1beta1.VirtualNodeSpec{ + MeshName: b.MeshName, + Listeners: []appmeshv1beta1.Listener{ + { + PortMapping: appmeshv1beta1.PortMapping{ + Port: AppContainerPort, + Protocol: "http", + }, + }, + }, + ServiceDiscovery: sd, + Backends: backends, + }, + } + return vn +} + +type RouteToWeightedVirtualNodes struct { + Path string + WeightedTargets []WeightedVirtualNode +} + +// WeightedVirtualNode is virtual node with weight +type WeightedVirtualNode struct { + VirtualNodeName string + Weight int64 +} + +func (b *ManifestBuilder) BuildServiceService(instanceName string) *corev1.Service { + svcName := instanceName + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: b.Namespace, + Name: svcName, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeClusterIP, + Ports: []corev1.ServicePort{ + { + Port: AppContainerPort, + TargetPort: intstr.FromInt(AppContainerPort), + Protocol: corev1.ProtocolTCP, + }, + }, + }, + } + return svc +} + +func (b *ManifestBuilder) BuildServiceVirtualService(instanceName string, routeCfgs []RouteToWeightedVirtualNodes) *appmeshv1beta1.VirtualService { + svcName := b.buildServiceServiceName(instanceName) + svcDNS := fmt.Sprintf("%s.%s", svcName, b.Namespace) + var routes []appmeshv1beta1.Route + for index, routeCfg := range routeCfgs { + var targets []appmeshv1beta1.WeightedTarget + for _, weightedTarget := range routeCfg.WeightedTargets { + targets = append(targets, appmeshv1beta1.WeightedTarget{ + VirtualNodeName: weightedTarget.VirtualNodeName, + Weight: weightedTarget.Weight, + }) + } + routes = append(routes, appmeshv1beta1.Route{ + Name: fmt.Sprintf("path-%d", index), + Http: &appmeshv1beta1.HttpRoute{ + Match: appmeshv1beta1.HttpRouteMatch{ + Prefix: routeCfg.Path, + }, + Action: appmeshv1beta1.HttpRouteAction{ + WeightedTargets: targets, + }, + }, + }) + } + vs := &appmeshv1beta1.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: b.Namespace, + Name: svcDNS, + }, + Spec: appmeshv1beta1.VirtualServiceSpec{ + MeshName: b.MeshName, + VirtualRouter: &appmeshv1beta1.VirtualRouter{ + Listeners: []appmeshv1beta1.VirtualRouterListener{ + { + PortMapping: appmeshv1beta1.PortMapping{ + Port: AppContainerPort, + Protocol: "http", + }, + }, + }, + }, + Routes: routes, + }, + } + return vs +} + +func (b *ManifestBuilder) buildNodeDNSServiceDiscovery(instanceName string) *appmeshv1beta1.ServiceDiscovery { + nodeServiceName := b.buildNodeServiceName(instanceName) + nodeServiceDNS := fmt.Sprintf("%s.%s", nodeServiceName, b.Namespace) + return &appmeshv1beta1.ServiceDiscovery{ + Dns: &appmeshv1beta1.DnsServiceDiscovery{ + HostName: nodeServiceDNS, + }, + } +} + +func (b *ManifestBuilder) buildNodeCloudMapServiceDiscovery(instanceName string) *appmeshv1beta1.ServiceDiscovery { + nodeServiceName := b.buildNodeServiceName(instanceName) + return &appmeshv1beta1.ServiceDiscovery{ + CloudMap: &appmeshv1beta1.CloudMapServiceDiscovery{ + NamespaceName: b.CloudMapNamespace, + ServiceName: nodeServiceName, + }, + } +} + +func (b *ManifestBuilder) buildNodeSelectors(instanceName string) map[string]string { + return map[string]string{ + "app.kubernetes.io/name": "fish-app", + "app.kubernetes.io/instance": instanceName, + } +} + +func (b *ManifestBuilder) buildNodeServiceName(instanceName string) string { + // I like to be explicit about implicit connections between Objects + return instanceName +} + +func (b *ManifestBuilder) buildServiceServiceName(instanceName string) string { + // I like to be explicit about implicit connections between Objects + return instanceName +} diff --git a/test/e2e/framework/collection/map.go b/test/e2e/framework/collection/map.go new file mode 100644 index 000000000..d5cc22e6f --- /dev/null +++ b/test/e2e/framework/collection/map.go @@ -0,0 +1,11 @@ +package collection + +func MergeStringMap(maps ...map[string]string) map[string]string { + ret := make(map[string]string) + for _, _map := range maps { + for k, v := range _map { + ret[k] = v + } + } + return ret +} diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go new file mode 100644 index 000000000..9366808d0 --- /dev/null +++ b/test/e2e/framework/framework.go @@ -0,0 +1,78 @@ +package framework + +import ( + meshclientset "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/client/clientset/versioned" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/helm" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/resource/deployment" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/resource/mesh" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/resource/namespace" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/resource/virtualnode" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/resource/virtualservice" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/servicediscovery" + "github.com/aws/aws-sdk-go/service/servicediscovery/servicediscoveryiface" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +type Framework struct { + Options Options + RestCfg *rest.Config + K8sClient kubernetes.Interface + K8sMeshClient meshclientset.Interface + + NSManager namespace.Manager + DPManager deployment.Manager + MeshManager mesh.Manager + VNManager virtualnode.Manager + VSManager virtualservice.Manager + HelmManager helm.Manager + + SDClient servicediscoveryiface.ServiceDiscoveryAPI +} + +func New(options Options) *Framework { + err := options.Validate() + Expect(err).NotTo(HaveOccurred()) + + restCfg, err := buildRestConfig(options) + Expect(err).NotTo(HaveOccurred()) + + k8sClient, err := kubernetes.NewForConfig(restCfg) + Expect(err).NotTo(HaveOccurred()) + + k8sMeshClient, err := meshclientset.NewForConfig(restCfg) + Expect(err).NotTo(HaveOccurred()) + + sess := session.Must(session.NewSession(aws.NewConfig().WithRegion(options.AWSRegion))) + sdClient := servicediscovery.New(sess) + f := &Framework{ + Options: options, + RestCfg: restCfg, + K8sClient: k8sClient, + K8sMeshClient: k8sMeshClient, + + HelmManager: helm.NewManager(options.KubeConfig), + NSManager: namespace.NewManager(k8sClient), + DPManager: deployment.NewManager(k8sClient), + MeshManager: mesh.NewManager(k8sMeshClient), + VNManager: virtualnode.NewManager(k8sMeshClient), + VSManager: virtualservice.NewManager(k8sMeshClient), + + SDClient: sdClient, + } + return f +} + +func buildRestConfig(options Options) (*rest.Config, error) { + restCfg, err := clientcmd.BuildConfigFromFlags("", options.KubeConfig) + if err != nil { + return nil, err + } + restCfg.QPS = 20 + restCfg.Burst = 50 + return restCfg, nil +} diff --git a/test/e2e/framework/helm/constants.go b/test/e2e/framework/helm/constants.go new file mode 100644 index 000000000..779620c9a --- /dev/null +++ b/test/e2e/framework/helm/constants.go @@ -0,0 +1,11 @@ +package helm + +const ( + eksHelmChartsRepo = "https://aws.github.io/eks-charts" + appMeshControllerHelmChart = "appmesh-controller" + appMeshInjectorHelmChart = "appmesh-inject" + + appMeshSystemNamespace = "appmesh-system" + appMeshControllerHelmReleaseName = "appmesh-controller" + appMeshInjectorHelmReleaseName = "appmesh-inject" +) diff --git a/test/e2e/framework/helm/release.go b/test/e2e/framework/helm/release.go new file mode 100644 index 000000000..1bd39acf2 --- /dev/null +++ b/test/e2e/framework/helm/release.go @@ -0,0 +1,106 @@ +package helm + +import ( + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + "github.com/pkg/errors" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/cli" + "helm.sh/helm/v3/pkg/release" + "k8s.io/cli-runtime/pkg/genericclioptions" + "strings" +) + +type Manager interface { + // reset appMesh controller to default one installed by helm charts + ResetAppMeshController() error + // upgrade appMesh controller to new one with image overridden. + UpgradeAppMeshController(controllerImage string) error + + // reset appMesh injector to default one installed by helm charts + ResetAppMeshInjector() error + // upgrade appMesh injector to new one with image overridden. + UpgradeAppMeshInjector(injectorImage string) error + + // Upgrade a helm release + UpgradeHelmRelease(chartRepo string, chartName string, namespace string, releaseName string, vals map[string]interface{}) (*release.Release, error) +} + +func NewManager(kubeConfig string) Manager { + return &defaultManager{kubeConfig: kubeConfig} +} + +type defaultManager struct { + kubeConfig string +} + +func (m *defaultManager) ResetAppMeshController() error { + vals := make(map[string]interface{}) + _, err := m.UpgradeHelmRelease(eksHelmChartsRepo, appMeshControllerHelmChart, appMeshSystemNamespace, appMeshControllerHelmReleaseName, vals) + return err +} + +func (m *defaultManager) UpgradeAppMeshController(controllerImage string) error { + vals := make(map[string]interface{}) + imageRepo, imageTag, err := splitImageRepoAndTag(controllerImage) + if err != nil { + return err + } + vals["image"] = map[string]interface{}{ + "repository": imageRepo, + "tag": imageTag, + } + _, err = m.UpgradeHelmRelease(eksHelmChartsRepo, appMeshControllerHelmChart, appMeshSystemNamespace, appMeshControllerHelmReleaseName, vals) + return err +} + +func (m *defaultManager) ResetAppMeshInjector() error { + vals := make(map[string]interface{}) + _, err := m.UpgradeHelmRelease(eksHelmChartsRepo, appMeshInjectorHelmChart, appMeshSystemNamespace, appMeshInjectorHelmReleaseName, vals) + return err +} + +func (m *defaultManager) UpgradeAppMeshInjector(injectorImage string) error { + vals := make(map[string]interface{}) + imageRepo, imageTag, err := splitImageRepoAndTag(injectorImage) + if err != nil { + return err + } + vals["image"] = map[string]interface{}{ + "repository": imageRepo, + "tag": imageTag, + } + _, err = m.UpgradeHelmRelease(eksHelmChartsRepo, appMeshInjectorHelmChart, appMeshSystemNamespace, appMeshInjectorHelmReleaseName, vals) + return err +} + +func (m *defaultManager) UpgradeHelmRelease(chartRepo string, chartName string, namespace string, releaseName string, vals map[string]interface{}) (*release.Release, error) { + cfgFlags := genericclioptions.NewConfigFlags(false) + cfgFlags.KubeConfig = &m.kubeConfig + cfgFlags.Namespace = &namespace + actionConfig := new(action.Configuration) + actionConfig.Init(cfgFlags, namespace, "secrets", func(format string, v ...interface{}) { + utils.Logf(format, v...) + }) + upgradeAction := action.NewUpgrade(actionConfig) + upgradeAction.ChartPathOptions.RepoURL = chartRepo + upgradeAction.Namespace = namespace + upgradeAction.ResetValues = true + upgradeAction.Wait = true + + cp, err := upgradeAction.ChartPathOptions.LocateChart(chartName, cli.New()) + chartRequested, err := loader.Load(cp) + if err != nil { + return nil, err + } + return upgradeAction.Run(releaseName, chartRequested, vals) +} + +// splitImageRepoAndTag parses a docker image in format : into `imageRepo` and `imageTag` +func splitImageRepoAndTag(dockerImage string) (string, string, error) { + parts := strings.Split(dockerImage, ":") + if len(parts) != 2 { + return "", "", errors.Errorf("dockerImage expects :, got: %s", dockerImage) + } + return parts[0], parts[1], nil +} diff --git a/test/e2e/framework/k8s/name.go b/test/e2e/framework/k8s/name.go new file mode 100644 index 000000000..bde1e2188 --- /dev/null +++ b/test/e2e/framework/k8s/name.go @@ -0,0 +1,13 @@ +package k8s + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +func NamespacedName(obj metav1.Object) types.NamespacedName { + return types.NamespacedName{ + Namespace: obj.GetNamespace(), + Name: obj.GetName(), + } +} diff --git a/test/e2e/framework/k8s/patch.go b/test/e2e/framework/k8s/patch.go new file mode 100644 index 000000000..44e463816 --- /dev/null +++ b/test/e2e/framework/k8s/patch.go @@ -0,0 +1,37 @@ +package k8s + +import ( + "encoding/json" + jsonpatch "github.com/evanphx/json-patch" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/strategicpatch" +) + +func CreateStrategicTwoWayMergePatch(oldObj metav1.Object, newObj metav1.Object, objType interface{}) ([]byte, error) { + oldData, err := json.Marshal(oldObj) + if err != nil { + return nil, errors.Wrapf(err, "failed to Marshal oldObj: %s/%s", oldObj.GetName(), oldObj.GetName()) + } + + newData, err := json.Marshal(newObj) + if err != nil { + return nil, errors.Wrapf(err, "failed to Marshal newObj: %s/%s", newObj.GetName(), newObj.GetName()) + } + + return strategicpatch.CreateTwoWayMergePatch(oldData, newData, objType) +} + +func CreateJSONMergePatch(oldObj metav1.Object, newObj metav1.Object, _ interface{}) ([]byte, error) { + oldData, err := json.Marshal(oldObj) + if err != nil { + return nil, errors.Wrapf(err, "failed to Marshal oldObj: %s/%s", oldObj.GetName(), oldObj.GetName()) + } + + newData, err := json.Marshal(newObj) + if err != nil { + return nil, errors.Wrapf(err, "failed to Marshal newObj: %s/%s", newObj.GetName(), newObj.GetName()) + } + + return jsonpatch.CreateMergePatch(oldData, newData) +} diff --git a/test/e2e/framework/k8s/port_forward.go b/test/e2e/framework/k8s/port_forward.go new file mode 100644 index 000000000..2b3bca834 --- /dev/null +++ b/test/e2e/framework/k8s/port_forward.go @@ -0,0 +1,33 @@ +package k8s + +import ( + "context" + "fmt" + "github.com/onsi/ginkgo" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/portforward" + "k8s.io/client-go/transport/spdy" + "net/http" +) + +// NewPortForwarder return a new port forwarder +func NewPortForwarder(ctx context.Context, restCfg *rest.Config, pod *corev1.Pod, ports []string, readyChan chan struct{}) (*portforward.PortForwarder, error) { + cs, _ := kubernetes.NewForConfig(restCfg) + req := cs.RESTClient().Post(). + Resource("pods"). + Namespace(pod.Namespace). + Name(pod.Name). + SubResource("portforward") + url := req.URL() + // the generated url above is incorrect, fix them below + url.Path = fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", pod.Namespace, pod.Name) + + transport, upgrader, err := spdy.RoundTripperFor(restCfg) + if err != nil { + return nil, err + } + dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, http.MethodPost, url) + return portforward.New(dialer, ports, ctx.Done(), readyChan, nil, ginkgo.GinkgoWriter) +} diff --git a/test/e2e/framework/options.go b/test/e2e/framework/options.go new file mode 100644 index 000000000..aecd2983f --- /dev/null +++ b/test/e2e/framework/options.go @@ -0,0 +1,52 @@ +package framework + +import ( + "flag" + "github.com/pkg/errors" + "k8s.io/client-go/tools/clientcmd" +) + +var GlobalOptions Options + +func init() { + GlobalOptions.BindFlags() +} + +type Options struct { + KubeConfig string + ClusterName string + AWSRegion string + AWSVPCID string + + // appMesh controller image. leave empty to use default one from helm chart. + ControllerImage string + + // appmesh injector image, leave empty to use default one from helm chart. + InjectorImage string +} + +func (options *Options) BindFlags() { + flag.StringVar(&options.KubeConfig, clientcmd.RecommendedConfigPathFlag, "", "Path to kubeconfig containing embedded authinfo (required)") + flag.StringVar(&options.ClusterName, "cluster-name", "", `Kubernetes cluster name (required)`) + flag.StringVar(&options.AWSRegion, "aws-region", "", `AWS Region for the kubernetes cluster`) + flag.StringVar(&options.AWSVPCID, "aws-vpc-id", "", `AWS VPC ID for the kubernetes cluster`) + + flag.StringVar(&options.ControllerImage, "controller-image", "", `appMesh controller image. leave empty to use default one from helm chart`) + flag.StringVar(&options.InjectorImage, "injector-image", "", `appMesh injector image, leave empty to use default one from helm chart`) +} + +func (options *Options) Validate() error { + if len(options.KubeConfig) == 0 { + return errors.Errorf("%s must be set!", clientcmd.RecommendedConfigPathFlag) + } + if len(options.ClusterName) == 0 { + return errors.Errorf("%s must be set!", "cluster-name") + } + if len(options.AWSRegion) == 0 { + return errors.Errorf("%s must be set!", "aws-region") + } + if len(options.AWSVPCID) == 0 { + return errors.Errorf("%s must be set!", "aws-vpc-id") + } + return nil +} diff --git a/test/e2e/framework/resource/deployment/manager.go b/test/e2e/framework/resource/deployment/manager.go new file mode 100644 index 000000000..51ea06468 --- /dev/null +++ b/test/e2e/framework/resource/deployment/manager.go @@ -0,0 +1,61 @@ +package deployment + +import ( + "context" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + appsv1 "k8s.io/api/apps/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +type Manager interface { + WaitUntilDeploymentReady(ctx context.Context, dp *appsv1.Deployment) (*appsv1.Deployment, error) + + WaitUntilDeploymentDeleted(ctx context.Context, dp *appsv1.Deployment) error +} + +func NewManager(cs kubernetes.Interface) Manager { + return &defaultManager{ + cs: cs, + } +} + +type defaultManager struct { + cs kubernetes.Interface +} + +func (m *defaultManager) WaitUntilDeploymentReady(ctx context.Context, dp *appsv1.Deployment) (*appsv1.Deployment, error) { + var ( + observedDP *appsv1.Deployment + err error + ) + return observedDP, wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + observedDP, err = m.cs.AppsV1().Deployments(dp.Namespace).Get(dp.Name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + if observedDP.Status.UpdatedReplicas == (*dp.Spec.Replicas) && + observedDP.Status.Replicas == (*dp.Spec.Replicas) && + observedDP.Status.AvailableReplicas == (*dp.Spec.Replicas) && + observedDP.Status.ObservedGeneration >= dp.Generation { + return true, nil + } + return false, nil + }, ctx.Done()) +} + +func (m *defaultManager) WaitUntilDeploymentDeleted(ctx context.Context, dp *appsv1.Deployment) error { + return wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + if _, err := m.cs.AppsV1().Deployments(dp.Namespace).Get(dp.Name, metav1.GetOptions{}); err != nil { + if apierrs.IsNotFound(err) { + return true, nil + } + utils.Logf("Error while waiting for Deployment to be terminated: %v", err) + return false, nil + } + return false, nil + }, ctx.Done()) +} diff --git a/test/e2e/framework/resource/mesh/manager.go b/test/e2e/framework/resource/mesh/manager.go new file mode 100644 index 000000000..d5a87b179 --- /dev/null +++ b/test/e2e/framework/resource/mesh/manager.go @@ -0,0 +1,44 @@ +package mesh + +import ( + "context" + appmeshv1beta1 "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/apis/appmesh/v1beta1" + meshclientset "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/client/clientset/versioned" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" +) + +type Manager interface { + WaitUntilMeshActive(ctx context.Context, mesh *appmeshv1beta1.Mesh) (*appmeshv1beta1.Mesh, error) +} + +func NewManager(cs meshclientset.Interface) Manager { + return &defaultManager{cs: cs} +} + +type defaultManager struct { + cs meshclientset.Interface +} + +func (m *defaultManager) WaitUntilMeshActive(ctx context.Context, mesh *appmeshv1beta1.Mesh) (*appmeshv1beta1.Mesh, error) { + var ( + observedMesh *appmeshv1beta1.Mesh + err error + ) + return observedMesh, wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + observedMesh, err = m.cs.AppmeshV1beta1().Meshes().Get(mesh.Name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + for _, condition := range observedMesh.Status.Conditions { + if condition.Type == appmeshv1beta1.MeshActive && condition.Status == corev1.ConditionTrue { + return true, nil + } + } + + return false, nil + }, ctx.Done()) +} diff --git a/test/e2e/framework/resource/namespace/manager.go b/test/e2e/framework/resource/namespace/manager.go new file mode 100644 index 000000000..25acac0e8 --- /dev/null +++ b/test/e2e/framework/resource/namespace/manager.go @@ -0,0 +1,72 @@ +package namespace + +import ( + "context" + "fmt" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + corev1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +type Manager interface { + AllocateNamespace(ctx context.Context, baseName string) (*corev1.Namespace, error) +} + +func NewManager(cs kubernetes.Interface) Manager { + return &defaultManager{ + cs: cs, + } +} + +type defaultManager struct { + cs kubernetes.Interface +} + +func (m *defaultManager) AllocateNamespace(ctx context.Context, baseName string) (*corev1.Namespace, error) { + name, err := m.findAvailableNamespaceName(ctx, baseName) + if err != nil { + return nil, err + } + namespaceObj := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + } + + var namespace *corev1.Namespace + if err := wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + var err error + namespace, err = m.cs.CoreV1().Namespaces().Create(namespaceObj) + if err != nil { + utils.Logf("Unexpected error while creating namespace: %v", err) + return false, nil + } + return true, nil + }, ctx.Done()); err != nil { + return nil, err + } + return namespace, nil +} + +// findAvailableNamespaceName random namespace name starting with baseName. +func (m *defaultManager) findAvailableNamespaceName(ctx context.Context, baseName string) (string, error) { + var name string + err := wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + name = fmt.Sprintf("%v-%v", baseName, utils.RandomDNS1123Label(6)) + _, err := m.cs.CoreV1().Namespaces().Get(name, metav1.GetOptions{}) + if err == nil { + // Already taken + return false, nil + } + if apierrs.IsNotFound(err) { + return true, nil + } + utils.Logf("Unexpected error while getting namespace: %v", err) + return false, nil + }, ctx.Done()) + + return name, err +} diff --git a/test/e2e/framework/resource/virtualnode/manager.go b/test/e2e/framework/resource/virtualnode/manager.go new file mode 100644 index 000000000..6ed0b1446 --- /dev/null +++ b/test/e2e/framework/resource/virtualnode/manager.go @@ -0,0 +1,60 @@ +package virtualnode + +import ( + "context" + appmeshv1beta1 "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/apis/appmesh/v1beta1" + meshclientset "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/client/clientset/versioned" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + corev1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" +) + +type Manager interface { + WaitUntilVirtualNodeActive(ctx context.Context, vn *appmeshv1beta1.VirtualNode) (*appmeshv1beta1.VirtualNode, error) + + WaitUntilVirtualNodeDeleted(ctx context.Context, vn *appmeshv1beta1.VirtualNode) error +} + +func NewManager(cs meshclientset.Interface) Manager { + return &defaultManager{cs: cs} +} + +type defaultManager struct { + cs meshclientset.Interface +} + +func (m *defaultManager) WaitUntilVirtualNodeActive(ctx context.Context, vn *appmeshv1beta1.VirtualNode) (*appmeshv1beta1.VirtualNode, error) { + var ( + observedVN *appmeshv1beta1.VirtualNode + err error + ) + return observedVN, wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + observedVN, err = m.cs.AppmeshV1beta1().VirtualNodes(vn.Namespace).Get(vn.Name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + for _, condition := range observedVN.Status.Conditions { + if condition.Type == appmeshv1beta1.VirtualNodeActive && condition.Status == corev1.ConditionTrue { + return true, nil + } + } + + return false, nil + }, ctx.Done()) +} + +func (m *defaultManager) WaitUntilVirtualNodeDeleted(ctx context.Context, vn *appmeshv1beta1.VirtualNode) error { + return wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + if _, err := m.cs.AppmeshV1beta1().VirtualNodes(vn.Namespace).Get(vn.Name, metav1.GetOptions{}); err != nil { + if apierrs.IsNotFound(err) { + return true, nil + } + utils.Logf("Error while waiting for VirtualNode to be terminated: %v", err) + return false, nil + } + return false, nil + }, ctx.Done()) +} diff --git a/test/e2e/framework/resource/virtualservice/manager.go b/test/e2e/framework/resource/virtualservice/manager.go new file mode 100644 index 000000000..018b8b298 --- /dev/null +++ b/test/e2e/framework/resource/virtualservice/manager.go @@ -0,0 +1,69 @@ +package virtualservice + +import ( + "context" + appmeshv1beta1 "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/apis/appmesh/v1beta1" + meshclientset "github.com/aws/aws-app-mesh-controller-for-k8s/pkg/client/clientset/versioned" + "github.com/aws/aws-app-mesh-controller-for-k8s/test/e2e/framework/utils" + corev1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" +) + +type Manager interface { + WaitUntilVirtualServiceActive(ctx context.Context, vs *appmeshv1beta1.VirtualService) (*appmeshv1beta1.VirtualService, error) + + WaitUntilVirtualServiceDeleted(ctx context.Context, vs *appmeshv1beta1.VirtualService) error +} + +func NewManager(cs meshclientset.Interface) Manager { + return &defaultManager{cs: cs} +} + +type defaultManager struct { + cs meshclientset.Interface +} + +func (m *defaultManager) WaitUntilVirtualServiceActive(ctx context.Context, vs *appmeshv1beta1.VirtualService) (*appmeshv1beta1.VirtualService, error) { + var ( + observedVS *appmeshv1beta1.VirtualService + err error + ) + return observedVS, wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + observedVS, err = m.cs.AppmeshV1beta1().VirtualServices(vs.Namespace).Get(vs.Name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + conditions := make(map[appmeshv1beta1.VirtualServiceConditionType]corev1.ConditionStatus) + for _, condition := range observedVS.Status.Conditions { + conditions[condition.Type] = condition.Status + } + readyConditionTypes := []appmeshv1beta1.VirtualServiceConditionType{ + appmeshv1beta1.VirtualServiceActive, + appmeshv1beta1.VirtualRouterActive, + appmeshv1beta1.RoutesActive, + } + for _, conditionType := range readyConditionTypes { + status, ok := conditions[conditionType] + if !ok || status != corev1.ConditionTrue { + return false, nil + } + } + return true, nil + }, ctx.Done()) +} + +func (m *defaultManager) WaitUntilVirtualServiceDeleted(ctx context.Context, vs *appmeshv1beta1.VirtualService) error { + return wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) { + if _, err := m.cs.AppmeshV1beta1().VirtualServices(vs.Namespace).Get(vs.Name, metav1.GetOptions{}); err != nil { + if apierrs.IsNotFound(err) { + return true, nil + } + utils.Logf("Error while waiting for VirtualService to be terminated: %v", err) + return false, nil + } + return false, nil + }, ctx.Done()) +} diff --git a/test/e2e/framework/utils/log.go b/test/e2e/framework/utils/log.go new file mode 100644 index 000000000..0f9b8fdfa --- /dev/null +++ b/test/e2e/framework/utils/log.go @@ -0,0 +1,25 @@ +package utils + +import ( + "fmt" + "time" + + "github.com/onsi/ginkgo" +) + +func nowStamp() string { + return time.Now().Format(time.StampMilli) +} + +func log(level string, format string, args ...interface{}) { + fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...) +} + +func Logf(format string, args ...interface{}) { + log("INFO", format, args...) +} + +func Failf(format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + ginkgo.Fail(msg) +} diff --git a/test/e2e/framework/utils/name.go b/test/e2e/framework/utils/name.go new file mode 100644 index 000000000..ca6c0f06d --- /dev/null +++ b/test/e2e/framework/utils/name.go @@ -0,0 +1,18 @@ +package utils + +import ( + "crypto/rand" + "encoding/hex" + "io" +) + +// RandomDNS1123Label generates a random DNS1123 compatible label with specified length +func RandomDNS1123Label(length int) string { + seedLen := (length + 1) / 2 + seedBuf := make([]byte, seedLen) + io.ReadFull(rand.Reader, seedBuf[:]) + + labelBuf := make([]byte, seedLen*2) + hex.Encode(labelBuf, seedBuf) + return string(labelBuf[:length]) +} diff --git a/test/e2e/framework/utils/poll.go b/test/e2e/framework/utils/poll.go new file mode 100644 index 000000000..0ab189eed --- /dev/null +++ b/test/e2e/framework/utils/poll.go @@ -0,0 +1,8 @@ +package utils + +import "time" + +const ( + PollIntervalShort = 2 * time.Second + PollIntervalMedium = 10 * time.Second +)