Move clustetctl to KRM

Change-Id: I79ccb7b7c92f27cf6fdaaa540d86db80a0a62b41
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
Closes: #568
This commit is contained in:
Ruslan Aliev 2021-05-30 17:01:31 -05:00
parent d1abe6e1ea
commit ab55bc5e86
74 changed files with 1241 additions and 2785 deletions

View File

@ -165,7 +165,7 @@ golint:
.PHONY: images
images: docker-image
images: docker-image-kubeval-validator docker-image-cloud-init docker-image-replacement-transformer docker-image-templater docker-image-toolbox
images: docker-image-clusterctl docker-image-kubeval-validator docker-image-cloud-init docker-image-replacement-transformer docker-image-templater docker-image-toolbox
.PHONY: docker-image
docker-image:
@ -208,6 +208,16 @@ ifeq ($(PUBLISH), true)
@docker push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/cloud-init:$(DOCKER_IMAGE_TAG)
endif
.PHONY: docker-image-clusterctl
docker-image-clusterctl:
@docker build $(PLUGINS_DIR)/clusterctl $(DOCKER_CMD_FLAGS) \
--label $(LABEL) \
--target $(DOCKER_TARGET_STAGE) \
--tag $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/clusterctl:$(DOCKER_IMAGE_TAG)
ifeq ($(PUBLISH), true)
@docker push $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/clusterctl:$(DOCKER_IMAGE_TAG)
endif
.PHONY: docker-image-kubeval-validator
docker-image-kubeval-validator:
@docker build $(PLUGINS_DIR)/kubeval-validator $(DOCKER_CMD_FLAGS) \

3
go.mod
View File

