diff --git a/Makefile b/Makefile
index d5e22c07e..671bd4c12 100644
--- a/Makefile
+++ b/Makefile
@@ -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) \
diff --git a/go.mod b/go.mod
index 0f658d2bd..899d706b4 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index 19afe36e7..9612e78f5 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/krm-functions/clusterctl/Dockerfile b/krm-functions/clusterctl/Dockerfile
new file mode 100644
index 000000000..63e7cacfb
--- /dev/null
+++ b/krm-functions/clusterctl/Dockerfile
@@ -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"]
diff --git a/krm-functions/clusterctl/Makefile b/krm-functions/clusterctl/Makefile
new file mode 100644
index 000000000..017c06ba4
--- /dev/null
+++ b/krm-functions/clusterctl/Makefile
@@ -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
diff --git a/krm-functions/clusterctl/README.md b/krm-functions/clusterctl/README.md
new file mode 100644
index 000000000..6a29e228d
--- /dev/null
+++ b/krm-functions/clusterctl/README.md
@@ -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.
diff --git a/krm-functions/clusterctl/certs/README.md b/krm-functions/clusterctl/certs/README.md
new file mode 100644
index 000000000..7d04f7ec4
--- /dev/null
+++ b/krm-functions/clusterctl/certs/README.md
@@ -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.
\ No newline at end of file
diff --git a/krm-functions/clusterctl/image/go.mod b/krm-functions/clusterctl/image/go.mod
new file mode 100644
index 000000000..32f8c42e6
--- /dev/null
+++ b/krm-functions/clusterctl/image/go.mod
@@ -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
diff --git a/krm-functions/clusterctl/image/go.sum b/krm-functions/clusterctl/image/go.sum
new file mode 100644
index 000000000..632ae1c2e
--- /dev/null
+++ b/krm-functions/clusterctl/image/go.sum
@@ -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=
diff --git a/krm-functions/clusterctl/image/main.go b/krm-functions/clusterctl/image/main.go
new file mode 100644
index 000000000..172b2bd23
--- /dev/null
+++ b/krm-functions/clusterctl/image/main.go
@@ -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)
+	}
+}
diff --git a/krm-functions/clusterctl/local-resource/example-use.yaml b/krm-functions/clusterctl/local-resource/example-use.yaml
new file mode 100755
index 000000000..f4e1244ad
--- /dev/null
+++ b/krm-functions/clusterctl/local-resource/example-use.yaml
@@ -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
diff --git a/krm-functions/kubeval-validator/Makefile b/krm-functions/kubeval-validator/Makefile
index 97325850d..0e87cee24 100644
--- a/krm-functions/kubeval-validator/Makefile
+++ b/krm-functions/kubeval-validator/Makefile
@@ -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
diff --git a/manifests/function/cabpk/v0.3.7/data/kustomization.yaml b/manifests/function/cabpk/v0.3.7/data/kustomization.yaml
new file mode 100644
index 000000000..65d5b6a21
--- /dev/null
+++ b/manifests/function/cabpk/v0.3.7/data/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+  - metadata.yaml
diff --git a/pkg/clusterctl/implementations/testdata/functions/2/version.yaml b/manifests/function/cabpk/v0.3.7/data/metadata.yaml
similarity index 52%
rename from pkg/clusterctl/implementations/testdata/functions/2/version.yaml
rename to manifests/function/cabpk/v0.3.7/data/metadata.yaml
index 2f73e855b..8afbc1921 100644
--- a/pkg/clusterctl/implementations/testdata/functions/2/version.yaml
+++ b/manifests/function/cabpk/v0.3.7/data/metadata.yaml
@@ -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
\ No newline at end of file
diff --git a/manifests/function/cabpk/v0.3.7/kustomization.yaml b/manifests/function/cabpk/v0.3.7/kustomization.yaml
index f211307cf..63acf08c4 100644
--- a/manifests/function/cabpk/v0.3.7/kustomization.yaml
+++ b/manifests/function/cabpk/v0.3.7/kustomization.yaml
@@ -5,6 +5,7 @@ commonLabels:
 
 bases:
 - crd
+- data
 - default
 - webhook
 
