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"