github.com/Beeketing/helm@v2.12.1+incompatible/pkg/tiller/release_server_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 tiller 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "os" 25 "regexp" 26 "testing" 27 "time" 28 29 "github.com/ghodss/yaml" 30 "github.com/golang/protobuf/ptypes/timestamp" 31 "github.com/technosophos/moniker" 32 "golang.org/x/net/context" 33 "google.golang.org/grpc/metadata" 34 "k8s.io/api/core/v1" 35 "k8s.io/cli-runtime/pkg/genericclioptions/resource" 36 "k8s.io/client-go/kubernetes/fake" 37 38 "k8s.io/helm/pkg/helm" 39 "k8s.io/helm/pkg/hooks" 40 "k8s.io/helm/pkg/kube" 41 "k8s.io/helm/pkg/proto/hapi/chart" 42 "k8s.io/helm/pkg/proto/hapi/release" 43 "k8s.io/helm/pkg/proto/hapi/services" 44 "k8s.io/helm/pkg/storage" 45 "k8s.io/helm/pkg/storage/driver" 46 "k8s.io/helm/pkg/tiller/environment" 47 ) 48 49 const notesText = "my notes here" 50 51 var manifestWithHook = `kind: ConfigMap 52 metadata: 53 name: test-cm 54 annotations: 55 "helm.sh/hook": post-install,pre-delete 56 data: 57 name: value` 58 59 var manifestWithCRDHook = ` 60 apiVersion: apiextensions.k8s.io/v1beta1 61 kind: CustomResourceDefinition 62 metadata: 63 name: crontabs.stable.example.com 64 annotations: 65 "helm.sh/hook": crd-install 66 spec: 67 group: stable.example.com 68 version: v1 69 scope: Namespaced 70 names: 71 plural: crontabs 72 singular: crontab 73 kind: CronTab 74 shortNames: 75 - ct 76 ` 77 78 var manifestWithTestHook = `kind: Pod 79 metadata: 80 name: finding-nemo, 81 annotations: 82 "helm.sh/hook": test-success 83 spec: 84 containers: 85 - name: nemo-test 86 image: fake-image 87 cmd: fake-command 88 ` 89 90 var manifestWithKeep = `kind: ConfigMap 91 metadata: 92 name: test-cm-keep 93 annotations: 94 "helm.sh/resource-policy": keep 95 data: 96 name: value 97 ` 98 99 var manifestWithUpgradeHooks = `kind: ConfigMap 100 metadata: 101 name: test-cm 102 annotations: 103 "helm.sh/hook": post-upgrade,pre-upgrade 104 data: 105 name: value` 106 107 var manifestWithRollbackHooks = `kind: ConfigMap 108 metadata: 109 name: test-cm 110 annotations: 111 "helm.sh/hook": post-rollback,pre-rollback 112 data: 113 name: value 114 ` 115 116 type chartOptions struct { 117 *chart.Chart 118 } 119 120 type chartOption func(*chartOptions) 121 122 func rsFixture() *ReleaseServer { 123 return NewReleaseServer(MockEnvironment(), fake.NewSimpleClientset(), false) 124 } 125 126 func buildChart(opts ...chartOption) *chart.Chart { 127 c := &chartOptions{ 128 Chart: &chart.Chart{ 129 // TODO: This should be more complete. 130 Metadata: &chart.Metadata{ 131 Name: "hello", 132 }, 133 // This adds a basic template and hooks. 134 Templates: []*chart.Template{ 135 {Name: "templates/hello", Data: []byte("hello: world")}, 136 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 137 }, 138 }, 139 } 140 141 for _, opt := range opts { 142 opt(c) 143 } 144 145 return c.Chart 146 } 147 148 func withKube(version string) chartOption { 149 return func(opts *chartOptions) { 150 opts.Metadata.KubeVersion = version 151 } 152 } 153 154 func withTiller(version string) chartOption { 155 return func(opts *chartOptions) { 156 opts.Metadata.TillerVersion = version 157 } 158 } 159 160 func withDependency(dependencyOpts ...chartOption) chartOption { 161 return func(opts *chartOptions) { 162 opts.Dependencies = append(opts.Dependencies, buildChart(dependencyOpts...)) 163 } 164 } 165 166 func withNotes(notes string) chartOption { 167 return func(opts *chartOptions) { 168 opts.Templates = append(opts.Templates, &chart.Template{ 169 Name: "templates/NOTES.txt", 170 Data: []byte(notes), 171 }) 172 } 173 } 174 175 func withSampleTemplates() chartOption { 176 return func(opts *chartOptions) { 177 sampleTemplates := []*chart.Template{ 178 // This adds basic templates and partials. 179 {Name: "templates/goodbye", Data: []byte("goodbye: world")}, 180 {Name: "templates/empty", Data: []byte("")}, 181 {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, 182 {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, 183 } 184 opts.Templates = append(opts.Templates, sampleTemplates...) 185 } 186 } 187 188 type installOptions struct { 189 *services.InstallReleaseRequest 190 } 191 192 type installOption func(*installOptions) 193 194 func withName(name string) installOption { 195 return func(opts *installOptions) { 196 opts.Name = name 197 } 198 } 199 200 func withDryRun() installOption { 201 return func(opts *installOptions) { 202 opts.DryRun = true 203 } 204 } 205 206 func withDisabledHooks() installOption { 207 return func(opts *installOptions) { 208 opts.DisableHooks = true 209 } 210 } 211 212 func withReuseName() installOption { 213 return func(opts *installOptions) { 214 opts.ReuseName = true 215 } 216 } 217 218 func withChart(chartOpts ...chartOption) installOption { 219 return func(opts *installOptions) { 220 opts.Chart = buildChart(chartOpts...) 221 } 222 } 223 224 func installRequest(opts ...installOption) *services.InstallReleaseRequest { 225 reqOpts := &installOptions{ 226 &services.InstallReleaseRequest{ 227 Namespace: "spaced", 228 Chart: buildChart(), 229 }, 230 } 231 232 for _, opt := range opts { 233 opt(reqOpts) 234 } 235 236 return reqOpts.InstallReleaseRequest 237 } 238 239 // chartStub creates a fully stubbed out chart. 240 func chartStub() *chart.Chart { 241 return buildChart(withSampleTemplates()) 242 } 243 244 // releaseStub creates a release stub, complete with the chartStub as its chart. 245 func releaseStub() *release.Release { 246 return namedReleaseStub("angry-panda", release.Status_DEPLOYED) 247 } 248 249 func namedReleaseStub(name string, status release.Status_Code) *release.Release { 250 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 251 return &release.Release{ 252 Name: name, 253 Info: &release.Info{ 254 FirstDeployed: &date, 255 LastDeployed: &date, 256 Status: &release.Status{Code: status}, 257 Description: "Named Release Stub", 258 }, 259 Chart: chartStub(), 260 Config: &chart.Config{Raw: `name: value`}, 261 Version: 1, 262 Hooks: []*release.Hook{ 263 { 264 Name: "test-cm", 265 Kind: "ConfigMap", 266 Path: "test-cm", 267 Manifest: manifestWithHook, 268 Events: []release.Hook_Event{ 269 release.Hook_POST_INSTALL, 270 release.Hook_PRE_DELETE, 271 }, 272 }, 273 { 274 Name: "finding-nemo", 275 Kind: "Pod", 276 Path: "finding-nemo", 277 Manifest: manifestWithTestHook, 278 Events: []release.Hook_Event{ 279 release.Hook_RELEASE_TEST_SUCCESS, 280 }, 281 }, 282 }, 283 } 284 } 285 286 func upgradeReleaseVersion(rel *release.Release) *release.Release { 287 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 288 289 rel.Info.Status.Code = release.Status_SUPERSEDED 290 return &release.Release{ 291 Name: rel.Name, 292 Info: &release.Info{ 293 FirstDeployed: rel.Info.FirstDeployed, 294 LastDeployed: &date, 295 Status: &release.Status{Code: release.Status_DEPLOYED}, 296 }, 297 Chart: rel.Chart, 298 Config: rel.Config, 299 Version: rel.Version + 1, 300 } 301 } 302 303 func TestValidName(t *testing.T) { 304 for name, valid := range map[string]error{ 305 "nina pinta santa-maria": errInvalidName, 306 "nina-pinta-santa-maria": nil, 307 "-nina": errInvalidName, 308 "pinta-": errInvalidName, 309 "santa-maria": nil, 310 "niƱa": errInvalidName, 311 "...": errInvalidName, 312 "pinta...": errInvalidName, 313 "santa...maria": nil, 314 "": errMissingRelease, 315 " ": errInvalidName, 316 ".nina.": errInvalidName, 317 "nina.pinta": nil, 318 "abcdefghi-abcdefghi-abcdefghi-abcdefghi-abcdefghi-abcd": errInvalidName, 319 } { 320 if valid != validateReleaseName(name) { 321 t.Errorf("Expected %q to be %t", name, valid) 322 } 323 } 324 } 325 326 func TestGetVersionSet(t *testing.T) { 327 rs := rsFixture() 328 vs, err := GetVersionSet(rs.clientset.Discovery()) 329 if err != nil { 330 t.Error(err) 331 } 332 if !vs.Has("v1") { 333 t.Errorf("Expected supported versions to at least include v1.") 334 } 335 if vs.Has("nosuchversion/v1") { 336 t.Error("Non-existent version is reported found.") 337 } 338 } 339 340 func TestUniqName(t *testing.T) { 341 rs := rsFixture() 342 343 rel1 := releaseStub() 344 rel2 := releaseStub() 345 rel2.Name = "happy-panda" 346 rel2.Info.Status.Code = release.Status_DELETED 347 348 rs.env.Releases.Create(rel1) 349 rs.env.Releases.Create(rel2) 350 351 tests := []struct { 352 name string 353 expect string 354 reuse bool 355 err bool 356 }{ 357 {"first", "first", false, false}, 358 {"", "[a-z]+-[a-z]+", false, false}, 359 {"angry-panda", "", false, true}, 360 {"happy-panda", "", false, true}, 361 {"happy-panda", "happy-panda", true, false}, 362 {"hungry-hungry-hungry-hungry-hungry-hungry-hungry-hungry-hippos", "", true, true}, // Exceeds max name length 363 } 364 365 for _, tt := range tests { 366 u, err := rs.uniqName(tt.name, tt.reuse) 367 if err != nil { 368 if tt.err { 369 continue 370 } 371 t.Fatal(err) 372 } 373 if tt.err { 374 t.Errorf("Expected an error for %q", tt.name) 375 } 376 if match, err := regexp.MatchString(tt.expect, u); err != nil { 377 t.Fatal(err) 378 } else if !match { 379 t.Errorf("Expected %q to match %q", u, tt.expect) 380 } 381 } 382 } 383 384 type fakeNamer struct { 385 name string 386 } 387 388 func NewFakeNamer(nam string) moniker.Namer { 389 return &fakeNamer{ 390 name: nam, 391 } 392 } 393 394 func (f *fakeNamer) Name() string { 395 return f.NameSep(" ") 396 } 397 398 func (f *fakeNamer) NameSep(sep string) string { 399 return f.name 400 } 401 402 func TestCreateUniqueName(t *testing.T) { 403 rs := rsFixture() 404 405 rel1 := releaseStub() 406 rel1.Name = "happy-panda" 407 408 rs.env.Releases.Create(rel1) 409 410 tests := []struct { 411 name string 412 expect string 413 err bool 414 }{ 415 {"happy-panda", "ERROR", true}, 416 {"wobbly-octopus", "[a-z]+-[a-z]+", false}, 417 } 418 419 for _, tt := range tests { 420 m := NewFakeNamer(tt.name) 421 u, err := rs.createUniqName(m) 422 if err != nil { 423 if tt.err { 424 continue 425 } 426 t.Fatal(err) 427 } 428 if tt.err { 429 t.Errorf("Expected an error for %q", tt.name) 430 } 431 if match, err := regexp.MatchString(tt.expect, u); err != nil { 432 t.Fatal(err) 433 } else if !match { 434 t.Errorf("Expected %q to match %q", u, tt.expect) 435 } 436 } 437 438 } 439 440 func releaseWithKeepStub(rlsName string) *release.Release { 441 ch := &chart.Chart{ 442 Metadata: &chart.Metadata{ 443 Name: "bunnychart", 444 }, 445 Templates: []*chart.Template{ 446 {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, 447 }, 448 } 449 450 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 451 return &release.Release{ 452 Name: rlsName, 453 Info: &release.Info{ 454 FirstDeployed: &date, 455 LastDeployed: &date, 456 Status: &release.Status{Code: release.Status_DEPLOYED}, 457 }, 458 Chart: ch, 459 Config: &chart.Config{Raw: `name: value`}, 460 Version: 1, 461 Manifest: manifestWithKeep, 462 } 463 } 464 465 func MockEnvironment() *environment.Environment { 466 e := environment.New() 467 e.Releases = storage.Init(driver.NewMemory()) 468 e.KubeClient = &environment.PrintingKubeClient{Out: ioutil.Discard} 469 return e 470 } 471 472 func newUpdateFailingKubeClient() *updateFailingKubeClient { 473 return &updateFailingKubeClient{ 474 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 475 } 476 477 } 478 479 type updateFailingKubeClient struct { 480 environment.PrintingKubeClient 481 } 482 483 func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { 484 return errors.New("Failed update in kube client") 485 } 486 487 func newHookFailingKubeClient() *hookFailingKubeClient { 488 return &hookFailingKubeClient{ 489 PrintingKubeClient: environment.PrintingKubeClient{Out: ioutil.Discard}, 490 } 491 } 492 493 type hookFailingKubeClient struct { 494 environment.PrintingKubeClient 495 } 496 497 func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { 498 return errors.New("Failed watch") 499 } 500 501 func newDeleteFailingKubeClient() *deleteFailingKubeClient { 502 return &deleteFailingKubeClient{ 503 PrintingKubeClient: environment.PrintingKubeClient{Out: ioutil.Discard}, 504 } 505 } 506 507 type deleteFailingKubeClient struct { 508 environment.PrintingKubeClient 509 } 510 511 func (d *deleteFailingKubeClient) Delete(ns string, r io.Reader) error { 512 return kube.ErrNoObjectsVisited 513 } 514 515 type mockListServer struct { 516 val *services.ListReleasesResponse 517 } 518 519 func (l *mockListServer) Send(res *services.ListReleasesResponse) error { 520 l.val = res 521 return nil 522 } 523 524 func (l *mockListServer) Context() context.Context { return helm.NewContext() } 525 func (l *mockListServer) SendMsg(v interface{}) error { return nil } 526 func (l *mockListServer) RecvMsg(v interface{}) error { return nil } 527 func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } 528 func (l *mockListServer) SetTrailer(m metadata.MD) {} 529 func (l *mockListServer) SetHeader(m metadata.MD) error { return nil } 530 531 type mockRunReleaseTestServer struct{} 532 533 func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error { 534 return nil 535 } 536 func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error { return nil } 537 func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil } 538 func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} 539 func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } 540 func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } 541 func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() } 542 543 type mockHooksManifest struct { 544 Metadata struct { 545 Name string 546 Annotations map[string]string 547 } 548 } 549 type mockHooksKubeClient struct { 550 Resources map[string]*mockHooksManifest 551 } 552 553 var errResourceExists = errors.New("resource already exists") 554 555 func (kc *mockHooksKubeClient) makeManifest(r io.Reader) (*mockHooksManifest, error) { 556 b, err := ioutil.ReadAll(r) 557 if err != nil { 558 return nil, err 559 } 560 561 manifest := &mockHooksManifest{} 562 err = yaml.Unmarshal(b, manifest) 563 if err != nil { 564 return nil, err 565 } 566 567 return manifest, nil 568 } 569 func (kc *mockHooksKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { 570 manifest, err := kc.makeManifest(r) 571 if err != nil { 572 return err 573 } 574 575 if _, hasKey := kc.Resources[manifest.Metadata.Name]; hasKey { 576 return errResourceExists 577 } 578 579 kc.Resources[manifest.Metadata.Name] = manifest 580 581 return nil 582 } 583 func (kc *mockHooksKubeClient) Get(ns string, r io.Reader) (string, error) { 584 return "", nil 585 } 586 func (kc *mockHooksKubeClient) Delete(ns string, r io.Reader) error { 587 manifest, err := kc.makeManifest(r) 588 if err != nil { 589 return err 590 } 591 592 delete(kc.Resources, manifest.Metadata.Name) 593 594 return nil 595 } 596 func (kc *mockHooksKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { 597 paramManifest, err := kc.makeManifest(r) 598 if err != nil { 599 return err 600 } 601 602 manifest, hasManifest := kc.Resources[paramManifest.Metadata.Name] 603 if !hasManifest { 604 return fmt.Errorf("mockHooksKubeClient.WatchUntilReady: no such resource %s found", paramManifest.Metadata.Name) 605 } 606 607 if manifest.Metadata.Annotations["mockHooksKubeClient/Emulate"] == "hook-failed" { 608 return fmt.Errorf("mockHooksKubeClient.WatchUntilReady: hook-failed") 609 } 610 611 return nil 612 } 613 func (kc *mockHooksKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { 614 return nil 615 } 616 func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) { 617 return []*resource.Info{}, nil 618 } 619 func (kc *mockHooksKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) { 620 return []*resource.Info{}, nil 621 } 622 func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { 623 return v1.PodUnknown, nil 624 } 625 626 func deletePolicyStub(kubeClient *mockHooksKubeClient) *ReleaseServer { 627 e := environment.New() 628 e.Releases = storage.Init(driver.NewMemory()) 629 e.KubeClient = kubeClient 630 631 clientset := fake.NewSimpleClientset() 632 return &ReleaseServer{ 633 ReleaseModule: &LocalReleaseModule{ 634 clientset: clientset, 635 }, 636 env: e, 637 clientset: clientset, 638 Log: func(_ string, _ ...interface{}) {}, 639 } 640 } 641 642 func deletePolicyHookStub(hookName string, extraAnnotations map[string]string, DeletePolicies []release.Hook_DeletePolicy) *release.Hook { 643 extraAnnotationsStr := "" 644 for k, v := range extraAnnotations { 645 extraAnnotationsStr += fmt.Sprintf(" \"%s\": \"%s\"\n", k, v) 646 } 647 648 return &release.Hook{ 649 Name: hookName, 650 Kind: "Job", 651 Path: hookName, 652 Manifest: fmt.Sprintf(`kind: Job 653 metadata: 654 name: %s 655 annotations: 656 "helm.sh/hook": pre-install,pre-upgrade 657 %sdata: 658 name: value`, hookName, extraAnnotationsStr), 659 Events: []release.Hook_Event{ 660 release.Hook_PRE_INSTALL, 661 release.Hook_PRE_UPGRADE, 662 }, 663 DeletePolicies: DeletePolicies, 664 } 665 } 666 667 func execHookShouldSucceed(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string) error { 668 err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600) 669 if err != nil { 670 return fmt.Errorf("expected hook %s to be successful: %s", hook.Name, err) 671 } 672 return nil 673 } 674 675 func execHookShouldFail(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string) error { 676 err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600) 677 if err == nil { 678 return fmt.Errorf("expected hook %s to be failed", hook.Name) 679 } 680 return nil 681 } 682 683 func execHookShouldFailWithError(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string, expectedError error) error { 684 err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600) 685 if err != expectedError { 686 return fmt.Errorf("expected hook %s to fail with error %v, got %v", hook.Name, expectedError, err) 687 } 688 return nil 689 } 690 691 type deletePolicyContext struct { 692 ReleaseServer *ReleaseServer 693 ReleaseName string 694 Namespace string 695 HookName string 696 KubeClient *mockHooksKubeClient 697 } 698 699 func newDeletePolicyContext() *deletePolicyContext { 700 kubeClient := &mockHooksKubeClient{ 701 Resources: make(map[string]*mockHooksManifest), 702 } 703 704 return &deletePolicyContext{ 705 KubeClient: kubeClient, 706 ReleaseServer: deletePolicyStub(kubeClient), 707 ReleaseName: "flying-carp", 708 Namespace: "river", 709 HookName: "migration-job", 710 } 711 } 712 713 func TestSuccessfulHookWithoutDeletePolicy(t *testing.T) { 714 ctx := newDeletePolicyContext() 715 hook := deletePolicyHookStub(ctx.HookName, nil, nil) 716 717 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 718 if err != nil { 719 t.Error(err) 720 } 721 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 722 t.Errorf("expected resource %s to be created by kube client", hook.Name) 723 } 724 } 725 726 func TestFailedHookWithoutDeletePolicy(t *testing.T) { 727 ctx := newDeletePolicyContext() 728 hook := deletePolicyHookStub(ctx.HookName, 729 map[string]string{"mockHooksKubeClient/Emulate": "hook-failed"}, 730 nil, 731 ) 732 733 err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 734 if err != nil { 735 t.Error(err) 736 } 737 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 738 t.Errorf("expected resource %s to be created by kube client", hook.Name) 739 } 740 } 741 742 func TestSuccessfulHookWithSucceededDeletePolicy(t *testing.T) { 743 ctx := newDeletePolicyContext() 744 hook := deletePolicyHookStub(ctx.HookName, 745 map[string]string{"helm.sh/hook-delete-policy": "hook-succeeded"}, 746 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED}, 747 ) 748 749 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 750 if err != nil { 751 t.Error(err) 752 } 753 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 754 t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) 755 } 756 } 757 758 func TestSuccessfulHookWithFailedDeletePolicy(t *testing.T) { 759 ctx := newDeletePolicyContext() 760 hook := deletePolicyHookStub(ctx.HookName, 761 map[string]string{"helm.sh/hook-delete-policy": "hook-failed"}, 762 []release.Hook_DeletePolicy{release.Hook_FAILED}, 763 ) 764 765 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 766 if err != nil { 767 t.Error(err) 768 } 769 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 770 t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) 771 } 772 } 773 774 func TestFailedHookWithSucceededDeletePolicy(t *testing.T) { 775 ctx := newDeletePolicyContext() 776 777 hook := deletePolicyHookStub(ctx.HookName, 778 map[string]string{ 779 "mockHooksKubeClient/Emulate": "hook-failed", 780 "helm.sh/hook-delete-policy": "hook-succeeded", 781 }, 782 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED}, 783 ) 784 785 err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 786 if err != nil { 787 t.Error(err) 788 } 789 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 790 t.Errorf("expected resource %s to be existing after hook failed", hook.Name) 791 } 792 } 793 794 func TestFailedHookWithFailedDeletePolicy(t *testing.T) { 795 ctx := newDeletePolicyContext() 796 797 hook := deletePolicyHookStub(ctx.HookName, 798 map[string]string{ 799 "mockHooksKubeClient/Emulate": "hook-failed", 800 "helm.sh/hook-delete-policy": "hook-failed", 801 }, 802 []release.Hook_DeletePolicy{release.Hook_FAILED}, 803 ) 804 805 err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 806 if err != nil { 807 t.Error(err) 808 } 809 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 810 t.Errorf("expected resource %s to be unexisting after hook failed", hook.Name) 811 } 812 } 813 814 func TestSuccessfulHookWithSuccededOrFailedDeletePolicy(t *testing.T) { 815 ctx := newDeletePolicyContext() 816 817 hook := deletePolicyHookStub(ctx.HookName, 818 map[string]string{ 819 "helm.sh/hook-delete-policy": "hook-succeeded,hook-failed", 820 }, 821 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_FAILED}, 822 ) 823 824 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 825 if err != nil { 826 t.Error(err) 827 } 828 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 829 t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) 830 } 831 } 832 833 func TestFailedHookWithSuccededOrFailedDeletePolicy(t *testing.T) { 834 ctx := newDeletePolicyContext() 835 836 hook := deletePolicyHookStub(ctx.HookName, 837 map[string]string{ 838 "mockHooksKubeClient/Emulate": "hook-failed", 839 "helm.sh/hook-delete-policy": "hook-succeeded,hook-failed", 840 }, 841 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_FAILED}, 842 ) 843 844 err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 845 if err != nil { 846 t.Error(err) 847 } 848 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 849 t.Errorf("expected resource %s to be unexisting after hook failed", hook.Name) 850 } 851 } 852 853 func TestHookAlreadyExists(t *testing.T) { 854 ctx := newDeletePolicyContext() 855 856 hook := deletePolicyHookStub(ctx.HookName, nil, nil) 857 858 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 859 if err != nil { 860 t.Error(err) 861 } 862 863 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 864 t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) 865 } 866 867 err = execHookShouldFailWithError(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade, errResourceExists) 868 if err != nil { 869 t.Error(err) 870 } 871 872 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 873 t.Errorf("expected resource %s to be existing after already exists error", hook.Name) 874 } 875 } 876 877 func TestHookDeletingWithBeforeHookCreationDeletePolicy(t *testing.T) { 878 ctx := newDeletePolicyContext() 879 880 hook := deletePolicyHookStub(ctx.HookName, 881 map[string]string{"helm.sh/hook-delete-policy": "before-hook-creation"}, 882 []release.Hook_DeletePolicy{release.Hook_BEFORE_HOOK_CREATION}, 883 ) 884 885 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 886 if err != nil { 887 t.Error(err) 888 } 889 890 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 891 t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) 892 } 893 894 err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) 895 if err != nil { 896 t.Error(err) 897 } 898 899 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 900 t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name) 901 } 902 } 903 904 func TestSuccessfulHookWithMixedDeletePolicies(t *testing.T) { 905 ctx := newDeletePolicyContext() 906 907 hook := deletePolicyHookStub(ctx.HookName, 908 map[string]string{ 909 "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", 910 }, 911 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, 912 ) 913 914 err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 915 if err != nil { 916 t.Error(err) 917 } 918 919 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 920 t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) 921 } 922 923 err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) 924 if err != nil { 925 t.Error(err) 926 } 927 928 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 929 t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) 930 } 931 } 932 933 func TestFailedHookWithMixedDeletePolicies(t *testing.T) { 934 ctx := newDeletePolicyContext() 935 936 hook := deletePolicyHookStub(ctx.HookName, 937 map[string]string{ 938 "mockHooksKubeClient/Emulate": "hook-failed", 939 "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", 940 }, 941 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, 942 ) 943 944 err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 945 if err != nil { 946 t.Error(err) 947 } 948 949 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 950 t.Errorf("expected resource %s to be existing after hook failed", hook.Name) 951 } 952 953 err = execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) 954 if err != nil { 955 t.Error(err) 956 } 957 958 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 959 t.Errorf("expected resource %s to be existing after hook failed", hook.Name) 960 } 961 } 962 963 func TestFailedThenSuccessfulHookWithMixedDeletePolicies(t *testing.T) { 964 ctx := newDeletePolicyContext() 965 966 hook := deletePolicyHookStub(ctx.HookName, 967 map[string]string{ 968 "mockHooksKubeClient/Emulate": "hook-failed", 969 "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", 970 }, 971 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, 972 ) 973 974 err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall) 975 if err != nil { 976 t.Error(err) 977 } 978 979 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource { 980 t.Errorf("expected resource %s to be existing after hook failed", hook.Name) 981 } 982 983 hook = deletePolicyHookStub(ctx.HookName, 984 map[string]string{ 985 "helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation", 986 }, 987 []release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION}, 988 ) 989 990 err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade) 991 if err != nil { 992 t.Error(err) 993 } 994 995 if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource { 996 t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name) 997 } 998 }