@ -21,7 +21,6 @@ require (
github.com/go-git/go-git/v5 v5.0.0
github.com/go-logr/zapr v0.1.1 // indirect
github.com/gophercloud/gophercloud v0.6.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de // indirect
github.com/gorilla/mux v1.7.4 // indirect
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
@ -29,7 +28,6 @@ require (
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.6.1
@ -46,7 +44,6 @@ require (
opendev.org/airship/go-redfish v0.0.0-20200318103738-db034d1d753a
opendev.org/airship/go-redfish/client v0.0.0-20200318103738-db034d1d753a
sigs.k8s.io/cli-utils v0.21.0
sigs.k8s.io/cluster-api v0.3.13
sigs.k8s.io/controller-runtime v0.5.14
sigs.k8s.io/kustomize/api v0.7.2
sigs.k8s.io/kustomize/kyaml v0.10.6

122
go.sum
View File

@ -33,12 +33,10 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
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/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
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.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
@ -67,10 +65,7 @@ github.com/ahmetb/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:ymXt5bw5uSNu
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053/go.mod h1:xW8sBma2LE3QxFSzCnH9qe6gAE2yO9GvQaWwX89HxbE=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@ -88,22 +83,12 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bifurcation/mint v0.0.0-20180715133206-93c51c6ce115/go.mod h1:zVt7zX3K/aDCk9Tj+VM7YymsX66ERvzCJzw8rFCX2JU=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM=
github.com/caddyserver/caddy v1.0.3/go.mod h1:G+ouvOY32gENkJC+jhgl62TyhvqEsFaDiZ4uw0RzP1E=
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 h1:HD4PLRzjuCVW79mQ0/pdsalOLHJ+FaEoqJLxfltpb2U=
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/cheekybits/genny v0.0.0-20170328200008-9127e812e1e9/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -111,7 +96,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY=
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/coredns/corefile-migration v1.0.11/go.mod h1:RMy/mXdeDlYwzt0vdMEJvT2hGJ2I86/eO0UdXmH9XNI=
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
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=
@ -155,8 +139,6 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a h1:pf3CyiWgjOLL7cjFos89AEOPCWSOoQt7tgbEk/SvBAg=
github.com/drone/envsubst v1.0.3-0.20200709223903-efdb65b94e5a/go.mod h1:N2jZmlMufstn1KEqvbHjw40h1KyTmnVzHcSc9bFiJ2g=
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/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
@ -170,8 +152,6 @@ github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/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 v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
@ -193,7 +173,6 @@ github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@ -206,7 +185,6 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
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=
@ -280,24 +258,20 @@ github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoM
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobuffalo/flect v0.2.2 h1:PAVD7sp0KOdfswjAw9BpLCU9hXo7wFSzgpQ+zNeks/A=
github.com/gobuffalo/flect v0.2.2/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
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 h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/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/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@ -341,14 +315,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -366,15 +335,11 @@ github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1a
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v0.6.0 h1:Xb2lcqZtml1XjgYZxbeayEemq7ASbeTp09m36gQFpEU=
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de h1:F7WD09S8QB4LrkEpka0dFPLSotH11HRpCsLIbIcJ7sU=
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
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/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc h1:f8eY6cV/x1x+HLjOp4r72s/31/V2aTUtg5oKRRPf8/Q=
@ -395,15 +360,12 @@ github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.0.0-20180201235237-0fb14efe8c47/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
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/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
@ -418,19 +380,15 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
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/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
@ -452,21 +410,14 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucas-clemente/aes12 v0.0.0-20171027163421-cd47fb39b79f/go.mod h1:JpH9J1c9oX6otFSgdUHwUBUizmKlrMjxWnIAjff4m04=
github.com/lucas-clemente/quic-clients v0.1.0/go.mod h1:y5xVIEoObKqULIKivu+gD/LU90pL73bTdtQjPBvtCBk=
github.com/lucas-clemente/quic-go v0.10.2/go.mod h1:hvaRS9IHjFLMq76puFJeWNfmn+H70QZ/CXoxqw9bzao=
github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58=
github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb h1:w1g9wNDIE/pHSTmAaUhv4TZQuPBS6GV3mMz5hkgziIU=
github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@ -476,19 +427,15 @@ 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/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno=
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
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-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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mholt/certmagic v0.6.2-0.20190624175158-6a42ef9fe8c2/go.mod h1:g4cOPxcjV0oFq3qwpjSA30LReKD8AoIfwAY9VvG35NY=
github.com/miekg/dns v1.1.3/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/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.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@ -499,7 +446,6 @@ github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdI
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
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/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@ -522,8 +468,6 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
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/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@ -546,16 +490,13 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
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/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
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/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -569,25 +510,20 @@ github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prY
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
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_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/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/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/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.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
@ -598,7 +534,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uY
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
@ -621,16 +556,11 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
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/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
@ -639,7 +569,6 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/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/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@ -648,8 +577,6 @@ 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/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
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=
@ -662,8 +589,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -715,10 +640,8 @@ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -762,11 +685,9 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190328230028-74de082e2cca/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -786,9 +707,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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=
@ -803,12 +723,10 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
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-20190124100055-b90733256f2e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -825,8 +743,6 @@ golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -846,9 +762,8 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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=
@ -865,7 +780,6 @@ golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
@ -900,9 +814,8 @@ google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEn
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/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -917,9 +830,8 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -940,9 +852,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
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/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mcuadros/go-syslog.v2 v2.2.1/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/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=
@ -955,7 +864,6 @@ gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
@ -994,7 +902,6 @@ k8s.io/apimachinery v0.17.9 h1:knQxNgMu57Oxlm12J6DS375kmGMeuWV0VNzRRUBB2Yk=
k8s.io/apimachinery v0.17.9/go.mod h1:Lg8zZ5iC/O8UjCqW6DNhcQG2m4TdjF9kwG3891OWbbA=
k8s.io/apiserver v0.0.0-20190918160949-bfa5e2e684ad/go.mod h1:XPCXEwhjaFN29a8NldXA901ElnKeKLrLtREO9ZhFyhg=
k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
k8s.io/apiserver v0.17.9 h1:q50QEJ51xdHy2Gl1lo9yJexiyixxof/yDUFdWNnZxh0=
k8s.io/apiserver v0.17.9/go.mod h1:Qaxd3EbeoPRBHVMtFyuKNAObqP6VAkzIMyWYz8KuE2k=
k8s.io/cli-runtime v0.0.0-20191214191754-e6dc6d5c8724/go.mod h1:wzlq80lvjgHW9if6MlE4OIGC86MDKsy5jtl9nxz/IYY=
k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI=
@ -1008,8 +915,6 @@ k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
k8s.io/client-go v0.17.9 h1:qUPhohX4bUBx0L7pfye02aPnu3PQ0t+B8dqHfGvt++k=
k8s.io/client-go v0.17.9/go.mod h1:3cM92qAd1XknA5IRkRfpJhl9OQjkYy97ZEUio70wVnI=
k8s.io/cluster-bootstrap v0.17.9 h1:IH/MwGor5/7bwHClz0PO/8pKq+SU1eSB1rs645pGu8Y=
k8s.io/cluster-bootstrap v0.17.9/go.mod h1:Q6nXn/sqVfMvT1VIJVPxFboYAoqH06PCjZnaYzbpZC0=
k8s.io/code-generator v0.0.0-20190912054826-cd179ad6a269/go.mod h1:V5BD6M4CyaN5m+VthcclXWsVcT1Hu+glwa1bi3MIsyE=
k8s.io/code-generator v0.0.0-20191214185510-0b9b3c99f9f2/go.mod h1:BjGKcoq1MRUmcssvHiSxodCco1T6nVIt4YeCT5CMSao=
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
@ -1054,12 +959,9 @@ opendev.org/airship/go-redfish/client v0.0.0-20200318103738-db034d1d753a/go.mod
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
sigs.k8s.io/cli-utils v0.21.0 h1:yvLvbzDyiFSmJFpjb6C6tqU2EqQ+hPK4GBUhS+myUm8=
sigs.k8s.io/cli-utils v0.21.0/go.mod h1:0n6pW2yhMbb0HxIcg8UeI5/Bi+Dh+7NOsXFdTudB/KY=
sigs.k8s.io/cluster-api v0.3.13 h1:dyhvxgt3M00Co06jrM332i27Tfozu9a0EN/qcmQXUFg=
sigs.k8s.io/cluster-api v0.3.13/go.mod h1:qGxyPTEJWNpII9SBkeRwv+Xvy6EZRLLLzaxVfBLsBpA=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/controller-runtime v0.5.14 h1:lmoRaPvLg9877ZOnjFivjtyIdqyLbWfcCEilxHXTEj4=
sigs.k8s.io/controller-runtime v0.5.14/go.mod h1:OTqxLuz7gVcrq+BHGUgedRu6b2VIKCEc7Pu4Jbwui0A=
sigs.k8s.io/kind v0.7.1-0.20200303021537-981bd80d3802/go.mod h1:HIZ3PWUezpklcjkqpFbnYOqaqsAE1JeCTEwkgvPLXjk=
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/kustomize/api v0.7.2 h1:ItTD/2XaKO8CosOMFZdaGFdUGTCHdQriW7zQ7AR98rs=

View File

@ -0,0 +1,44 @@
ARG GO_IMAGE=amd64/golang:1.16.6-alpine
ARG PLUGINS_BUILD_IMAGE=alpine:3.12.0
ARG PLUGINS_RELEASE_IMAGE=alpine:3.12.0
FROM ${PLUGINS_BUILD_IMAGE} as ctls
# Inject custom root certificate authorities if needed
# Docker does not have a good conditional copy statement and requires that a source file exists
# to complete the copy function without error. Therefore the README.md file will be copied to
# the image every time even if there are no .crt files.
RUN apk update && apk add curl
COPY ./certs/* /usr/local/share/ca-certificates/
RUN update-ca-certificates
ARG CCTL_VERSION=0.3.21
RUN curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v${CCTL_VERSION}/clusterctl-linux-amd64 -o /clusterctl
RUN chmod +x /clusterctl
FROM ${GO_IMAGE} as function
# Inject custom root certificate authorities if needed
# Docker does not have a good conditional copy statement and requires that a source file exists
# to complete the copy function without error. Therefore the README.md file will be copied to
# the image every time even if there are no .crt files.
COPY ./certs/* /usr/local/share/ca-certificates/
RUN update-ca-certificates
ENV PATH "/usr/local/go/bin:$PATH"
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY image/ .
RUN go mod download
RUN go build -v -o /usr/local/bin/config-function ./
FROM ${PLUGINS_RELEASE_IMAGE} as release
# Inject custom root certificate authorities if needed
# Docker does not have a good conditional copy statement and requires that a source file exists
# to complete the copy function without error. Therefore the README.md file will be copied to
# the image every time even if there are no .crt files.
RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/*
COPY ./certs/* /usr/local/share/ca-certificates/
RUN update-ca-certificates
COPY --from=ctls /clusterctl /usr/local/bin/
COPY --from=function /usr/local/bin/config-function /usr/local/bin/config-function
ENV HOME=/workdir
WORKDIR $HOME/.cluster-api
RUN chmod -R a+w $HOME/.cluster-api
CMD ["config-function"]

View File

@ -0,0 +1,78 @@
.PHONY: generate license fix vet fmt test build tidy image
SHELL := /bin/bash
GOBIN := $(shell go env GOPATH)/bin
# docker image options
DOCKER_REGISTRY ?= quay.io
DOCKER_IMAGE_NAME ?= clusterctl
DOCKER_IMAGE_PREFIX ?= airshipit
DOCKER_IMAGE_TAG ?= latest
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)
PUBLISH ?= false
DOCKER_FORCE_CLEAN ?= true
# proxy options
PROXY ?= http://proxy.foo.com:8000
NO_PROXY ?= localhost,127.0.0.1,.svc.cluster.local
USE_PROXY ?= false
.PHONY: build
build:
(cd image && go build -v -o $(GOBIN)/config-function .)
.PHONY: all
all: generate license build fix vet fmt test lint tidy
.PHONY: fix
fix:
(cd image && go fix ./...)
.PHONY: fmt
fmt:
(cd image && go fmt ./...)
.PHONY: generate
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
(cd image && GOBIN=$(GOBIN) go generate ./...)
.PHONY: tidy
tidy:
(cd image && go mod tidy)
.PHONY: fix
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
(cd image && $(GOBIN)/golangci-lint run ./...)
.PHONY: test
test:
(cd image && go test -cover ./...)
.PHONY: vet
vet:
(cd image && go vet ./...)
.PHONY: image
image:
ifeq ($(USE_PROXY), true)
cd image && \
docker build . --network=host \
--build-arg http_proxy=$(PROXY) \
--build-arg https_proxy=$(PROXY) \
--build-arg HTTP_PROXY=$(PROXY) \
--build-arg HTTPS_PROXY=$(PROXY) \
--build-arg no_proxy=$(NO_PROXY) \
--build-arg NO_PROXY=$(NO_PROXY) \
--tag $(DOCKER_IMAGE) \
--force-rm=$(DOCKER_FORCE_CLEAN)
else
cd image && \
docker build . --network=host \
--tag $(DOCKER_IMAGE) \
--force-rm=$(DOCKER_FORCE_CLEAN)
endif
ifeq ($(PUBLISH), true)
@docker push $(DOCKER_IMAGE)
endif

View File

@ -0,0 +1,22 @@
# Clusterctl
This is a KRM function which invokes
[clusterctl](https://github.com/kubernetes-sigs/cluster-api/tree/master/cmd/clusterctl)
with appropriate action and options.
## Function implementation
The function is implemented as an [image](image), and built using `make docker-image-clusterctl`.
### Function configuration
As input options, the KRM function receives a struct with command line options, configuration data and
repo components which is defined in airshipctl. See the `ClusterctlOptions` struct definition in v1alpha airshipctl API for the documentation.
## Function invocation
The function invoked by airshipctl command via `airshipctl phase run`:
airshipctl phase run <phase_name>
if appropriate phase has Clusterctl executor defined.

View File

@ -0,0 +1,6 @@
# Additional Docker image root certificate authorities
If you require additional certificate authorities for your Docker image:
* Add ASCII PEM encoded .crt files to this directory
* The files will be copied into your docker image at build time.
To update manually copy the .crt files to /usr/local/share/ca-certificates/ and run sudo update-ca-certificates.

View File

@ -0,0 +1,5 @@
module opendev.org/airship/airshipctl/krm-functions/clusterctl/image
go 1.16
require sigs.k8s.io/kustomize/kyaml v0.11.0

View File

@ -0,0 +1,225 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
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/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/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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/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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
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/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
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/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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 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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
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.3.1/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 v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/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/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/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 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.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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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/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/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
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.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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
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 v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20190311183353-d8887717615a/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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/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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-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-20180917221912-90fa682c2a6e/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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@ -0,0 +1,97 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
const (
clusterctl = "clusterctl"
clusterAPIConfig = "clusterctl.yaml"
dirPerm = 0755
filePerm = 0644
)
// ClusterctlOptions holds all necessary data to run clusterctl inside of KRM
type ClusterctlOptions struct {
CmdOptions []string `json:"cmd-options,omitempty"`
Config []byte `json:"config,omitempty"`
Components map[string][]byte `json:"components,omitempty"`
}
// Run prepares config, repo tree and executes clusterctl with appropriate options
func (c *ClusterctlOptions) Run([]*yaml.RNode) ([]*yaml.RNode, error) {
if err := c.buildRepoTree(); err != nil {
return nil, err
}
if err := ioutil.WriteFile(clusterAPIConfig, c.Config, filePerm); err != nil {
return nil, err
}
return nil, runCmd(clusterctl, c.CmdOptions)
}
func (c *ClusterctlOptions) buildRepoTree() error {
for f, component := range c.Components {
componentDir := filepath.Dir(f)
if _, err := os.Stat(componentDir); os.IsNotExist(err) {
if err := os.MkdirAll(componentDir, dirPerm); err != nil {
return err
}
}
if err := ioutil.WriteFile(f, component, filePerm); err != nil {
return err
}
}
return nil
}
func runCmd(cmd string, opts []string) error {
printMsg("#%s %s\n", cmd, strings.Join(opts, " "))
c := exec.Command(cmd, opts...)
// allows to observe realtime output from script
w := io.Writer(os.Stderr)
c.Stdout = w
c.Stderr = w
return c.Run()
}
// printMsg is a convenient function to print output to stderr
func printMsg(format string, a ...interface{}) {
if _, err := fmt.Fprintf(os.Stderr, format, a...); err != nil {}
}
func main() {
cfg := &ClusterctlOptions{}
if err := command.Build(framework.SimpleProcessor{Filter: kio.FilterFunc(cfg.Run), Config: cfg},
command.StandaloneDisabled, false).Execute(); err != nil {
printMsg("\n")
os.Exit(1)
}
}

View File

@ -0,0 +1,46 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
kind: Clusterctl
metadata:
annotations:
config.kubernetes.io/path: clusterctl_clusterctl_init.yaml
labels:
airshipit.org/deploy-k8s: "false"
name: clusterctl_init
images:
bootstrap-kubeadm/kube-rbac-proxy:
repository: gcr.io/kubebuilder
tag: v0.4.1
bootstrap-kubeadm/kubeadm-bootstrap-controller:
repository: us.gcr.io/k8s-artifacts-prod/cluster-api
tag: v0.3.7
cert-manager:
repository: quay.io/jetstack
init-options:
bootstrap-providers: kubeadm:v0.3.7
control-plane-providers: kubeadm:v0.3.7
core-provider: cluster-api:v0.3.7
infrastructure-providers: metal3:v0.4.0
providers:
- name: metal3
type: InfrastructureProvider
url: airshipctl/manifests/function/capm3/v0.4.0
- name: kubeadm
type: BootstrapProvider
url: airshipctl/manifests/function/cabpk/v0.3.7
- name: cluster-api
type: CoreProvider
url: airshipctl/manifests/function/capi/v0.3.7
- name: kubeadm
type: ControlPlaneProvider
url: airshipctl/manifests/function/cacpk/v0.3.7

View File

@ -7,7 +7,7 @@ GOBIN := $(shell go env GOPATH)/bin
DOCKER_REGISTRY ?= quay.io
DOCKER_IMAGE_NAME ?= kubeval-validator
DOCKER_IMAGE_PREFIX ?= airshipit
DOCKER_IMAGE_TAG ?= v0.1.0
DOCKER_IMAGE_TAG ?= latest
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)
PUBLISH ?= false
DOCKER_FORCE_CLEAN ?= true

View File

@ -0,0 +1,2 @@
resources:
- metadata.yaml

View File

@ -1,19 +1,11 @@
---
apiVersion: airshipit.org/v1alpha1
kind: Testversion
metadata:
name: version-2
spec:
version: v0.0.2
---
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
metadata:
name: repository-metadata
labels:
airshipit.org/deploy-k8s: "false"
releaseSeries:
- major: 0
minor: 3
contract: v1alpha3
- major: 0
minor: 2
contract: v1alpha2

View File

@ -5,6 +5,7 @@ commonLabels:
bases:
- crd
- data
- default
- webhook

View File

@ -0,0 +1,2 @@
resources:
- metadata.yaml

View File

@ -0,0 +1,11 @@
---
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
metadata:
name: repository-metadata
labels:
airshipit.org/deploy-k8s: "false"
releaseSeries:
- major: 0
minor: 3
contract: v1alpha3

View File

@ -5,6 +5,7 @@ commonLabels:
bases:
- crd
- data
- default
- webhook

View File

@ -0,0 +1,2 @@
resources:
- metadata.yaml

View File

@ -0,0 +1,11 @@
---
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
metadata:
name: repository-metadata
labels:
airshipit.org/deploy-k8s: "false"
releaseSeries:
- major: 0
minor: 3
contract: v1alpha3

View File

@ -5,6 +5,7 @@ commonLabels:
bases:
- crd
- data
- webhook
- default

View File

@ -6,33 +6,22 @@ metadata:
name: clusterctl_init
init-options:
core-provider: "cluster-api:v0.3.7"
bootstrap-providers:
- "kubeadm:v0.3.7"
infrastructure-providers:
- "metal3:v0.4.0"
control-plane-providers:
- "kubeadm:v0.3.7"
bootstrap-providers: "kubeadm:v0.3.7"
infrastructure-providers: "metal3:v0.4.0"
control-plane-providers: "kubeadm:v0.3.7"
providers:
- name: "metal3"
type: "InfrastructureProvider"
variable-substitution: true
versions:
v0.4.0: airshipctl/manifests/function/capm3/v0.4.0
url: airshipctl/manifests/function/capm3/v0.4.0
- name: "kubeadm"
type: "BootstrapProvider"
variable-substitution: true
versions:
v0.3.7: airshipctl/manifests/function/cabpk/v0.3.7
url: airshipctl/manifests/function/cabpk/v0.3.7
- name: "cluster-api"
type: "CoreProvider"
variable-substitution: true
versions:
v0.3.7: airshipctl/manifests/function/capi/v0.3.7
url: airshipctl/manifests/function/capi/v0.3.7
- name: "kubeadm"
type: "ControlPlaneProvider"
variable-substitution: true
versions:
v0.3.7: airshipctl/manifests/function/cacpk/v0.3.7
url: airshipctl/manifests/function/cacpk/v0.3.7
# The default image repository and tag for a specific component
# can be overriden here

View File

@ -512,3 +512,14 @@ configRef:
kind: ConfigMap
name: kubectl-check-ingress-ctrl
apiVersion: v1
---
apiVersion: airshipit.org/v1alpha1
kind: GenericContainer
metadata:
name: clusterctl
labels:
airshipit.org/deploy-k8s: "false"
spec:
type: krm
image: localhost/clusterctl:latest
hostNetwork: true

View File

@ -15,9 +15,7 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
)
// +kubebuilder:object:root=true
@ -33,13 +31,27 @@ type Clusterctl struct {
MoveOptions *MoveOptions `json:"move-options,omitempty"`
// AdditionalComponentVariables are variables that will be available to clusterctl
// when reading provider components
AdditionalComponentVariables map[string]string `json:"additional-vars,omitempty"`
// EnvVars if set to true, allows to source variables for cluster-api components
// for environment variables.
EnvVars bool `json:"env-vars,omitempty"`
ImageMetas map[string]ImageMeta `json:"images,omitempty"`
AdditionalComponentVariables map[string]string `json:"additional-vars,omitempty"`
ImageMetas map[string]ImageMeta `json:"images,omitempty"`
}
const (
// CoreProviderType is a type reserved for Cluster API core repository.
CoreProviderType = "CoreProvider"
// BootstrapProviderType is the type associated with codebases that provide
// bootstrapping capabilities.
BootstrapProviderType = "BootstrapProvider"
// InfrastructureProviderType is the type associated with codebases that provide
// infrastructure capabilities.
InfrastructureProviderType = "InfrastructureProvider"
// ControlPlaneProviderType is the type associated with codebases that provide
// control-plane capabilities.
ControlPlaneProviderType = "ControlPlaneProvider"
)
// ImageMeta is part of clusterctl config
type ImageMeta struct {
Repository string `json:"repository,omitempty"`
@ -50,20 +62,8 @@ type ImageMeta struct {
type Provider struct {
Name string `json:"name,"`
Type string `json:"type,"`
URL string `json:"url,omitempty"`
// IsClusterctlRepository if set to true, clusterctl provider's repository implementation will be used
// if omitted or set to false, airshipctl repository implementation will be used.
IsClusterctlRepository bool `json:"clusterctl-repository,omitempty"`
// Map of versions where each key is a version and value is path relative to target path of the manifest
// ignored if IsClusterctlRepository is set to true
Versions map[string]string `json:"versions,omitempty"`
// VariableSubstitution indicates weather you want to substitute variables in the cluster-api manifests
// if set to true, variables will be substituted only if they are defined either in Environment or
// in AdditionalComponentVariables, if not they will be left as is.
VariableSubstitution bool `json:"variable-substitution,omitempty"`
// URL can contain remote URL of upstream Provider or relative to target path of the manifest
URL string `json:"url,omitempty"`
}
// InitOptions container with exposed clusterctl InitOptions
@ -72,19 +72,17 @@ type InitOptions struct {
// cluster-api core provider's latest release is used.
CoreProvider string `json:"core-provider,omitempty"`
// BootstrapProviders and versions (e.g. kubeadm:v0.3.0) to add to the management cluster.
// BootstrapProviders and versions (comma separated, e.g. kubeadm:v0.3.0) to add to the management cluster.
// If unspecified, the kubeadm bootstrap provider's latest release is used.
BootstrapProviders []string `json:"bootstrap-providers,omitempty"`
BootstrapProviders string `json:"bootstrap-providers,omitempty"`
// InfrastructureProviders and versions (e.g. aws:v0.5.0) to add to the management cluster.
InfrastructureProviders []string `json:"infrastructure-providers,omitempty"`
// InfrastructureProviders and versions (comma separated, e.g. aws:v0.5.0,metal3:v0.4.0)
// to add to the management cluster.
InfrastructureProviders string `json:"infrastructure-providers,omitempty"`
// ControlPlaneProviders and versions (e.g. kubeadm:v0.3.0) to add to the management cluster.
// ControlPlaneProviders and versions (comma separated, e.g. kubeadm:v0.3.0) to add to the management cluster.
// If unspecified, the kubeadm control plane provider latest release is used.
ControlPlaneProviders []string `json:"control-plane-providers,omitempty"`
// KubeConfigRef reference to KubeConfig document
KubeConfigRef *corev1.ObjectReference `json:"kubeConfigRef,omitempty"`
ControlPlaneProviders string `json:"control-plane-providers,omitempty"`
}
// ActionType for clusterctl
@ -96,20 +94,10 @@ const (
Move ActionType = "move"
)
// Provider returns provider filtering by name and type
func (c *Clusterctl) Provider(name string, providerType clusterctlv1.ProviderType) *Provider {
t := string(providerType)
for _, prov := range c.Providers {
if prov.Name == name && prov.Type == t {
return prov
}
}
return nil
}
// MoveOptions carries the options supported by move.
type MoveOptions struct {
// The namespace where the workload cluster is hosted. If unspecified, the target context's namespace is used.
// Namespace where the objects describing the workload cluster exists. If unspecified, the current
// namespace will be used.
Namespace string `json:"namespace,omitempty"`
}
@ -122,3 +110,20 @@ func DefaultClusterctl() *Clusterctl {
ImageMetas: make(map[string]ImageMeta),
}
}
// ClusterctlOptions holds all necessary data to run clusterctl inside of KRM
type ClusterctlOptions struct {
CmdOptions []string `json:"cmd-options,omitempty"`
Config []byte `json:"config,omitempty"`
Components map[string][]byte `json:"components,omitempty"`
}
// GetKubeconfigOptions carries all the options to retrieve kubeconfig from parent cluster
type GetKubeconfigOptions struct {
// Timeout is the maximum length of time to retrieve kubeconfig
Timeout string
// Namespace is the namespace in which secret is placed.
ManagedClusterNamespace string
// ManagedClusterName is the name of the managed cluster.
ManagedClusterName string
}

View File

@ -1,76 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"testing"
"github.com/stretchr/testify/assert"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
)
func TestProvider(t *testing.T) {
cctl := &Clusterctl{
Providers: []*Provider{
{
Name: "kubeadm",
URL: "/home/providers/kubeadm/v0.3.5/components.yaml",
Type: "BootstrapProvider",
IsClusterctlRepository: true,
},
},
}
tests := []struct {
name string
getName string
getType string
expectedProvider *Provider
}{
{
name: "repo options exist",
getName: "kubeadm",
getType: "BootstrapProvider",
expectedProvider: &Provider{
Name: "kubeadm",
URL: "/home/providers/kubeadm/v0.3.5/components.yaml",
Type: "BootstrapProvider",
IsClusterctlRepository: true,
},
},
{
name: "repo name does not exist",
getName: "does not exist",
getType: "BootstrapProvider",
expectedProvider: nil,
},
{
name: "type does not exist",
getName: "kubeadm",
getType: "does not exist",
expectedProvider: nil,
},
}
for _, tt := range tests {
getName := tt.getName
getType := tt.getType
expectedProvider := tt.expectedProvider
t.Run(tt.name, func(t *testing.T) {
actualProvider := cctl.Provider(getName, clusterctlv1.ProviderType(getType))
assert.Equal(t, expectedProvider, actualProvider)
})
}
}

View File

@ -444,14 +444,14 @@ func (in *Clusterctl) DeepCopyInto(out *Clusterctl) {
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(Provider)
(*in).DeepCopyInto(*out)
**out = **in
}
}
}
if in.InitOptions != nil {
in, out := &in.InitOptions, &out.InitOptions
*out = new(InitOptions)
(*in).DeepCopyInto(*out)
**out = **in
}
if in.MoveOptions != nil {
in, out := &in.MoveOptions, &out.MoveOptions
@ -492,6 +492,46 @@ func (in *Clusterctl) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterctlOptions) DeepCopyInto(out *ClusterctlOptions) {
*out = *in
if in.CmdOptions != nil {
in, out := &in.CmdOptions, &out.CmdOptions
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.Config != nil {
in, out := &in.Config, &out.Config
*out = make([]byte, len(*in))
copy(*out, *in)
}
if in.Components != nil {
in, out := &in.Components, &out.Components
*out = make(map[string][]byte, len(*in))
for key, val := range *in {
var outVal []byte
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make([]byte, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterctlOptions.
func (in *ClusterctlOptions) DeepCopy() *ClusterctlOptions {
if in == nil {
return nil
}
out := new(ClusterctlOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *EndPointSpec) DeepCopyInto(out *EndPointSpec) {
*out = *in
@ -626,6 +666,21 @@ func (in *GenericContainerSpec) DeepCopy() *GenericContainerSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GetKubeconfigOptions) DeepCopyInto(out *GetKubeconfigOptions) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GetKubeconfigOptions.
func (in *GetKubeconfigOptions) DeepCopy() *GetKubeconfigOptions {
if in == nil {
return nil
}
out := new(GetKubeconfigOptions)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HostNetworkingSpec) DeepCopyInto(out *HostNetworkingSpec) {
*out = *in
@ -795,26 +850,6 @@ func (in *ImageURLSpec) DeepCopy() *ImageURLSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InitOptions) DeepCopyInto(out *InitOptions) {
*out = *in
if in.BootstrapProviders != nil {
in, out := &in.BootstrapProviders, &out.BootstrapProviders
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.InfrastructureProviders != nil {
in, out := &in.InfrastructureProviders, &out.InfrastructureProviders
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ControlPlaneProviders != nil {
in, out := &in.ControlPlaneProviders, &out.ControlPlaneProviders
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.KubeConfigRef != nil {
in, out := &in.KubeConfigRef, &out.KubeConfigRef
*out = new(v1.ObjectReference)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InitOptions.
@ -1397,13 +1432,6 @@ func (in *PhaseStep) DeepCopy() *PhaseStep {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Provider) DeepCopyInto(out *Provider) {
*out = *in
if in.Versions != nil {
in, out := &in.Versions, &out.Versions
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Provider.

View File

@ -1,166 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
clusterctlclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client"
clusterctlconfig "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/yamlprocessor"
clog "sigs.k8s.io/cluster-api/cmd/clusterctl/log"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/clusterctl/implementations"
"opendev.org/airship/airshipctl/pkg/log"
)
var _ Interface = &Client{}
const (
// BootstrapProviderType is a local copy of appropriate type from cluster-api
BootstrapProviderType = v1alpha3.BootstrapProviderType
// CoreProviderType is a local copy of appropriate type from cluster-api
CoreProviderType = v1alpha3.CoreProviderType
// ControlPlaneProviderType is a local copy of appropriate type from cluster-api
ControlPlaneProviderType = v1alpha3.ControlPlaneProviderType
// InfrastructureProviderType is a local copy of appropriate type from cluster-api
InfrastructureProviderType = v1alpha3.InfrastructureProviderType
)
// Interface is abstraction to Clusterctl
type Interface interface {
Init(kubeconfigPath, kubeconfigContext string) error
Move(fromKubeconfigPath, fromKubeconfigContext, toKubeconfigPath, toKubeconfigContext, namespace string) error
Render(options RenderOptions) ([]byte, error)
}
// Client Implements interface to Clusterctl
type Client struct {
clusterctlClient clusterctlclient.Client
initOptions clusterctlclient.InitOptions
moveOptions clusterctlclient.MoveOptions
repoFactory RepositoryFactory
}
// RenderOptions is used to get providers from RepoFactory for Render method
type RenderOptions struct {
ProviderName string
ProviderVersion string
ProviderType string
}
// GetKubeconfigOptions carries all the options to retrieve kubeconfig from parent cluster
type GetKubeconfigOptions struct {
// Timeout is the maximum length of time to retrieve kubeconfig
Timeout string
// Namespace is the namespace in which secret is placed.
ManagedClusterNamespace string
// ManagedClusterName is the name of the managed cluster.
ManagedClusterName string
}
// NewClient returns instance of clusterctl client
func NewClient(root string, debug bool, options *airshipv1.Clusterctl) (Interface, error) {
if debug {
debugVerbosity := 5
clog.SetLogger(clog.NewLogger(clog.WithThreshold(&debugVerbosity)))
}
initOptions := options.InitOptions
var cio clusterctlclient.InitOptions
if initOptions != nil {
cio = clusterctlclient.InitOptions{
BootstrapProviders: initOptions.BootstrapProviders,
CoreProvider: initOptions.CoreProvider,
InfrastructureProviders: initOptions.InfrastructureProviders,
ControlPlaneProviders: initOptions.ControlPlaneProviders,
}
}
cclient, rf, err := newClusterctlClient(root, options)
if err != nil {
return nil, err
}
return &Client{clusterctlClient: cclient, initOptions: cio, repoFactory: rf}, nil
}
// Init implements interface to Clusterctl
func (c *Client) Init(kubeconfigPath, kubeconfigContext string) error {
log.Print("Starting cluster-api initiation")
c.initOptions.Kubeconfig = clusterctlclient.Kubeconfig{
Path: kubeconfigPath,
Context: kubeconfigContext,
}
_, err := c.clusterctlClient.Init(c.initOptions)
return err
}
// newConfig returns clusterctl config client
func newConfig(options *airshipv1.Clusterctl, root string) (clusterctlconfig.Client, error) {
for _, provider := range options.Providers {
if !provider.IsClusterctlRepository {
provider.URL = root
}
}
reader, err := implementations.NewAirshipReader(options)
if err != nil {
return nil, err
}
return clusterctlconfig.New("", clusterctlconfig.InjectReader(reader))
}
func newClusterctlClient(root string, options *airshipv1.Clusterctl) (clusterctlclient.Client,
RepositoryFactory, error) {
cconf, err := newConfig(options, root)
if err != nil {
return nil, RepositoryFactory{}, err
}
rf := RepositoryFactory{
Options: options,
ConfigClient: cconf,
}
// option config factory
ocf := clusterctlclient.InjectConfig(cconf)
// option repository factory
orf := clusterctlclient.InjectRepositoryFactory(rf.ClientRepositoryFactory())
// options cluster client factory
occf := clusterctlclient.InjectClusterClientFactory(rf.ClusterClientFactory())
client, err := clusterctlclient.New("", ocf, orf, occf)
return client, rf, err
}
// Render returns requested components as yaml
func (c *Client) Render(renderOptions RenderOptions) ([]byte, error) {
provider, err := c.repoFactory.ConfigClient.Providers().Get(renderOptions.ProviderName,
v1alpha3.ProviderType(renderOptions.ProviderType))
if err != nil {
return nil, err
}
crf := c.repoFactory.ClientRepositoryFactory()
repoClient, err := crf(clusterctlclient.RepositoryClientFactoryInput{
Provider: provider,
Processor: yamlprocessor.NewSimpleProcessor(),
})
if err != nil {
return nil, err
}
components, err := repoClient.Components().Get(repository.ComponentsOptions{Version: renderOptions.ProviderVersion})
if err != nil {
return nil, err
}
return components.Yaml()
}

View File

@ -1,174 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/yaml"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
)
var (
testConfig = `apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
labels:
airshipit.org/deploy-k8s: "false"
name: clusterctl-v1
init-options: {}
providers:
- name: "aws"
type: "InfrastructureProvider"
url: "/manifests/capi/infra/aws/v0.3.0"
clusterctl-repository: true
- name: "custom-infra"
type: "InfrastructureProvider"
url: "/manifests/capi/custom-infra/aws/v0.3.0"
clusterctl-repository: true
- name: "custom-airship-infra"
type: "InfrastructureProvider"
versions:
v0.3.1: functions/capi/infrastructure/v0.3.1
v0.3.2: functions/capi/infrastructure/v0.3.2
images:
cert-manager/cert-manager-cainjector:
repository: "myorg.io/local-repo"
tag: "v0.1"`
)
func TestNewConfig(t *testing.T) {
tests := []struct {
name string
conf *airshipv1.Clusterctl
presentProvider string
presentType string
expectedURL string
}{
{
name: "clusterctl single repo",
presentProvider: "kubeadm",
presentType: "BootstrapProvider",
expectedURL: "/home/providers/kubeadm/v0.3.5/components.yaml",
conf: &airshipv1.Clusterctl{
Providers: []*airshipv1.Provider{
{
Name: "kubeadm",
URL: "/home/providers/kubeadm/v0.3.5/components.yaml",
Type: "BootstrapProvider",
IsClusterctlRepository: true,
},
},
},
},
{
name: "multiple repos with airship",
presentProvider: "airship-repo",
presentType: "InfrastructureProvider",
expectedURL: testDataDir,
conf: &airshipv1.Clusterctl{
Providers: []*airshipv1.Provider{
{
Name: "airship-repo",
URL: "/home/providers/my-repo/v0.3.5/components.yaml",
Type: "InfrastructureProvider",
IsClusterctlRepository: false,
Versions: map[string]string{
"v0.3.1": "some-path",
},
},
{
Name: "kubeadm",
URL: "/home/providers/kubeadm/v0.3.5/components.yaml",
Type: "BootstrapProvider",
IsClusterctlRepository: true,
},
},
},
},
}
for _, tt := range tests {
conf := tt.conf
url := tt.expectedURL
provName := tt.presentProvider
provType := tt.presentType
t.Run(tt.name, func(t *testing.T) {
got, err := newConfig(conf, testDataDir)
require.NoError(t, err)
providerClient := got.Providers()
provider, err := providerClient.Get(provName, clusterctlv1.ProviderType(provType))
require.NoError(t, err)
assert.Equal(t, url, provider.URL())
})
}
}
func TestImageMeta(t *testing.T) {
tests := []struct {
name string
conf *airshipv1.Clusterctl
component string
image string
want string
}{
{
name: "clusterctl image override ",
component: "cert-manager",
image: "myorg.io/local-repo/cert-manager-cainjector:v0.1",
conf: &airshipv1.Clusterctl{
ImageMetas: map[string]airshipv1.ImageMeta{
"cert-manager/cert-manager-cainjector": {
Repository: "myorg.io/local-repo",
Tag: "v0.1",
},
},
},
want: "myorg.io/local-repo/cert-manager-cainjector:v0.1",
},
}
for _, tt := range tests {
conf := tt.conf
t.Run(tt.name, func(t *testing.T) {
got, err := newConfig(conf, testDataDir)
require.NoError(t, err)
image, err := got.ImageMeta().AlterImage(tt.component, tt.image)
require.NoError(t, err)
assert.Equal(t, image, tt.want)
})
}
}
func TestNewClientEmptyOptions(t *testing.T) {
c := &airshipv1.Clusterctl{}
client, err := NewClient("", true, c)
require.NoError(t, err)
require.NotNil(t, client)
}
func TestNewClient(t *testing.T) {
c := &airshipv1.Clusterctl{}
err := yaml.Unmarshal([]byte(testConfig), c)
require.NoError(t, err)
client, err := NewClient("", true, c)
require.NoError(t, err)
require.NotNil(t, client)
}

View File

@ -1,38 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"fmt"
)
// ErrProviderNotDefined is returned when wrong AuthType is provided
type ErrProviderNotDefined struct {
ProviderName string
}
func (e ErrProviderNotDefined) Error() string {
return fmt.Sprintf("provider %s is not defined in Clusterctl document", e.ProviderName)
}
// ErrProviderRepoNotFound is returned when wrong AuthType is provided
type ErrProviderRepoNotFound struct {
ProviderName string
ProviderType string
}
func (e ErrProviderRepoNotFound) Error() string {
return fmt.Sprintf("failed to find repository for provider %s of type %s", e.ProviderName, e.ProviderType)
}

View File

@ -1,105 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/client"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/clusterctl/implementations"
"opendev.org/airship/airshipctl/pkg/log"
)
// RepositoryFactory returns an injection factory to work with clusterctl client
type RepositoryFactory struct {
Options *airshipv1.Clusterctl
ConfigClient config.Client
}
// ClusterClientFactory returns cluster factory function for clusterctl client
func (f RepositoryFactory) ClusterClientFactory() client.ClusterClientFactory {
return func(input client.ClusterClientFactoryInput) (cluster.Client, error) {
o := cluster.InjectRepositoryFactory(f.repoFactoryClusterClient(input))
return cluster.New(cluster.Kubeconfig{
Path: input.Kubeconfig.Path,
Context: input.Kubeconfig.Context}, f.ConfigClient, o), nil
}
}
// ClientRepositoryFactory returns repo factory function for clusterctl client
func (f RepositoryFactory) ClientRepositoryFactory() client.RepositoryClientFactory {
return f.repoFactory
}
// These two functions are basically the same, but have different with signatures
func (f RepositoryFactory) repoFactoryClusterClient(
input client.ClusterClientFactoryInput) cluster.RepositoryClientFactory {
return func(provider config.Provider,
configClient config.Client,
options ...repository.Option,
) (repository.Client, error) {
return f.repoFactory(client.RepositoryClientFactoryInput{
Provider: provider,
Processor: input.Processor,
})
}
}
func (f RepositoryFactory) repoFactory(input client.RepositoryClientFactoryInput) (repository.Client, error) {
name := input.Provider.Name()
repoType := input.Provider.Type()
airProv := f.Options.Provider(name, repoType)
if airProv == nil {
return nil, ErrProviderRepoNotFound{ProviderName: name, ProviderType: string(repoType)}
}
// if repository is not clusterctl type, construct an airshipctl implementation of repository interface
if !airProv.IsClusterctlRepository {
// Get repository version map
versions := airProv.Versions
if len(versions) == 0 {
return nil, ErrProviderRepoNotFound{ProviderName: name, ProviderType: string(repoType)}
}
// construct a repository for this provider using root and version map
repo, err := implementations.NewRepository(input.Provider.URL(), versions)
if err != nil {
return nil, err
}
// inject repository into repository client
o := repository.InjectRepository(repo)
// inject yaml processor into repository
oProcessor := repository.InjectYamlProcessor(input.Processor)
log.Printf("Creating airshipctl repository implementation interface for provider %s of type %s\n",
name,
repoType)
repoClient, err := repository.New(input.Provider, f.ConfigClient, o, oProcessor)
if err != nil {
return nil, err
}
return &implementations.RepositoryClient{
Client: repoClient,
ProviderType: string(repoType),
ProviderName: name,
VariableSubstitution: airProv.VariableSubstitution}, nil
}
log.Printf("Creating clusterctl repository implementation interface for provider %s of type %s\n",
name,
repoType)
// if repository is clusterctl pass, simply use default clusterctl repository interface
return repository.New(input.Provider, f.ConfigClient)
}

View File

@ -1,299 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
clusterctlclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client"
clusterctlconfig "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/yamlprocessor"
"sigs.k8s.io/yaml"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
)
const (
testDataDir = "testdata"
)
var (
testConfigFactory = `apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
labels:
airshipit.org/deploy-k8s: "false"
name: clusterctl-v1
init-options: {}
providers:
- name: "aws"
type: "InfrastructureProvider"
url: "/manifests/capi/infra/infrastructure-aws/v0.3.0/components.yaml"
clusterctl-repository: true
- name: "custom-infra"
type: "InfrastructureProvider"
url: "/manifests/capi/infra/infrastructure-custom-infra/v0.3.0/components.yaml"
clusterctl-repository: true
- name: "custom-airship-infra"
type: "InfrastructureProvider"
versions:
v0.3.1: functions/capi/infrastructure/v0.3.1
v0.3.2: functions/capi/infrastructure/v0.3.2`
)
func testOptions(t *testing.T, input string) *airshipv1.Clusterctl {
t.Helper()
o := &airshipv1.Clusterctl{}
err := yaml.Unmarshal([]byte(input), o)
require.NoError(t, err)
return o
}
func testNewConfig(t *testing.T, o *airshipv1.Clusterctl) clusterctlconfig.Client {
t.Helper()
configClient, err := newConfig(o, testDataDir)
require.NoError(t, err)
require.NotNil(t, configClient)
return configClient
}
// TestFactory checks if airship repository interface is selected for providers that are not
// of airship type, and that this interface methods return correct components
func TestFactory(t *testing.T) {
o := testOptions(t, testConfigFactory)
configClient := testNewConfig(t, o)
factory := RepositoryFactory{
Options: o,
ConfigClient: configClient,
}
repoFactory := factory.ClientRepositoryFactory()
require.NotNil(t, repoFactory)
pclient := configClient.Providers()
require.NotNil(t, pclient)
tests := []struct {
name string
expectedVersions []string
useVersion string
useName string
useType string
expectErr bool
expectedNamespace string
}{
{
name: "custom airship v1",
expectedVersions: []string{"v0.3.1", "v0.3.2"},
useVersion: "v0.3.1",
useName: "custom-airship-infra",
useType: "InfrastructureProvider",
expectErr: false,
expectedNamespace: "version-one",
},
{
name: "custom airship v2",
expectedVersions: []string{"v0.3.1", "v0.3.2"},
useVersion: "v0.3.2",
useName: "custom-airship-infra",
useType: "InfrastructureProvider",
expectErr: false,
expectedNamespace: "version-two",
},
}
for _, tt := range tests {
expectedVersions := tt.expectedVersions
useVersion := tt.useVersion
expectErr := tt.expectErr
useName := tt.useName
useType := tt.useType
expectedNamespace := tt.expectedNamespace
t.Run(tt.name, func(t *testing.T) {
provider, err := pclient.Get(useName, clusterctlv1.ProviderType(useType))
require.NoError(t, err)
require.NotNil(t, provider)
repo, err := repoFactory(clusterctlclient.RepositoryClientFactoryInput{
Provider: provider,
Processor: yamlprocessor.NewSimpleProcessor(),
})
require.NoError(t, err)
require.NotNil(t, repo)
versions, err := repo.GetVersions()
require.NoError(t, err)
sort.Strings(expectedVersions)
sort.Strings(versions)
assert.Equal(t, testDataDir, repo.URL())
assert.Equal(t, expectedVersions, versions)
components := repo.Components()
require.NotNil(t, components)
// namespaces are left blank, since namespace is provided in the document set
component, err := components.Get(repository.ComponentsOptions{
Version: useVersion,
})
require.NoError(t, err)
require.NotNil(t, component)
b, err := component.Yaml()
if expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
actualNamespace := &v1.Namespace{}
err = yaml.Unmarshal(b, actualNamespace)
require.NoError(t, err)
assert.Equal(t, expectedNamespace, actualNamespace.GetName())
}
})
}
}
func TestClientRepositoryFactory(t *testing.T) {
o := testOptions(t, testConfigFactory)
configClient := testNewConfig(t, o)
factory := RepositoryFactory{
Options: o,
ConfigClient: configClient,
}
clusterclientFactory := factory.ClusterClientFactory()
clusterClient, err := clusterclientFactory(clusterctlclient.ClusterClientFactoryInput{
Kubeconfig: clusterctlclient.Kubeconfig{
Path: "testdata/kubeconfig.yaml",
Context: ""},
Processor: yamlprocessor.NewSimpleProcessor(),
})
assert.NoError(t, err)
assert.NotNil(t, clusterClient)
}
func TestRepoFactoryFunction(t *testing.T) {
o := testOptions(t, testConfigFactory)
configClient := testNewConfig(t, o)
factory := RepositoryFactory{
Options: o,
ConfigClient: configClient,
}
pclient := configClient.Providers()
require.NotNil(t, pclient)
provider, err := pclient.Get("custom-airship-infra", "InfrastructureProvider")
require.NoError(t, err)
repoClient, err := factory.repoFactory(clusterctlclient.RepositoryClientFactoryInput{
Provider: provider,
Processor: yamlprocessor.NewSimpleProcessor(),
})
require.NoError(t, err)
require.NotNil(t, repoClient)
versions, err := repoClient.GetVersions()
expectedVersions := []string{"v0.3.1", "v0.3.2"}
sort.Strings(versions)
sort.Strings(expectedVersions)
require.NoError(t, err)
assert.Equal(t, expectedVersions, versions)
}
func TestClusterctlRepoFactoryFunction(t *testing.T) {
o := testOptions(t, testConfigFactory)
configClient := testNewConfig(t, o)
factory := RepositoryFactory{
Options: o,
ConfigClient: configClient,
}
pclient := configClient.Providers()
provider, err := pclient.Get("aws", "InfrastructureProvider")
require.NoError(t, err)
repoClient, err := factory.repoFactory(clusterctlclient.RepositoryClientFactoryInput{
Provider: provider,
Processor: yamlprocessor.NewSimpleProcessor(),
})
require.NoError(t, err)
require.NotNil(t, repoClient)
}
// Test error cases
func TestRepositoryFactoryErrors(t *testing.T) {
// set one default provider clusterctl is properly initialized
defProv := &airshipv1.Provider{
Name: "aws",
Type: "InfrastructureProvider",
Versions: map[string]string{
"v0.3.3": testConfig + "/functions/capi/v0.3.3",
},
}
o := &airshipv1.Clusterctl{
Providers: []*airshipv1.Provider{defProv},
}
configClient := testNewConfig(t, o)
require.NotNil(t, configClient)
factory := RepositoryFactory{
Options: o,
ConfigClient: configClient,
}
rf := factory.ClientRepositoryFactory()
require.NotNil(t, rf)
pclient := configClient.Providers()
require.NotNil(t, pclient)
// save provider so then we can run tests against it, while modifying original airship clustetrctl conf
provider, err := pclient.Get("aws", "InfrastructureProvider")
require.NoError(t, err)
require.NotNil(t, provider)
tests := []struct {
name string
airProvs []*airshipv1.Provider
}{
{
name: "providers are nil",
airProvs: nil,
},
{
name: "versions are nil",
airProvs: []*airshipv1.Provider{
{
Name: "aws",
Type: "InfrastructureProvider",
Versions: nil,
},
},
},
{
name: "versions can't be parsed",
airProvs: []*airshipv1.Provider{
{
Name: "aws",
Type: "InfrastructureProvider",
Versions: map[string]string{
"can't parse version": "wrong path",
},
},
},
},
}
for _, tt := range tests {
airProvs := tt.airProvs
t.Run(tt.name, func(t *testing.T) {
// set airship providers so it does not correspond to clusterctl provider
o.Providers = airProvs
crc, err := factory.repoFactory(clusterctlclient.RepositoryClientFactoryInput{
Provider: provider,
Processor: yamlprocessor.NewSimpleProcessor(),
})
// expect error since we have mismatch of airship providers vs clusterctl providers
require.Nil(t, crc)
assert.Error(t, err)
})
}
}

View File

@ -1,54 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"github.com/pkg/errors"
clusterctlclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
)
// Move implements interface to Clusterctl
func (c *Client) Move(fromKubeconfigPath, fromKubeconfigContext,
toKubeconfigPath, toKubeconfigContext, namespace string) error {
var err error
// ephemeral cluster client
pFrom := cluster.New(cluster.Kubeconfig{
Path: fromKubeconfigPath,
Context: fromKubeconfigContext}, nil).Proxy()
// If namespace is empty, try to detect it.
if namespace == "" {
var currentNamespace string
currentNamespace, err = pFrom.CurrentNamespace()
if err != nil {
return err
}
namespace = currentNamespace
}
// clusterctl move
c.moveOptions = clusterctlclient.MoveOptions{
FromKubeconfig: clusterctlclient.Kubeconfig{Path: fromKubeconfigPath, Context: fromKubeconfigContext},
ToKubeconfig: clusterctlclient.Kubeconfig{Path: toKubeconfigPath, Context: toKubeconfigContext},
Namespace: namespace,
}
err = c.clusterctlClient.Move(c.moveOptions)
if err != nil {
return errors.Wrapf(err, "error during clusterctl move")
}
return nil
}

View File

@ -1,24 +0,0 @@
apiVersion: airshipit.org/v1alpha1
kind: KubeConfig
metadata:
name: sample-name
config:
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://10.23.25.101:6443
name: dummycluster_ephemeral
contexts:
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: dummy_cluster
current-context: dummy_cluster
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBeHVGZE5HUlhwdDdDVkhScDlrQzdRVnA2WDIyWVR4a2REK0dSa2ZHYzN2cVhwTTlpCmFsdWlIaWdYY1hSQ09qZzBwbjNsT2RuajF5RmFmakZGdDVnNUtjT25TSllQZkFsWkZYS0pDaFFDdHIweW00N28KUVJKQ0tWUVhteXF3UlpCWlRiUU16NEFhcjVqaUNEdkhhNElkSzh3VkdMN2d2MFNKWWFXQVBiK2hkWkdjeGNyVApncytQbzZpNTJoOXZCMTg2dk83UTVVUkxpM0dTY284Rnc5TksvWFY1bGhkeVFFTlhjNlVzUGdYQzdURG52U3ZECmZ2VDZFbGU5V3JDOXloMXgvb1A4OVpqU09LRENPTElHZUNCWnFieEJCekJLZkRQakEyVmJFbjFMWmdFU2htYTYKVlJGQmxoajE2MENra0MxTGMzMVBwUHBuOGxGQzBacDNaODFaSnNzT3MyTzR3MHVFQnAwc0hFZy9NRG05VmsxbQpORjIwTFJLZUxaQlBYbUlkbkhCT3F2aU1NOElsY1M3djc2cXFNOVZaT0lVcjZ1T3BKb3BtTlI5U0lrWUVGV3VkCkI4RVBpeVlXeDAzVEt1aVpybzF6Z3Zra3FaQlpJYStDNkdiZWFlVnZWQ0pOaU82dDFFOE5KKytYNElJYWVua0UKaitCbW5ZQzRlei9ra2xUWjZ4V2o4dVVRNjNCSDNCYkRCYXJUOS94WXEzR1pPVFhuYjBtKzA4VUMzVEtZRlh3KwpXOTV1Nmx0dVBLZUwrVldiWTF0N081N0g1N1FreVdSVm1wOXNORXFmc25wZ21zWmlZRTlTMEZhRWhqZWhraFRVCk5DdndnZ2VjWjl0V0ZsUExRd2ZZQUVDNjlRK29wRlU3ZytMVVRYSHdEcko0N0pNS3VuSDhrQ1Rtc1owQ0F3RUEKQVFLQ0FnQUJ2U1N3ZVpRZW5HSDhsUXY4SURMQzdvU1ZZd0xxNWlCUDdEdjJsN00wYStKNWlXcWwzV2s4ZEVOSQpOYWtDazAwNmkyMCtwVDROdW5mdEZJYzBoTHN6TjBlMkpjRzY1dVlGZnZ2ZHY3RUtZZnNZU3hhU3d4TWJBMlkxCmNCa2NjcGVsUzBhMVpieFYvck16T1RxVUlRNGFQTzJPU3RUeU55b3dWVjhhcXh0QlNPV2pBUlA2VjlBOHNSUDIKNlVGeVFnM2thdjRla3d0S0M5TW85MEVvcGlkSXNnYy9IYk5kQm5tMFJDUnY0bU1DNmVPTXp0NGx0UVNldG0rcwpaRkUwZkM5cjkwRjE4RUVlUjZHTEYxdGhIMzlKTWFFcjYrc3F6TlZXU1VPVGxNN2M5SE55QTJIcnJudnhVUVNOCmF3SkZWSEFOY1hJSjBqcW9icmR6MTdMbGtIRVFGczNLdjRlcDR3REJKMlF0eisxdUFvY1JoV3ZSaWJxWEQ3THgKVmpPdGRyT1h3ZFQxY2ZrKzZRc1RMWUFKR3ptdDdsY1M2QjNnYzJHWmNJWGwyNVlqTUQ1ZVhpa1dEc3hYWmt1UAorb3MzVGhxeGZIS25ITmxtYk9SSVpDMW92Q1NkSTRWZVpzalk0MUs5K0dNaXdXSk1kektpRkp3NlR2blRSUldTCkxod2EzUTlBVmMvTEg0SC9PbU9qWDc0QTNZSWwrRDFVUHd3VzAvMmw4S3BNM0VWZ21XalJMV1ZIRnBNTGJNSlcKZVZKd3dKUmF3bWZLdHZ6bU9KRHlhTXJJblhqTDMvSE1EaWtwU3JhRzFyTnc1SUozOXJZdEFIUUQ1L1VuZlRkSApLNXVjakVucTdPdDMyR1ozcHJvRTU1ZGFBY0hQbktuOGpYZ1ZKTUQyOWh5cEZvL2ZRUUtDQVFFQStBbjRoSDFFCm9GK3FlcWlvYXR3N2cwaVdQUDNCeklxOEZWbWtsRlZBYVF5U28wU2QxWFBybmErR0RFQVd0cHlsVjF5ZkZkR2oKSHc4YXU5NnpUZnRuNWZCRkQxWG1NTkNZeTcrM293V3ArK1NwYUMvMTYzN1dvb3lLRjBjVFNvcWEzZEVuRUtSSwp4TGF2a0lFUTI3OXRBNFVUK0dVK3pTb0NPUFBNNE1JS3poR0FDczZ1anRySzFNcXpwK0JhYldzRlBuN2J1bStVCkRHSFIrNCtab2tBL1Q2N2luYlRxZUwwVzJCNjRMckFURHpZL3Y4NlRGbW1aallEaHRKR1JIWVZUOU9XSXR0RVkKNnZtUDN0a1dOTWt0R2w4bTFiQ0FHQ1JlcGtycUhxWXNMWG5GQ2ZZSFFtOXNpaGgvM3JFVjZ1MUYxZCt0U3JFMgprU1ZVOHhVWDUwbHFNUUtDQVFFQXpVTjZaS0lRNldkT09FR3ZyMExRL1hVczI0bUczN3lGMjhJUDJEcWFBWWVzCnJza2xTdjdlSU9TZWV3MW1CRHVCRkl2bkZvcTVsRlA3cXhWcEIyWjNNSGlDMVNaclZSZjlQTjdCNGFzcmNyMCsKdDB2S0NXWFFIaTVQQXhucXdYb2E2N0Q1bnkwdnlvV0lVUXAyZEZMdkIwQmp0b3MvajJFaHpJZk5WMm1UOW15bgpWQXZOWEdtZnc4SVJCL1diMGkzQ3c0Wityb1l1dTJkRHo2UUwzUFVvN1hLS3ljZzR1UzU1eksvcWZPc09lYm5mCnpsd3ZqbGxNSitmVFFHNzMrQnpINE5IWGs2akZZQzU4eXBrdXd0cmJmYk1pSkZOWThyV1ptL01Nd1VDWlZDQ3kKeUlxQ3FHQVB6b2kyU05zSEtaTlJqN3ZZQ3dQQVd6TzFidjFGcC9hM0xRS0NBUUVBeG0zTGw4cFROVzF6QjgrWApkRzJkV3FpZU1FcmRXRklBcDUvZ1R4NW9lZUdxQ2QxaDJ4cHlldUtwZlhGaitsRVU0Ty9qQU9TRjk5bndqQzFjCkNsMit2Ni9ZdjZ6N2l6L0ZqUEpoNlpRbGFiT0RaeXMvTkZkelEvVGtvRHluRFRJWE5LOFc3blJRc0ZCcDRWT3YKZGUwTlBBeWhiazBvMFo3eXlqY1lSeEpVN0lnSmhCdldmOGcvRGI3ZnZNUjU4eUR6d0F4aW9pS1RNTmlzMFBBUAplMEtrbzQySUU1eGhHNWhDQjBHRUhTMlZBYzFuY0gzRkk5LzFETVAzVEtwTGltOVlQQW5JdG1CTzYrUWNtYTNYCjJ3QzZDV2ZudkhvSDc4aGd3KzRZbjg1V2QwYjhQN3pJRC9qdHZ3aGNlMzMxeDh4cjJ1Nm5ScUxBd1pzNCs0SjcKYmZkSWNRS0NBUUFDL2JlNzNheTNhZnoyenVZN2ZKTEZEcjhQbCtweU9qSU5LTC9JVzlwQXFYUjN1NUNpamlJNApnbnhZdUxKQzM0Y2JBSXJtaGpEOEcxa3dmZ2hneGpwNFoxa290LzJhYU5ZVTIvNGhScmhFWE1PY01pdUloWVpKCjJrem1jNnM3RklkdDVjOU5aWUFyeUZSYk1mYlY3UnQwbEppZllWb1V3Y3FYUzJkUG5jYzlNUW9qTEdUYXN1TlUKRy9EWmw5ZWtjV3hFSXlLWGNuY2QzZnhiK3p6OUJFbUxaRDduZjlacnhHU2IrZmhGeDdzWFJRRWc1YkQvdHdkbwpFWFcvbTU1YmJEZnhhNzFqZG5NaDJxdVEzRGlWT0ZFNGZMTERxcjlDRWlsaDMySFJNeHJJNGcwWTVRUFFaazMwCnFZTldmbktWUllOTHYrWC9DeGZ6ZkVacGpxRkVPRkVsQW9JQkFRQ0t6R2JGdmx6d1BaUmh4czd2VXYxOXlIUXAKQzFmR3gwb0tpRDFSNWZwWVBrT0VRQWVudEFKRHNyYVRsNy9rSDY5V09VbUQ1T3gxbWpyRFB0a1M4WnhXYlJXeApGYjJLK3JxYzRtcGFacGROV09OTkszK3RNZmsrb0FRcWUySU1JV253NUhmbVpjNE1QY0t0bkZQYlJTTkF0aktwCkQ2aG9oL3BXMmdjRFA0cVpNWVZvRW04MVZYZEZDUGhOYitNYnUvU3gyaFB4U0dXYTVGaTczeEtwWWp5M3BISlQKWFoyY2lHN0VNQ3NKZW9HS2FRdmNCY1kvNGlSRGFoV0hWcmlsSVhJQXJQdXdmVUIybzZCZFR0allHeU5sZ2NmeApxWEt4aXBTaEE2VlNienVnR3pkdEdNeEUyekRHVEkxOXFSQy96OUNEREM1ZTJTQUZqbEJUV0QyUHJjcU4KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K

View File

@ -1,2 +0,0 @@
resources:
- kubeconfig.yaml

View File

@ -1,24 +0,0 @@
apiVersion: airshipit.org/v1alpha1
kind: KubeConfig
metadata:
name: sample-name
config:
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1USXlOakE0TWpneU5Gb1hEVEk1TVRJeU16QTRNamd5TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTTFSClM0d3lnajNpU0JBZjlCR0JUS1p5VTFwYmdDaGQ2WTdJektaZWRoakM2K3k1ZEJpWm81ZUx6Z2tEc2gzOC9YQ1MKenFPS2V5cE5RcDN5QVlLdmJKSHg3ODZxSFZZNjg1ZDVYVDNaOHNyVVRzVDR5WmNzZHAzV3lHdDM0eXYzNi9BSQoxK1NlUFErdU5JemN6bzNEdWhXR0ZoQjk3VjZwRitFUTBlVWN5bk05c2hkL3AwWVFzWDR1ZlhxaENENVpzZnZUCnBka3UvTWkyWnVGUldUUUtNeGpqczV3Z2RBWnBsNnN0L2ZkbmZwd1Q5cC9WTjRuaXJnMEsxOURTSFFJTHVrU2MKb013bXNBeDJrZmxITWhPazg5S3FpMEloL2cyczRFYTRvWURZemt0Y2JRZ24wd0lqZ2dmdnVzM3pRbEczN2lwYQo4cVRzS2VmVGdkUjhnZkJDNUZNQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFJek9BL00xWmRGUElzd2VoWjFuemJ0VFNURG4KRHMyVnhSV0VnclFFYzNSYmV3a1NkbTlBS3MwVGR0ZHdEbnBEL2tRYkNyS2xEeFF3RWg3NFZNSFZYYkFadDdsVwpCSm90T21xdXgxYThKYklDRTljR0FHRzFvS0g5R29jWERZY0JzOTA3ckxIdStpVzFnL0xVdG5hN1dSampqZnBLCnFGelFmOGdJUHZIM09BZ3B1RVVncUx5QU8ya0VnelZwTjZwQVJxSnZVRks2TUQ0YzFmMnlxWGxwNXhrN2dFSnIKUzQ4WmF6d0RmWUVmV3Jrdld1YWdvZ1M2SktvbjVEZ0Z1ZHhINXM2Snl6R3lPVnZ0eG1TY2FvOHNxaCs3UXkybgoyLzFVcU5ZK0hlN0x4d04rYkhwYkIxNUtIMTU5ZHNuS3BRbjRORG1jSTZrVnJ3MDVJMUg5ZGRBbGF0bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://10.23.25.101:6443
name: dummycluster_ephemeral
contexts:
- context:
cluster: dummycluster_ephemeral
user: kubernetes-admin
name: dummy_cluster
current-context: dummy_cluster
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQwRENDQXJnQ0ZFdFBveEZYSjVrVFNWTXQ0OVlqcHBQL3hCYnlNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NakF3TVRJME1Ua3hOVEV3V2hjTk1qa3hNakF5TVRreApOVEV3V2pBME1Sa3dGd1lEVlFRRERCQnJkV0psY201bGRHVnpMV0ZrYldsdU1SY3dGUVlEVlFRS0RBNXplWE4wClpXMDZiV0Z6ZEdWeWN6Q0NBaUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQU1iaFhUUmsKVjZiZXdsUjBhZlpBdTBGYWVsOXRtRThaSFEvaGtaSHhuTjc2bDZUUFltcGJvaDRvRjNGMFFqbzROS1o5NVRuWgo0OWNoV240eFJiZVlPU25EcDBpV0Qzd0pXUlZ5aVFvVUFyYTlNcHVPNkVFU1FpbFVGNXNxc0VXUVdVMjBETStBCkdxK1k0Z2c3eDJ1Q0hTdk1GUmkrNEw5RWlXR2xnRDIvb1hXUm5NWEswNExQajZPb3Vkb2Zid2RmT3J6dTBPVkUKUzR0eGtuS1BCY1BUU3YxMWVaWVhja0JEVjNPbExENEZ3dTB3NTcwcnczNzAraEpYdlZxd3Zjb2RjZjZEL1BXWQowamlnd2ppeUJuZ2dXYW04UVFjd1Nud3o0d05sV3hKOVMyWUJFb1ptdWxVUlFaWVk5ZXRBcEpBdFMzTjlUNlQ2ClovSlJRdEdhZDJmTldTYkxEck5qdU1OTGhBYWRMQnhJUHpBNXZWWk5aalJkdEMwU25pMlFUMTVpSFp4d1RxcjQKakRQQ0pYRXU3KytxcWpQVldUaUZLK3JqcVNhS1pqVWZVaUpHQkJWcm5RZkJENHNtRnNkTjB5cm9tYTZOYzRMNQpKS21RV1NHdmd1aG0zbW5sYjFRaVRZanVyZFJQRFNmdmwrQ0NHbnA1QkkvZ1pwMkF1SHMvNUpKVTJlc1ZvL0xsCkVPdHdSOXdXd3dXcTAvZjhXS3R4bVRrMTUyOUp2dFBGQXQweW1CVjhQbHZlYnVwYmJqeW5pL2xWbTJOYmV6dWUKeCtlMEpNbGtWWnFmYkRSS243SjZZSnJHWW1CUFV0QldoSVkzb1pJVTFEUXI4SUlIbkdmYlZoWlR5ME1IMkFCQQp1dlVQcUtSVk80UGkxRTF4OEE2eWVPeVRDcnB4L0pBazVyR2RBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFECmdnRUJBSWNFM1BxZHZDTVBIMnJzMXJESk9ESHY3QWk4S01PVXZPRi90RjlqR2EvSFBJbkh3RlVFNEltbldQeDYKVUdBMlE1bjFsRDFGQlU0T0M4eElZc3VvS1VQVHk1T0t6SVNMNEZnL0lEcG54STlrTXlmNStMR043aG8rblJmawpCZkpJblVYb0tERW1neHZzSWFGd1h6bGtSTDJzL1lKYUZRRzE1Uis1YzFyckJmd2dJOFA5Tkd6aEM1cXhnSmovCm04K3hPMGhXUmJIYklrQ21NekRib2pCSWhaL00rb3VYR1doei9TakpodXhZTVBnek5MZkFGcy9PMTVaSjd3YXcKZ3ZoSGc3L2E5UzRvUCtEYytPa3VrMkV1MUZjL0E5WHpWMzc5aWhNWW5ub3RQMldWeFZ3b0ZZQUg0NUdQcDZsUApCQmwyNnkxc2JMbjl6aGZYUUJIMVpFN0EwZVE9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBeHVGZE5HUlhwdDdDVkhScDlrQzdRVnA2WDIyWVR4a2REK0dSa2ZHYzN2cVhwTTlpCmFsdWlIaWdYY1hSQ09qZzBwbjNsT2RuajF5RmFmakZGdDVnNUtjT25TSllQZkFsWkZYS0pDaFFDdHIweW00N28KUVJKQ0tWUVhteXF3UlpCWlRiUU16NEFhcjVqaUNEdkhhNElkSzh3VkdMN2d2MFNKWWFXQVBiK2hkWkdjeGNyVApncytQbzZpNTJoOXZCMTg2dk83UTVVUkxpM0dTY284Rnc5TksvWFY1bGhkeVFFTlhjNlVzUGdYQzdURG52U3ZECmZ2VDZFbGU5V3JDOXloMXgvb1A4OVpqU09LRENPTElHZUNCWnFieEJCekJLZkRQakEyVmJFbjFMWmdFU2htYTYKVlJGQmxoajE2MENra0MxTGMzMVBwUHBuOGxGQzBacDNaODFaSnNzT3MyTzR3MHVFQnAwc0hFZy9NRG05VmsxbQpORjIwTFJLZUxaQlBYbUlkbkhCT3F2aU1NOElsY1M3djc2cXFNOVZaT0lVcjZ1T3BKb3BtTlI5U0lrWUVGV3VkCkI4RVBpeVlXeDAzVEt1aVpybzF6Z3Zra3FaQlpJYStDNkdiZWFlVnZWQ0pOaU82dDFFOE5KKytYNElJYWVua0UKaitCbW5ZQzRlei9ra2xUWjZ4V2o4dVVRNjNCSDNCYkRCYXJUOS94WXEzR1pPVFhuYjBtKzA4VUMzVEtZRlh3KwpXOTV1Nmx0dVBLZUwrVldiWTF0N081N0g1N1FreVdSVm1wOXNORXFmc25wZ21zWmlZRTlTMEZhRWhqZWhraFRVCk5DdndnZ2VjWjl0V0ZsUExRd2ZZQUVDNjlRK29wRlU3ZytMVVRYSHdEcko0N0pNS3VuSDhrQ1Rtc1owQ0F3RUEKQVFLQ0FnQUJ2U1N3ZVpRZW5HSDhsUXY4SURMQzdvU1ZZd0xxNWlCUDdEdjJsN00wYStKNWlXcWwzV2s4ZEVOSQpOYWtDazAwNmkyMCtwVDROdW5mdEZJYzBoTHN6TjBlMkpjRzY1dVlGZnZ2ZHY3RUtZZnNZU3hhU3d4TWJBMlkxCmNCa2NjcGVsUzBhMVpieFYvck16T1RxVUlRNGFQTzJPU3RUeU55b3dWVjhhcXh0QlNPV2pBUlA2VjlBOHNSUDIKNlVGeVFnM2thdjRla3d0S0M5TW85MEVvcGlkSXNnYy9IYk5kQm5tMFJDUnY0bU1DNmVPTXp0NGx0UVNldG0rcwpaRkUwZkM5cjkwRjE4RUVlUjZHTEYxdGhIMzlKTWFFcjYrc3F6TlZXU1VPVGxNN2M5SE55QTJIcnJudnhVUVNOCmF3SkZWSEFOY1hJSjBqcW9icmR6MTdMbGtIRVFGczNLdjRlcDR3REJKMlF0eisxdUFvY1JoV3ZSaWJxWEQ3THgKVmpPdGRyT1h3ZFQxY2ZrKzZRc1RMWUFKR3ptdDdsY1M2QjNnYzJHWmNJWGwyNVlqTUQ1ZVhpa1dEc3hYWmt1UAorb3MzVGhxeGZIS25ITmxtYk9SSVpDMW92Q1NkSTRWZVpzalk0MUs5K0dNaXdXSk1kektpRkp3NlR2blRSUldTCkxod2EzUTlBVmMvTEg0SC9PbU9qWDc0QTNZSWwrRDFVUHd3VzAvMmw4S3BNM0VWZ21XalJMV1ZIRnBNTGJNSlcKZVZKd3dKUmF3bWZLdHZ6bU9KRHlhTXJJblhqTDMvSE1EaWtwU3JhRzFyTnc1SUozOXJZdEFIUUQ1L1VuZlRkSApLNXVjakVucTdPdDMyR1ozcHJvRTU1ZGFBY0hQbktuOGpYZ1ZKTUQyOWh5cEZvL2ZRUUtDQVFFQStBbjRoSDFFCm9GK3FlcWlvYXR3N2cwaVdQUDNCeklxOEZWbWtsRlZBYVF5U28wU2QxWFBybmErR0RFQVd0cHlsVjF5ZkZkR2oKSHc4YXU5NnpUZnRuNWZCRkQxWG1NTkNZeTcrM293V3ArK1NwYUMvMTYzN1dvb3lLRjBjVFNvcWEzZEVuRUtSSwp4TGF2a0lFUTI3OXRBNFVUK0dVK3pTb0NPUFBNNE1JS3poR0FDczZ1anRySzFNcXpwK0JhYldzRlBuN2J1bStVCkRHSFIrNCtab2tBL1Q2N2luYlRxZUwwVzJCNjRMckFURHpZL3Y4NlRGbW1aallEaHRKR1JIWVZUOU9XSXR0RVkKNnZtUDN0a1dOTWt0R2w4bTFiQ0FHQ1JlcGtycUhxWXNMWG5GQ2ZZSFFtOXNpaGgvM3JFVjZ1MUYxZCt0U3JFMgprU1ZVOHhVWDUwbHFNUUtDQVFFQXpVTjZaS0lRNldkT09FR3ZyMExRL1hVczI0bUczN3lGMjhJUDJEcWFBWWVzCnJza2xTdjdlSU9TZWV3MW1CRHVCRkl2bkZvcTVsRlA3cXhWcEIyWjNNSGlDMVNaclZSZjlQTjdCNGFzcmNyMCsKdDB2S0NXWFFIaTVQQXhucXdYb2E2N0Q1bnkwdnlvV0lVUXAyZEZMdkIwQmp0b3MvajJFaHpJZk5WMm1UOW15bgpWQXZOWEdtZnc4SVJCL1diMGkzQ3c0Wityb1l1dTJkRHo2UUwzUFVvN1hLS3ljZzR1UzU1eksvcWZPc09lYm5mCnpsd3ZqbGxNSitmVFFHNzMrQnpINE5IWGs2akZZQzU4eXBrdXd0cmJmYk1pSkZOWThyV1ptL01Nd1VDWlZDQ3kKeUlxQ3FHQVB6b2kyU05zSEtaTlJqN3ZZQ3dQQVd6TzFidjFGcC9hM0xRS0NBUUVBeG0zTGw4cFROVzF6QjgrWApkRzJkV3FpZU1FcmRXRklBcDUvZ1R4NW9lZUdxQ2QxaDJ4cHlldUtwZlhGaitsRVU0Ty9qQU9TRjk5bndqQzFjCkNsMit2Ni9ZdjZ6N2l6L0ZqUEpoNlpRbGFiT0RaeXMvTkZkelEvVGtvRHluRFRJWE5LOFc3blJRc0ZCcDRWT3YKZGUwTlBBeWhiazBvMFo3eXlqY1lSeEpVN0lnSmhCdldmOGcvRGI3ZnZNUjU4eUR6d0F4aW9pS1RNTmlzMFBBUAplMEtrbzQySUU1eGhHNWhDQjBHRUhTMlZBYzFuY0gzRkk5LzFETVAzVEtwTGltOVlQQW5JdG1CTzYrUWNtYTNYCjJ3QzZDV2ZudkhvSDc4aGd3KzRZbjg1V2QwYjhQN3pJRC9qdHZ3aGNlMzMxeDh4cjJ1Nm5ScUxBd1pzNCs0SjcKYmZkSWNRS0NBUUFDL2JlNzNheTNhZnoyenVZN2ZKTEZEcjhQbCtweU9qSU5LTC9JVzlwQXFYUjN1NUNpamlJNApnbnhZdUxKQzM0Y2JBSXJtaGpEOEcxa3dmZ2hneGpwNFoxa290LzJhYU5ZVTIvNGhScmhFWE1PY01pdUloWVpKCjJrem1jNnM3RklkdDVjOU5aWUFyeUZSYk1mYlY3UnQwbEppZllWb1V3Y3FYUzJkUG5jYzlNUW9qTEdUYXN1TlUKRy9EWmw5ZWtjV3hFSXlLWGNuY2QzZnhiK3p6OUJFbUxaRDduZjlacnhHU2IrZmhGeDdzWFJRRWc1YkQvdHdkbwpFWFcvbTU1YmJEZnhhNzFqZG5NaDJxdVEzRGlWT0ZFNGZMTERxcjlDRWlsaDMySFJNeHJJNGcwWTVRUFFaazMwCnFZTldmbktWUllOTHYrWC9DeGZ6ZkVacGpxRkVPRkVsQW9JQkFRQ0t6R2JGdmx6d1BaUmh4czd2VXYxOXlIUXAKQzFmR3gwb0tpRDFSNWZwWVBrT0VRQWVudEFKRHNyYVRsNy9rSDY5V09VbUQ1T3gxbWpyRFB0a1M4WnhXYlJXeApGYjJLK3JxYzRtcGFacGROV09OTkszK3RNZmsrb0FRcWUySU1JV253NUhmbVpjNE1QY0t0bkZQYlJTTkF0aktwCkQ2aG9oL3BXMmdjRFA0cVpNWVZvRW04MVZYZEZDUGhOYitNYnUvU3gyaFB4U0dXYTVGaTczeEtwWWp5M3BISlQKWFoyY2lHN0VNQ3NKZW9HS2FRdmNCY1kvNGlSRGFoV0hWcmlsSVhJQXJQdXdmVUIybzZCZFR0allHeU5sZ2NmeApxWEt4aXBTaEE2VlNienVnR3pkdEdNeEUyekRHVEkxOXFSQy96OUNEREM1ZTJTQUZqbEJUV0QyUHJjcU4KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K

View File

@ -1,2 +0,0 @@
resources:
- kubeconfig.yaml

View File

@ -1,2 +0,0 @@
resources:
- version.yaml

View File

@ -1,14 +0,0 @@
---
apiVersion: airshipit.org/v1alpha1
kind: Testversion
metadata:
name: version-1
spec:
version: v0.3.1
---
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: version-one

View File

@ -1,2 +0,0 @@
resources:
- version.yaml

View File

@ -1,2 +0,0 @@
phase:
path: executor_move

View File

@ -1,41 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
"opendev.org/airship/airshipctl/pkg/log"
)
var _ repository.ComponentsClient = &ComponentsClient{}
// ComponentsClient override Get() method to return same components,
// but in our implementation we skip variable substitution.
type ComponentsClient struct {
client repository.ComponentsClient
providerType string
providerName string
variableSubstitution bool
}
// Get returns the components from a repository but without variable substitution
func (cc *ComponentsClient) Get(options repository.ComponentsOptions) (repository.Components, error) {
// Invert variable substitution, so that by default clusterctl will not substitute variables
options.SkipVariables = !cc.variableSubstitution
log.Printf("Getting airshipctl provider components, skipping variable substitution: %t.\n"+
"Provider type: %s, name: %s\n", options.SkipVariables, cc.providerType, cc.providerName)
return cc.client.Get(options)
}

View File

@ -1,55 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"fmt"
)
// ErrVersionNotDefined is returned when requested version is not present in repository
type ErrVersionNotDefined struct {
Version string
}
func (e ErrVersionNotDefined) Error() string {
return fmt.Sprintf(`version %s is not defined in the repository`, e.Version)
}
// ErrNoVersionsAvailable is returned when version map is empty or not defined
type ErrNoVersionsAvailable struct {
Versions map[string]string
}
func (e ErrNoVersionsAvailable) Error() string {
return fmt.Sprintf(`version map is empty or not defined, %v`, e.Versions)
}
// ErrValueForVariableNotSet is returned when version map is empty or not defined
type ErrValueForVariableNotSet struct {
Variable string
}
func (e ErrValueForVariableNotSet) Error() string {
return fmt.Sprintf("value for variable %q is not set", e.Variable)
}
// ErrAppendNotAllowed is returned when version map is empty or not defined
type ErrAppendNotAllowed struct {
Variables map[string]string
}
func (e ErrAppendNotAllowed) Error() string {
return fmt.Sprintf(`variables %v, are not allowed to be appended from clusterctl.AdditoinalVariables`, e.Variables)
}

View File

@ -1,155 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"os"
"regexp"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/yaml"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/log"
)
var _ config.Reader = &AirshipReader{}
const (
// TODO this must come as as ProviderConfigKey from clusterctl/client/config pkg
// see https://github.com/kubernetes-sigs/cluster-api/blob/master/cmd/clusterctl/client/config/imagemeta_client.go#L27
imagesConfigKey = "images"
)
// AirshipReader provides a reader implementation backed by a map
type AirshipReader struct {
variables map[string]string
varsFromEnv bool
}
// configProvider is a mirror of config.Provider, re-implemented here in order to
// avoid circular dependencies between pkg/client/config and pkg/internal/test
type configProvider struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Type clusterctlv1.ProviderType `json:"type,omitempty"`
}
type imageMeta struct {
Repository string `json:"repository,omitempty"`
Tag string `json:"tag,omitempty"`
}
// Init implementation of clusterctl reader interface
// This is dummy method that is must be present to implement Reader interface
func (f *AirshipReader) Init(config string) error {
return nil
}
// Get implementation of clusterctl reader interface
func (f *AirshipReader) Get(key string) (string, error) {
// if value is set in variables - return it, variables from variables map take precedence over
// env variables
if val, ok := f.variables[key]; ok {
return val, nil
}
// if we are allowed to check environment variables and key is allowed to be taken from env
// look it up and return
if f.varsFromEnv && allowFromEnv(key) {
val, ok := os.LookupEnv(key)
if ok {
return val, nil
}
}
// if neither env nor variables slice has the var, return error
return "", ErrValueForVariableNotSet{Variable: key}
}
// Set implementation of clusterctl reader interface
func (f *AirshipReader) Set(key, value string) {
// TODO handle empty keys
f.variables[key] = value
}
// UnmarshalKey implementation of clusterctl reader interface
func (f *AirshipReader) UnmarshalKey(key string, rawval interface{}) error {
data, err := f.Get(key)
if err != nil {
return err
}
return yaml.Unmarshal([]byte(data), rawval)
}
func allowFromEnv(key string) bool {
variableRegEx := regexp.MustCompile(`^([A-Z0-9_$]+)$`)
log.Debugf("Verifying that variable %s is allowed to be taken from environment", key)
return variableRegEx.MatchString(key)
}
func allowAppend(key, _ string) bool {
// TODO Investigate if more validation should be done here
forbiddenVars := map[string]string{
config.ProvidersConfigKey: "",
imagesConfigKey: "",
}
_, forbid := forbiddenVars[key]
log.Debugf("Verifying that variable %s is allowed to be appended", key)
return !forbid
}
// NewAirshipReader returns airship implementation of clusterctl reader interface
func NewAirshipReader(options *airshipv1.Clusterctl) (*AirshipReader, error) {
variables := map[string]string{}
providers := []configProvider{}
images := map[string]imageMeta{}
for _, prov := range options.Providers {
appendProvider := configProvider{
Name: prov.Name,
Type: clusterctlv1.ProviderType(prov.Type),
URL: prov.URL,
}
providers = append(providers, appendProvider)
}
providersYaml, err := yaml.Marshal(providers)
if err != nil {
return nil, err
}
for key, val := range options.AdditionalComponentVariables {
// if variable is not allowed, it will be ignored
if allowAppend(key, val) {
variables[key] = val
}
}
for key, val := range options.ImageMetas {
imageVal := imageMeta{
Repository: val.Repository,
Tag: val.Tag,
}
images[key] = imageVal
}
imagesYaml, err := yaml.Marshal(images)
if err != nil {
return nil, err
}
// Add providers to config
variables[config.ProvidersConfigKey] = string(providersYaml)
variables[imagesConfigKey] = string(imagesYaml)
return &AirshipReader{
variables: variables,
varsFromEnv: options.EnvVars,
}, nil
}

View File

@ -1,319 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
)
func makeValidOptions() *airshipv1.Clusterctl {
return &airshipv1.Clusterctl{
Providers: []*airshipv1.Provider{
{
Name: "metal3",
Type: "InfrastructureProvider",
Versions: map[string]string{
"v0.3.1": "manifests/function/capm3/v0.3.1",
},
},
{
Name: "kubeadm",
Type: "BootstrapProvider",
Versions: map[string]string{
"v0.3.3": "manifests/function/cabpk/v0.3.3",
},
},
{
Name: "cluster-api",
Type: "InfrastructureProvider",
Versions: map[string]string{
"v0.3.3": "manifests/function/capi/v0.3.3",
},
},
{
Name: "kubeadm",
Type: "ControlPlaneProvider",
Versions: map[string]string{
"v0.3.3": "manifests/function/cacpk/v0.3.3",
},
},
},
ImageMetas: map[string]airshipv1.ImageMeta{
"all": {
Repository: "myorg.io/all-repo",
},
},
}
}
func TestNewReader(t *testing.T) {
tests := []struct {
name string
options *airshipv1.Clusterctl
}{
{
// make sure we get no panic here
name: "pass empty options",
options: &airshipv1.Clusterctl{},
},
{
name: "pass airshipctl valid config",
options: makeValidOptions(),
},
}
for _, tt := range tests {
options := tt.options
t.Run(tt.name, func(t *testing.T) {
reader, err := NewAirshipReader(options)
require.NoError(t, err)
assert.NotNil(t, reader)
})
}
}
func TestGet(t *testing.T) {
tests := []struct {
name string
options *airshipv1.Clusterctl
key string
expectedErr error
expectedResult string
}{
{
// make sure we get no panic here
name: "pass empty options",
options: &airshipv1.Clusterctl{},
key: "FOO",
expectedErr: ErrValueForVariableNotSet{Variable: "FOO"},
},
{
name: "pass airshipctl valid config",
options: makeValidOptions(),
key: "providers",
expectedErr: nil,
expectedResult: `- name: metal3
type: InfrastructureProvider
- name: kubeadm
type: BootstrapProvider
- name: cluster-api
type: InfrastructureProvider
- name: kubeadm
type: ControlPlaneProvider
`,
},
{
name: "image repo override for all clusterctl components",
options: makeValidOptions(),
key: "images",
expectedErr: nil,
expectedResult: `all:
repository: myorg.io/all-repo
`,
},
{
name: "image override for cert-manager components",
options: &airshipv1.Clusterctl{
ImageMetas: map[string]airshipv1.ImageMeta{
"cert-manager": {
Repository: "myorg.io/certmanager-repo",
Tag: "v0.1",
},
},
},
key: "images",
expectedErr: nil,
expectedResult: `cert-manager:
repository: myorg.io/certmanager-repo
tag: v0.1
`,
},
{
name: "image override for cainjector of cert-manager",
options: &airshipv1.Clusterctl{
ImageMetas: map[string]airshipv1.ImageMeta{
"cert-manager/cert-manager-cainjector": {
Repository: "myorg.io/certmanagercainjector-repo",
Tag: "v0.1",
},
},
},
key: "images",
expectedErr: nil,
expectedResult: `cert-manager/cert-manager-cainjector:
repository: myorg.io/certmanagercainjector-repo
tag: v0.1
`,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
reader, err := NewAirshipReader(tt.options)
require.NoError(t, err)
require.NotNil(t, reader)
value, err := reader.Get(tt.key)
assert.Equal(t, tt.expectedErr, err)
assert.Equal(t, tt.expectedResult, value)
})
}
}
func TestSetGet(t *testing.T) {
tests := []struct {
name string
setKey string
setGetValue string
expectedErr error
}{
{
// should return empty string
name: "set simple key",
setKey: "FOO",
expectedErr: nil,
setGetValue: "",
},
{
name: "set providers",
setKey: "providers",
expectedErr: nil,
setGetValue: `- name: metal3
type: InfrastructureProvider
- name: kubeadm
type: BootstrapProvider
- name: cluster-api
type: InfrastructureProvider
- name: kubeadm
type: ControlPlaneProvider
`,
},
{
// set empty
name: "empty key",
setKey: "",
setGetValue: "some key",
expectedErr: nil,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
reader, err := NewAirshipReader(&airshipv1.Clusterctl{})
require.NoError(t, err)
require.NotNil(t, reader)
reader.Set(tt.setKey, tt.setGetValue)
result, err := reader.Get(tt.setKey)
require.Equal(t, tt.expectedErr, err)
assert.Equal(t, tt.setGetValue, result)
})
}
}
// Test verifies that options provider returns
func TestUnmarshalProviders(t *testing.T) {
options := &airshipv1.Clusterctl{
Providers: []*airshipv1.Provider{
{
Name: config.Metal3ProviderName,
Type: string(clusterctlv1.InfrastructureProviderType),
},
{
Name: config.KubeadmBootstrapProviderName,
Type: string(clusterctlv1.BootstrapProviderType),
},
{
Name: config.ClusterAPIProviderName,
Type: string(clusterctlv1.CoreProviderType),
},
{
Name: config.KubeadmControlPlaneProviderName,
Type: string(clusterctlv1.ControlPlaneProviderType),
},
},
}
providers := []configProvider{}
reader, err := NewAirshipReader(options)
require.NoError(t, err)
require.NotNil(t, reader)
// check if we can unmarshal provider key into correct struct
err = reader.UnmarshalKey(config.ProvidersConfigKey, &providers)
require.NoError(t, err)
assert.Len(t, providers, 4)
for _, actualProvider := range providers {
assert.NotNil(t, options.Provider(actualProvider.Name, actualProvider.Type))
}
}
func TestUnmarshal(t *testing.T) {
tests := []struct {
name string
expectErr bool
variables map[string]string
getKey string
unmarshal interface{}
}{
{
name: "unmarshal into nil",
getKey: "Foo",
expectErr: true,
},
{
name: "value doesn't exist",
getKey: "Foo",
variables: map[string]string{},
unmarshal: []configProvider{},
expectErr: true,
},
{
name: "value doesn't exist",
getKey: "foo",
expectErr: false,
variables: map[string]string{
"foo": "foo: bar",
},
unmarshal: &struct {
Foo string `json:"foo,omitempty"`
}{},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
reader, err := NewAirshipReader(&airshipv1.Clusterctl{})
require.NoError(t, err)
require.NotNil(t, reader)
reader.variables = tt.variables
if tt.expectErr {
assert.Error(t, reader.UnmarshalKey(tt.getKey, tt.unmarshal))
} else {
assert.NoError(t, reader.UnmarshalKey(tt.getKey, tt.unmarshal))
}
})
}
}
// This test is simply for test coverage of the Reader interface
func TestInit(t *testing.T) {
reader, err := NewAirshipReader(&airshipv1.Clusterctl{})
require.NoError(t, err)
require.NotNil(t, reader)
assert.NoError(t, reader.Init("anything"))
}

View File

@ -1,139 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"bytes"
"path/filepath"
"k8s.io/apimachinery/pkg/util/version"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/log"
)
const (
metaDataFilePath = "metadata.yaml"
dummyComponentPath = "components.yaml"
)
// Repository implements Repository from clusterctl project
type Repository struct {
root string
versions map[string]string
defaultVersion string
}
var _ repository.Repository = &Repository{}
// ComponentsPath always returns same value, since it is not relevant without real filesystem
func (r *Repository) ComponentsPath() string {
return dummyComponentPath
}
// GetVersions retrieve all versions from the repository
func (r *Repository) GetVersions() ([]string, error) {
versions := make([]string, 0, len(r.versions))
for availableVersion := range r.versions {
_, err := version.ParseSemantic(availableVersion)
if err != nil {
// discard releases with tags that are not a valid semantic versions (the user can point explicitly to such releases)
continue
}
versions = append(versions, availableVersion)
}
return versions, nil
}
// DefaultVersion highest version available
func (r *Repository) DefaultVersion() string {
return r.defaultVersion
}
// RootPath not relevant without real filesystem
func (r *Repository) RootPath() string {
return r.root
}
// GetFile returns all kubernetes resources that belong to cluster-api
func (r *Repository) GetFile(version string, filePath string) ([]byte, error) {
if version == "latest" {
// default should be latest
version = r.defaultVersion
}
path, ok := r.versions[version]
if !ok {
return nil, ErrVersionNotDefined{Version: version}
}
kustomizePath := filepath.Join(r.root, path)
log.Debugf("Building cluster-api provider component documents from kustomize path at %s", kustomizePath)
bundle, err := document.NewBundleByPath(kustomizePath)
if err != nil {
return nil, err
}
// TODO when clusterctl will have a defined set of expected files in repository
// revisit this implementation
// metadata.yaml should return a bytes containing clusterctlv1.Metadata or error
if filePath == metaDataFilePath {
doc, errMeta := bundle.SelectOne(document.NewClusterctlMetadataSelector())
if errMeta != nil {
return nil, errMeta
}
return doc.AsYAML()
}
filteredBundle, err := bundle.SelectBundle(document.NewDeployToK8sSelector())
if err != nil {
return nil, err
}
buffer := bytes.NewBuffer([]byte{})
err = filteredBundle.Write(buffer)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}
// NewRepository builds instance of repository
func NewRepository(root string, versions map[string]string) (repository.Repository, error) {
var latestVersion *version.Version
var latestStringVersion string
// calculate latest version and delete versions that do not obey version syntax
for ver := range versions {
availableSemVersion, err := version.ParseSemantic(ver)
if err != nil {
// ignore and delete version if we can't parse it.
fmtMsg := "Invalid version %s in repository versions map %q, ignoring it. " +
"Version must obey the the Semantic Versioning specification (http://semver.org/)"
log.Debugf(fmtMsg, ver, versions)
// delete the version so actual version list is clean
delete(versions, ver)
continue
}
if latestVersion == nil || latestVersion.LessThan(availableSemVersion) {
latestVersion = availableSemVersion
latestStringVersion = ver
}
}
if latestStringVersion == "" {
return nil, ErrNoVersionsAvailable{Versions: versions}
}
return &Repository{
root: root,
versions: versions,
defaultVersion: latestStringVersion}, nil
}

View File

@ -1,45 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
"opendev.org/airship/airshipctl/pkg/log"
)
var _ config.Provider = &RepositoryClient{}
// RepositoryClient override Components() method to return same components client,
// but in our implementation we skip variable substitution.
type RepositoryClient struct {
repository.Client
VariableSubstitution bool
ProviderType string
ProviderName string
}
// Components provide access to YAML file for creating provider components.
func (rc *RepositoryClient) Components() repository.ComponentsClient {
log.Debugf("Setting up airshipctl provider Components client\n"+
"Provider type: %s, name: %s\n", rc.ProviderType, rc.ProviderName)
return &ComponentsClient{
client: rc.Client.Components(),
providerName: rc.ProviderName,
providerType: rc.ProviderType,
variableSubstitution: rc.VariableSubstitution,
}
}

View File

@ -1,214 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
clusterctlconfig "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
)
func TestRepositoryClient(t *testing.T) {
airRepoClient := testRepoClient(testRepoOpts{
kustRoot: "functions/4",
envVars: false,
additionalVars: map[string]string{},
}, t)
// get the components of the repository with empty options, all defaults should work
// SkipVariables is to true, to make sure that it is ignored in this implementation, and instead
// taken from airship clusterctl provider option, which disables var substitution by default
c, err := airRepoClient.Components().Get(repository.ComponentsOptions{SkipVariables: true})
require.NoError(t, err)
// No errors must be returned since there is are no variables that need to be substituted
assert.NotNil(t, c)
// Make sure that target namespace is the same as defined by repository implementation bundle
assert.Equal(t, "newnamespace", c.TargetNamespace())
// Make sure that variables for substitution are actually found
require.Len(t, c.Variables(), 1)
// make sure that variable name is correct
assert.Equal(t, "PROVISIONING_IP", c.Variables()[0])
}
func TestMissingVariableRepoClient(t *testing.T) {
airRepoClient := testRepoClient(testRepoOpts{
kustRoot: "functions/5",
envVars: true,
additionalVars: map[string]string{},
varSubstitution: true,
}, t)
envVars := map[string]string{
"AZURE_SUBSCRIPTION_ID_B64": "c29tZS1iYXNlNjQtSUQtdGV4dAo=",
"AZURE_TENANT_ID_B64": "c29tZS1iYXNlNjQtVEVOQU5ULUlELXRleHQK",
"AZURE_CLIENT_ID_B64": "c29tZS1iYXNlNjQtQ0xJRU5ULUlELXRleHQK",
}
for key, val := range envVars {
os.Setenv(key, val)
defer os.Unsetenv(key)
}
c, err := airRepoClient.Components().Get(repository.ComponentsOptions{})
require.Error(t, err)
assert.Contains(t, err.Error(), `value for variables [AZURE_CLIENT_SECRET_B64] is not set`)
assert.Nil(t, c)
}
func TestEnvVariableSubstitutionRepoClient(t *testing.T) {
airRepoClient := testRepoClient(testRepoOpts{
kustRoot: "functions/5",
envVars: true,
additionalVars: map[string]string{},
varSubstitution: true,
}, t)
envVars := map[string]string{
"AZURE_SUBSCRIPTION_ID_B64": "c29tZS1iYXNlNjQtSUQtdGV4dAo=",
"AZURE_TENANT_ID_B64": "c29tZS1iYXNlNjQtVEVOQU5ULUlELXRleHQK",
"AZURE_CLIENT_ID_B64": "c29tZS1iYXNlNjQtQ0xJRU5ULUlELXRleHQK",
"AZURE_CLIENT_SECRET_B64": "c29tZS1iYXNlNjQtQ0xJRU5ULVNFQ1JFVC10ZXh0Cg==",
}
for key, val := range envVars {
os.Setenv(key, val)
defer os.Unsetenv(key)
}
c, err := airRepoClient.Components().Get(repository.ComponentsOptions{})
require.NoError(t, err)
assert.NotNil(t, c)
assert.Len(t, c.Variables(), len(dataKeyMapping()))
// find secret containing env variables
for _, obj := range c.InstanceObjs() {
if obj.GetKind() == "Secret" {
cm := &v1.ConfigMap{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), cm)
require.NoError(t, err)
for key, expectedVal := range envVars {
dataKey, exists := dataKeyMapping()[key]
require.True(t, exists)
actualVal, exists := cm.Data[dataKey]
require.True(t, exists)
assert.Equal(t, expectedVal, actualVal)
}
}
}
}
func TestAdditionalVariableSubstitutionRepoClient(t *testing.T) {
vars := map[string]string{
"AZURE_SUBSCRIPTION_ID_B64": "c29tZS1iYXNlNjQtSUQtdGV4dAo=",
"AZURE_TENANT_ID_B64": "c29tZS1iYXNlNjQtVEVOQU5ULUlELXRleHQK",
"AZURE_CLIENT_ID_B64": "c29tZS1iYXNlNjQtQ0xJRU5ULUlELXRleHQK",
"AZURE_CLIENT_SECRET_B64": "c29tZS1iYXNlNjQtc2VjcmV0Cg==",
}
airRepoClient := testRepoClient(testRepoOpts{
kustRoot: "functions/5",
envVars: false,
additionalVars: vars,
varSubstitution: true,
}, t)
c, err := airRepoClient.Components().Get(repository.ComponentsOptions{})
require.NoError(t, err)
assert.NotNil(t, c)
assert.Len(t, c.Variables(), len(dataKeyMapping()))
for _, obj := range c.InstanceObjs() {
if obj.GetKind() == "Secret" {
cm := &v1.ConfigMap{}
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), cm)
require.NoError(t, err)
for key, expectedVal := range vars {
dataKey, exists := dataKeyMapping()[key]
require.True(t, exists)
actualVal, exists := cm.Data[dataKey]
require.True(t, exists)
assert.Equal(t, expectedVal, actualVal)
}
}
}
}
type testRepoOpts struct {
kustRoot string
envVars bool
additionalVars map[string]string
varSubstitution bool
}
func testRepoClient(opts testRepoOpts, t *testing.T) repository.Client {
t.Helper()
providerName := "metal3"
providerType := "InfrastructureProvider"
// this version contains a variable that is suppose to be substituted by clusterctl
// and we will test if the variable is found and not substituted
versions := map[string]string{
"v0.2.3": opts.kustRoot,
}
cctl := &airshipv1.Clusterctl{
AdditionalComponentVariables: opts.additionalVars,
EnvVars: opts.envVars,
Providers: []*airshipv1.Provider{
{
Name: providerName,
Type: providerType,
URL: "/dummy/path/v0.3.2/components.yaml",
Versions: versions,
},
},
}
// create instance of airship reader interface implementation for clusterctl and inject it
reader, err := NewAirshipReader(cctl)
require.NoError(t, err)
require.NotNil(t, reader)
optionReader := clusterctlconfig.InjectReader(reader)
require.NotNil(t, optionReader)
configClient, err := clusterctlconfig.New("", optionReader)
require.NoError(t, err)
require.NotNil(t, configClient)
// get the provider from configuration client, in which we injected our reader
provider, err := configClient.Providers().Get(providerName, clusterctlv1.ProviderType(providerType))
require.NoError(t, err)
require.NotNil(t, provider)
// Create instance of airship repository interface implementation for clusterctl
repo, err := NewRepository("testdata", versions)
require.NoError(t, err)
require.NotNil(t, repo)
// Inject the repository in repository client
optionsRepo := repository.InjectRepository(repo)
repoClient, err := repository.New(provider, configClient, optionsRepo)
require.NoError(t, err)
require.NotNil(t, repoClient)
return &RepositoryClient{
ProviderName: providerName,
ProviderType: providerType,
Client: repoClient,
VariableSubstitution: opts.varSubstitution,
}
}
func dataKeyMapping() map[string]string {
return map[string]string{
"AZURE_SUBSCRIPTION_ID_B64": "subscription-id",
"AZURE_TENANT_ID_B64": "tenant-id",
"AZURE_CLIENT_ID_B64": "client-id",
"AZURE_CLIENT_SECRET_B64": "client-secret",
}
}

View File

@ -1,302 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package implementations_test
import (
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
versionclient "k8s.io/apimachinery/pkg/util/version"
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
"sigs.k8s.io/yaml"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"opendev.org/airship/airshipctl/pkg/clusterctl/implementations"
)
type Version struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec VersionSpec `json:"spec"`
}
type VersionSpec struct {
Version string `json:"version"`
}
func TestNewRepository(t *testing.T) {
tests := []struct {
name string
root string
versions map[string]string
defaultVersion string
Error error
}{
{
name: "simple repository success",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/capi",
},
Error: nil,
defaultVersion: "v0.0.1",
},
{
name: "invalid version",
root: "testdata",
versions: map[string]string{
"malformed-version": "functions/capi",
},
Error: implementations.ErrNoVersionsAvailable{Versions: map[string]string{}},
},
{
name: "multiple repository versions",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/1",
"v0.2.3": "functions/2",
"v7.0.2": "functions/3",
"v0.3.2": "functions/4",
"v4.0.2": "functions/5",
"v1.0.2": "functions/6",
},
Error: nil,
defaultVersion: "v7.0.2",
},
{
name: "Empty version",
root: "testdata",
versions: map[string]string{},
Error: implementations.ErrNoVersionsAvailable{Versions: map[string]string{}},
},
}
for _, tt := range tests {
repo, err := implementations.NewRepository(tt.root, tt.versions)
expectedErr := tt.Error
defaultVersion := tt.defaultVersion
t.Run(tt.name, func(t *testing.T) {
if expectedErr != nil {
assert.Equal(t, expectedErr, err)
assert.Nil(t, repo)
} else {
assert.NoError(t, err)
assert.NotNil(t, repo)
assert.Equal(t, defaultVersion, repo.DefaultVersion())
}
})
}
}
func TestGetFile(t *testing.T) {
tests := []struct {
name string
root string
versions map[string]string
expectErr bool
resultVersion string
versionToUse string
fileToUse string
resultContract string
}{
{
name: "single version",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/1",
},
expectErr: false,
resultVersion: "v0.0.1",
versionToUse: "v0.0.1",
},
{
name: "latest version",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/1",
"v0.0.2": "functions/2",
"v0.0.3": "functions/3",
},
expectErr: false,
resultVersion: "v0.0.3",
versionToUse: "latest",
},
{
name: "failed to bundle",
root: "testdata",
versions: map[string]string{
"v1.3.2": "does-not-exist",
},
versionToUse: "v1.3.2",
expectErr: true,
},
{
name: "multiple repository versions",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/1",
"v0.2.3": "functions/2",
"v7.0.2": "functions/3",
},
expectErr: false,
resultVersion: "v0.0.2",
versionToUse: "v0.2.3",
},
{
name: "version doesn't exist",
root: "testdata",
versions: map[string]string{
"v1.3.2": "does-not-exist",
},
versionToUse: "v1.3.3",
expectErr: true,
},
{
name: "test valid metadata",
root: "testdata",
versions: map[string]string{
"v0.2.3": "functions/2",
},
expectErr: false,
versionToUse: "v0.2.3",
fileToUse: "metadata.yaml",
resultContract: "v1alpha2",
},
{
name: "test valid metadata",
root: "testdata",
versions: map[string]string{
"v0.2.0": "functions/1",
},
expectErr: true,
versionToUse: "v0.2.3",
fileToUse: "metadata.yaml",
resultContract: "v1alpha2",
},
}
for _, tt := range tests {
root := tt.root
versions := tt.versions
resultVersion := tt.resultVersion
versionToUse := tt.versionToUse
expectErr := tt.expectErr
fileToUse := tt.fileToUse
resultContract := tt.resultContract
t.Run(tt.name, func(t *testing.T) {
repo, err := implementations.NewRepository(root, versions)
require.NoError(t, err)
assert.NotNil(t, repo)
b, err := repo.GetFile(versionToUse, fileToUse)
if expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
if fileToUse == "metadata.yaml" {
gotMetadata := metadata(t, b)
parsedVersion, err := versionclient.ParseSemantic(versionToUse)
require.NoError(t, err)
assert.Equal(t, resultContract, gotMetadata.GetReleaseSeriesForVersion(parsedVersion).Contract)
} else {
gotVersion := version(t, b)
assert.Equal(t, resultVersion, gotVersion.Spec.Version)
}
}
})
}
}
func version(t *testing.T, versionBytes []byte) *Version {
t.Helper()
ver := &Version{}
err := yaml.Unmarshal(versionBytes, ver)
require.NoError(t, err)
return ver
}
func metadata(t *testing.T, metadataBytes []byte) *clusterctlv1.Metadata {
t.Helper()
m := &clusterctlv1.Metadata{}
err := yaml.Unmarshal(metadataBytes, m)
require.NoError(t, err)
return m
}
func TestComponentsPath(t *testing.T) {
versions := map[string]string{
"v0.0.1": "functions/1",
}
repo, err := implementations.NewRepository("testdata", versions)
require.NoError(t, err)
assert.NotEmpty(t, repo.ComponentsPath())
}
func TestRootPath(t *testing.T) {
versions := map[string]string{
"v0.0.1": "functions/1",
}
repo, err := implementations.NewRepository("testdata", versions)
require.NoError(t, err)
assert.Equal(t, "testdata", repo.RootPath())
}
func TestGetVersions(t *testing.T) {
tests := []struct {
name string
root string
versions map[string]string
expectedVersions []string
}{
{
name: "single version",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/1",
},
expectedVersions: []string{"v0.0.1"},
},
{
name: "multiple repository versions",
root: "testdata",
versions: map[string]string{
"v0.0.1": "functions/1",
"v0.2.3": "functions/2",
"v7.0.2": "functions/3",
"malformed-version": "doesn't matter",
},
expectedVersions: []string{"v0.0.1", "v0.2.3", "v7.0.2"},
},
}
for _, tt := range tests {
root := tt.root
versions := tt.versions
expectedVersions := tt.expectedVersions
t.Run(tt.name, func(t *testing.T) {
repo, err := implementations.NewRepository(root, versions)
require.NoError(t, err)
actualVersions, err := repo.GetVersions()
assert.NoError(t, err)
// this will make sure that slices are sorted in a same way
sort.Strings(expectedVersions)
sort.Strings(actualVersions)
assert.Equal(t, expectedVersions, actualVersions)
})
}
}

View File

@ -1,2 +0,0 @@
resources:
- version.yaml

View File

@ -1,7 +0,0 @@
---
apiVersion: airshipit.org/v1alpha1
kind: Testversion
metadata:
name: version-1
spec:
version: v0.0.1

View File

@ -1,2 +0,0 @@
resources:
- version.yaml

View File

@ -1,2 +0,0 @@
resources:
- version.yaml

View File

@ -1,7 +0,0 @@
---
apiVersion: airshipit.org/v1alpha1
kind: Testversion
metadata:
name: version-3
spec:
version: v0.0.3

View File

@ -1 +0,0 @@
dhcp-boot=tag:ipxe,http://${PROVISIONING_IP}:80/dualboot.ipxe

View File

@ -1,12 +0,0 @@
resources:
- resources.yaml
generatorOptions:
disableNameSuffixHash: true
## this contains a variable that matches a regexp for clusterctl variable subsitution
## check the regexp here https://github.com/kubernetes-sigs/cluster-api/blob/v0.3.5/cmd/clusterctl/client/repository/components.go#L55
configMapGenerator:
- name: ironic-config-files
files:
- dnsmasq.conf

View File

@ -1,17 +0,0 @@
## contains a namespace that should be idenitifed by components interface,
---
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
metadata:
name: repository-metadata
releaseSeries:
- major: 0
minor: 3
contract: v1alpha3
- major: 0
minor: 2
contract: v1alpha2
---
kind: Namespace
metadata:
name: newnamespace

View File

@ -1,29 +0,0 @@
## contains a namespace that should be idenitifed by components interface,
---
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
metadata:
name: repository-metadata
releaseSeries:
- major: 0
minor: 3
contract: v1alpha3
- major: 0
minor: 2
contract: v1alpha2
---
kind: Namespace
metadata:
name: newnamespace
---
apiVersion: v1
kind: Secret
metadata:
name: manager-bootstrap-credentials
namespace: system
type: Opaque
data:
subscription-id: ${AZURE_SUBSCRIPTION_ID_B64}
tenant-id: ${AZURE_TENANT_ID_B64}
client-id: ${AZURE_CLIENT_ID_B64}
client-secret: ${AZURE_CLIENT_SECRET_B64}

View File

@ -1,3 +0,0 @@
# Contains variables that should be substituted
resources:
- azure-resources.yaml

View File

@ -48,6 +48,15 @@ const (
// CRDKind is a kind for custom resource definition documents
CRDKind = "CustomResourceDefinition"
// ClusterctlContainerGroup defines Group for clustertctl container
ClusterctlContainerGroup = "airshipit.org"
// ClusterctlContainerVersion defines Version for clustertctl container
ClusterctlContainerVersion = "v1alpha1"
// ClusterctlContainerKind defines Kind for clustertctl container
ClusterctlContainerKind = "GenericContainer"
// ClusterctlContainerName defines Name for clustertctl container
ClusterctlContainerName = "clusterctl"
)
// KustomizationFile is used for kustomization file

View File

@ -202,3 +202,11 @@ func NewValidatorExecutorSelector() Selector {
func NewCRDSelector() Selector {
return NewSelector().ByKind(CRDKind)
}
// NewClusterctlContainerExecutorSelector returns selector to get executor documents for clusterctl container
func NewClusterctlContainerExecutorSelector() Selector {
return NewSelector().ByGvk(ClusterctlContainerGroup,
ClusterctlContainerVersion,
ClusterctlContainerKind).
ByName(ClusterctlContainerName)
}

View File

@ -23,7 +23,6 @@ import (
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/utils"
@ -237,7 +236,7 @@ func (b *Builder) fromClusterAPI(clusterName string, ref v1alpha1.KubeconfigSour
log.Debugf("Getting child kubeconfig from parent, parent context '%s', parent kubeconfig '%s'",
parentContext, f)
return FromSecret(b.client, &client.GetKubeconfigOptions{
return FromSecret(b.client, &v1alpha1.GetKubeconfigOptions{
Timeout: ref.Timeout,
ManagedClusterNamespace: ref.Namespace,
ManagedClusterName: ref.Name,

View File

@ -28,7 +28,6 @@ import (
"sigs.k8s.io/yaml"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/log"
@ -113,7 +112,7 @@ func FromAPIalphaV1(apiObj *v1alpha1.KubeConfig) KubeSourceFunc {
}
// FromSecret returns KubeSource type, uses client interface to kubernetes cluster
func FromSecret(c corev1.CoreV1Interface, o *client.GetKubeconfigOptions) KubeSourceFunc {
func FromSecret(c corev1.CoreV1Interface, o *v1alpha1.GetKubeconfigOptions) KubeSourceFunc {
return func() ([]byte, error) {
if o.ManagedClusterName == "" {
return nil, ErrClusterNameEmpty{}

View File

@ -35,7 +35,6 @@ import (
kustfs "sigs.k8s.io/kustomize/api/filesys"
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
@ -220,7 +219,7 @@ func (s *SecretMockInterface) Patch(_ string, _ types.PatchType, _ []byte, _ ...
func TestFromSecret(t *testing.T) {
tests := []struct {
name string
options *client.GetKubeconfigOptions
options *v1alpha1.GetKubeconfigOptions
getSecret *apiv1.Secret
getErr error
expectedData []byte
@ -228,30 +227,30 @@ func TestFromSecret(t *testing.T) {
}{
{
name: "empty cluster name",
options: &client.GetKubeconfigOptions{},
options: &v1alpha1.GetKubeconfigOptions{},
expectedErr: kubeconfig.ErrClusterNameEmpty{},
},
{
name: "multiple retries and error",
options: &client.GetKubeconfigOptions{ManagedClusterName: "cluster", Timeout: "1s"},
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster", Timeout: "1s"},
getErr: errors.New("error"),
expectedErr: wait.ErrWaitTimeout,
},
{
name: "empty secret object",
options: &client.GetKubeconfigOptions{ManagedClusterName: "cluster"},
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster"},
getSecret: &apiv1.Secret{},
expectedErr: kubeconfig.ErrMalformedKubeconfig{ClusterName: "cluster"},
},
{
name: "empty data value",
options: &client.GetKubeconfigOptions{ManagedClusterName: "cluster"},
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster"},
getSecret: &apiv1.Secret{Data: map[string][]byte{"value": {}}},
expectedErr: kubeconfig.ErrMalformedKubeconfig{ClusterName: "cluster"},
},
{
name: "successfully get kubeconfig",
options: &client.GetKubeconfigOptions{ManagedClusterName: "cluster"},
options: &v1alpha1.GetKubeconfigOptions{ManagedClusterName: "cluster"},
getSecret: &apiv1.Secret{Data: map[string][]byte{"value": []byte(testValidKubeconfig)}},
expectedData: []byte(testValidKubeconfig),
},

320
pkg/phase/executors/clusterctl.go Executable file → Normal file
View File

@ -18,11 +18,16 @@ import (
"bytes"
"fmt"
"io"
"net/url"
"os"
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/kyaml/yaml"
airshipv1 "opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
"opendev.org/airship/airshipctl/pkg/container"
"opendev.org/airship/airshipctl/pkg/document"
airerrors "opendev.org/airship/airshipctl/pkg/errors"
"opendev.org/airship/airshipctl/pkg/events"
@ -33,62 +38,236 @@ import (
"opendev.org/airship/airshipctl/pkg/phase/ifc"
)
const clusterAPIOverrides = "/workdir/.cluster-api/overrides"
var _ ifc.Executor = &ClusterctlExecutor{}
// ClusterctlExecutor phase executor
type ClusterctlExecutor struct {
clusterName string
targetPath string
client.Interface
clusterMap clustermap.ClusterMap
options *airshipv1.Clusterctl
kubecfg kubeconfig.Interface
execObj *airshipv1.GenericContainer
clientFunc container.ClientV1Alpha1FactoryFunc
cctlOpts *airshipv1.ClusterctlOptions
}
// NewClusterctlExecutor creates instance of 'clusterctl init' phase executor
var typeMap = map[string]string{
airshipv1.BootstrapProviderType: "bootstrap",
airshipv1.ControlPlaneProviderType: "control-plane",
airshipv1.InfrastructureProviderType: "infrastructure",
airshipv1.CoreProviderType: "core",
}
// NewClusterctlExecutor creates instance of 'clusterctl' phase executor
func NewClusterctlExecutor(cfg ifc.ExecutorConfig) (ifc.Executor, error) {
options := airshipv1.DefaultClusterctl()
if err := cfg.ExecutorDocument.ToAPIObject(options, airshipv1.Scheme); err != nil {
return nil, err
}
client, err := client.NewClient(cfg.TargetPath, log.DebugEnabled(), options)
cctlOpts := &airshipv1.ClusterctlOptions{
Components: map[string][]byte{},
}
if err := initRepoData(options, cctlOpts, cfg.TargetPath); err != nil {
return nil, err
}
doc, err := cfg.PhaseConfigBundle.SelectOne(document.NewClusterctlContainerExecutorSelector())
if err != nil {
return nil, err
}
apiObj := airshipv1.DefaultGenericContainer()
err = doc.ToAPIObject(apiObj, airshipv1.Scheme)
if err != nil {
return nil, err
}
clientFunc := container.NewClientV1Alpha1
if cfg.ContainerFunc != nil {
clientFunc = cfg.ContainerFunc
}
return &ClusterctlExecutor{
clusterName: cfg.ClusterName,
Interface: client,
options: options,
cctlOpts: cctlOpts,
kubecfg: cfg.KubeConfig,
clusterMap: cfg.ClusterMap,
targetPath: cfg.TargetPath,
execObj: apiObj,
clientFunc: clientFunc,
}, nil
}
func initRepoData(c *airshipv1.Clusterctl, o *airshipv1.ClusterctlOptions, targetPath string) error {
for _, prv := range c.Providers {
rURL, err := url.Parse(prv.URL)
if err != nil {
return err
}
if rURL.Scheme != "" || filepath.IsAbs(prv.URL) {
continue
}
componentDir := filepath.Join(clusterAPIOverrides,
fmt.Sprintf("%s-%s", typeMap[prv.Type], prv.Name), filepath.Base(prv.URL))
if prv.Type == airshipv1.CoreProviderType {
componentDir = filepath.Join(clusterAPIOverrides, prv.Name, filepath.Base(prv.URL))
}
kustomizePath := filepath.Join(targetPath, prv.URL)
log.Debugf("Building cluster-api provider component documents from kustomize path at '%s'", kustomizePath)
bundle, err := document.NewBundleByPath(kustomizePath)
if err != nil {
return err
}
doc, err := bundle.SelectOne(document.NewClusterctlMetadataSelector())
if err != nil {
return err
}
metadata, err := doc.AsYAML()
if err != nil {
return err
}
o.Components[filepath.Join(componentDir, "metadata.yaml")] = metadata
filteredBundle, err := bundle.SelectBundle(document.NewDeployToK8sSelector())
if err != nil {
return err
}
buffer := &bytes.Buffer{}
if err = filteredBundle.Write(buffer); err != nil {
return err
}
prv.URL = filepath.Join(componentDir, fmt.Sprintf("%s-components.yaml", typeMap[prv.Type]))
o.Components[prv.URL] = buffer.Bytes()
}
return nil
}
// Run clusterctl init as a phase runner
func (c *ClusterctlExecutor) Run(evtCh chan events.Event, opts ifc.RunOptions) {
defer close(evtCh)
if log.DebugEnabled() {
c.cctlOpts.CmdOptions = append(c.cctlOpts.CmdOptions, "-v5")
}
cctlConfig := map[string]interface{}{
"providers": c.options.Providers,
"images": c.options.ImageMetas,
}
for k, v := range c.options.AdditionalComponentVariables {
cctlConfig[k] = v
}
var err error
c.cctlOpts.Config, err = yaml.Marshal(cctlConfig)
if err != nil {
handleError(evtCh, err)
}
switch c.options.Action {
case airshipv1.Move:
c.move(opts, evtCh)
case airshipv1.Init:
c.init(opts, evtCh)
c.init(evtCh)
case airshipv1.Move:
c.move(opts.DryRun, evtCh)
default:
handleError(evtCh, errors.ErrUnknownExecutorAction{Action: string(c.options.Action), ExecutorName: "clusterctl"})
}
}
func (c *ClusterctlExecutor) move(opts ifc.RunOptions, evtCh chan events.Event) {
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveStart,
Message: "starting clusterctl move executor",
})
ns := c.options.MoveOptions.Namespace
func (c *ClusterctlExecutor) run() error {
opts, err := yaml.Marshal(c.cctlOpts)
if err != nil {
return err
}
c.execObj.Config = string(opts)
return c.clientFunc("", &bytes.Buffer{}, os.Stdout, c.execObj, c.targetPath).Run()
}
func (c *ClusterctlExecutor) getKubeconfig() (string, string, func(), error) {
kubeConfigFile, cleanup, err := c.kubecfg.GetFile()
if err != nil {
return "", "", nil, err
}
context, err := c.clusterMap.ClusterKubeconfigContext(c.clusterName)
if err != nil {
cleanup()
return "", "", nil, err
}
c.execObj.Spec.StorageMounts = append(c.execObj.Spec.StorageMounts, airshipv1.StorageMount{
MountType: "bind",
Src: kubeConfigFile,
DstPath: kubeConfigFile,
ReadWriteMode: false,
})
return kubeConfigFile, context, cleanup, nil
}
func (c *ClusterctlExecutor) init(evtCh chan events.Event) {
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitStart,
Message: "starting clusterctl init executor",
})
kubecfg, context, cleanup, err := c.getKubeconfig()
if err != nil {
handleError(evtCh, err)
return
}
defer cleanup()
c.cctlOpts.CmdOptions = append(c.cctlOpts.CmdOptions,
"init",
"--kubeconfig", kubecfg,
"--kubeconfig-context", context,
)
initMap := map[string]string{
typeMap[airshipv1.BootstrapProviderType]: c.options.InitOptions.BootstrapProviders,
typeMap[airshipv1.ControlPlaneProviderType]: c.options.InitOptions.ControlPlaneProviders,
typeMap[airshipv1.InfrastructureProviderType]: c.options.InitOptions.InfrastructureProviders,
typeMap[airshipv1.CoreProviderType]: c.options.InitOptions.CoreProvider,
}
for k, v := range initMap {
if v != "" {
c.cctlOpts.CmdOptions = append(c.cctlOpts.CmdOptions, fmt.Sprintf("--%s=%s", k, v))
}
}
if err = c.run(); err != nil {
handleError(evtCh, err)
return
}
eventMsg := "clusterctl init completed successfully"
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitEnd,
Message: eventMsg,
})
}
func (c *ClusterctlExecutor) move(dryRun bool, evtCh chan events.Event) {
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveStart,
Message: "starting clusterctl move executor",
})
kubecfg, context, cleanup, err := c.getKubeconfig()
if err != nil {
handleError(evtCh, err)
return
}
defer cleanup()
fromCluster, err := c.clusterMap.ParentCluster(c.clusterName)
if err != nil {
handleError(evtCh, err)
@ -99,78 +278,36 @@ func (c *ClusterctlExecutor) move(opts ifc.RunOptions, evtCh chan events.Event)
handleError(evtCh, err)
return
}
toContext, err := c.clusterMap.ClusterKubeconfigContext(c.clusterName)
if err != nil {
c.cctlOpts.CmdOptions = append(
c.cctlOpts.CmdOptions,
"move",
"--kubeconfig", kubecfg,
"--kubeconfig-context", fromContext,
"--to-kubeconfig", kubecfg,
"--to-kubeconfig-context", context,
"--namespace", c.options.MoveOptions.Namespace,
)
if dryRun {
c.cctlOpts.CmdOptions = append(
c.cctlOpts.CmdOptions,
"--dry-run",
)
}
if err = c.run(); err != nil {
handleError(evtCh, err)
return
}
log.Print("command 'clusterctl move' is going to be executed")
// TODO (kkalynovskyi) add more details to dry-run, for now if dry run is set we skip move command
if !opts.DryRun {
err = c.Move(kubeConfigFile, fromContext, kubeConfigFile, toContext, ns)
if err != nil {
handleError(evtCh, err)
return
}
}
eventMsg := "clusterctl move completed successfully"
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveEnd,
Message: "clusterctl move completed successfully",
})
}
func (c *ClusterctlExecutor) init(opts ifc.RunOptions, evtCh chan events.Event) {
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitStart,
Message: "starting clusterctl init executor",
})
kubeConfigFile, cleanup, err := c.kubecfg.GetFile()
if err != nil {
handleError(evtCh, err)
return
}
defer cleanup()
if opts.DryRun {
// TODO (dukov) add more details to dry-run
log.Print("command 'clusterctl init' is going to be executed")
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitEnd,
Message: "clusterctl init dry-run completed successfully",
})
return
}
context, err := c.clusterMap.ClusterKubeconfigContext(c.clusterName)
if err != nil {
handleError(evtCh, err)
return
}
eventMsg := "clusterctl init completed successfully"
// Use cluster name as context in kubeconfig file
err = c.Init(kubeConfigFile, context)
if err != nil && isAlreadyExistsError(err) {
// log the already existed/initialized error as warning and continue
eventMsg = fmt.Sprintf("WARNING: clusterctl is already initialized, received an error : %s", err.Error())
} else if err != nil {
handleError(evtCh, err)
return
}
evtCh <- events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitEnd,
Message: eventMsg,
})
}
func isAlreadyExistsError(err error) bool {
return strings.Contains(err.Error(), "there is already an instance")
}
// Validate executor configuration and documents
func (c *ClusterctlExecutor) Validate() error {
switch c.options.Action {
@ -190,33 +327,10 @@ func (c *ClusterctlExecutor) Validate() error {
// Render executor documents
func (c *ClusterctlExecutor) Render(w io.Writer, ro ifc.RenderOptions) error {
dataAll := bytes.NewBuffer([]byte{})
typeMap := map[string][]string{
string(client.BootstrapProviderType): c.options.InitOptions.BootstrapProviders,
string(client.ControlPlaneProviderType): c.options.InitOptions.ControlPlaneProviders,
string(client.InfrastructureProviderType): c.options.InitOptions.InfrastructureProviders,
string(client.CoreProviderType): (map[bool][]string{true: {c.options.InitOptions.CoreProvider},
false: {}})[c.options.InitOptions.CoreProvider != ""],
}
for prvType, prvList := range typeMap {
for _, prv := range prvList {
res := strings.Split(prv, ":")
if len(res) != 2 {
return errors.ErrUnableParseProvider{
Provider: prv,
ProviderType: prvType,
}
}
data, err := c.Interface.Render(client.RenderOptions{
ProviderName: res[0],
ProviderVersion: res[1],
ProviderType: prvType,
})
if err != nil {
return err
}
dataAll.Write(data)
dataAll.Write([]byte("\n---\n"))
dataAll := &bytes.Buffer{}
for path, data := range c.cctlOpts.Components {
if strings.Contains(path, "components.yaml") {
dataAll.Write(append(data, []byte("\n---\n")...))
}
}

326
pkg/phase/executors/clusterctl_test.go Executable file → Normal file
View File

@ -18,6 +18,7 @@ import (
"bytes"
goerrors "errors"
"fmt"
"io"
"testing"
"time"
@ -26,14 +27,13 @@ import (
"opendev.org/airship/airshipctl/pkg/api/v1alpha1"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/container"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/events"
"opendev.org/airship/airshipctl/pkg/fs"
"opendev.org/airship/airshipctl/pkg/k8s/kubeconfig"
"opendev.org/airship/airshipctl/pkg/phase/executors"
"opendev.org/airship/airshipctl/pkg/phase/executors/errors"
"opendev.org/airship/airshipctl/pkg/phase/ifc"
testfs "opendev.org/airship/airshipctl/testutil/fs"
)
var (
@ -52,8 +52,7 @@ init-options:
providers:
- name: "cluster-api"
type: "CoreProvider"
versions:
v0.3.2: functions/capi/infrastructure/v0.3.2`
url: functions/capi/v0.3.2`
executorConfigTmplGood = `
apiVersion: airshipit.org/v1alpha1
@ -73,72 +72,200 @@ move-options:
providers:
- name: "cluster-api"
type: "CoreProvider"
versions:
v0.3.3: manifests/function/capi/v0.3.3`
url: functions/capi/v0.3.2`
renderedDocs = `---
apiVersion: v1
kind: Namespace
metadata:
labels:
cluster.x-k8s.io/provider: cluster-api
clusterctl.cluster.x-k8s.io: ""
control-plane: controller-manager
name: version-two
...
`
krmExecDoc = `---
apiVersion: airshipit.org/v1alpha1
kind: GenericContainer
metadata:
name: clusterctl
labels:
airshipit.org/deploy-k8s: "false"
spec:
type: krm
image: localhost/clusterctl:latest
hostNetwork: true
`
)
func TestNewClusterctlExecutor(t *testing.T) {
sampleCfgDoc := executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init"))
testCases := []struct {
name string
expectedErr error
name string
targetPath string
phaseCfgBundle document.Bundle
execDoc document.Document
errContains string
}{
{
name: "New Clusterctl Executor",
name: "invalid executor document",
execDoc: executorDoc(t, `
apiVersion: test.org/v1alpha1
kind: testkind
metadata:
name: testname
`),
errContains: "no kind \"testkind\" is registered for version \"test.org/v1alpha1\"",
},
{
name: "broken kustomize entrypoint",
targetPath: "/not/exist",
execDoc: executorDoc(t, `
apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
name: clusterctl
providers:
- name: "cluster-api"
type: "CoreProvider"
url: functions/capi/v0.3.2"
`),
errContains: "no such file or directory",
},
{
name: "no metadata available",
targetPath: "./testdata",
execDoc: executorDoc(t, `
apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
name: clusterctl
providers:
- name: "cluster-api"
type: "CoreProvider"
url: functions/capi/v0.3.2/no-metadata
`),
errContains: "document filtered by selector " +
"[Group=\"clusterctl.cluster.x-k8s.io\", Version=\"v1alpha3\", Kind=\"Metadata\"] found no documents",
},
{
name: "no container executor available",
targetPath: "./testdata",
phaseCfgBundle: executorBundle(t, ""),
execDoc: executorDoc(t, `
apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
name: clusterctl
providers:
- name: "cluster-api"
type: "CoreProvider"
url: functions/capi/v0.3.2
`),
errContains: "document filtered by selector [Group=\"airshipit.org\", " +
"Version=\"v1alpha1\", Kind=\"GenericContainer\", Name=\"clusterctl\"] found no documents",
},
{
name: "successfully create executor",
targetPath: "./testdata",
phaseCfgBundle: executorBundle(t, krmExecDoc),
execDoc: executorDoc(t, `
apiVersion: airshipit.org/v1alpha1
kind: Clusterctl
metadata:
name: clusterctl
providers:
- name: "cluster-api"
type: "CoreProvider"
url: functions/capi/v0.3.2
`),
},
}
for _, test := range testCases {
tt := test
t.Run(tt.name, func(t *testing.T) {
_, actualErr := executors.NewClusterctlExecutor(ifc.ExecutorConfig{
ExecutorDocument: sampleCfgDoc,
executor, actualErr := executors.NewClusterctlExecutor(ifc.ExecutorConfig{
ExecutorDocument: tt.execDoc,
TargetPath: tt.targetPath,
PhaseConfigBundle: tt.phaseCfgBundle,
})
assert.Equal(t, tt.expectedErr, actualErr)
if tt.errContains != "" {
require.Nil(t, executor)
require.NotNil(t, actualErr)
require.Contains(t, actualErr.Error(), tt.errContains)
} else {
require.NoError(t, actualErr)
require.NotNil(t, executor)
}
})
}
}
var _ clustermap.ClusterMap = &ClusterMapMockInterface{}
type ClusterMapMockInterface struct {
MockClusterKubeconfigContext func(string) (string, error)
MockParentCluster func(string) (string, error)
}
func (c ClusterMapMockInterface) ValidateClusterMap() error {
panic("implement me")
}
func (c ClusterMapMockInterface) ParentCluster(s string) (string, error) {
return c.MockParentCluster(s)
}
func (c ClusterMapMockInterface) AllClusters() []string {
panic("implement me")
}
func (c ClusterMapMockInterface) ClusterKubeconfigContext(s string) (string, error) {
return c.MockClusterKubeconfigContext(s)
}
func (c ClusterMapMockInterface) Sources(_ string) ([]v1alpha1.KubeconfigSource, error) {
panic("implement me")
}
func (c ClusterMapMockInterface) Write(_ io.Writer, _ clustermap.WriteOptions) error {
panic("implement me")
}
var _ container.ClientV1Alpha1 = &MockClientFuncInterface{}
type MockClientFuncInterface struct {
MockRun func() error
}
func (c MockClientFuncInterface) Run() error {
return c.MockRun()
}
func TestClusterctlExecutorRun(t *testing.T) {
errTmpFile := goerrors.New("TmpFile error")
errCtx := goerrors.New("context error")
errParent := goerrors.New("parent cluster error")
testCases := []struct {
name string
cfgDoc document.Document
fs fs.FileSystem
bundlePath string
kubecfg kubeconfig.Interface
expectedEvt []events.Event
clusterMap clustermap.ClusterMap
clientFunc container.ClientV1Alpha1FactoryFunc
}{
{
name: "Error unknown action",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "someAction")),
bundlePath: "testdata/executor_init",
name: "Error unknown action",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "someAction")),
expectedEvt: []events.Event{
wrapError(errors.ErrUnknownExecutorAction{Action: "someAction", ExecutorName: "clusterctl"}),
},
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
},
{
name: "Error temporary file",
name: "Failed get kubeconfig file - init",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init")),
fs: testfs.MockFileSystem{
MockTempFile: func(string, string) (fs.File, error) {
return nil, errTmpFile
},
},
bundlePath: "testdata/executor_init",
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", nil, errTmpFile
}},
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitStart,
@ -147,21 +274,86 @@ func TestClusterctlExecutorRun(t *testing.T) {
},
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
},
{
name: "Failed get kubeconfig file - move",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", nil, errTmpFile
}},
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveStart,
}),
wrapError(errTmpFile),
},
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
},
{
name: "Failed get kubeconfig context - init",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init")),
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", func() {}, nil
}},
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitStart,
}),
wrapError(errCtx),
},
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
return "", errCtx
}},
},
{
name: "Failed get kubeconfig context - move",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", func() {}, nil
}},
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveStart,
}),
wrapError(errCtx),
},
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
return "", errCtx
}},
},
{
name: "Failed get parent cluster",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", func() {}, nil
}},
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveStart,
}),
wrapError(errParent),
},
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
return "ctx", nil
},
MockParentCluster: func(s string) (string, error) {
return "", errParent
}},
},
{
name: "Regular Run init",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "init")),
fs: testfs.MockFileSystem{
MockTempFile: func(string, string) (fs.File, error) {
return testfs.TestFile{
MockName: func() string { return "filename" },
MockWrite: func([]byte) (int, error) { return 0, nil },
MockClose: func() error { return nil },
}, nil
},
MockRemoveAll: func() error { return nil },
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", func() {}, nil
}},
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
return "cluster", nil
}},
clientFunc: func(_ string, _ io.Reader, _ io.Writer,
_ *v1alpha1.GenericContainer, _ string) container.ClientV1Alpha1 {
return MockClientFuncInterface{MockRun: func() error {
return nil
}}
},
bundlePath: "testdata/executor_init",
clusterMap: clustermap.NewClusterMap(v1alpha1.DefaultClusterMap()),
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlInitStart,
@ -171,27 +363,52 @@ func TestClusterctlExecutorRun(t *testing.T) {
}),
},
},
// TODO add move tests here
{
name: "Regular Run move",
cfgDoc: executorDoc(t, fmt.Sprintf(executorConfigTmplGood, "move")),
kubecfg: fakeKubeConfig{getFile: func() (string, kubeconfig.Cleanup, error) {
return "", func() {}, nil
}},
clusterMap: ClusterMapMockInterface{MockClusterKubeconfigContext: func(s string) (string, error) {
return "cluster", nil
},
MockParentCluster: func(s string) (string, error) {
return "parentCluster", nil
}},
clientFunc: func(_ string, _ io.Reader, _ io.Writer,
_ *v1alpha1.GenericContainer, _ string) container.ClientV1Alpha1 {
return MockClientFuncInterface{MockRun: func() error {
return nil
}}
},
expectedEvt: []events.Event{
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveStart,
}),
events.NewEvent().WithClusterctlEvent(events.ClusterctlEvent{
Operation: events.ClusterctlMoveEnd,
}),
},
},
}
for _, test := range testCases {
tt := test
t.Run(tt.name, func(t *testing.T) {
kubeCfg := kubeconfig.NewKubeConfig(
kubeconfig.FromByte([]byte("someKubeConfig")),
kubeconfig.InjectFileSystem(tt.fs),
)
executor, err := executors.NewClusterctlExecutor(
ifc.ExecutorConfig{
ExecutorDocument: tt.cfgDoc,
KubeConfig: kubeCfg,
ClusterMap: tt.clusterMap,
TargetPath: "testdata",
PhaseConfigBundle: executorBundle(t, krmExecDoc),
ExecutorDocument: tt.cfgDoc,
KubeConfig: tt.kubecfg,
ClusterMap: tt.clusterMap,
ContainerFunc: tt.clientFunc,
})
require.NoError(t, err)
ch := make(chan events.Event)
go executor.Run(ch, ifc.RunOptions{DryRun: true})
var actualEvt []events.Event
for evt := range ch {
// Skip timmestamp for comparison
// Skip timestamp for comparison
evt.Timestamp = time.Time{}
if evt.Type == events.ClusterctlType {
// Set message to empty string, so it's not compared
@ -200,7 +417,7 @@ func TestClusterctlExecutorRun(t *testing.T) {
actualEvt = append(actualEvt, evt)
}
for i := range tt.expectedEvt {
// Skip timmestamp for comparison
// Skip timestamp for comparison
tt.expectedEvt[i].Timestamp = time.Time{}
}
assert.Equal(t, tt.expectedEvt, actualEvt)
@ -237,10 +454,12 @@ func TestClusterctlExecutorValidate(t *testing.T) {
for _, test := range testCases {
tt := test
t.Run(tt.name, func(t *testing.T) {
sampleCfgDoc := executorDoc(t, fmt.Sprintf(test.executorConfigTmpl, test.actionType, test.actionType))
sampleCfgDoc := executorDoc(t, fmt.Sprintf(test.executorConfigTmpl, test.actionType))
executor, err := executors.NewClusterctlExecutor(
ifc.ExecutorConfig{
ExecutorDocument: sampleCfgDoc,
TargetPath: "testdata",
ExecutorDocument: sampleCfgDoc,
PhaseConfigBundle: executorBundle(t, krmExecDoc),
})
require.NoError(t, err)
err = executor.Validate()
@ -258,8 +477,9 @@ func TestClusterctlExecutorRender(t *testing.T) {
sampleCfgDoc := executorDoc(t, fmt.Sprintf(executorConfigTmpl, "init"))
executor, err := executors.NewClusterctlExecutor(
ifc.ExecutorConfig{
TargetPath: "../../clusterctl/client/testdata",
ExecutorDocument: sampleCfgDoc,
TargetPath: "testdata",
ExecutorDocument: sampleCfgDoc,
PhaseConfigBundle: executorBundle(t, krmExecDoc),
})
require.NoError(t, err)
actualOut := &bytes.Buffer{}

0
pkg/phase/executors/common.go Executable file → Normal file
View File

8
pkg/phase/executors/common_test.go Executable file → Normal file
View File

@ -96,6 +96,14 @@ func executorDoc(t *testing.T, s string) document.Document {
return doc
}
// executorBundle converts string to bundle object
func executorBundle(t *testing.T, s string) document.Bundle {
b, err := document.NewBundleFromBytes([]byte(s))
require.NoError(t, err)
require.NotNil(t, b)
return b
}
// TODO replace this test bundle factory with one that uses bundle mock
func testBundleFactory() document.BundleFactoryFunc {
return func() (document.Bundle, error) {

0
pkg/phase/executors/errors/errors.go Executable file → Normal file
View File

View File

@ -0,0 +1,3 @@
resources:
- version.yaml
- metadata.yaml

View File

@ -0,0 +1,11 @@
---
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
metadata:
name: repository-metadata
labels:
airshipit.org/deploy-k8s: "false"
releaseSeries:
- major: 0
minor: 3
contract: v1alpha3

View File

@ -19,6 +19,7 @@ import (
"time"
"opendev.org/airship/airshipctl/pkg/cluster/clustermap"
"opendev.org/airship/airshipctl/pkg/container"
"opendev.org/airship/airshipctl/pkg/document"
"opendev.org/airship/airshipctl/pkg/events"
inventoryifc "opendev.org/airship/airshipctl/pkg/inventory/ifc"
@ -70,4 +71,5 @@ type ExecutorConfig struct {
BundleFactory document.BundleFactoryFunc
PhaseConfigBundle document.Bundle
Inventory inventoryifc.Inventory
ContainerFunc container.ClientV1Alpha1FactoryFunc
}

View File

@ -1,68 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package clusterctl
import (
"fmt"
"github.com/stretchr/testify/mock"
"opendev.org/airship/airshipctl/pkg/clusterctl/client"
)
var _ client.Interface = &MockInterface{}
// MockInterface provides mock interface for clusterctl
type MockInterface struct {
mock.Mock
}
// Init to be implemented
func (m *MockInterface) Init(kubeconfigPath, kubeconfigContext string) error {
return nil
}
// Move to be implemented
func (m *MockInterface) Move(fkp, fkc, tkp, tkc, namespace string) error {
return nil
}
// Render to be implemented
func (m *MockInterface) Render(client.RenderOptions) ([]byte, error) {
return nil, nil
}
// GetKubeconfig allows to control exepected input to the function and check expected output
// example usage:
// c := &clusterctl.MockInterface{
// Mock: mock.Mock{},
// }
// c.On("GetKubeconfig").Once().Return(&client.GetKubeconfigOptions{
// ParentKubeconfigPath: filepath.Join("testdata", kubeconfig.KubeconfigPrefix),
// ParentKubeconfigContext: "dummy_cluster",
// ManagedClusterNamespace: clustermap.DefaultClusterAPIObjNamespace,
// ManagedClusterName: childCluster,
// }, "kubeconfig data", nil)
// first argument in return function is what you expect as input
// second argument is resulting expected string
// third is resulting error
func (m *MockInterface) GetKubeconfig(options *client.GetKubeconfigOptions) (string, error) {
args := m.Called(options)
expectedResult, ok := args.Get(0).(string)
if !ok {
return "", fmt.Errorf("wrong input")
}
return expectedResult, args.Error(1)
}