github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/pkg/action/install_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 17 package action 18 19 import ( 20 "fmt" 21 "reflect" 22 "regexp" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 27 "helm.sh/helm/pkg/release" 28 ) 29 30 type nameTemplateTestCase struct { 31 tpl string 32 expected string 33 expectedErrorStr string 34 } 35 36 func installAction(t *testing.T) *Install { 37 config := actionConfigFixture(t) 38 instAction := NewInstall(config) 39 instAction.Namespace = "spaced" 40 instAction.ReleaseName = "test-install-release" 41 42 return instAction 43 } 44 45 func TestInstallRelease(t *testing.T) { 46 is := assert.New(t) 47 instAction := installAction(t) 48 instAction.rawValues = map[string]interface{}{} 49 res, err := instAction.Run(buildChart()) 50 if err != nil { 51 t.Fatalf("Failed install: %s", err) 52 } 53 is.Equal(res.Name, "test-install-release", "Expected release name.") 54 is.Equal(res.Namespace, "spaced") 55 56 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 57 is.NoError(err) 58 59 is.Len(rel.Hooks, 1) 60 is.Equal(rel.Hooks[0].Manifest, manifestWithHook) 61 is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) 62 is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") 63 64 is.NotEqual(len(res.Manifest), 0) 65 is.NotEqual(len(rel.Manifest), 0) 66 is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 67 is.Equal(rel.Info.Description, "Install complete") 68 } 69 70 func TestInstallRelease_NoName(t *testing.T) { 71 instAction := installAction(t) 72 instAction.ReleaseName = "" 73 instAction.rawValues = map[string]interface{}{} 74 _, err := instAction.Run(buildChart()) 75 if err == nil { 76 t.Fatal("expected failure when no name is specified") 77 } 78 assert.Contains(t, err.Error(), "name is required") 79 } 80 81 func TestInstallRelease_WithNotes(t *testing.T) { 82 is := assert.New(t) 83 instAction := installAction(t) 84 instAction.ReleaseName = "with-notes" 85 instAction.rawValues = map[string]interface{}{} 86 res, err := instAction.Run(buildChart(withNotes("note here"))) 87 if err != nil { 88 t.Fatalf("Failed install: %s", err) 89 } 90 91 is.Equal(res.Name, "with-notes") 92 is.Equal(res.Namespace, "spaced") 93 94 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 95 is.NoError(err) 96 is.Len(rel.Hooks, 1) 97 is.Equal(rel.Hooks[0].Manifest, manifestWithHook) 98 is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) 99 is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") 100 is.NotEqual(len(res.Manifest), 0) 101 is.NotEqual(len(rel.Manifest), 0) 102 is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 103 is.Equal(rel.Info.Description, "Install complete") 104 105 is.Equal(rel.Info.Notes, "note here") 106 } 107 108 func TestInstallRelease_WithNotesRendered(t *testing.T) { 109 is := assert.New(t) 110 instAction := installAction(t) 111 instAction.ReleaseName = "with-notes" 112 instAction.rawValues = map[string]interface{}{} 113 res, err := instAction.Run(buildChart(withNotes("got-{{.Release.Name}}"))) 114 if err != nil { 115 t.Fatalf("Failed install: %s", err) 116 } 117 118 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 119 is.NoError(err) 120 121 expectedNotes := fmt.Sprintf("got-%s", res.Name) 122 is.Equal(expectedNotes, rel.Info.Notes) 123 is.Equal(rel.Info.Description, "Install complete") 124 } 125 126 func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) { 127 // Regression: Make sure that the child's notes don't override the parent's 128 is := assert.New(t) 129 instAction := installAction(t) 130 instAction.ReleaseName = "with-notes" 131 instAction.rawValues = map[string]interface{}{} 132 res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child")))) 133 if err != nil { 134 t.Fatalf("Failed install: %s", err) 135 } 136 137 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 138 is.Equal("with-notes", rel.Name) 139 is.NoError(err) 140 is.Equal("parent", rel.Info.Notes) 141 is.Equal(rel.Info.Description, "Install complete") 142 } 143 144 func TestInstallRelease_DryRun(t *testing.T) { 145 is := assert.New(t) 146 instAction := installAction(t) 147 instAction.DryRun = true 148 instAction.rawValues = map[string]interface{}{} 149 res, err := instAction.Run(buildChart(withSampleTemplates())) 150 if err != nil { 151 t.Fatalf("Failed install: %s", err) 152 } 153 154 is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 155 is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world") 156 is.Contains(res.Manifest, "hello: Earth") 157 is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}") 158 is.NotContains(res.Manifest, "empty") 159 160 _, err = instAction.cfg.Releases.Get(res.Name, res.Version) 161 is.Error(err) 162 is.Len(res.Hooks, 1) 163 is.True(res.Hooks[0].LastRun.IsZero(), "expect hook to not be marked as run") 164 is.Equal(res.Info.Description, "Dry run complete") 165 } 166 167 func TestInstallRelease_NoHooks(t *testing.T) { 168 is := assert.New(t) 169 instAction := installAction(t) 170 instAction.DisableHooks = true 171 instAction.ReleaseName = "no-hooks" 172 instAction.cfg.Releases.Create(releaseStub()) 173 174 instAction.rawValues = map[string]interface{}{} 175 res, err := instAction.Run(buildChart()) 176 if err != nil { 177 t.Fatalf("Failed install: %s", err) 178 } 179 180 is.True(res.Hooks[0].LastRun.IsZero(), "hooks should not run with no-hooks") 181 } 182 183 func TestInstallRelease_FailedHooks(t *testing.T) { 184 is := assert.New(t) 185 instAction := installAction(t) 186 instAction.ReleaseName = "failed-hooks" 187 instAction.cfg.KubeClient = newHookFailingKubeClient() 188 189 instAction.rawValues = map[string]interface{}{} 190 res, err := instAction.Run(buildChart()) 191 is.Error(err) 192 is.Contains(res.Info.Description, "failed post-install") 193 is.Equal(res.Info.Status, release.StatusFailed) 194 } 195 196 func TestInstallRelease_ReplaceRelease(t *testing.T) { 197 is := assert.New(t) 198 instAction := installAction(t) 199 instAction.Replace = true 200 201 rel := releaseStub() 202 rel.Info.Status = release.StatusUninstalled 203 instAction.cfg.Releases.Create(rel) 204 instAction.ReleaseName = rel.Name 205 206 instAction.rawValues = map[string]interface{}{} 207 res, err := instAction.Run(buildChart()) 208 is.NoError(err) 209 210 // This should have been auto-incremented 211 is.Equal(2, res.Version) 212 is.Equal(res.Name, rel.Name) 213 214 getres, err := instAction.cfg.Releases.Get(rel.Name, res.Version) 215 is.NoError(err) 216 is.Equal(getres.Info.Status, release.StatusDeployed) 217 } 218 219 func TestInstallRelease_KubeVersion(t *testing.T) { 220 is := assert.New(t) 221 instAction := installAction(t) 222 instAction.rawValues = map[string]interface{}{} 223 _, err := instAction.Run(buildChart(withKube(">=0.0.0"))) 224 is.NoError(err) 225 226 // This should fail for a few hundred years 227 instAction.ReleaseName = "should-fail" 228 instAction.rawValues = map[string]interface{}{} 229 _, err = instAction.Run(buildChart(withKube(">=99.0.0"))) 230 is.Error(err) 231 is.Contains(err.Error(), "chart requires kubernetesVersion") 232 } 233 234 func TestNameTemplate(t *testing.T) { 235 testCases := []nameTemplateTestCase{ 236 // Just a straight up nop please 237 { 238 tpl: "foobar", 239 expected: "foobar", 240 expectedErrorStr: "", 241 }, 242 // Random numbers at the end for fun & profit 243 { 244 tpl: "foobar-{{randNumeric 6}}", 245 expected: "foobar-[0-9]{6}$", 246 expectedErrorStr: "", 247 }, 248 // Random numbers in the middle for fun & profit 249 { 250 tpl: "foobar-{{randNumeric 4}}-baz", 251 expected: "foobar-[0-9]{4}-baz$", 252 expectedErrorStr: "", 253 }, 254 // No such function 255 { 256 tpl: "foobar-{{randInt}}", 257 expected: "", 258 expectedErrorStr: "function \"randInt\" not defined", 259 }, 260 // Invalid template 261 { 262 tpl: "foobar-{{", 263 expected: "", 264 expectedErrorStr: "unexpected unclosed action", 265 }, 266 } 267 268 for _, tc := range testCases { 269 270 n, err := TemplateName(tc.tpl) 271 if err != nil { 272 if tc.expectedErrorStr == "" { 273 t.Errorf("Was not expecting error, but got: %v", err) 274 continue 275 } 276 re, compErr := regexp.Compile(tc.expectedErrorStr) 277 if compErr != nil { 278 t.Errorf("Expected error string failed to compile: %v", compErr) 279 continue 280 } 281 if !re.MatchString(err.Error()) { 282 t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err) 283 continue 284 } 285 } 286 if err == nil && tc.expectedErrorStr != "" { 287 t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr) 288 } 289 290 if tc.expected != "" { 291 re, err := regexp.Compile(tc.expected) 292 if err != nil { 293 t.Errorf("Expected string failed to compile: %v", err) 294 continue 295 } 296 if !re.MatchString(n) { 297 t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n) 298 } 299 } 300 } 301 } 302 303 func TestMergeValues(t *testing.T) { 304 nestedMap := map[string]interface{}{ 305 "foo": "bar", 306 "baz": map[string]string{ 307 "cool": "stuff", 308 }, 309 } 310 anotherNestedMap := map[string]interface{}{ 311 "foo": "bar", 312 "baz": map[string]string{ 313 "cool": "things", 314 "awesome": "stuff", 315 }, 316 } 317 flatMap := map[string]interface{}{ 318 "foo": "bar", 319 "baz": "stuff", 320 } 321 anotherFlatMap := map[string]interface{}{ 322 "testing": "fun", 323 } 324 325 testMap := mergeValues(flatMap, nestedMap) 326 equal := reflect.DeepEqual(testMap, nestedMap) 327 if !equal { 328 t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap) 329 } 330 331 testMap = mergeValues(nestedMap, flatMap) 332 equal = reflect.DeepEqual(testMap, flatMap) 333 if !equal { 334 t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap) 335 } 336 337 testMap = mergeValues(nestedMap, anotherNestedMap) 338 equal = reflect.DeepEqual(testMap, anotherNestedMap) 339 if !equal { 340 t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap) 341 } 342 343 testMap = mergeValues(anotherFlatMap, anotherNestedMap) 344 expectedMap := map[string]interface{}{ 345 "testing": "fun", 346 "foo": "bar", 347 "baz": map[string]string{ 348 "cool": "things", 349 "awesome": "stuff", 350 }, 351 } 352 equal = reflect.DeepEqual(testMap, expectedMap) 353 if !equal { 354 t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap) 355 } 356 }