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