diff --git a/manifests/function/cacpk/v0.3.7/data/kustomization.yaml b/manifests/function/cacpk/v0.3.7/data/kustomization.yaml
new file mode 100644
index 000000000..65d5b6a21
--- /dev/null
+++ b/manifests/function/cacpk/v0.3.7/data/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+  - metadata.yaml
diff --git a/manifests/function/cacpk/v0.3.7/data/metadata.yaml b/manifests/function/cacpk/v0.3.7/data/metadata.yaml
new file mode 100644
index 000000000..8afbc1921
--- /dev/null
+++ b/manifests/function/cacpk/v0.3.7/data/metadata.yaml
@@ -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
diff --git a/manifests/function/cacpk/v0.3.7/kustomization.yaml b/manifests/function/cacpk/v0.3.7/kustomization.yaml
index 15967b1c0..f9bdbeb74 100644
--- a/manifests/function/cacpk/v0.3.7/kustomization.yaml
+++ b/manifests/function/cacpk/v0.3.7/kustomization.yaml
@@ -5,6 +5,7 @@ commonLabels:
 
 bases:
 - crd
+- data
 - default
 - webhook
 
diff --git a/manifests/function/capi/v0.3.7/data/kustomization.yaml b/manifests/function/capi/v0.3.7/data/kustomization.yaml
new file mode 100644
index 000000000..65d5b6a21
--- /dev/null
+++ b/manifests/function/capi/v0.3.7/data/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+  - metadata.yaml
diff --git a/manifests/function/capi/v0.3.7/data/metadata.yaml b/manifests/function/capi/v0.3.7/data/metadata.yaml
new file mode 100644
index 000000000..8afbc1921
--- /dev/null
+++ b/manifests/function/capi/v0.3.7/data/metadata.yaml
@@ -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
diff --git a/manifests/function/capi/v0.3.7/kustomization.yaml b/manifests/function/capi/v0.3.7/kustomization.yaml
index 94df3ce22..75d349a5f 100644
--- a/manifests/function/capi/v0.3.7/kustomization.yaml
+++ b/manifests/function/capi/v0.3.7/kustomization.yaml
@@ -5,6 +5,7 @@ commonLabels:
 
 bases:
 - crd
+- data
 - webhook
 - default
 
