diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..de7f780 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +BUILD=`date +%FT%T%z` + +LDFLAGS=-ldflags "-X main.buildDate=${BUILD}" +LDFLAGS_STATIC=-ldflags "-s -X main.buildDate=${BUILD}" + +.PHONY: deps build dist + +test: + go test git.martyn.berlin/martyn/helm-import/cmd + +build: + go build cmd ${LDFLAGS} + +deps: + GO111MODULE=on go mod download + +dist: + mkdir -p bin || true + cd cmd && go build ${LDFLAGS_STATIC} -installsuffix cgo -o ../bin/helm-import . diff --git a/build/ci/drone.yml b/build/ci/drone.yml new file mode 100755 index 0000000..3ddd5d8 --- /dev/null +++ b/build/ci/drone.yml @@ -0,0 +1,58 @@ +kind: pipeline +type: docker +name: linux-amd64-taggedver + +platform: + arch: amd64 + os: linux + +steps: +- name: build + image: golang + commands: + - pwd + - mkdir -p /go/src/git.martyn.berlin/martyn + - ln -s /drone/src /go/src/git.martyn.berlin/martyn/helm-import + - cd /go/src/git.martyn.berlin/martyn/helm-import + - make deps + - make dist + +- name: publish + image: plugins/docker:18 + settings: + auto_tag: true + auto_tag_suffix: linux-amd64 + dockerfile: build/package/Dockerfile + repo: imartyn/helm-import + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + - push + - tag + +trigger: + ref: + - refs/tags/v* + +--- +kind: pipeline +type: docker +name: linux-amd64-devel-main + +platform: + arch: amd64 + os: linux + +steps: +- name: build + image: golang + commands: + - pwd + - mkdir -p /go/src/git.martyn.berlin/martyn + - ln -s /drone/src /go/src/git.martyn.berlin/martyn/helm-import + - cd /go/src/git.martyn.berlin/martyn/helm-import + - make deps + - make dist diff --git a/build/package/Dockerfile b/build/package/Dockerfile new file mode 100755 index 0000000..a5a0b8f --- /dev/null +++ b/build/package/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1.14.12-alpine3.12 AS libjq +RUN apk --no-cache add curl ca-certificates alpine-sdk && \ + cd /tmp ; curl -L -fsS https://github.com/stedolan/jq/releases/download/jq-1.5/jq-1.5.tar.gz | tar -xz && \ + cd jq-1.5 && \ + ./configure --disable-maintainer-mode --prefix=/usr && \ + make install-libLTLIBRARIES install-includeHEADERS + +FROM libjq AS builder +RUN update-ca-certificates +COPY go.mod /go/src/git.martyn.berlin/martyn/helm-import/ +COPY cmd/ /go/src/git.martyn.berlin/martyn/helm-import/cmd/ +COPY internal/ /go/src/git.martyn.berlin/martyn/helm-import/internal/ +COPY Makefile /go/src/git.martyn.berlin/martyn/helm-import/ +RUN cd /go/src/git.martyn.berlin/martyn/helm-import/; make deps && make dist + +FROM alpine:3.12 +COPY --from=builder /go/src/git.martyn.berlin/martyn/helm-import/bin/helm-import / +COPY --from=builder /usr/lib/libjq.* /usr/lib/ diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..7976dfb --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,252 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "path/filepath" + "strings" + + "gopkg.in/yaml.v2" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + + "git.martyn.berlin/martyn/helm-import/internal/exporters" + // + // Uncomment to load all auth plugins + // "k8s.io/client-go/plugin/pkg/client/auth" + // + // Or uncomment to load specific auth plugins + // _ "k8s.io/client-go/plugin/pkg/client/auth/azure" + // _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + // _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + // _ "k8s.io/client-go/plugin/pkg/client/auth/openstack" +) + +type objectFlags []string + +func (i *objectFlags) String() string { + return strings.Join(*i, ", ") +} + +func (i *objectFlags) Set(value string) error { + *i = append(*i, value) + return nil +} + +type jqCommands []string + +func (i *jqCommands) String() string { + return strings.Join(*i, ", ") +} + +func (i *jqCommands) Set(value string) error { + *i = append(*i, value) + return nil +} + +var deployments objectFlags +var services objectFlags +var configmaps objectFlags +var secrets objectFlags +var ingresses objectFlags +var namespace string +var noBuiltinMods bool +var lessOpinions bool +var deploymentJqCommands jqCommands +var serviceJqCommands jqCommands +var ingressJqCommands jqCommands +var globalJqCommands jqCommands +var configmapJqCommands jqCommands +var secretJqCommands jqCommands + +type chartDef struct { + APIVersion string `json: "apiVersion"` + Name string `json: "name"` + Description string `json: "description"` + Type string `json: "type"` + Version string `json: "version"` + AppVersion string `json: "appVersion"` +} + +var chart chartDef + +func main() { + chartYamlFile, err := ioutil.ReadFile("Chart.yaml") + if err != nil { + panic("Run this from the chart folder, where Chart.yaml is. Error: " + err.Error()) + } + err = yaml.Unmarshal(chartYamlFile, &chart) + if err != nil { + panic("Could not read the chart yaml as expected. Error: " + err.Error()) + } + + var kubeconfig *string + if home := homedir.HomeDir(); home != "" { + kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") + } else { + kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") + } + + flag.StringVar(&namespace, "namespace", "default", "namespace to work in") + + flag.Var(&deployments, "deployment", "deployment to import into the helm chart (can be specified multiple times)") + flag.Var(&services, "service", "service to import into the helm chart (can be specified multiple times)") + flag.Var(&configmaps, "configmap", "configmap to import into the helm chart (can be specified multiple times)") + flag.Var(&secrets, "secret", "secret to import into the helm chart (can be specified multiple times)") + flag.Var(&ingresses, "ingress", "ingress to import into the helm chart (can be specified multiple times)") + + flag.BoolVar(&noBuiltinMods, "nobuiltinmods", false, "don't do the built-in modifications, only ones passed in by user") + flag.BoolVar(&lessOpinions, "lessopinions", false, "just import the objects into files, don't get opinionated") + + flag.Var(&globalJqCommands, "globalJqCommands", "extra jq commands to apply to all k8s objects") + flag.Var(&deploymentJqCommands, "deploymentJqCommands", "extra jq commands to apply to deployments") + flag.Var(&serviceJqCommands, "serviceJqCommands", "extra jq commands to apply to services") + flag.Var(&configmapJqCommands, "configmapJqCommands", "extra jq commands to apply to configmaps") + flag.Var(&secretJqCommands, "secretJqCommands", "extra jq commands to apply to secrets") + flag.Var(&ingressJqCommands, "ingressJqCommands", "extra jq commands to apply to ingresses") + + flag.Parse() + + // use the current context in kubeconfig + config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) + if err != nil { + panic(err.Error()) + } + + // create the clientset + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err.Error()) + } + + var y = "" + fileString := "" + for _, deployment := range deployments { + deploymentObject, err := clientset.AppsV1().Deployments(namespace).Get(deployment, metav1.GetOptions{}) + if errors.IsNotFound(err) { + fmt.Printf("Deployment %s in namespace %s not found\n", deployment, namespace) + } else if statusError, isStatus := err.(*errors.StatusError); isStatus { + fmt.Printf("Error getting deployment %s in namespace %s: %v\n", + deployment, namespace, statusError.ErrStatus.Message) + } else if err != nil { + panic(err.Error()) + } else { + fmt.Printf("Found deployment %s in namespace %s\n", deployment, namespace) + y = exporters.Deployment(deploymentObject, append(deploymentJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) + if fileString != "" { + fileString += "---\n" + } + fileString += y + } + } + if y != "" { + err = ioutil.WriteFile("templates/imported-deployment.yaml", []byte(fileString), 0644) + if err != nil { + panic(err.Error()) + } + } + + for _, service := range services { + serviceObject, err := clientset.CoreV1().Services(namespace).Get(service, metav1.GetOptions{}) + if errors.IsNotFound(err) { + fmt.Printf("service %s in namespace %s not found\n", service, namespace) + } else if statusError, isStatus := err.(*errors.StatusError); isStatus { + fmt.Printf("Error getting service %s in namespace %s: %v\n", + service, namespace, statusError.ErrStatus.Message) + } else if err != nil { + panic(err.Error()) + } else { + fmt.Printf("Found service %s in namespace %s\n", service, namespace) + y = exporters.Service(serviceObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) + if fileString != "" { + fileString += "---\n" + } + fileString += y + } + } + if y != "" { + err = ioutil.WriteFile("templates/imported-service.yaml", []byte(fileString), 0644) + if err != nil { + panic(err.Error()) + } + } + + for _, configmap := range configmaps { + configmapObject, err := clientset.CoreV1().ConfigMaps(namespace).Get(configmap, metav1.GetOptions{}) + if errors.IsNotFound(err) { + fmt.Printf("configmap %s in namespace %s not found\n", configmap, namespace) + } else if statusError, isStatus := err.(*errors.StatusError); isStatus { + fmt.Printf("Error getting configmap %s in namespace %s: %v\n", + configmap, namespace, statusError.ErrStatus.Message) + } else if err != nil { + panic(err.Error()) + } else { + fmt.Printf("Found configmap %s in namespace %s\n", configmap, namespace) + y = exporters.Configmap(configmapObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) + } + if fileString != "" { + fileString += "---\n" + } + fileString += y + } + if y != "" { + err = ioutil.WriteFile("templates/imported-configmap.yaml", []byte(fileString), 0644) + if err != nil { + panic(err.Error()) + } + } + + for _, secret := range secrets { + secretObject, err := clientset.CoreV1().Secrets(namespace).Get(secret, metav1.GetOptions{}) + if errors.IsNotFound(err) { + fmt.Printf("secret %s in namespace %s not found\n", secret, namespace) + } else if statusError, isStatus := err.(*errors.StatusError); isStatus { + fmt.Printf("Error getting secret %s in namespace %s: %v\n", + secret, namespace, statusError.ErrStatus.Message) + } else if err != nil { + panic(err.Error()) + } else { + fmt.Printf("Found secret %s in namespace %s\n", secret, namespace) + y = exporters.Secret(secretObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) + } + if fileString != "" { + fileString += "---\n" + } + fileString += y + } + if y != "" { + err = ioutil.WriteFile("templates/imported-secret.yaml", []byte(fileString), 0644) + if err != nil { + panic(err.Error()) + } + } + + for _, ingress := range ingresses { + ingressObject, err := clientset.ExtensionsV1beta1().Ingresses(namespace).Get(ingress, metav1.GetOptions{}) + if errors.IsNotFound(err) { + fmt.Printf("ingress %s in namespace %s not found\n", ingress, namespace) + } else if statusError, isStatus := err.(*errors.StatusError); isStatus { + fmt.Printf("Error getting ingress %s in namespace %s: %v\n", + ingress, namespace, statusError.ErrStatus.Message) + } else if err != nil { + panic(err.Error()) + } else { + fmt.Printf("Found ingress %s in namespace %s\n", ingress, namespace) + y = exporters.Ingress(ingressObject, append(serviceJqCommands, globalJqCommands...), noBuiltinMods, chart.Name, lessOpinions) + } + if fileString != "" { + fileString += "---\n" + } + fileString += y + } + if y != "" { + err = ioutil.WriteFile("templates/imported-ingress.yaml", []byte(fileString), 0644) + if err != nil { + panic(err.Error()) + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dc82edd --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module git.martyn.berlin/martyn/helm-import + +go 1.13 + +require ( + github.com/ashb/jqrepl v0.1.0 + github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 // indirect + gopkg.in/yaml.v2 v2.2.8 + k8s.io/api v0.16.15 + k8s.io/apimachinery v0.16.15 + k8s.io/client-go v0.16.15 + sigs.k8s.io/yaml v1.1.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2e6c16e --- /dev/null +++ b/go.sum @@ -0,0 +1,200 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ashb/jqrepl v0.1.0 h1:lugLTnp0F8mKE6pfhf5eExcjSTvUAuGXnB9pVixTfSY= +github.com/ashb/jqrepl v0.1.0/go.mod h1:dFTYRmXpWcRRjMqu2SMMxRoXONIdB/aN4/auvtre64I= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +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/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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/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 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= +golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.16.15 h1:6yvV9YNGwnebDAsA4Sfj+1b1S9j5OYfmckjTdc9b1bI= +k8s.io/api v0.16.15/go.mod h1:8z880CLtpCJqHWe9vmBkZMQeMKHNvdTQuqLW2QUefUA= +k8s.io/apimachinery v0.16.15 h1:4cmEfuRsKuV8pMpaQ6z0AKEUXZ3r+u/NKaz5dvIjySk= +k8s.io/apimachinery v0.16.15/go.mod h1:xAtIC8Gj83Pn2OCs2g57wZpZembRhJhiXIlQIqanwas= +k8s.io/client-go v0.16.15 h1:cuSmM5begnN77V0beNgmhQ9yob6TFUnN+YaqAfRBD40= +k8s.io/client-go v0.16.15/go.mod h1:onpbkg9XeonG579HOlK9RS56ixfOJdbBM5dKluyFM8c= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/internal/exporters/configmap.go b/internal/exporters/configmap.go new file mode 100755 index 0000000..9170ef0 --- /dev/null +++ b/internal/exporters/configmap.go @@ -0,0 +1,74 @@ +package exporters + +import ( + "encoding/json" + "regexp" + + v1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" +) + +// Configmap removes basic stuff like namespace, status, managedBy and lastAppliedConfiguration +func Configmap(cm *v1.ConfigMap, jqcommands []string, nobuiltinmods bool, chartname string, lessOpinions bool) string { + if !nobuiltinmods { + // All the below do not belong in a helm chart yaml + delete(cm.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + delete(cm.ObjectMeta.Annotations, "deployment.kubernetes.io/revision") + cm.ObjectMeta.ManagedFields = nil + cm.ObjectMeta.Namespace = "" + cm.ObjectMeta.ResourceVersion = "" + cm.ObjectMeta.Generation = 0 + cm.ObjectMeta.SelfLink = "" + cm.ObjectMeta.UID = "" + jqcommands = append(jqcommands, "del(.metadata.creationTimestamp)") + + // Here's where we get more opinionated though + if !lessOpinions { + jqcommands = append(jqcommands, ".metadata.name = \"{{ $fullName }}\"") + jqcommands = append(jqcommands, ".metadata.labels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".labels\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".metadata.annotations += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".annotations\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".data += { \"HELMTEMPLATEDELETEKEY0\": \"{{- range $key, $value := .Values."+chartname+".env }}\" }") + jqcommands = append(jqcommands, ".data += { \"HELMTEMPLATEDELETEKEY1\": \"{{ $key }}: {{ $value | quote }}\" }") + jqcommands = append(jqcommands, ".data += { \"HELMTEMPLATEDELETEKEY2\": \"{{- end }}\" }") + } + } + // Jump to json for jq ;-) + jsonForm, err := json.Marshal(cm) + if err != nil { + panic(err.Error()) + } + var intermediateJSONForm []byte + var finalJSONForm []byte + + if len(jqcommands) > 0 { + for _, command := range jqcommands { + intermediateJSONForm = jQOnJSONByteArray(command, jsonForm) + jsonForm = intermediateJSONForm + } + finalJSONForm = jsonForm + } else { + finalJSONForm = jsonForm + } + + y, err := yaml.JSONToYAML(finalJSONForm) + if err != nil { + panic(err.Error()) + } + ret := "{{- $fullName := include \"" + chartname + ".fullname\" . -}}\n" + ret += "kind: ConfigMap\n" + ret += "apiVersion: v1\n" + out := string(y) + var re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY0: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY1: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY2: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`(?s)- HELMTEMPLATEDELETEKEYANDARRAYMARKER: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + + ret += string(out) + return ret +} diff --git a/internal/exporters/deployment.go b/internal/exporters/deployment.go new file mode 100755 index 0000000..275fab9 --- /dev/null +++ b/internal/exporters/deployment.go @@ -0,0 +1,79 @@ +package exporters + +import ( + "encoding/json" + "regexp" + + v1 "k8s.io/api/apps/v1" + "sigs.k8s.io/yaml" +) + +// Deployment removes basic stuff like namespace, status, managedBy and lastAppliedConfiguration +func Deployment(deploy *v1.Deployment, jqcommands []string, nobuiltinmods bool, chartname string, lessOpinions bool) string { + if !nobuiltinmods { + // All the below do not belong in a helm chart yaml + delete(deploy.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + delete(deploy.ObjectMeta.Annotations, "deployment.kubernetes.io/revision") + deploy.ObjectMeta.ManagedFields = nil + deploy.ObjectMeta.Namespace = "" + deploy.ObjectMeta.ResourceVersion = "" + deploy.ObjectMeta.Generation = 0 + deploy.ObjectMeta.SelfLink = "" + deploy.ObjectMeta.UID = "" + jqcommands = append(jqcommands, "del(.metadata.creationTimestamp)") + jqcommands = append(jqcommands, "del(.status)") + if !lessOpinions { + jqcommands = append(jqcommands, ".metadata.name = \"{{ $fullName }}\"") + jqcommands = append(jqcommands, ".spec.replicas = \"{{ .values.replicaCount }}\"") + jqcommands = append(jqcommands, ".metadata.labels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".labels\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".metadata.annotations += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".annotations\\\" . | nindent 4 }}\"}") + // damn, "checksum/config" works as a key in yaml, not in json! + jqcommands = append(jqcommands, ".metadata.annotations += {checksumConfig: \"{{- include (print $.Template.BasePath \\\"/configmap.yaml\\\") . | sha256sum }}\"}") + jqcommands = append(jqcommands, ".metadata.annotations += {checksumSecret: \"{{- include (print $.Template.BasePath \\\"/secret.yaml\\\") . | sha256sum }}\"}") + jqcommands = append(jqcommands, ".spec.selector.matchLabels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".selectorLabels\\\" . | nindent 6 }}\"}") + jqcommands = append(jqcommands, ".spec.template.metadata.labels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".labels\\\" . | nindent 8 }}\"}") + jqcommands = append(jqcommands, ".spec.template.metadata.annotations += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".annotations\\\" . | nindent 8 }}\"}") + jqcommands = append(jqcommands, ".spec.template.spec.serviceAccountName = \"{{ include \\\""+chartname+".serviceAccountName\\\" . }}\"") + jqcommands = append(jqcommands, ".spec.template.spec.securityContext += {HELMTEMPLATEDELETEKEY: \"{{- toYaml .podSecurityContext | nindent 8 }}\"}") + jqcommands = append(jqcommands, ".spec.template.spec.containers[0].env += [{HELMTEMPLATEDELETEKEYANDARRAYMARKER: \"{{- range $key, $value := .Values."+chartname+".env }}\"}]") + jqcommands = append(jqcommands, ".spec.template.spec.containers[0].env += [{ name: \"{{ $key }}\", valueFrom: {configMapKeyRef: {name: \"{{ $fullName }}\", key: \"{{ $key | quote }}\" }} }]") + jqcommands = append(jqcommands, ".spec.template.spec.containers[0].env += [{HELMTEMPLATEDELETEKEYANDARRAYMARKER: \"{{- end }}\"}]") + jqcommands = append(jqcommands, ".spec.template.spec.containers[0].env += [{HELMTEMPLATEDELETEKEYANDARRAYMARKER: \"{{- range $key, $value := .Values."+chartname+".secretenv }}\"}]") + jqcommands = append(jqcommands, ".spec.template.spec.containers[0].env += [{ name: \"{{ $key }}\", valueFrom: {secretKeyRef: {name: \"{{ $fullName }}\", key: \"{{ $key | quote }}\" }} }]") + jqcommands = append(jqcommands, ".spec.template.spec.containers[0].env += [{HELMTEMPLATEDELETEKEYANDARRAYMARKER: \"{{- end }}\"}]") + } + } + // Jump to json for jq ;-) + jsonForm, err := json.Marshal(deploy) + if err != nil { + panic(err.Error()) + } + var intermediateJSONForm []byte + var finalJSONForm []byte + + if len(jqcommands) > 0 { + for _, command := range jqcommands { + intermediateJSONForm = jQOnJSONByteArray(command, jsonForm) + jsonForm = intermediateJSONForm + } + finalJSONForm = jsonForm + } else { + finalJSONForm = jsonForm + } + + y, err := yaml.JSONToYAML(finalJSONForm) + if err != nil { + panic(err.Error()) + } + ret := "{{- $fullName := include \"" + chartname + ".fullname\" . -}}\n" + ret += "kind: Deployment\n" + ret += "apiVersion: Apps/v1\n" + out := string(y) + var re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`(?s)- HELMTEMPLATEDELETEKEYANDARRAYMARKER: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + + ret += string(out) + return ret +} diff --git a/internal/exporters/ingress.go b/internal/exporters/ingress.go new file mode 100755 index 0000000..7d06fbc --- /dev/null +++ b/internal/exporters/ingress.go @@ -0,0 +1,72 @@ +package exporters + +import ( + "encoding/json" + "regexp" + + v1beta1 "k8s.io/api/extensions/v1beta1" + "sigs.k8s.io/yaml" +) + +// Configmap removes basic stuff like namespace, status, managedBy and lastAppliedConfiguration +func Ingress(ing *v1beta1.Ingress, jqcommands []string, nobuiltinmods bool, chartname string, lessOpinions bool) string { + if !nobuiltinmods { + // All the below do not belong in a helm chart yaml + delete(ing.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + delete(ing.ObjectMeta.Annotations, "deployment.kubernetes.io/revision") + ing.ObjectMeta.ManagedFields = nil + ing.ObjectMeta.Namespace = "" + ing.ObjectMeta.ResourceVersion = "" + ing.ObjectMeta.Generation = 0 + ing.ObjectMeta.SelfLink = "" + ing.ObjectMeta.UID = "" + jqcommands = append(jqcommands, "del(.metadata.creationTimestamp)") + + // Here's where we get more opinionated though + if !lessOpinions { + jqcommands = append(jqcommands, ".metadata.name = \"{{ $fullName }}\"") + jqcommands = append(jqcommands, ".metadata.labels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".labels\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".metadata.annotations += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".annotations\\\" . | nindent 4 }}\"}") + } + } + // Jump to json for jq ;-) + jsonForm, err := json.Marshal(ing) + if err != nil { + panic(err.Error()) + } + var intermediateJSONForm []byte + var finalJSONForm []byte + + if len(jqcommands) > 0 { + for _, command := range jqcommands { + intermediateJSONForm = jQOnJSONByteArray(command, jsonForm) + jsonForm = intermediateJSONForm + } + finalJSONForm = jsonForm + } else { + finalJSONForm = jsonForm + } + + y, err := yaml.JSONToYAML(finalJSONForm) + if err != nil { + panic(err.Error()) + } + ret := "{{- if .Values.ingress.enabled -}}\n" + ret += "{{- $fullName := include \"" + chartname + ".fullname\" . -}}\n" + ret += "{{- $releaseName := .Release.Name}}\n" + ret += "{{- $svcPort := .Values.service.port -}}\n" + ret += "{{- if semverCompare \">=1.14-0\" .Capabilities.KubeVersion.GitVersion -}}\n" + ret += "apiVersion: networking.k8s.io/v1beta1\n" + ret += "{{- else -}}\n" + ret += "apiVersion: extensions/v1beta1\n" + ret += "{{- end }}\n" + ret += "kind: Ingress\n" + out := string(y) + var re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`(?s)- HELMTEMPLATEDELETEKEYANDARRAYMARKER: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + + ret += string(out) + return ret +} diff --git a/internal/exporters/secret.go b/internal/exporters/secret.go new file mode 100755 index 0000000..947430f --- /dev/null +++ b/internal/exporters/secret.go @@ -0,0 +1,74 @@ +package exporters + +import ( + "encoding/json" + "regexp" + + v1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" +) + +// Secret removes basic stuff like namespace, status, managedBy and lastAppliedConfiguration +func Secret(secret *v1.Secret, jqcommands []string, nobuiltinmods bool, chartname string, lessOpinions bool) string { + if !nobuiltinmods { + // All the below do not belong in a helm chart yaml + delete(secret.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + delete(secret.ObjectMeta.Annotations, "deployment.kubernetes.io/revision") + secret.ObjectMeta.ManagedFields = nil + secret.ObjectMeta.Namespace = "" + secret.ObjectMeta.ResourceVersion = "" + secret.ObjectMeta.Generation = 0 + secret.ObjectMeta.SelfLink = "" + secret.ObjectMeta.UID = "" + jqcommands = append(jqcommands, "del(.metadata.creationTimestamp)") + + // Here's where we get more opinionated though + if !lessOpinions { + jqcommands = append(jqcommands, ".metadata.name = \"{{ $fullName }}\"") + jqcommands = append(jqcommands, ".metadata.labels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".labels\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".metadata.annotations += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".annotations\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".data += { \"HELMTEMPLATEDELETEKEY0\": \"{{- range $key, $value := .Values."+chartname+".secretenv }}\" }") + jqcommands = append(jqcommands, ".data += { \"HELMTEMPLATEDELETEKEY1\": \"{{ $key }}: {{ $value | quote }}\" }") + jqcommands = append(jqcommands, ".data += { \"HELMTEMPLATEDELETEKEY2\": \"{{- end }}\" }") + } + } + // Jump to json for jq ;-) + jsonForm, err := json.Marshal(secret) + if err != nil { + panic(err.Error()) + } + var intermediateJSONForm []byte + var finalJSONForm []byte + + if len(jqcommands) > 0 { + for _, command := range jqcommands { + intermediateJSONForm = jQOnJSONByteArray(command, jsonForm) + jsonForm = intermediateJSONForm + } + finalJSONForm = jsonForm + } else { + finalJSONForm = jsonForm + } + + y, err := yaml.JSONToYAML(finalJSONForm) + if err != nil { + panic(err.Error()) + } + ret := "{{- $fullName := include \"" + chartname + ".fullname\" . -}}\n" + ret += "kind: Secret\n" + ret += "apiVersion: v1\n" + out := string(y) + var re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY0: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY1: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY2: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`(?s)- HELMTEMPLATEDELETEKEYANDARRAYMARKER: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + + ret += string(out) + return ret +} diff --git a/internal/exporters/service.go b/internal/exporters/service.go new file mode 100755 index 0000000..c1033c9 --- /dev/null +++ b/internal/exporters/service.go @@ -0,0 +1,72 @@ +package exporters + +import ( + "encoding/json" + "regexp" + + v1 "k8s.io/api/core/v1" + "sigs.k8s.io/yaml" +) + +// Service removes basic stuff like namespace, status, managedBy and lastAppliedConfiguration +func Service(svc *v1.Service, jqcommands []string, nobuiltinmods bool, chartname string, lessOpinions bool) string { + if !nobuiltinmods { + // All the below do not belong in a helm chart yaml + delete(svc.ObjectMeta.Annotations, "kubectl.kubernetes.io/last-applied-configuration") + delete(svc.ObjectMeta.Annotations, "deployment.kubernetes.io/revision") + svc.ObjectMeta.ManagedFields = nil + svc.ObjectMeta.Namespace = "" + svc.ObjectMeta.ResourceVersion = "" + svc.ObjectMeta.Generation = 0 + svc.ObjectMeta.SelfLink = "" + svc.ObjectMeta.UID = "" + jqcommands = append(jqcommands, "del(.metadata.creationTimestamp)") + jqcommands = append(jqcommands, "del(.status)") + + // Here's where we get more opinionated though + if !lessOpinions { + jqcommands = append(jqcommands, ".metadata.name = \"{{ $fullName }}\"") + jqcommands = append(jqcommands, ".metadata.labels += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".labels\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, ".metadata.annotations += {HELMTEMPLATEDELETEKEY: \"{{- include \\\""+chartname+".annotations\\\" . | nindent 4 }}\"}") + jqcommands = append(jqcommands, "del(.spec.clusterIP)") + jqcommands = append(jqcommands, ".spec.externalTrafficPolicy = \"{{ .Values.service.externalTrafficPolicy }}\"") + jqcommands = append(jqcommands, ".spec.type = \"{{ .Values.service.type }}\"") + jqcommands = append(jqcommands, "del(.spec.ports[].nodePort)") + } + } + // Jump to json for jq ;-) + jsonForm, err := json.Marshal(svc) + if err != nil { + panic(err.Error()) + } + var intermediateJSONForm []byte + var finalJSONForm []byte + + if len(jqcommands) > 0 { + for _, command := range jqcommands { + intermediateJSONForm = jQOnJSONByteArray(command, jsonForm) + jsonForm = intermediateJSONForm + } + finalJSONForm = jsonForm + } else { + finalJSONForm = jsonForm + } + + y, err := yaml.JSONToYAML(finalJSONForm) + if err != nil { + panic(err.Error()) + } + ret := "{{- if .Values.service.enabled -}}\n" + ret += "{{- $fullName := include \"" + chartname + ".fullname\" . -}}\n" + ret += "kind: Service\n" + ret += "apiVersion: v1\n" + out := string(y) + var re = regexp.MustCompile(`HELMTEMPLATEDELETEKEY: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + re = regexp.MustCompile(`(?s)- HELMTEMPLATEDELETEKEYANDARRAYMARKER: '([^']*)'`) + out = re.ReplaceAllString(out, "$1") + + ret += string(out) + ret += "{{- end -}}" // if .Values.service.enabled + return ret +} diff --git a/internal/exporters/yamltools.go b/internal/exporters/yamltools.go new file mode 100755 index 0000000..ec78106 --- /dev/null +++ b/internal/exporters/yamltools.go @@ -0,0 +1,31 @@ +package exporters + +import ( + "fmt" + + "github.com/ashb/jqrepl/jq" +) + +func jQOnJSONByteArray(command string, jsonByteArray []byte) []byte { + jqInForm, err := jq.JvFromJSONBytes(jsonByteArray) + if err != nil { + panic(err.Error()) + } + jqObj, err := jq.New() + if err != nil { + panic(err.Error()) + } + errs := jqObj.Compile(command, jq.JvArray()) + if errs != nil { + for _, e := range errs { + fmt.Printf("E: %s\n", e.Error()) + } + panic("Errors above!") + } + jQForm, err := jqObj.Execute(jqInForm) + if err != nil { + panic(err.Error()) + } + finalJSONForm := []byte(jQForm[0].Dump(jq.JvPrintNone)) + return finalJSONForm +}