gitlab.com/sparetimecoders/build-tools@v0.1.0/pkg/kubectl/kubectl.go (about) 1 package kubectl 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "github.com/liamg/tml" 8 "gitlab.com/sparetimecoders/build-tools/pkg/config" 9 "io" 10 "io/ioutil" 11 _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" 12 "k8s.io/kubernetes/pkg/kubectl/cmd" 13 cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 14 "os" 15 "path/filepath" 16 "sort" 17 "strings" 18 ) 19 20 type Kubectl interface { 21 Apply(input string) error 22 Cleanup() 23 DeploymentExists(name string) bool 24 RolloutStatus(name string) bool 25 DeploymentEvents(name string) string 26 PodEvents(name string) string 27 } 28 29 type kubectl struct { 30 args map[string]string 31 tempDir string 32 out io.Writer 33 eout io.Writer 34 } 35 36 var newKubectlCmd = cmd.NewKubectlCommand 37 38 func New(environment *config.Environment, out, eout io.Writer) Kubectl { 39 name, _ := ioutil.TempDir(os.TempDir(), "build-tools") 40 41 arg := argsFromEnvironment(environment, name, out) 42 return &kubectl{args: arg, tempDir: name, out: out, eout: eout} 43 } 44 45 func argsFromEnvironment(e *config.Environment, tempDir string, out io.Writer) map[string]string { 46 kubeConfigArg := "kubeconfig" 47 args := make(map[string]string) 48 if len(e.Context) > 0 { 49 args["context"] = e.Context 50 } 51 if len(e.Namespace) > 0 { 52 args["namespace"] = e.Namespace 53 } 54 if content, exist := os.LookupEnv(envKubeConfigContent); exist { 55 // Not a file, create file from content 56 kubeconfigFile := filepath.Join(tempDir, kubeConfigArg) 57 _ = ioutil.WriteFile(kubeconfigFile, []byte(content), 0777) 58 args[kubeConfigArg] = kubeconfigFile 59 } else if len(e.Kubeconfig) > 0 { 60 args[kubeConfigArg] = e.Kubeconfig 61 } 62 if _, exists := args[kubeConfigArg]; exists { 63 _, _ = fmt.Fprintln(out, tml.Sprintf("Using kubeconfig: <green>'%s'</green>", args[kubeConfigArg])) 64 } 65 66 return args 67 } 68 69 func (k kubectl) defaultArgs() (args []string) { 70 var keys []string 71 for key, _ := range k.args { 72 keys = append(keys, key) 73 } 74 sort.Strings(keys) 75 for _, key := range keys { 76 args = append(args, fmt.Sprintf("--%s", key), k.args[key]) 77 } 78 79 return 80 } 81 82 func (k kubectl) Apply(input string) error { 83 file := filepath.Join(k.tempDir, "content.yaml") 84 err := ioutil.WriteFile(file, []byte(input), 0777) 85 if err != nil { 86 return err 87 } 88 89 args := append(k.defaultArgs(), "apply", "-f", file) 90 c := newKubectlCmd(os.Stdin, os.Stdout, os.Stderr) 91 c.SetArgs(args) 92 return c.Execute() 93 } 94 95 func (k kubectl) Cleanup() { 96 _ = os.RemoveAll(k.tempDir) 97 } 98 99 func (k kubectl) DeploymentExists(name string) bool { 100 args := k.defaultArgs() 101 args = append(args, "get", "deployment", name) 102 _, _ = fmt.Fprintf(k.out, "kubectl %s\n", strings.Join(args, " ")) 103 c := newKubectlCmd(os.Stdin, k.out, k.eout) 104 c.SetArgs(args) 105 return c.Execute() == nil 106 } 107 108 func (k kubectl) RolloutStatus(name string) bool { 109 args := k.defaultArgs() 110 args = append(args, "rollout", "status", "deployment", "--timeout=2m", name) 111 _, _ = fmt.Fprintf(k.out, "kubectl %s\n", strings.Join(args, " ")) 112 c := newKubectlCmd(os.Stdin, os.Stdout, os.Stderr) 113 c.SetArgs(args) 114 success := true 115 cmdutil.BehaviorOnFatal(func(str string, code int) { 116 fmt.Println(str) 117 success = false 118 }) 119 if err := c.Execute(); err != nil { 120 success = false 121 } 122 return success 123 } 124 125 func (k kubectl) DeploymentEvents(name string) string { 126 args := k.defaultArgs() 127 args = append(args, "describe", "deployment", name, "--show-events=true") 128 _, _ = fmt.Fprintf(k.out, "kubectl %s\n", strings.Join(args, " ")) 129 buffer := bytes.Buffer{} 130 c := newKubectlCmd(os.Stdin, &buffer, &buffer) 131 c.SetArgs(args) 132 if err := c.Execute(); err != nil { 133 return err.Error() 134 } 135 return k.extractEvents(buffer.String()) 136 } 137 138 func (k kubectl) PodEvents(name string) string { 139 args := k.defaultArgs() 140 args = append(args, "describe", "pods", "-l", fmt.Sprintf("app=%s", name), "--show-events=true") 141 _, _ = fmt.Fprintf(k.out, "kubectl %s\n", strings.Join(args, " ")) 142 buffer := bytes.Buffer{} 143 c := newKubectlCmd(os.Stdin, &buffer, &buffer) 144 c.SetArgs(args) 145 if err := c.Execute(); err != nil { 146 return err.Error() 147 } 148 return k.extractEvents(buffer.String()) 149 } 150 151 func (k kubectl) extractEvents(output string) string { 152 scanner := bufio.NewScanner(strings.NewReader(output)) 153 var events strings.Builder 154 found := false 155 for scanner.Scan() { 156 text := scanner.Text() 157 if found || (strings.Index(text, "Events:") == 0 && strings.Index(text, "<none>") == -1) { 158 found = true 159 events.WriteString(text) 160 events.WriteString("\n") 161 } 162 } 163 164 return events.String() 165 } 166 167 var _ Kubectl = &kubectl{} 168 169 const envKubeConfigContent = "KUBECONFIG_CONTENT"