diff --git a/manifests/function/clusterctl/clusterctl.yaml b/manifests/function/clusterctl/clusterctl.yaml
index 8542c2e45..0c01307f6 100644
--- a/manifests/function/clusterctl/clusterctl.yaml
+++ b/manifests/function/clusterctl/clusterctl.yaml
@@ -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
diff --git a/manifests/phases/executors.yaml b/manifests/phases/executors.yaml
index 35797b3c1..b3fa1aff9 100644
--- a/manifests/phases/executors.yaml
+++ b/manifests/phases/executors.yaml
@@ -529,3 +529,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
diff --git a/pkg/api/v1alpha1/clusterctl_types.go b/pkg/api/v1alpha1/clusterctl_types.go
index 473ee0152..777623502 100644
--- a/pkg/api/v1alpha1/clusterctl_types.go
+++ b/pkg/api/v1alpha1/clusterctl_types.go
@@ -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
+}
diff --git a/pkg/api/v1alpha1/clusterctl_types_test.go b/pkg/api/v1alpha1/clusterctl_types_test.go
deleted file mode 100644
index d0282fc8e..000000000
--- a/pkg/api/v1alpha1/clusterctl_types_test.go
+++ /dev/null
@@ -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)
-		})
-	}
-}
diff --git a/pkg/api/v1alpha1/zz_generated.deepcopy.go b/pkg/api/v1alpha1/zz_generated.deepcopy.go
index 500b8a525..6c82066c3 100644
--- a/pkg/api/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/api/v1alpha1/zz_generated.deepcopy.go
@@ -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.
diff --git a/pkg/clusterctl/client/client.go b/pkg/clusterctl/client/client.go
deleted file mode 100644
index 29d3edf32..000000000
--- a/pkg/clusterctl/client/client.go
+++ /dev/null
@@ -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()
-}
diff --git a/pkg/clusterctl/client/client_test.go b/pkg/clusterctl/client/client_test.go
deleted file mode 100644
index c6ddd1936..000000000
--- a/pkg/clusterctl/client/client_test.go
+++ /dev/null
@@ -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)
-}
diff --git a/pkg/clusterctl/client/errors.go b/pkg/clusterctl/client/errors.go
deleted file mode 100644
index ccd6d7431..000000000
--- a/pkg/clusterctl/client/errors.go
+++ /dev/null
@@ -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)
-}
diff --git a/pkg/clusterctl/client/factory.go b/pkg/clusterctl/client/factory.go
deleted file mode 100644
index d2e44a997..000000000
--- a/pkg/clusterctl/client/factory.go
+++ /dev/null
@@ -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)
-}
diff --git a/pkg/clusterctl/client/factory_test.go b/pkg/clusterctl/client/factory_test.go
deleted file mode 100644
index 425de9bd1..000000000
--- a/pkg/clusterctl/client/factory_test.go
+++ /dev/null
@@ -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)
-		})
-	}
-}
diff --git a/pkg/clusterctl/client/move.go b/pkg/clusterctl/client/move.go
deleted file mode 100644
index 40a47539e..000000000
--- a/pkg/clusterctl/client/move.go
+++ /dev/null
@@ -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
-}
diff --git a/pkg/clusterctl/client/testdata/executor_init/kubeconfig.yaml b/pkg/clusterctl/client/testdata/executor_init/kubeconfig.yaml
deleted file mode 100644
index fccb7aa59..000000000
--- a/pkg/clusterctl/client/testdata/executor_init/kubeconfig.yaml
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/pkg/clusterctl/client/testdata/executor_init/kustomization.yaml b/pkg/clusterctl/client/testdata/executor_init/kustomization.yaml
deleted file mode 100644
index 64b49bda4..000000000
--- a/pkg/clusterctl/client/testdata/executor_init/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
-  - kubeconfig.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/client/testdata/executor_move/kubeconfig.yaml b/pkg/clusterctl/client/testdata/executor_move/kubeconfig.yaml
deleted file mode 100644
index fccb7aa59..000000000
--- a/pkg/clusterctl/client/testdata/executor_move/kubeconfig.yaml
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/pkg/clusterctl/client/testdata/executor_move/kustomization.yaml b/pkg/clusterctl/client/testdata/executor_move/kustomization.yaml
deleted file mode 100644
index 64b49bda4..000000000
--- a/pkg/clusterctl/client/testdata/executor_move/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
-  - kubeconfig.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.1/kustomization.yaml b/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.1/kustomization.yaml
deleted file mode 100644
index 3ee92e092..000000000
--- a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.1/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
- - version.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.1/version.yaml b/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.1/version.yaml
deleted file mode 100644
index 8ed26986a..000000000
--- a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.1/version.yaml
+++ /dev/null
@@ -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
diff --git a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.2/kustomization.yaml b/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.2/kustomization.yaml
deleted file mode 100644
index 3ee92e092..000000000
--- a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.2/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
- - version.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/client/testdata/metadata.yaml b/pkg/clusterctl/client/testdata/metadata.yaml
deleted file mode 100644
index d88db8f8c..000000000
--- a/pkg/clusterctl/client/testdata/metadata.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-phase:
-  path: executor_move
\ No newline at end of file
diff --git a/pkg/clusterctl/implementations/components_client.go b/pkg/clusterctl/implementations/components_client.go
deleted file mode 100644
index 87bcb4641..000000000
--- a/pkg/clusterctl/implementations/components_client.go
+++ /dev/null
@@ -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)
-}
diff --git a/pkg/clusterctl/implementations/errors.go b/pkg/clusterctl/implementations/errors.go
deleted file mode 100644
index f82a6532b..000000000
--- a/pkg/clusterctl/implementations/errors.go
+++ /dev/null
@@ -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)
-}
diff --git a/pkg/clusterctl/implementations/reader.go b/pkg/clusterctl/implementations/reader.go
deleted file mode 100644
index 324587da8..000000000
--- a/pkg/clusterctl/implementations/reader.go
+++ /dev/null
@@ -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
-}
diff --git a/pkg/clusterctl/implementations/reader_test.go b/pkg/clusterctl/implementations/reader_test.go
deleted file mode 100644
index df3064dcc..000000000
--- a/pkg/clusterctl/implementations/reader_test.go
+++ /dev/null
@@ -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"))
-}
diff --git a/pkg/clusterctl/implementations/repository.go b/pkg/clusterctl/implementations/repository.go
deleted file mode 100644
index cf2bb68e1..000000000
--- a/pkg/clusterctl/implementations/repository.go
+++ /dev/null
@@ -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
-}
diff --git a/pkg/clusterctl/implementations/repository_client.go b/pkg/clusterctl/implementations/repository_client.go
deleted file mode 100644
index 06474dc73..000000000
--- a/pkg/clusterctl/implementations/repository_client.go
+++ /dev/null
@@ -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,
-	}
-}
diff --git a/pkg/clusterctl/implementations/repository_client_test.go b/pkg/clusterctl/implementations/repository_client_test.go
deleted file mode 100644
index d51e66d5a..000000000
--- a/pkg/clusterctl/implementations/repository_client_test.go
+++ /dev/null
@@ -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",
-	}
-}
diff --git a/pkg/clusterctl/implementations/repository_test.go b/pkg/clusterctl/implementations/repository_test.go
deleted file mode 100644
index f474b060f..000000000
--- a/pkg/clusterctl/implementations/repository_test.go
+++ /dev/null
@@ -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)
-		})
-	}
-}
diff --git a/pkg/clusterctl/implementations/testdata/functions/1/kustomization.yaml b/pkg/clusterctl/implementations/testdata/functions/1/kustomization.yaml
deleted file mode 100644
index 3ee92e092..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/1/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
- - version.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/implementations/testdata/functions/1/version.yaml b/pkg/clusterctl/implementations/testdata/functions/1/version.yaml
deleted file mode 100644
index dc76bf228..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/1/version.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-apiVersion: airshipit.org/v1alpha1
-kind: Testversion
-metadata:
-  name: version-1
-spec:
-  version: v0.0.1
diff --git a/pkg/clusterctl/implementations/testdata/functions/2/kustomization.yaml b/pkg/clusterctl/implementations/testdata/functions/2/kustomization.yaml
deleted file mode 100644
index 3ee92e092..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/2/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
- - version.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/implementations/testdata/functions/3/kustomization.yaml b/pkg/clusterctl/implementations/testdata/functions/3/kustomization.yaml
deleted file mode 100644
index 3ee92e092..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/3/kustomization.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-resources:
- - version.yaml
\ No newline at end of file
diff --git a/pkg/clusterctl/implementations/testdata/functions/3/version.yaml b/pkg/clusterctl/implementations/testdata/functions/3/version.yaml
deleted file mode 100644
index f42d675a6..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/3/version.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-apiVersion: airshipit.org/v1alpha1
-kind: Testversion
-metadata:
-  name: version-3
-spec:
-  version: v0.0.3
diff --git a/pkg/clusterctl/implementations/testdata/functions/4/dnsmasq.conf b/pkg/clusterctl/implementations/testdata/functions/4/dnsmasq.conf
deleted file mode 100644
index 455931b41..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/4/dnsmasq.conf
+++ /dev/null
@@ -1 +0,0 @@
-dhcp-boot=tag:ipxe,http://${PROVISIONING_IP}:80/dualboot.ipxe
diff --git a/pkg/clusterctl/implementations/testdata/functions/4/kustomization.yaml b/pkg/clusterctl/implementations/testdata/functions/4/kustomization.yaml
deleted file mode 100644
index 983f1851a..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/4/kustomization.yaml
+++ /dev/null
@@ -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
diff --git a/pkg/clusterctl/implementations/testdata/functions/4/resources.yaml b/pkg/clusterctl/implementations/testdata/functions/4/resources.yaml
deleted file mode 100644
index 360417576..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/4/resources.yaml
+++ /dev/null
@@ -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
diff --git a/pkg/clusterctl/implementations/testdata/functions/5/azure-resources.yaml b/pkg/clusterctl/implementations/testdata/functions/5/azure-resources.yaml
deleted file mode 100644
index 9e6934326..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/5/azure-resources.yaml
+++ /dev/null
@@ -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}
\ No newline at end of file
diff --git a/pkg/clusterctl/implementations/testdata/functions/5/kustomization.yaml b/pkg/clusterctl/implementations/testdata/functions/5/kustomization.yaml
deleted file mode 100644
index 35440e624..000000000
--- a/pkg/clusterctl/implementations/testdata/functions/5/kustomization.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-# Contains variables that should be substituted
-resources:
- - azure-resources.yaml
diff --git a/pkg/document/constants.go b/pkg/document/constants.go
index 81014c9f4..edfff6d38 100644
--- a/pkg/document/constants.go
+++ b/pkg/document/constants.go
@@ -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
diff --git a/pkg/document/selectors.go b/pkg/document/selectors.go
index ba7814711..c9e1b62ef 100644
--- a/pkg/document/selectors.go
+++ b/pkg/document/selectors.go
@@ -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)
+}
diff --git a/pkg/k8s/kubeconfig/builder.go b/pkg/k8s/kubeconfig/builder.go
index d47e6e46c..f5a7f2cbc 100644
--- a/pkg/k8s/kubeconfig/builder.go
+++ b/pkg/k8s/kubeconfig/builder.go
@@ -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,
diff --git a/pkg/k8s/kubeconfig/kubeconfig.go b/pkg/k8s/kubeconfig/kubeconfig.go
index a2e5df1b1..cda64cc7b 100644
--- a/pkg/k8s/kubeconfig/kubeconfig.go
+++ b/pkg/k8s/kubeconfig/kubeconfig.go
@@ -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{}
diff --git a/pkg/k8s/kubeconfig/kubeconfig_test.go b/pkg/k8s/kubeconfig/kubeconfig_test.go
index c183007dc..1bfba5958 100644
--- a/pkg/k8s/kubeconfig/kubeconfig_test.go
+++ b/pkg/k8s/kubeconfig/kubeconfig_test.go
@@ -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),
 		},
