github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/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 "context" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "regexp" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 33 "github.com/stefanmcshane/helm/internal/test" 34 "github.com/stefanmcshane/helm/pkg/chart" 35 "github.com/stefanmcshane/helm/pkg/chartutil" 36 kubefake "github.com/stefanmcshane/helm/pkg/kube/fake" 37 "github.com/stefanmcshane/helm/pkg/release" 38 "github.com/stefanmcshane/helm/pkg/storage/driver" 39 helmtime "github.com/stefanmcshane/helm/pkg/time" 40 ) 41 42 type nameTemplateTestCase struct { 43 tpl string 44 expected string 45 expectedErrorStr string 46 } 47 48 func installAction(t *testing.T) *Install { 49 config := actionConfigFixture(t) 50 instAction := NewInstall(config) 51 instAction.Namespace = "spaced" 52 instAction.ReleaseName = "test-install-release" 53 54 return instAction 55 } 56 57 func TestInstallRelease(t *testing.T) { 58 is := assert.New(t) 59 req := require.New(t) 60 61 instAction := installAction(t) 62 vals := map[string]interface{}{} 63 ctx, done := context.WithCancel(context.Background()) 64 res, err := instAction.RunWithContext(ctx, buildChart(), vals) 65 if err != nil { 66 t.Fatalf("Failed install: %s", err) 67 } 68 is.Equal(res.Name, "test-install-release", "Expected release name.") 69 is.Equal(res.Namespace, "spaced") 70 71 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 72 is.NoError(err) 73 74 is.Len(rel.Hooks, 1) 75 is.Equal(rel.Hooks[0].Manifest, manifestWithHook) 76 is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) 77 is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") 78 79 is.NotEqual(len(res.Manifest), 0) 80 is.NotEqual(len(rel.Manifest), 0) 81 is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 82 is.Equal(rel.Info.Description, "Install complete") 83 84 // Detecting previous bug where context termination after successful release 85 // caused release to fail. 86 done() 87 time.Sleep(time.Millisecond * 100) 88 lastRelease, err := instAction.cfg.Releases.Last(rel.Name) 89 req.NoError(err) 90 is.Equal(lastRelease.Info.Status, release.StatusDeployed) 91 } 92 93 func TestInstallReleaseWithValues(t *testing.T) { 94 is := assert.New(t) 95 instAction := installAction(t) 96 userVals := map[string]interface{}{ 97 "nestedKey": map[string]interface{}{ 98 "simpleKey": "simpleValue", 99 }, 100 } 101 expectedUserValues := map[string]interface{}{ 102 "nestedKey": map[string]interface{}{ 103 "simpleKey": "simpleValue", 104 }, 105 } 106 res, err := instAction.Run(buildChart(withSampleValues()), userVals) 107 if err != nil { 108 t.Fatalf("Failed install: %s", err) 109 } 110 is.Equal(res.Name, "test-install-release", "Expected release name.") 111 is.Equal(res.Namespace, "spaced") 112 113 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 114 is.NoError(err) 115 116 is.Len(rel.Hooks, 1) 117 is.Equal(rel.Hooks[0].Manifest, manifestWithHook) 118 is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) 119 is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") 120 121 is.NotEqual(len(res.Manifest), 0) 122 is.NotEqual(len(rel.Manifest), 0) 123 is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 124 is.Equal("Install complete", rel.Info.Description) 125 is.Equal(expectedUserValues, rel.Config) 126 } 127 128 func TestInstallReleaseClientOnly(t *testing.T) { 129 is := assert.New(t) 130 instAction := installAction(t) 131 instAction.ClientOnly = true 132 instAction.Run(buildChart(), nil) // disregard output 133 134 is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities) 135 is.Equal(instAction.cfg.KubeClient, &kubefake.PrintingKubeClient{Out: ioutil.Discard}) 136 } 137 138 func TestInstallRelease_NoName(t *testing.T) { 139 instAction := installAction(t) 140 instAction.ReleaseName = "" 141 vals := map[string]interface{}{} 142 _, err := instAction.Run(buildChart(), vals) 143 if err == nil { 144 t.Fatal("expected failure when no name is specified") 145 } 146 assert.Contains(t, err.Error(), "no name provided") 147 } 148 149 func TestInstallRelease_WithNotes(t *testing.T) { 150 is := assert.New(t) 151 instAction := installAction(t) 152 instAction.ReleaseName = "with-notes" 153 vals := map[string]interface{}{} 154 res, err := instAction.Run(buildChart(withNotes("note here")), vals) 155 if err != nil { 156 t.Fatalf("Failed install: %s", err) 157 } 158 159 is.Equal(res.Name, "with-notes") 160 is.Equal(res.Namespace, "spaced") 161 162 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 163 is.NoError(err) 164 is.Len(rel.Hooks, 1) 165 is.Equal(rel.Hooks[0].Manifest, manifestWithHook) 166 is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall) 167 is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete") 168 is.NotEqual(len(res.Manifest), 0) 169 is.NotEqual(len(rel.Manifest), 0) 170 is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 171 is.Equal(rel.Info.Description, "Install complete") 172 173 is.Equal(rel.Info.Notes, "note here") 174 } 175 176 func TestInstallRelease_WithNotesRendered(t *testing.T) { 177 is := assert.New(t) 178 instAction := installAction(t) 179 instAction.ReleaseName = "with-notes" 180 vals := map[string]interface{}{} 181 res, err := instAction.Run(buildChart(withNotes("got-{{.Release.Name}}")), vals) 182 if err != nil { 183 t.Fatalf("Failed install: %s", err) 184 } 185 186 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 187 is.NoError(err) 188 189 expectedNotes := fmt.Sprintf("got-%s", res.Name) 190 is.Equal(expectedNotes, rel.Info.Notes) 191 is.Equal(rel.Info.Description, "Install complete") 192 } 193 194 func TestInstallRelease_WithChartAndDependencyParentNotes(t *testing.T) { 195 // Regression: Make sure that the child's notes don't override the parent's 196 is := assert.New(t) 197 instAction := installAction(t) 198 instAction.ReleaseName = "with-notes" 199 vals := map[string]interface{}{} 200 res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals) 201 if err != nil { 202 t.Fatalf("Failed install: %s", err) 203 } 204 205 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 206 is.Equal("with-notes", rel.Name) 207 is.NoError(err) 208 is.Equal("parent", rel.Info.Notes) 209 is.Equal(rel.Info.Description, "Install complete") 210 } 211 212 func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) { 213 // Regression: Make sure that the child's notes don't override the parent's 214 is := assert.New(t) 215 instAction := installAction(t) 216 instAction.ReleaseName = "with-notes" 217 instAction.SubNotes = true 218 vals := map[string]interface{}{} 219 res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals) 220 if err != nil { 221 t.Fatalf("Failed install: %s", err) 222 } 223 224 rel, err := instAction.cfg.Releases.Get(res.Name, res.Version) 225 is.Equal("with-notes", rel.Name) 226 is.NoError(err) 227 // test run can return as either 'parent\nchild' or 'child\nparent' 228 if !strings.Contains(rel.Info.Notes, "parent") && !strings.Contains(rel.Info.Notes, "child") { 229 t.Fatalf("Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes) 230 } 231 is.Equal(rel.Info.Description, "Install complete") 232 } 233 234 func TestInstallRelease_DryRun(t *testing.T) { 235 is := assert.New(t) 236 instAction := installAction(t) 237 instAction.DryRun = true 238 vals := map[string]interface{}{} 239 res, err := instAction.Run(buildChart(withSampleTemplates()), vals) 240 if err != nil { 241 t.Fatalf("Failed install: %s", err) 242 } 243 244 is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world") 245 is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world") 246 is.Contains(res.Manifest, "hello: Earth") 247 is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}") 248 is.NotContains(res.Manifest, "empty") 249 250 _, err = instAction.cfg.Releases.Get(res.Name, res.Version) 251 is.Error(err) 252 is.Len(res.Hooks, 1) 253 is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "expect hook to not be marked as run") 254 is.Equal(res.Info.Description, "Dry run complete") 255 } 256 257 // Regression test for #7955: Lookup must not connect to Kubernetes on a dry-run. 258 func TestInstallRelease_DryRun_Lookup(t *testing.T) { 259 is := assert.New(t) 260 instAction := installAction(t) 261 instAction.DryRun = true 262 vals := map[string]interface{}{} 263 264 mockChart := buildChart(withSampleTemplates()) 265 mockChart.Templates = append(mockChart.Templates, &chart.File{ 266 Name: "templates/lookup", 267 Data: []byte(`goodbye: {{ lookup "v1" "Namespace" "" "___" }}`), 268 }) 269 270 res, err := instAction.Run(mockChart, vals) 271 if err != nil { 272 t.Fatalf("Failed install: %s", err) 273 } 274 275 is.Contains(res.Manifest, "goodbye: map[]") 276 } 277 278 func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) { 279 is := assert.New(t) 280 instAction := installAction(t) 281 instAction.DryRun = true 282 vals := map[string]interface{}{} 283 _, err := instAction.Run(buildChart(withSampleIncludingIncorrectTemplates()), vals) 284 expectedErr := "\"hello/templates/incorrect\" at <.Values.bad.doh>: nil pointer evaluating interface {}.doh" 285 if err == nil { 286 t.Fatalf("Install should fail containing error: %s", expectedErr) 287 } 288 if err != nil { 289 is.Contains(err.Error(), expectedErr) 290 } 291 } 292 293 func TestInstallRelease_NoHooks(t *testing.T) { 294 is := assert.New(t) 295 instAction := installAction(t) 296 instAction.DisableHooks = true 297 instAction.ReleaseName = "no-hooks" 298 instAction.cfg.Releases.Create(releaseStub()) 299 300 vals := map[string]interface{}{} 301 res, err := instAction.Run(buildChart(), vals) 302 if err != nil { 303 t.Fatalf("Failed install: %s", err) 304 } 305 306 is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "hooks should not run with no-hooks") 307 } 308 309 func TestInstallRelease_FailedHooks(t *testing.T) { 310 is := assert.New(t) 311 instAction := installAction(t) 312 instAction.ReleaseName = "failed-hooks" 313 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 314 failer.WatchUntilReadyError = fmt.Errorf("Failed watch") 315 instAction.cfg.KubeClient = failer 316 317 vals := map[string]interface{}{} 318 res, err := instAction.Run(buildChart(), vals) 319 is.Error(err) 320 is.Contains(res.Info.Description, "failed post-install") 321 is.Equal(release.StatusFailed, res.Info.Status) 322 } 323 324 func TestInstallRelease_ReplaceRelease(t *testing.T) { 325 is := assert.New(t) 326 instAction := installAction(t) 327 instAction.Replace = true 328 329 rel := releaseStub() 330 rel.Info.Status = release.StatusUninstalled 331 instAction.cfg.Releases.Create(rel) 332 instAction.ReleaseName = rel.Name 333 334 vals := map[string]interface{}{} 335 res, err := instAction.Run(buildChart(), vals) 336 is.NoError(err) 337 338 // This should have been auto-incremented 339 is.Equal(2, res.Version) 340 is.Equal(res.Name, rel.Name) 341 342 getres, err := instAction.cfg.Releases.Get(rel.Name, res.Version) 343 is.NoError(err) 344 is.Equal(getres.Info.Status, release.StatusDeployed) 345 } 346 347 func TestInstallRelease_KubeVersion(t *testing.T) { 348 is := assert.New(t) 349 instAction := installAction(t) 350 vals := map[string]interface{}{} 351 _, err := instAction.Run(buildChart(withKube(">=0.0.0")), vals) 352 is.NoError(err) 353 354 // This should fail for a few hundred years 355 instAction.ReleaseName = "should-fail" 356 vals = map[string]interface{}{} 357 _, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals) 358 is.Error(err) 359 is.Contains(err.Error(), "chart requires kubeVersion") 360 } 361 362 func TestInstallRelease_Wait(t *testing.T) { 363 is := assert.New(t) 364 instAction := installAction(t) 365 instAction.ReleaseName = "come-fail-away" 366 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 367 failer.WaitError = fmt.Errorf("I timed out") 368 instAction.cfg.KubeClient = failer 369 instAction.Wait = true 370 vals := map[string]interface{}{} 371 372 res, err := instAction.Run(buildChart(), vals) 373 is.Error(err) 374 is.Contains(res.Info.Description, "I timed out") 375 is.Equal(res.Info.Status, release.StatusFailed) 376 } 377 func TestInstallRelease_Wait_Interrupted(t *testing.T) { 378 is := assert.New(t) 379 instAction := installAction(t) 380 instAction.ReleaseName = "interrupted-release" 381 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 382 failer.WaitDuration = 10 * time.Second 383 instAction.cfg.KubeClient = failer 384 instAction.Wait = true 385 vals := map[string]interface{}{} 386 387 ctx := context.Background() 388 ctx, cancel := context.WithCancel(ctx) 389 time.AfterFunc(time.Second, cancel) 390 391 res, err := instAction.RunWithContext(ctx, buildChart(), vals) 392 is.Error(err) 393 is.Contains(res.Info.Description, "Release \"interrupted-release\" failed: context canceled") 394 is.Equal(res.Info.Status, release.StatusFailed) 395 } 396 func TestInstallRelease_WaitForJobs(t *testing.T) { 397 is := assert.New(t) 398 instAction := installAction(t) 399 instAction.ReleaseName = "come-fail-away" 400 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 401 failer.WaitError = fmt.Errorf("I timed out") 402 instAction.cfg.KubeClient = failer 403 instAction.Wait = true 404 instAction.WaitForJobs = true 405 vals := map[string]interface{}{} 406 407 res, err := instAction.Run(buildChart(), vals) 408 is.Error(err) 409 is.Contains(res.Info.Description, "I timed out") 410 is.Equal(res.Info.Status, release.StatusFailed) 411 } 412 413 func TestInstallRelease_Atomic(t *testing.T) { 414 is := assert.New(t) 415 416 t.Run("atomic uninstall succeeds", func(t *testing.T) { 417 instAction := installAction(t) 418 instAction.ReleaseName = "come-fail-away" 419 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 420 failer.WaitError = fmt.Errorf("I timed out") 421 instAction.cfg.KubeClient = failer 422 instAction.Atomic = true 423 vals := map[string]interface{}{} 424 425 res, err := instAction.Run(buildChart(), vals) 426 is.Error(err) 427 is.Contains(err.Error(), "I timed out") 428 is.Contains(err.Error(), "atomic") 429 430 // Now make sure it isn't in storage any more 431 _, err = instAction.cfg.Releases.Get(res.Name, res.Version) 432 is.Error(err) 433 is.Equal(err, driver.ErrReleaseNotFound) 434 }) 435 436 t.Run("atomic uninstall fails", func(t *testing.T) { 437 instAction := installAction(t) 438 instAction.ReleaseName = "come-fail-away-with-me" 439 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 440 failer.WaitError = fmt.Errorf("I timed out") 441 failer.DeleteError = fmt.Errorf("uninstall fail") 442 instAction.cfg.KubeClient = failer 443 instAction.Atomic = true 444 vals := map[string]interface{}{} 445 446 _, err := instAction.Run(buildChart(), vals) 447 is.Error(err) 448 is.Contains(err.Error(), "I timed out") 449 is.Contains(err.Error(), "uninstall fail") 450 is.Contains(err.Error(), "an error occurred while uninstalling the release") 451 }) 452 } 453 func TestInstallRelease_Atomic_Interrupted(t *testing.T) { 454 455 is := assert.New(t) 456 instAction := installAction(t) 457 instAction.ReleaseName = "interrupted-release" 458 failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient) 459 failer.WaitDuration = 10 * time.Second 460 instAction.cfg.KubeClient = failer 461 instAction.Atomic = true 462 vals := map[string]interface{}{} 463 464 ctx := context.Background() 465 ctx, cancel := context.WithCancel(ctx) 466 time.AfterFunc(time.Second, cancel) 467 468 res, err := instAction.RunWithContext(ctx, buildChart(), vals) 469 is.Error(err) 470 is.Contains(err.Error(), "context canceled") 471 is.Contains(err.Error(), "atomic") 472 is.Contains(err.Error(), "uninstalled") 473 474 // Now make sure it isn't in storage any more 475 _, err = instAction.cfg.Releases.Get(res.Name, res.Version) 476 is.Error(err) 477 is.Equal(err, driver.ErrReleaseNotFound) 478 479 } 480 func TestNameTemplate(t *testing.T) { 481 testCases := []nameTemplateTestCase{ 482 // Just a straight up nop please 483 { 484 tpl: "foobar", 485 expected: "foobar", 486 expectedErrorStr: "", 487 }, 488 // Random numbers at the end for fun & profit 489 { 490 tpl: "foobar-{{randNumeric 6}}", 491 expected: "foobar-[0-9]{6}$", 492 expectedErrorStr: "", 493 }, 494 // Random numbers in the middle for fun & profit 495 { 496 tpl: "foobar-{{randNumeric 4}}-baz", 497 expected: "foobar-[0-9]{4}-baz$", 498 expectedErrorStr: "", 499 }, 500 // No such function 501 { 502 tpl: "foobar-{{randInteger}}", 503 expected: "", 504 expectedErrorStr: "function \"randInteger\" not defined", 505 }, 506 // Invalid template 507 { 508 tpl: "foobar-{{", 509 expected: "", 510 expectedErrorStr: "template: name-template:1: unclosed action", 511 }, 512 } 513 514 for _, tc := range testCases { 515 516 n, err := TemplateName(tc.tpl) 517 if err != nil { 518 if tc.expectedErrorStr == "" { 519 t.Errorf("Was not expecting error, but got: %v", err) 520 continue 521 } 522 re, compErr := regexp.Compile(tc.expectedErrorStr) 523 if compErr != nil { 524 t.Errorf("Expected error string failed to compile: %v", compErr) 525 continue 526 } 527 if !re.MatchString(err.Error()) { 528 t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err) 529 continue 530 } 531 } 532 if err == nil && tc.expectedErrorStr != "" { 533 t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr) 534 } 535 536 if tc.expected != "" { 537 re, err := regexp.Compile(tc.expected) 538 if err != nil { 539 t.Errorf("Expected string failed to compile: %v", err) 540 continue 541 } 542 if !re.MatchString(n) { 543 t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n) 544 } 545 } 546 } 547 } 548 549 func TestInstallReleaseOutputDir(t *testing.T) { 550 is := assert.New(t) 551 instAction := installAction(t) 552 vals := map[string]interface{}{} 553 554 dir := t.TempDir() 555 556 instAction.OutputDir = dir 557 558 _, err := instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals) 559 if err != nil { 560 t.Fatalf("Failed install: %s", err) 561 } 562 563 _, err = os.Stat(filepath.Join(dir, "hello/templates/goodbye")) 564 is.NoError(err) 565 566 _, err = os.Stat(filepath.Join(dir, "hello/templates/hello")) 567 is.NoError(err) 568 569 _, err = os.Stat(filepath.Join(dir, "hello/templates/with-partials")) 570 is.NoError(err) 571 572 _, err = os.Stat(filepath.Join(dir, "hello/templates/rbac")) 573 is.NoError(err) 574 575 test.AssertGoldenFile(t, filepath.Join(dir, "hello/templates/rbac"), "rbac.txt") 576 577 _, err = os.Stat(filepath.Join(dir, "hello/templates/empty")) 578 is.True(os.IsNotExist(err)) 579 } 580 581 func TestInstallOutputDirWithReleaseName(t *testing.T) { 582 is := assert.New(t) 583 instAction := installAction(t) 584 vals := map[string]interface{}{} 585 586 dir := t.TempDir() 587 588 instAction.OutputDir = dir 589 instAction.UseReleaseName = true 590 instAction.ReleaseName = "madra" 591 592 newDir := filepath.Join(dir, instAction.ReleaseName) 593 594 _, err := instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals) 595 if err != nil { 596 t.Fatalf("Failed install: %s", err) 597 } 598 599 _, err = os.Stat(filepath.Join(newDir, "hello/templates/goodbye")) 600 is.NoError(err) 601 602 _, err = os.Stat(filepath.Join(newDir, "hello/templates/hello")) 603 is.NoError(err) 604 605 _, err = os.Stat(filepath.Join(newDir, "hello/templates/with-partials")) 606 is.NoError(err) 607 608 _, err = os.Stat(filepath.Join(newDir, "hello/templates/rbac")) 609 is.NoError(err) 610 611 test.AssertGoldenFile(t, filepath.Join(newDir, "hello/templates/rbac"), "rbac.txt") 612 613 _, err = os.Stat(filepath.Join(newDir, "hello/templates/empty")) 614 is.True(os.IsNotExist(err)) 615 } 616 617 func TestNameAndChart(t *testing.T) { 618 is := assert.New(t) 619 instAction := installAction(t) 620 chartName := "./foo" 621 622 name, chrt, err := instAction.NameAndChart([]string{chartName}) 623 if err != nil { 624 t.Fatal(err) 625 } 626 is.Equal(instAction.ReleaseName, name) 627 is.Equal(chartName, chrt) 628 629 instAction.GenerateName = true 630 _, _, err = instAction.NameAndChart([]string{"foo", chartName}) 631 if err == nil { 632 t.Fatal("expected an error") 633 } 634 is.Equal("cannot set --generate-name and also specify a name", err.Error()) 635 636 instAction.GenerateName = false 637 instAction.NameTemplate = "{{ . }}" 638 _, _, err = instAction.NameAndChart([]string{"foo", chartName}) 639 if err == nil { 640 t.Fatal("expected an error") 641 } 642 is.Equal("cannot set --name-template and also specify a name", err.Error()) 643 644 instAction.NameTemplate = "" 645 instAction.ReleaseName = "" 646 _, _, err = instAction.NameAndChart([]string{chartName}) 647 if err == nil { 648 t.Fatal("expected an error") 649 } 650 is.Equal("must either provide a name or specify --generate-name", err.Error()) 651 652 instAction.NameTemplate = "" 653 instAction.ReleaseName = "" 654 _, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"}) 655 if err == nil { 656 t.Fatal("expected an error") 657 } 658 is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error()) 659 } 660 661 func TestNameAndChartGenerateName(t *testing.T) { 662 is := assert.New(t) 663 instAction := installAction(t) 664 665 instAction.ReleaseName = "" 666 instAction.GenerateName = true 667 668 tests := []struct { 669 Name string 670 Chart string 671 ExpectedName string 672 }{ 673 { 674 "local filepath", 675 "./chart", 676 fmt.Sprintf("chart-%d", helmtime.Now().Unix()), 677 }, 678 { 679 "dot filepath", 680 ".", 681 fmt.Sprintf("chart-%d", helmtime.Now().Unix()), 682 }, 683 { 684 "empty filepath", 685 "", 686 fmt.Sprintf("chart-%d", helmtime.Now().Unix()), 687 }, 688 { 689 "packaged chart", 690 "chart.tgz", 691 fmt.Sprintf("chart-%d", helmtime.Now().Unix()), 692 }, 693 { 694 "packaged chart with .tar.gz extension", 695 "chart.tar.gz", 696 fmt.Sprintf("chart-%d", helmtime.Now().Unix()), 697 }, 698 { 699 "packaged chart with local extension", 700 "./chart.tgz", 701 fmt.Sprintf("chart-%d", helmtime.Now().Unix()), 702 }, 703 } 704 705 for _, tc := range tests { 706 tc := tc 707 t.Run(tc.Name, func(t *testing.T) { 708 t.Parallel() 709 710 name, chrt, err := instAction.NameAndChart([]string{tc.Chart}) 711 if err != nil { 712 t.Fatal(err) 713 } 714 715 is.Equal(tc.ExpectedName, name) 716 is.Equal(tc.Chart, chrt) 717 }) 718 } 719 }