github.com/grafana/tanka@v0.26.1-0.20240506093700-c22cfc35c21a/pkg/kubernetes/client/get.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/Masterminds/semver"
    11  
    12  	"github.com/grafana/tanka/pkg/kubernetes/manifest"
    13  )
    14  
    15  // Get retrieves a single Kubernetes object from the cluster
    16  func (k Kubectl) Get(namespace, kind, name string) (manifest.Manifest, error) {
    17  	return k.get(namespace, kind, []string{name}, getOpts{})
    18  }
    19  
    20  // GetByLabels retrieves all objects matched by the given labels from the cluster.
    21  // Set namespace to empty string for --all-namespaces
    22  func (k Kubectl) GetByLabels(namespace, kind string, labels map[string]string) (manifest.List, error) {
    23  	lArgs := make([]string, 0, len(labels))
    24  	for k, v := range labels {
    25  		lArgs = append(lArgs, fmt.Sprintf("-l=%s=%s", k, v))
    26  	}
    27  	// Needed to properly filter for resources that should be pruned
    28  	if k.info.ClientVersion.GreaterThan(semver.MustParse("1.21.0")) {
    29  		lArgs = append(lArgs, "--show-managed-fields")
    30  	}
    31  	var opts getOpts
    32  	if namespace == "" {
    33  		opts.allNamespaces = true
    34  	}
    35  	list, err := k.get(namespace, kind, lArgs, opts)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	return unwrapList(list)
    41  }
    42  
    43  // GetByState returns the full object, including runtime fields for each
    44  // resource in the state
    45  func (k Kubectl) GetByState(data manifest.List, opts GetByStateOpts) (manifest.List, error) {
    46  	list, err := k.get("", "", []string{"-f", "-"}, getOpts{
    47  		ignoreNotFound: opts.IgnoreNotFound,
    48  		stdin:          data.String(),
    49  	})
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	return unwrapList(list)
    55  }
    56  
    57  type getOpts struct {
    58  	allNamespaces  bool
    59  	ignoreNotFound bool
    60  	stdin          string
    61  }
    62  
    63  func (k Kubectl) get(namespace, kind string, selector []string, opts getOpts) (manifest.Manifest, error) {
    64  	// build cli flags and args
    65  	argv := []string{
    66  		"-o", "json",
    67  	}
    68  	if opts.ignoreNotFound {
    69  		argv = append(argv, "--ignore-not-found")
    70  	}
    71  
    72  	if opts.allNamespaces {
    73  		argv = append(argv, "--all-namespaces")
    74  	} else if namespace != "" {
    75  		argv = append(argv, "-n", namespace)
    76  	}
    77  
    78  	if kind != "" {
    79  		argv = append(argv, kind)
    80  	}
    81  
    82  	argv = append(argv, selector...)
    83  
    84  	// setup command environment
    85  	cmd := k.ctl("get", argv...)
    86  	var sout, serr bytes.Buffer
    87  	cmd.Stdout = &sout
    88  	cmd.Stderr = &serr
    89  	if opts.stdin != "" {
    90  		cmd.Stdin = strings.NewReader(opts.stdin)
    91  	}
    92  
    93  	// run command
    94  	if err := cmd.Run(); err != nil {
    95  		return nil, parseGetErr(err, serr.String())
    96  	}
    97  
    98  	// return error if nothing was returned
    99  	// because parsing empty output as json would cause errors
   100  	if sout.Len() == 0 {
   101  		return nil, ErrorNothingReturned{}
   102  	}
   103  
   104  	// parse result
   105  	var m manifest.Manifest
   106  	if err := json.Unmarshal(sout.Bytes(), &m); err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	return m, nil
   111  }
   112  
   113  func parseGetErr(err error, stderr string) error {
   114  	if strings.HasPrefix(stderr, "Error from server (NotFound)") {
   115  		return ErrorNotFound{stderr}
   116  	}
   117  	if strings.HasPrefix(stderr, "error: the server doesn't have a resource type") {
   118  		return ErrorUnknownResource{stderr}
   119  	}
   120  
   121  	return errors.New(strings.TrimPrefix(fmt.Sprintf("%s\n%s", stderr, err), "\n"))
   122  }
   123  
   124  func unwrapList(list manifest.Manifest) (manifest.List, error) {
   125  	if list.Kind() != "List" {
   126  		return nil, fmt.Errorf("expected kind `List` but got `%s` instead", list.Kind())
   127  	}
   128  
   129  	items := list["items"].([]interface{})
   130  	ms := make(manifest.List, 0, len(items))
   131  	for _, i := range items {
   132  		ms = append(ms, manifest.Manifest(i.(map[string]interface{})))
   133  	}
   134  
   135  	return ms, nil
   136  }