1
16 package action
17
18 import (
19 "flag"
20 "io"
21 "testing"
22
23 fakeclientset "k8s.io/client-go/kubernetes/fake"
24
25 "helm.sh/helm/v3/pkg/chart"
26 "helm.sh/helm/v3/pkg/chartutil"
27 kubefake "helm.sh/helm/v3/pkg/kube/fake"
28 "helm.sh/helm/v3/pkg/registry"
29 "helm.sh/helm/v3/pkg/release"
30 "helm.sh/helm/v3/pkg/storage"
31 "helm.sh/helm/v3/pkg/storage/driver"
32 "helm.sh/helm/v3/pkg/time"
33 )
34
35 var verbose = flag.Bool("test.log", false, "enable test logging")
36
37 func actionConfigFixture(t *testing.T) *Configuration {
38 t.Helper()
39
40 registryClient, err := registry.NewClient()
41 if err != nil {
42 t.Fatal(err)
43 }
44
45 return &Configuration{
46 Releases: storage.Init(driver.NewMemory()),
47 KubeClient: &kubefake.FailingKubeClient{PrintingKubeClient: kubefake.PrintingKubeClient{Out: io.Discard}},
48 Capabilities: chartutil.DefaultCapabilities,
49 RegistryClient: registryClient,
50 Log: func(format string, v ...interface{}) {
51 t.Helper()
52 if *verbose {
53 t.Logf(format, v...)
54 }
55 },
56 }
57 }
58
59 var manifestWithHook = `kind: ConfigMap
60 metadata:
61 name: test-cm
62 annotations:
63 "helm.sh/hook": post-install,pre-delete,post-upgrade
64 data:
65 name: value`
66
67 var manifestWithTestHook = `kind: Pod
68 metadata:
69 name: finding-nemo,
70 annotations:
71 "helm.sh/hook": test
72 spec:
73 containers:
74 - name: nemo-test
75 image: fake-image
76 cmd: fake-command
77 `
78
79 var rbacManifests = `apiVersion: rbac.authorization.k8s.io/v1
80 kind: Role
81 metadata:
82 name: schedule-agents
83 rules:
84 - apiGroups: [""]
85 resources: ["pods", "pods/exec", "pods/log"]
86 verbs: ["*"]
87
88 ---
89
90 apiVersion: rbac.authorization.k8s.io/v1
91 kind: RoleBinding
92 metadata:
93 name: schedule-agents
94 namespace: {{ default .Release.Namespace}}
95 roleRef:
96 apiGroup: rbac.authorization.k8s.io
97 kind: Role
98 name: schedule-agents
99 subjects:
100 - kind: ServiceAccount
101 name: schedule-agents
102 namespace: {{ .Release.Namespace }}
103 `
104
105 type chartOptions struct {
106 *chart.Chart
107 }
108
109 type chartOption func(*chartOptions)
110
111 func buildChart(opts ...chartOption) *chart.Chart {
112 c := &chartOptions{
113 Chart: &chart.Chart{
114
115 Metadata: &chart.Metadata{
116 APIVersion: "v1",
117 Name: "hello",
118 Version: "0.1.0",
119 },
120
121 Templates: []*chart.File{
122 {Name: "templates/hello", Data: []byte("hello: world")},
123 {Name: "templates/hooks", Data: []byte(manifestWithHook)},
124 },
125 },
126 }
127
128 for _, opt := range opts {
129 opt(c)
130 }
131
132 return c.Chart
133 }
134
135 func withName(name string) chartOption {
136 return func(opts *chartOptions) {
137 opts.Metadata.Name = name
138 }
139 }
140
141 func withSampleValues() chartOption {
142 values := map[string]interface{}{
143 "someKey": "someValue",
144 "nestedKey": map[string]interface{}{
145 "simpleKey": "simpleValue",
146 "anotherNestedKey": map[string]interface{}{
147 "yetAnotherNestedKey": map[string]interface{}{
148 "youReadyForAnotherNestedKey": "No",
149 },
150 },
151 },
152 }
153 return func(opts *chartOptions) {
154 opts.Values = values
155 }
156 }
157
158 func withValues(values map[string]interface{}) chartOption {
159 return func(opts *chartOptions) {
160 opts.Values = values
161 }
162 }
163
164 func withNotes(notes string) chartOption {
165 return func(opts *chartOptions) {
166 opts.Templates = append(opts.Templates, &chart.File{
167 Name: "templates/NOTES.txt",
168 Data: []byte(notes),
169 })
170 }
171 }
172
173 func withDependency(dependencyOpts ...chartOption) chartOption {
174 return func(opts *chartOptions) {
175 opts.AddDependency(buildChart(dependencyOpts...))
176 }
177 }
178
179 func withMetadataDependency(dependency chart.Dependency) chartOption {
180 return func(opts *chartOptions) {
181 opts.Metadata.Dependencies = append(opts.Metadata.Dependencies, &dependency)
182 }
183 }
184
185 func withSampleTemplates() chartOption {
186 return func(opts *chartOptions) {
187 sampleTemplates := []*chart.File{
188
189 {Name: "templates/goodbye", Data: []byte("goodbye: world")},
190 {Name: "templates/empty", Data: []byte("")},
191 {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)},
192 {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)},
193 }
194 opts.Templates = append(opts.Templates, sampleTemplates...)
195 }
196 }
197
198 func withSampleSecret() chartOption {
199 return func(opts *chartOptions) {
200 sampleSecret := &chart.File{Name: "templates/secret.yaml", Data: []byte("apiVersion: v1\nkind: Secret\n")}
201 opts.Templates = append(opts.Templates, sampleSecret)
202 }
203 }
204
205 func withSampleIncludingIncorrectTemplates() chartOption {
206 return func(opts *chartOptions) {
207 sampleTemplates := []*chart.File{
208
209 {Name: "templates/goodbye", Data: []byte("goodbye: world")},
210 {Name: "templates/empty", Data: []byte("")},
211 {Name: "templates/incorrect", Data: []byte("{{ .Values.bad.doh }}")},
212 {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)},
213 {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)},
214 }
215 opts.Templates = append(opts.Templates, sampleTemplates...)
216 }
217 }
218
219 func withMultipleManifestTemplate() chartOption {
220 return func(opts *chartOptions) {
221 sampleTemplates := []*chart.File{
222 {Name: "templates/rbac", Data: []byte(rbacManifests)},
223 }
224 opts.Templates = append(opts.Templates, sampleTemplates...)
225 }
226 }
227
228 func withKube(version string) chartOption {
229 return func(opts *chartOptions) {
230 opts.Metadata.KubeVersion = version
231 }
232 }
233
234
235 func releaseStub() *release.Release {
236 return namedReleaseStub("angry-panda", release.StatusDeployed)
237 }
238
239 func namedReleaseStub(name string, status release.Status) *release.Release {
240 now := time.Now()
241 return &release.Release{
242 Name: name,
243 Info: &release.Info{
244 FirstDeployed: now,
245 LastDeployed: now,
246 Status: status,
247 Description: "Named Release Stub",
248 },
249 Chart: buildChart(withSampleTemplates()),
250 Config: map[string]interface{}{"name": "value"},
251 Version: 1,
252 Hooks: []*release.Hook{
253 {
254 Name: "test-cm",
255 Kind: "ConfigMap",
256 Path: "test-cm",
257 Manifest: manifestWithHook,
258 Events: []release.HookEvent{
259 release.HookPostInstall,
260 release.HookPreDelete,
261 },
262 },
263 {
264 Name: "finding-nemo",
265 Kind: "Pod",
266 Path: "finding-nemo",
267 Manifest: manifestWithTestHook,
268 Events: []release.HookEvent{
269 release.HookTest,
270 },
271 },
272 },
273 }
274 }
275
276 func TestGetVersionSet(t *testing.T) {
277 client := fakeclientset.NewSimpleClientset()
278
279 vs, err := GetVersionSet(client.Discovery())
280 if err != nil {
281 t.Error(err)
282 }
283
284 if !vs.Has("v1") {
285 t.Errorf("Expected supported versions to at least include v1.")
286 }
287 if vs.Has("nosuchversion/v1") {
288 t.Error("Non-existent version is reported found.")
289 }
290 }
291
View as plain text