github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/action/action_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 package action 17 18 import ( 19 "flag" 20 "io/ioutil" 21 "testing" 22 23 fakeclientset "k8s.io/client-go/kubernetes/fake" 24 25 "github.com/stefanmcshane/helm/pkg/chart" 26 "github.com/stefanmcshane/helm/pkg/chartutil" 27 kubefake "github.com/stefanmcshane/helm/pkg/kube/fake" 28 "github.com/stefanmcshane/helm/pkg/registry" 29 "github.com/stefanmcshane/helm/pkg/release" 30 "github.com/stefanmcshane/helm/pkg/storage" 31 "github.com/stefanmcshane/helm/pkg/storage/driver" 32 "github.com/stefanmcshane/helm/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: ioutil.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 // TODO: This should be more complete. 115 Metadata: &chart.Metadata{ 116 APIVersion: "v1", 117 Name: "hello", 118 Version: "0.1.0", 119 }, 120 // This adds a basic template and hooks. 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 // This adds basic templates and partials. 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 withSampleIncludingIncorrectTemplates() chartOption { 199 return func(opts *chartOptions) { 200 sampleTemplates := []*chart.File{ 201 // This adds basic templates and partials. 202 {Name: "templates/goodbye", Data: []byte("goodbye: world")}, 203 {Name: "templates/empty", Data: []byte("")}, 204 {Name: "templates/incorrect", Data: []byte("{{ .Values.bad.doh }}")}, 205 {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, 206 {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, 207 } 208 opts.Templates = append(opts.Templates, sampleTemplates...) 209 } 210 } 211 212 func withMultipleManifestTemplate() chartOption { 213 return func(opts *chartOptions) { 214 sampleTemplates := []*chart.File{ 215 {Name: "templates/rbac", Data: []byte(rbacManifests)}, 216 } 217 opts.Templates = append(opts.Templates, sampleTemplates...) 218 } 219 } 220 221 func withKube(version string) chartOption { 222 return func(opts *chartOptions) { 223 opts.Metadata.KubeVersion = version 224 } 225 } 226 227 // releaseStub creates a release stub, complete with the chartStub as its chart. 228 func releaseStub() *release.Release { 229 return namedReleaseStub("angry-panda", release.StatusDeployed) 230 } 231 232 func namedReleaseStub(name string, status release.Status) *release.Release { 233 now := time.Now() 234 return &release.Release{ 235 Name: name, 236 Info: &release.Info{ 237 FirstDeployed: now, 238 LastDeployed: now, 239 Status: status, 240 Description: "Named Release Stub", 241 }, 242 Chart: buildChart(withSampleTemplates()), 243 Config: map[string]interface{}{"name": "value"}, 244 Version: 1, 245 Hooks: []*release.Hook{ 246 { 247 Name: "test-cm", 248 Kind: "ConfigMap", 249 Path: "test-cm", 250 Manifest: manifestWithHook, 251 Events: []release.HookEvent{ 252 release.HookPostInstall, 253 release.HookPreDelete, 254 }, 255 }, 256 { 257 Name: "finding-nemo", 258 Kind: "Pod", 259 Path: "finding-nemo", 260 Manifest: manifestWithTestHook, 261 Events: []release.HookEvent{ 262 release.HookTest, 263 }, 264 }, 265 }, 266 } 267 } 268 269 func TestGetVersionSet(t *testing.T) { 270 client := fakeclientset.NewSimpleClientset() 271 272 vs, err := GetVersionSet(client.Discovery()) 273 if err != nil { 274 t.Error(err) 275 } 276 277 if !vs.Has("v1") { 278 t.Errorf("Expected supported versions to at least include v1.") 279 } 280 if vs.Has("nosuchversion/v1") { 281 t.Error("Non-existent version is reported found.") 282 } 283 }