-
Notifications
You must be signed in to change notification settings - Fork 29
/
runner.go
198 lines (155 loc) · 3.8 KB
/
runner.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package chartify
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
)
type RunCommandFunc func(name string, args []string, dir string, stdout, stderr io.Writer, env map[string]string) error
type Runner struct {
// HelmBinary is the name or the path to `helm` command
HelmBinary string
// KustomizeBinary is the name or the path to `kustomize` command
KustomizeBinary string
isHelm3 bool
RunCommand RunCommandFunc
CopyFile func(src, dst string) error
WriteFile func(filename string, data []byte, perm os.FileMode) error
ReadFile func(filename string) ([]byte, error)
ReadDir func(dirname string) ([]os.DirEntry, error)
Walk func(root string, walkFn filepath.WalkFunc) error
MakeTempDir func(release, chart string, opts *ChartifyOpts) string
Exists func(path string) (bool, error)
// Logf is the alternative log function used by chartify
Logf func(string, ...interface{})
}
type Option func(*Runner) error
func HelmBin(b string) Option {
return func(r *Runner) error {
r.HelmBinary = b
return nil
}
}
func UseHelm3(u bool) Option {
return func(r *Runner) error {
r.isHelm3 = u
return nil
}
}
func WithLogf(logf func(string, ...interface{})) Option {
return func(r *Runner) error {
r.Logf = logf
return nil
}
}
func KustomizeBin(b string) Option {
return func(r *Runner) error {
r.KustomizeBinary = b
return nil
}
}
func New(opts ...Option) *Runner {
r := &Runner{
RunCommand: RunCommand,
CopyFile: CopyFile,
WriteFile: os.WriteFile,
ReadFile: os.ReadFile,
ReadDir: os.ReadDir,
Walk: filepath.Walk,
Exists: exists,
Logf: printf,
MakeTempDir: makeTempDir,
}
for i := range opts {
if err := opts[i](r); err != nil {
panic(err)
}
}
return r
}
func (r *Runner) helmBin() string {
if r.HelmBinary != "" {
return r.HelmBinary
}
return os.Getenv("HELM_BIN")
}
func (r *Runner) kustomizeBin() string {
if r.KustomizeBinary != "" {
return r.KustomizeBinary
}
return "kustomize"
}
func (r *Runner) run(cmd string, args ...string) (string, error) {
bytes, err := r.runBytes("", cmd, args...)
var out string
if bytes != nil {
out = string(bytes)
}
return out, err
}
func (r *Runner) runInDir(dir, cmd string, args ...string) (string, error) {
bytes, err := r.runBytes(dir, cmd, args...)
var out string
if bytes != nil {
out = string(bytes)
}
return out, err
}
func (r *Runner) runBytes(dir, cmd string, args ...string) ([]byte, error) {
nameArgs := strings.Split(cmd, " ")
name := nameArgs[0]
if len(nameArgs) > 2 {
a := append([]string{}, nameArgs[1:]...)
a = append(a, args...)
args = a
}
bytes, errBytes, err := r.captureBytes(name, args, dir)
if err != nil {
c := strings.Join(append([]string{name}, args...), " ")
wrappedErr := fmt.Errorf(`%w
COMMAND:
%s
OUTPUT:
%s`,
err,
indent(c, " "),
indent(string(errBytes), " "),
)
return bytes, wrappedErr
}
return bytes, nil
}
func (r *Runner) IsHelm3() bool {
if r.isHelm3 {
return true
}
// Support explicit opt-in via environment variable
if os.Getenv("HELM_X_HELM3") != "" {
return true
}
// Autodetect from `helm version`
out, err := r.run(r.helmBin(), "version", "--client", "--short")
if err != nil {
panic(err)
}
return strings.HasPrefix(out, "v3.")
}
func (r *Runner) captureBytes(binary string, args []string, dir string) ([]byte, []byte, error) {
r.Logf("running %s %s", binary, strings.Join(args, " "))
_, err := exec.LookPath(binary)
if err != nil {
return nil, nil, err
}
var stdout, stderr bytes.Buffer
err = r.RunCommand(binary, args, dir, &stdout, &stderr, map[string]string{})
if err != nil {
r.Logf(stderr.String())
}
return stdout.Bytes(), stderr.Bytes(), err
}
func printf(format string, vars ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", vars...)
}