diff --git a/pkg/phase/executors/clusterctl.go b/pkg/phase/executors/clusterctl.go
old mode 100755
new mode 100644
index 1a8e0464b..ca41e8343
--- a/pkg/phase/executors/clusterctl.go
+++ b/pkg/phase/executors/clusterctl.go
@@ -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")...))
 		}
 	}
 
diff --git a/pkg/phase/executors/clusterctl_test.go b/pkg/phase/executors/clusterctl_test.go
old mode 100755
new mode 100644
index 9146c8c37..8a038e29e
--- a/pkg/phase/executors/clusterctl_test.go
+++ b/pkg/phase/executors/clusterctl_test.go
@@ -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{}
diff --git a/pkg/phase/executors/common.go b/pkg/phase/executors/common.go
old mode 100755
new mode 100644
diff --git a/pkg/phase/executors/common_test.go b/pkg/phase/executors/common_test.go
old mode 100755
new mode 100644
index 076286815..876672a14
--- a/pkg/phase/executors/common_test.go
+++ b/pkg/phase/executors/common_test.go
@@ -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) {
diff --git a/pkg/phase/executors/errors/errors.go b/pkg/phase/executors/errors/errors.go
old mode 100755
new mode 100644
diff --git a/pkg/phase/executors/testdata/functions/capi/v0.3.2/kustomization.yaml b/pkg/phase/executors/testdata/functions/capi/v0.3.2/kustomization.yaml
new file mode 100644
index 000000000..f37e47ee0
--- /dev/null
+++ b/pkg/phase/executors/testdata/functions/capi/v0.3.2/kustomization.yaml
@@ -0,0 +1,3 @@
+resources:
+  - version.yaml
+  - metadata.yaml
diff --git a/pkg/phase/executors/testdata/functions/capi/v0.3.2/metadata.yaml b/pkg/phase/executors/testdata/functions/capi/v0.3.2/metadata.yaml
new file mode 100644
index 000000000..8afbc1921
--- /dev/null
+++ b/pkg/phase/executors/testdata/functions/capi/v0.3.2/metadata.yaml
@@ -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
diff --git a/pkg/clusterctl/client/testdata/executor_init_empty/kustomization.yaml b/pkg/phase/executors/testdata/functions/capi/v0.3.2/no-metadata/kustomization.yaml
similarity index 100%
rename from pkg/clusterctl/client/testdata/executor_init_empty/kustomization.yaml
rename to pkg/phase/executors/testdata/functions/capi/v0.3.2/no-metadata/kustomization.yaml
diff --git a/pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.2/version.yaml b/pkg/phase/executors/testdata/functions/capi/v0.3.2/version.yaml
similarity index 100%
rename from pkg/clusterctl/client/testdata/functions/capi/infrastructure/v0.3.2/version.yaml
rename to pkg/phase/executors/testdata/functions/capi/v0.3.2/version.yaml
diff --git a/pkg/phase/ifc/executor.go b/pkg/phase/ifc/executor.go
index 0288d9edb..b8ce92b20 100644
--- a/pkg/phase/ifc/executor.go
+++ b/pkg/phase/ifc/executor.go
@@ -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
 }
diff --git a/testutil/clusterctl/client.go b/testutil/clusterctl/client.go
deleted file mode 100644
index 6c47b768c..000000000
--- a/testutil/clusterctl/client.go
+++ /dev/null
@@ -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)
-}