diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 699015f2..622636da 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -37,10 +37,20 @@ jobs: fi - name: Build run: sudo go build -o ./bin/tk ./cmd/tk - - name: Run integration tests + - name: Run check e2e tests run: | ./bin/tk check + - name: Run install version e2e tests + run: | + ./bin/tk install --version=master --namespace=test --verbose + - name: Run uninstall e2e tests + run: | + ./bin/tk uninstall --namespace=test --crds --silent + - name: Run dev install e2e tests + run: | ./bin/tk install --manifests ./manifests/install/ + - name: Run create source e2e tests + run: | ./bin/tk create source podinfo --git-url https://github.com/stefanprodan/podinfo-deploy --git-semver=">=0.0.1-rc.1 <0.1.0" - name: Debug failure if: failure() diff --git a/cmd/tk/check.go b/cmd/tk/check.go index b201c05f..6e6cceea 100644 --- a/cmd/tk/check.go +++ b/cmd/tk/check.go @@ -1,6 +1,7 @@ package main import ( + "context" "os" "os/exec" "strings" @@ -31,17 +32,20 @@ func init() { } func runCheckCmd(cmd *cobra.Command, args []string) error { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + logAction("starting verification") checkFailed := false if !sshCheck() { checkFailed = true } - if !kubectlCheck(">=1.18.0") { + if !kubectlCheck(ctx, ">=1.18.0") { checkFailed = true } - if !kustomizeCheck(">=3.5.0") { + if !kustomizeCheck(ctx, ">=3.5.0") { checkFailed = true } @@ -79,14 +83,15 @@ func sshCheck() bool { return ok } -func kubectlCheck(version string) bool { +func kubectlCheck(ctx context.Context, version string) bool { _, err := exec.LookPath("kubectl") if err != nil { logFailure("kubectl not found") return false } - output, err := execCommand("kubectl version --client --short | awk '{ print $3 }'") + command := "kubectl version --client --short | awk '{ print $3 }'" + output, err := utils.execCommand(ctx, ModeCapture, command) if err != nil { logFailure("kubectl version can't be determined") return false @@ -108,21 +113,23 @@ func kubectlCheck(version string) bool { return true } -func kustomizeCheck(version string) bool { +func kustomizeCheck(ctx context.Context, version string) bool { _, err := exec.LookPath("kustomize") if err != nil { logFailure("kustomize not found") return false } - output, err := execCommand("kustomize version --short | awk '{ print $1 }' | cut -c2-") + command := "kustomize version --short | awk '{ print $1 }' | cut -c2-" + output, err := utils.execCommand(ctx, ModeCapture, command) if err != nil { logFailure("kustomize version can't be determined") return false } if strings.Contains(output, "kustomize/") { - output, err = execCommand("kustomize version --short | awk '{ print $1 }' | cut -c12-") + command = "kustomize version --short | awk '{ print $1 }' | cut -c12-" + output, err = utils.execCommand(ctx, ModeCapture, command) if err != nil { logFailure("kustomize version can't be determined") return false @@ -146,7 +153,7 @@ func kustomizeCheck(version string) bool { } func kubernetesCheck(version string) bool { - client, err := kubernetesClient() + client, err := utils.kubeClient(kubeconfig) if err != nil { logFailure("kubernetes client initialization failed: %s", err.Error()) return false diff --git a/cmd/tk/create_source.go b/cmd/tk/create_source.go index 543a6777..656bbb90 100644 --- a/cmd/tk/create_source.go +++ b/cmd/tk/create_source.go @@ -5,11 +5,9 @@ import ( "bytes" "context" "fmt" - "io" "io/ioutil" "net/url" "os" - "os/exec" "strings" "text/template" @@ -44,7 +42,6 @@ var ( sourceGitSemver string sourceUsername string sourcePassword string - sourceVerbose bool ) func init() { @@ -53,7 +50,6 @@ func init() { createSourceCmd.Flags().StringVar(&sourceGitSemver, "git-semver", "", "git tag semver range") createSourceCmd.Flags().StringVarP(&sourceUsername, "username", "u", "", "basic authentication username") createSourceCmd.Flags().StringVarP(&sourcePassword, "password", "p", "", "basic authentication password") - createSourceCmd.Flags().BoolVarP(&sourceVerbose, "verbose", "", false, "print generated source object") createCmd.AddCommand(createSourceCmd) } @@ -84,12 +80,12 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error { withAuth := false if strings.HasPrefix(sourceGitURL, "ssh") { - if err := generateSSH(name, u.Host, tmpDir); err != nil { + if err := generateSSH(ctx, name, u.Host, tmpDir); err != nil { return err } withAuth = true } else if sourceUsername != "" && sourcePassword != "" { - if err := generateBasicAuth(name); err != nil { + if err := generateBasicAuth(ctx, name); err != nil { return err } withAuth = true @@ -129,69 +125,59 @@ func createSourceCmdRun(cmd *cobra.Command, args []string) error { return fmt.Errorf("source flush failed: %w", err) } - if sourceVerbose { + if verbose { fmt.Print(data.String()) } command := fmt.Sprintf("echo '%s' | kubectl apply -f-", data.String()) - c := exec.CommandContext(ctx, "/bin/sh", "-c", command) - - var stdoutBuf, stderrBuf bytes.Buffer - c.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) - c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) - - err = c.Run() - if err != nil { + if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { return fmt.Errorf("source apply failed") } logAction("waiting for source sync") - if output, err := execCommand(fmt.Sprintf( - "kubectl -n %s wait gitrepository/%s --for=condition=ready --timeout=1m", - namespace, name)); err != nil { - return fmt.Errorf("source sync failed: %s", output) - } else { - fmt.Print(output) + command = fmt.Sprintf("kubectl -n %s wait gitrepository/%s --for=condition=ready --timeout=1m", + namespace, name) + if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { + return fmt.Errorf("source sync failed") } - + logSuccess("source %s is ready", name) return nil } -func generateBasicAuth(name string) error { +func generateBasicAuth(ctx context.Context, name string) error { logAction("saving credentials") credentials := fmt.Sprintf("--from-literal=username='%s' --from-literal=password='%s'", sourceUsername, sourcePassword) secret := fmt.Sprintf("kubectl -n %s create secret generic %s %s --dry-run=client -oyaml | kubectl apply -f-", namespace, name, credentials) - if output, err := execCommand(secret); err != nil { - return fmt.Errorf("kubectl create secret failed: %s", output) - } else { - fmt.Print(output) + if _, err := utils.execCommand(ctx, ModeOS, secret); err != nil { + return fmt.Errorf("kubectl create secret failed") } return nil } -func generateSSH(name, host, tmpDir string) error { +func generateSSH(ctx context.Context, name, host, tmpDir string) error { logAction("generating host key for %s", host) - keyscan := fmt.Sprintf("ssh-keyscan %s > %s/known_hosts", host, tmpDir) - if output, err := execCommand(keyscan); err != nil { - return fmt.Errorf("ssh-keyscan failed: %s", output) + command := fmt.Sprintf("ssh-keyscan %s > %s/known_hosts", host, tmpDir) + if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { + return fmt.Errorf("ssh-keyscan failed") } logAction("generating deploy key") - keygen := fmt.Sprintf("ssh-keygen -b 2048 -t rsa -f %s/identity -q -N \"\"", tmpDir) - if output, err := execCommand(keygen); err != nil { - return fmt.Errorf("ssh-keygen failed: %s", output) + command = fmt.Sprintf("ssh-keygen -b 2048 -t rsa -f %s/identity -q -N \"\"", tmpDir) + if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { + return fmt.Errorf("ssh-keygen failed") } - deployKey, err := execCommand(fmt.Sprintf("cat %s/identity.pub", tmpDir)) - if err != nil { + command = fmt.Sprintf("cat %s/identity.pub", tmpDir) + if deployKey, err := utils.execCommand(ctx, ModeCapture, command); err != nil { return fmt.Errorf("unable to read identity.pub: %w", err) + } else { + fmt.Print(deployKey) } - fmt.Print(deployKey) prompt := promptui.Prompt{ Label: "Have you added the deploy key to your repository", IsConfirm: true, @@ -206,10 +192,8 @@ func generateSSH(name, host, tmpDir string) error { tmpDir, tmpDir, tmpDir) secret := fmt.Sprintf("kubectl -n %s create secret generic %s %s --dry-run=client -oyaml | kubectl apply -f-", namespace, name, files) - if output, err := execCommand(secret); err != nil { - return fmt.Errorf("kubectl create secret failed: %s", output) - } else { - fmt.Print(output) + if _, err := utils.execCommand(ctx, ModeOS, secret); err != nil { + return fmt.Errorf("create secret failed") } return nil } diff --git a/cmd/tk/install.go b/cmd/tk/install.go index 1cb12467..3cda8504 100644 --- a/cmd/tk/install.go +++ b/cmd/tk/install.go @@ -1,12 +1,11 @@ package main import ( - "bytes" "context" "fmt" - "io" + "io/ioutil" "os" - "os/exec" + "path" "strings" "github.com/spf13/cobra" @@ -16,78 +15,178 @@ var installCmd = &cobra.Command{ Use: "install", Short: "Install the toolkit components", Long: ` -The Install command deploys the toolkit components +The install command deploys the toolkit components on the configured Kubernetes cluster in ~/.kube/config`, - Example: ` install --manifests github.com/fluxcd/toolkit//manifests/install --dry-run`, + Example: ` install --version=master --namespace=gitops-systems`, RunE: installCmdRun, } var ( installDryRun bool installManifestsPath string + installVersion string ) func init() { installCmd.Flags().BoolVarP(&installDryRun, "dry-run", "", false, "only print the object that would be applied") + installCmd.Flags().StringVarP(&installVersion, "version", "v", "master", + "toolkit tag or branch") installCmd.Flags().StringVarP(&installManifestsPath, "manifests", "", "", - "path to the manifest directory") - + "path to the manifest directory, dev only") rootCmd.AddCommand(installCmd) } func installCmdRun(cmd *cobra.Command, args []string) error { - if installManifestsPath == "" { - return fmt.Errorf("no manifests specified") - } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() - if !strings.HasPrefix(installManifestsPath, "github.com/") { + var kustomizePath string + if installVersion == "" && !strings.HasPrefix(installManifestsPath, "github.com/") { if _, err := os.Stat(installManifestsPath); err != nil { return fmt.Errorf("manifests not found: %w", err) } + kustomizePath = installManifestsPath } - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() + tmpDir, err := ioutil.TempDir("", namespace) + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) - dryRun := "" - if installDryRun { - dryRun = "--dry-run=client" + logAction("generating install manifests") + if kustomizePath == "" { + err = genInstallManifests(installVersion, namespace, tmpDir) + if err != nil { + return fmt.Errorf("install failed: %w", err) + } + kustomizePath = tmpDir + } + + manifest := path.Join(tmpDir, fmt.Sprintf("%s.yaml", namespace)) + command := fmt.Sprintf("kustomize build %s > %s", kustomizePath, manifest) + if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { + return fmt.Errorf("install failed") } - command := fmt.Sprintf("kustomize build %s | kubectl apply -f- %s", - installManifestsPath, dryRun) - c := exec.CommandContext(ctx, "/bin/sh", "-c", command) - var stdoutBuf, stderrBuf bytes.Buffer - c.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) - c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) + command = fmt.Sprintf("cat %s", manifest) + if yaml, err := utils.execCommand(ctx, ModeCapture, command); err != nil { + return fmt.Errorf("install failed: %w", err) + } else { + if verbose { + fmt.Print(yaml) + } + } + logSuccess("build completed") logAction("installing components in %s namespace", namespace) - err := c.Run() - if err != nil { - logFailure("install failed") - os.Exit(1) + applyOutput := ModeStderrOS + if verbose { + applyOutput = ModeOS + } + dryRun := "" + if installDryRun { + dryRun = "--dry-run=client" + applyOutput = ModeOS + } + command = fmt.Sprintf("cat %s | kubectl apply -f- %s", manifest, dryRun) + if _, err := utils.execCommand(ctx, applyOutput, command); err != nil { + return fmt.Errorf("install failed") } if installDryRun { logSuccess("install dry-run finished") return nil + } else { + logSuccess("install completed") } logAction("verifying installation") for _, deployment := range []string{"source-controller", "kustomize-controller"} { command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s", namespace, deployment, timeout.String()) - c = exec.CommandContext(ctx, "/bin/sh", "-c", command) - c.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) - c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) - err := c.Run() - if err != nil { - logFailure("install failed") - os.Exit(1) + if _, err := utils.execCommand(ctx, applyOutput, command); err != nil { + return fmt.Errorf("install failed") + } else { + logSuccess("%s ready", deployment) } } logSuccess("install finished") return nil } + +var namespaceTmpl = `--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{.Namespace}} +` + +var labelsTmpl = `--- +apiVersion: builtin +kind: LabelTransformer +metadata: + name: labels +labels: + app.kubernetes.io/instance: {{.Namespace}} + app.kubernetes.io/version: "{{.Version}}" +fieldSpecs: + - path: metadata/labels + create: true +` + +var kustomizationTmpl = `--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: {{.Namespace}} +transformers: + - labels.yaml +resources: + - namespace.yaml + - roles + - github.com/fluxcd/toolkit/manifests/bases/source-controller?ref={{.Version}} + - github.com/fluxcd/toolkit/manifests/bases/kustomize-controller?ref={{.Version}} + - github.com/fluxcd/toolkit/manifests/policies?ref={{.Version}} +` + +var kustomizationRolesTmpl = `--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - github.com/fluxcd/toolkit/manifests/rbac?ref={{.Version}} +nameSuffix: -{{.Namespace}} +` + +func genInstallManifests(ver, ns, tmpDir string) error { + model := struct { + Version string + Namespace string + }{ + Version: ver, + Namespace: ns, + } + + if err := utils.execTemplate(model, namespaceTmpl, path.Join(tmpDir, "namespace.yaml")); err != nil { + return fmt.Errorf("generate namespace failed: %w", err) + } + + if err := utils.execTemplate(model, labelsTmpl, path.Join(tmpDir, "labels.yaml")); err != nil { + return fmt.Errorf("generate labels failed: %w", err) + } + + if err := utils.execTemplate(model, kustomizationTmpl, path.Join(tmpDir, "kustomization.yaml")); err != nil { + return fmt.Errorf("generate kustomization failed: %w", err) + } + + if err := os.MkdirAll(path.Join(tmpDir, "roles"), os.ModePerm); err != nil { + return fmt.Errorf("generate roles failed: %w", err) + } + + if err := utils.execTemplate(model, kustomizationRolesTmpl, path.Join(tmpDir, "roles/kustomization.yaml")); err != nil { + return fmt.Errorf("generate roles failed: %w", err) + } + + return nil +} diff --git a/cmd/tk/main.go b/cmd/tk/main.go index b6b5797f..68a954b3 100644 --- a/cmd/tk/main.go +++ b/cmd/tk/main.go @@ -4,15 +4,12 @@ import ( "fmt" "log" "os" - "os/exec" "path/filepath" "time" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" - "k8s.io/client-go/kubernetes" _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/client-go/tools/clientcmd" ) var VERSION = "0.0.1" @@ -30,6 +27,8 @@ var ( kubeconfig string namespace string timeout time.Duration + verbose bool + utils Utils ) func init() { @@ -37,6 +36,8 @@ func init() { "the namespace scope for this operation") rootCmd.PersistentFlags().DurationVarP(&timeout, "timeout", "", 5*time.Minute, "timeout for this operation") + rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "", false, + "print generated objects") } func main() { @@ -56,29 +57,6 @@ func homeDir() string { return os.Getenv("USERPROFILE") // windows } -func kubernetesClient() (*kubernetes.Clientset, error) { - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) - if err != nil { - return nil, err - } - - client, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - - return client, nil -} - -func execCommand(command string) (string, error) { - c := exec.Command("/bin/sh", "-c", command) - output, err := c.CombinedOutput() - if err != nil { - return "", err - } - return string(output), nil -} - func logAction(format string, a ...interface{}) { fmt.Println(`✚`, fmt.Sprintf(format, a...)) } diff --git a/cmd/tk/uninstall.go b/cmd/tk/uninstall.go index aeea68cf..c3244209 100644 --- a/cmd/tk/uninstall.go +++ b/cmd/tk/uninstall.go @@ -1,12 +1,8 @@ package main import ( - "bytes" "context" "fmt" - "io" - "os" - "os/exec" "github.com/manifoldco/promptui" "github.com/spf13/cobra" @@ -25,6 +21,7 @@ cluster role bindings and CRDs`, var ( uninstallCRDs bool uninstallDryRun bool + uninstallSilent bool ) func init() { @@ -32,6 +29,8 @@ func init() { "removes all CRDs previously installed") uninstallCmd.Flags().BoolVarP(&uninstallDryRun, "dry-run", "", false, "only print the object that would be deleted") + uninstallCmd.Flags().BoolVarP(&uninstallSilent, "silent", "", false, + "delete components without asking for confirmation") rootCmd.AddCommand(uninstallCmd) } @@ -43,14 +42,13 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error { dryRun := "" if uninstallDryRun { dryRun = "--dry-run=client" - } else { + } else if !uninstallSilent { prompt := promptui.Prompt{ Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace), IsConfirm: true, } if _, err := prompt.Run(); err != nil { - logFailure("aborting") - os.Exit(1) + return fmt.Errorf("aborting") } } @@ -59,19 +57,11 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error { kinds += ",crds" } + logAction("uninstalling components") command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --timeout=%s %s", kinds, namespace, timeout.String(), dryRun) - c := exec.CommandContext(ctx, "/bin/sh", "-c", command) - - var stdoutBuf, stderrBuf bytes.Buffer - c.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) - c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) - - logAction("uninstalling components") - err := c.Run() - if err != nil { - logFailure("uninstall failed") - os.Exit(1) + if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { + return fmt.Errorf("uninstall failed") } logSuccess("uninstall finished") diff --git a/cmd/tk/utils.go b/cmd/tk/utils.go new file mode 100644 index 00000000..0bb511b3 --- /dev/null +++ b/cmd/tk/utils.go @@ -0,0 +1,100 @@ +package main + +import ( + "bufio" + "bytes" + "context" + "io" + "os" + "os/exec" + "text/template" + + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +type Utils struct { +} + +type ExecMode string + +const ( + ModeOS ExecMode = "os.stderr|stdout" + ModeStderrOS ExecMode = "os.stderr" + ModeCapture ExecMode = "capture.stderr|stdout" +) + +func (*Utils) execCommand(ctx context.Context, mode ExecMode, command string) (string, error) { + var stdoutBuf, stderrBuf bytes.Buffer + c := exec.CommandContext(ctx, "/bin/sh", "-c", command) + + if mode == ModeStderrOS { + c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) + } + if mode == ModeOS { + c.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf) + c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf) + } + + if mode == ModeStderrOS || mode == ModeOS { + if err := c.Run(); err != nil { + return "", err + } else { + return "", nil + } + } + + if mode == ModeCapture { + if output, err := c.CombinedOutput(); err != nil { + return "", err + } else { + return string(output), nil + } + } + + return "", nil +} + +func (*Utils) execTemplate(obj interface{}, tmpl, filename string) error { + t, err := template.New("tmpl").Parse(tmpl) + if err != nil { + return err + } + + var data bytes.Buffer + writer := bufio.NewWriter(&data) + if err := t.Execute(writer, obj); err != nil { + return err + } + + if err := writer.Flush(); err != nil { + return err + } + + file, err := os.Create(filename) + if err != nil { + return err + } + defer file.Close() + + _, err = io.WriteString(file, data.String()) + if err != nil { + return err + } + + return file.Sync() +} + +func (*Utils) kubeClient(config string) (*kubernetes.Clientset, error) { + cfg, err := clientcmd.BuildConfigFromFlags("", config) + if err != nil { + return nil, err + } + + client, err := kubernetes.NewForConfig(cfg) + if err != nil { + return nil, err + } + + return client, nil +} diff --git a/go.mod b/go.mod index 826f0689..29c9b457 100644 --- a/go.mod +++ b/go.mod @@ -6,5 +6,5 @@ require ( github.com/blang/semver v3.5.1+incompatible github.com/manifoldco/promptui v0.7.0 github.com/spf13/cobra v0.0.6 - k8s.io/client-go v0.18.0 + k8s.io/client-go v0.18.2 ) diff --git a/go.sum b/go.sum index eb16da42..cb09b154 100644 --- a/go.sum +++ b/go.sum @@ -298,12 +298,12 @@ 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.18.0 h1:lwYk8Vt7rsVTwjRU6pzEsa9YNhThbmbocQlKvNBB4EQ= -k8s.io/api v0.18.0/go.mod h1:q2HRQkfDzHMBZL9l/y9rH63PkQl4vae0xRT+8prbrK8= -k8s.io/apimachinery v0.18.0 h1:fuPfYpk3cs1Okp/515pAf0dNhL66+8zk8RLbSX+EgAE= -k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= -k8s.io/client-go v0.18.0 h1:yqKw4cTUQraZK3fcVCMeSa+lqKwcjZ5wtcOIPnxQno4= -k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= +k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8= +k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78= +k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= +k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= +k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= +k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU= 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=