From 4989826124eed8a3b74d3fa9cbc00959a5ee89b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAnton?= Date: Thu, 15 Oct 2020 20:37:23 +0300 Subject: [PATCH] Get rid of shell-out and invoke kubetcl directly via exec --- cmd/gotk/bootstrap.go | 13 ++++++------- cmd/gotk/check.go | 24 +++++++++++++++++------- cmd/gotk/install.go | 14 ++++++-------- cmd/gotk/uninstall.go | 30 ++++++++++++++++++++---------- cmd/gotk/utils.go | 5 +++-- 5 files changed, 52 insertions(+), 34 deletions(-) diff --git a/cmd/gotk/bootstrap.go b/cmd/gotk/bootstrap.go index 91c3fa22..35c05845 100644 --- a/cmd/gotk/bootstrap.go +++ b/cmd/gotk/bootstrap.go @@ -150,15 +150,14 @@ func generateInstallManifests(targetPath, namespace, tmpDir string, localManifes } func applyInstallManifests(ctx context.Context, manifestPath string, components []string) error { - command := fmt.Sprintf("kubectl apply -f %s", manifestPath) - if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { + kubectlArgs := []string{"apply", "-f", manifestPath} + if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil { return fmt.Errorf("install failed") } for _, deployment := range components { - command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s", - namespace, deployment, timeout.String()) - if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { + kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()} + if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil { return fmt.Errorf("install failed") } } @@ -239,8 +238,8 @@ func generateSyncManifests(url, branch, name, namespace, targetPath, tmpDir stri } func applySyncManifests(ctx context.Context, kubeClient client.Client, name, namespace, targetPath, tmpDir string) error { - command := fmt.Sprintf("kubectl apply -k %s", filepath.Join(tmpDir, targetPath, namespace)) - if _, err := utils.execCommand(ctx, ModeStderrOS, command); err != nil { + kubectlArgs := []string{"apply", "-k", filepath.Join(tmpDir, targetPath, namespace)} + if _, err := utils.execKubectlCommand(ctx, ModeStderrOS, kubectlArgs...); err != nil { return err } diff --git a/cmd/gotk/check.go b/cmd/gotk/check.go index fbbf904e..64b86b5c 100644 --- a/cmd/gotk/check.go +++ b/cmd/gotk/check.go @@ -18,13 +18,14 @@ package main import ( "context" - "fmt" + "encoding/json" "os" "os/exec" "strings" "github.com/blang/semver/v4" "github.com/spf13/cobra" + apimachineryversion "k8s.io/apimachinery/pkg/version" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" ) @@ -48,6 +49,10 @@ var ( checkComponents []string ) +type kubectlVersion struct { + ClientVersion *apimachineryversion.Info `json:"clientVersion"` +} + func init() { checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false, "only run pre-installation checks") @@ -97,14 +102,20 @@ func kubectlCheck(ctx context.Context, version string) bool { return false } - command := "kubectl version --client --short | awk '{ print $3 }'" - output, err := utils.execCommand(ctx, ModeCapture, command) + kubectlArgs := []string{"version", "--client", "--output", "json"} + output, err := utils.execKubectlCommand(ctx, ModeCapture, kubectlArgs...) if err != nil { logger.Failuref("kubectl version can't be determined") return false } - v, err := semver.ParseTolerant(output) + kv := &kubectlVersion{} + if err = json.Unmarshal([]byte(output), kv); err != nil { + logger.Failuref("kubectl version output can't be unmarshaled") + return false + } + + v, err := semver.ParseTolerant(kv.ClientVersion.GitVersion) if err != nil { logger.Failuref("kubectl version can't be parsed") return false @@ -161,9 +172,8 @@ func componentsCheck() bool { ok := true for _, deployment := range checkComponents { - command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s", - namespace, deployment, timeout.String()) - if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil { + kubectlArgs := []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()} + if output, err := utils.execKubectlCommand(ctx, ModeCapture, kubectlArgs...); err != nil { logger.Failuref("%s: %s", deployment, strings.TrimSuffix(output, "\n")) ok = false } else { diff --git a/cmd/gotk/install.go b/cmd/gotk/install.go index 5aefbcb2..72a87be5 100644 --- a/cmd/gotk/install.go +++ b/cmd/gotk/install.go @@ -158,14 +158,13 @@ func installCmdRun(cmd *cobra.Command, args []string) error { if verbose { applyOutput = ModeOS } - dryRun := "" + + kubectlArgs := []string{"apply", "-f", manifest} if installDryRun { - dryRun = "--dry-run=client" + args = append(args, "--dry-run=client") applyOutput = ModeOS } - - command := fmt.Sprintf("kubectl apply -f %s %s", manifest, dryRun) - if _, err := utils.execCommand(ctx, applyOutput, command); err != nil { + if _, err := utils.execKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil { return fmt.Errorf("install failed") } @@ -178,9 +177,8 @@ func installCmdRun(cmd *cobra.Command, args []string) error { logger.Waitingf("verifying installation") for _, deployment := range installComponents { - command = fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s", - namespace, deployment, timeout.String()) - if _, err := utils.execCommand(ctx, applyOutput, command); err != nil { + kubectlArgs = []string{"-n", namespace, "rollout", "status", "deployment", deployment, "--timeout", timeout.String()} + if _, err := utils.execKubectlCommand(ctx, applyOutput, kubectlArgs...); err != nil { return fmt.Errorf("install failed") } else { logger.Successf("%s ready", deployment) diff --git a/cmd/gotk/uninstall.go b/cmd/gotk/uninstall.go index 61f15981..89e1aa97 100644 --- a/cmd/gotk/uninstall.go +++ b/cmd/gotk/uninstall.go @@ -71,10 +71,8 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error { return err } - dryRun := "" - if uninstallDryRun { - dryRun = "--dry-run=server" - } else if !uninstallSilent { + dryRun := "--dry-run=server" + if !uninstallDryRun && !uninstallSilent { prompt := promptui.Prompt{ Label: fmt.Sprintf("Are you sure you want to delete the %s namespace", namespace), IsConfirm: true, @@ -105,9 +103,15 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error { sourcev1.HelmRepositoryKind, helmv2.HelmReleaseKind, } { - command := fmt.Sprintf("kubectl -n %s delete %s --all --ignore-not-found --timeout=%s %s", - namespace, kind, timeout.String(), dryRun) - if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { + kubectlArgs := []string{ + "-n", namespace, + "delete", kind, "--all", "--ignore-not-found", + "--timeout", timeout.String(), + } + if uninstallDryRun { + kubectlArgs = append(kubectlArgs, dryRun) + } + if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil { return fmt.Errorf("uninstall failed: %w", err) } } @@ -123,9 +127,15 @@ func uninstallCmdRun(cmd *cobra.Command, args []string) error { logger.Actionf("uninstalling components") for _, kind := range kinds { - command := fmt.Sprintf("kubectl delete %s -l app.kubernetes.io/instance=%s --ignore-not-found --timeout=%s %s", - kind, namespace, timeout.String(), dryRun) - if _, err := utils.execCommand(ctx, ModeOS, command); err != nil { + kubectlArgs := []string{ + "delete", kind, + "-l", fmt.Sprintf("app.kubernetes.io/instance=%s", namespace), + "--ignore-not-found", "--timeout", timeout.String(), + } + if uninstallDryRun { + kubectlArgs = append(kubectlArgs, dryRun) + } + if _, err := utils.execKubectlCommand(ctx, ModeOS, kubectlArgs...); err != nil { return fmt.Errorf("uninstall failed: %w", err) } } diff --git a/cmd/gotk/utils.go b/cmd/gotk/utils.go index 2d80e71d..45c01f3a 100644 --- a/cmd/gotk/utils.go +++ b/cmd/gotk/utils.go @@ -60,9 +60,10 @@ const ( ModeCapture ExecMode = "capture.stderr|stdout" ) -func (*Utils) execCommand(ctx context.Context, mode ExecMode, command string) (string, error) { +func (*Utils) execKubectlCommand(ctx context.Context, mode ExecMode, args ...string) (string, error) { var stdoutBuf, stderrBuf bytes.Buffer - c := exec.CommandContext(ctx, "/bin/sh", "-c", command) + + c := exec.CommandContext(ctx, "kubectl", args...) if mode == ModeStderrOS { c.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)