github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+incompatible/pkg/tiller/release_server_test.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 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 "os" 24 "regexp" 25 "strings" 26 "testing" 27 28 "github.com/golang/protobuf/ptypes/timestamp" 29 "golang.org/x/net/context" 30 grpc "google.golang.org/grpc" 31 "google.golang.org/grpc/metadata" 32 "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" 33 34 "k8s.io/helm/pkg/helm" 35 "k8s.io/helm/pkg/proto/hapi/chart" 36 "k8s.io/helm/pkg/proto/hapi/release" 37 "k8s.io/helm/pkg/proto/hapi/services" 38 "k8s.io/helm/pkg/storage" 39 "k8s.io/helm/pkg/storage/driver" 40 "k8s.io/helm/pkg/tiller/environment" 41 ) 42 43 const notesText = "my notes here" 44 45 var manifestWithHook = `apiVersion: v1 46 kind: ConfigMap 47 metadata: 48 name: test-cm 49 annotations: 50 "helm.sh/hook": post-install,pre-delete 51 data: 52 name: value 53 ` 54 55 var manifestWithTestHook = ` 56 apiVersion: v1 57 kind: Pod 58 metadata: 59 name: finding-nemo, 60 annotations: 61 "helm.sh/hook": test-success 62 spec: 63 containers: 64 - name: nemo-test 65 image: fake-image 66 cmd: fake-command 67 ` 68 69 var manifestWithKeep = `apiVersion: v1 70 kind: ConfigMap 71 metadata: 72 name: test-cm-keep 73 annotations: 74 "helm.sh/resource-policy": keep 75 data: 76 name: value 77 ` 78 79 var manifestWithUpgradeHooks = `apiVersion: v1 80 kind: ConfigMap 81 metadata: 82 name: test-cm 83 annotations: 84 "helm.sh/hook": post-upgrade,pre-upgrade 85 data: 86 name: value 87 ` 88 89 var manifestWithRollbackHooks = `apiVersion: v1 90 kind: ConfigMap 91 metadata: 92 name: test-cm 93 annotations: 94 "helm.sh/hook": post-rollback,pre-rollback 95 data: 96 name: value 97 ` 98 99 func rsFixture() *ReleaseServer { 100 return &ReleaseServer{ 101 env: MockEnvironment(), 102 clientset: fake.NewSimpleClientset(), 103 } 104 } 105 106 // chartStub creates a fully stubbed out chart. 107 func chartStub() *chart.Chart { 108 return &chart.Chart{ 109 // TODO: This should be more complete. 110 Metadata: &chart.Metadata{ 111 Name: "hello", 112 }, 113 // This adds basic templates, partials, and hooks. 114 Templates: []*chart.Template{ 115 {Name: "templates/hello", Data: []byte("hello: world")}, 116 {Name: "templates/goodbye", Data: []byte("goodbye: world")}, 117 {Name: "templates/empty", Data: []byte("")}, 118 {Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, 119 {Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, 120 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 121 }, 122 } 123 } 124 125 // releaseStub creates a release stub, complete with the chartStub as its chart. 126 func releaseStub() *release.Release { 127 return namedReleaseStub("angry-panda", release.Status_DEPLOYED) 128 } 129 130 func namedReleaseStub(name string, status release.Status_Code) *release.Release { 131 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 132 return &release.Release{ 133 Name: name, 134 Info: &release.Info{ 135 FirstDeployed: &date, 136 LastDeployed: &date, 137 Status: &release.Status{Code: status}, 138 Description: "Named Release Stub", 139 }, 140 Chart: chartStub(), 141 Config: &chart.Config{Raw: `name: value`}, 142 Version: 1, 143 Hooks: []*release.Hook{ 144 { 145 Name: "test-cm", 146 Kind: "ConfigMap", 147 Path: "test-cm", 148 Manifest: manifestWithHook, 149 Events: []release.Hook_Event{ 150 release.Hook_POST_INSTALL, 151 release.Hook_PRE_DELETE, 152 }, 153 }, 154 { 155 Name: "finding-nemo", 156 Kind: "Pod", 157 Path: "finding-nemo", 158 Manifest: manifestWithTestHook, 159 Events: []release.Hook_Event{ 160 release.Hook_RELEASE_TEST_SUCCESS, 161 }, 162 }, 163 }, 164 } 165 } 166 167 func upgradeReleaseVersion(rel *release.Release) *release.Release { 168 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 169 170 rel.Info.Status.Code = release.Status_SUPERSEDED 171 return &release.Release{ 172 Name: rel.Name, 173 Info: &release.Info{ 174 FirstDeployed: rel.Info.FirstDeployed, 175 LastDeployed: &date, 176 Status: &release.Status{Code: release.Status_DEPLOYED}, 177 }, 178 Chart: rel.Chart, 179 Config: rel.Config, 180 Version: rel.Version + 1, 181 } 182 } 183 184 func TestValidName(t *testing.T) { 185 for name, valid := range map[string]bool{ 186 "nina pinta santa-maria": false, 187 "nina-pinta-santa-maria": true, 188 "-nina": false, 189 "pinta-": false, 190 "santa-maria": true, 191 "niƱa": false, 192 "...": false, 193 "pinta...": false, 194 "santa...maria": true, 195 "": false, 196 " ": false, 197 ".nina.": false, 198 "nina.pinta": true, 199 } { 200 if valid != ValidName.MatchString(name) { 201 t.Errorf("Expected %q to be %t", name, valid) 202 } 203 } 204 } 205 206 func TestGetVersionSet(t *testing.T) { 207 rs := rsFixture() 208 vs, err := getVersionSet(rs.clientset.Discovery()) 209 if err != nil { 210 t.Error(err) 211 } 212 if !vs.Has("v1") { 213 t.Errorf("Expected supported versions to at least include v1.") 214 } 215 if vs.Has("nosuchversion/v1") { 216 t.Error("Non-existent version is reported found.") 217 } 218 } 219 220 func TestUniqName(t *testing.T) { 221 rs := rsFixture() 222 223 rel1 := releaseStub() 224 rel2 := releaseStub() 225 rel2.Name = "happy-panda" 226 rel2.Info.Status.Code = release.Status_DELETED 227 228 rs.env.Releases.Create(rel1) 229 rs.env.Releases.Create(rel2) 230 231 tests := []struct { 232 name string 233 expect string 234 reuse bool 235 err bool 236 }{ 237 {"first", "first", false, false}, 238 {"", "[a-z]+-[a-z]+", false, false}, 239 {"angry-panda", "", false, true}, 240 {"happy-panda", "", false, true}, 241 {"happy-panda", "happy-panda", true, false}, 242 {"hungry-hungry-hungry-hungry-hungry-hungry-hungry-hungry-hippos", "", true, true}, // Exceeds max name length 243 } 244 245 for _, tt := range tests { 246 u, err := rs.uniqName(tt.name, tt.reuse) 247 if err != nil { 248 if tt.err { 249 continue 250 } 251 t.Fatal(err) 252 } 253 if tt.err { 254 t.Errorf("Expected an error for %q", tt.name) 255 } 256 if match, err := regexp.MatchString(tt.expect, u); err != nil { 257 t.Fatal(err) 258 } else if !match { 259 t.Errorf("Expected %q to match %q", u, tt.expect) 260 } 261 } 262 } 263 264 func TestInstallRelease(t *testing.T) { 265 c := helm.NewContext() 266 rs := rsFixture() 267 268 // TODO: Refactor this into a mock. 269 req := &services.InstallReleaseRequest{ 270 Namespace: "spaced", 271 Chart: &chart.Chart{ 272 Metadata: &chart.Metadata{Name: "hello"}, 273 Templates: []*chart.Template{ 274 {Name: "templates/hello", Data: []byte("hello: world")}, 275 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 276 }, 277 }, 278 } 279 res, err := rs.InstallRelease(c, req) 280 if err != nil { 281 t.Fatalf("Failed install: %s", err) 282 } 283 if res.Release.Name == "" { 284 t.Errorf("Expected release name.") 285 } 286 if res.Release.Namespace != "spaced" { 287 t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) 288 } 289 290 rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 291 if err != nil { 292 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 293 } 294 295 t.Logf("rel: %v", rel) 296 297 if len(rel.Hooks) != 1 { 298 t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) 299 } 300 if rel.Hooks[0].Manifest != manifestWithHook { 301 t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) 302 } 303 304 if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { 305 t.Errorf("Expected event 0 is post install") 306 } 307 if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { 308 t.Errorf("Expected event 0 is pre-delete") 309 } 310 311 if len(res.Release.Manifest) == 0 { 312 t.Errorf("No manifest returned: %v", res.Release) 313 } 314 315 if len(rel.Manifest) == 0 { 316 t.Errorf("Expected manifest in %v", res) 317 } 318 319 if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { 320 t.Errorf("unexpected output: %s", rel.Manifest) 321 } 322 323 if rel.Info.Description != "Install complete" { 324 t.Errorf("unexpected description: %s", rel.Info.Description) 325 } 326 } 327 328 func TestInstallReleaseWithNotes(t *testing.T) { 329 c := helm.NewContext() 330 rs := rsFixture() 331 332 // TODO: Refactor this into a mock. 333 req := &services.InstallReleaseRequest{ 334 Namespace: "spaced", 335 Chart: &chart.Chart{ 336 Metadata: &chart.Metadata{Name: "hello"}, 337 Templates: []*chart.Template{ 338 {Name: "templates/hello", Data: []byte("hello: world")}, 339 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 340 {Name: "templates/NOTES.txt", Data: []byte(notesText)}, 341 }, 342 }, 343 } 344 res, err := rs.InstallRelease(c, req) 345 if err != nil { 346 t.Fatalf("Failed install: %s", err) 347 } 348 if res.Release.Name == "" { 349 t.Errorf("Expected release name.") 350 } 351 if res.Release.Namespace != "spaced" { 352 t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) 353 } 354 355 rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 356 if err != nil { 357 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 358 } 359 360 t.Logf("rel: %v", rel) 361 362 if len(rel.Hooks) != 1 { 363 t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) 364 } 365 if rel.Hooks[0].Manifest != manifestWithHook { 366 t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) 367 } 368 369 if rel.Info.Status.Notes != notesText { 370 t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) 371 } 372 373 if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { 374 t.Errorf("Expected event 0 is post install") 375 } 376 if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { 377 t.Errorf("Expected event 0 is pre-delete") 378 } 379 380 if len(res.Release.Manifest) == 0 { 381 t.Errorf("No manifest returned: %v", res.Release) 382 } 383 384 if len(rel.Manifest) == 0 { 385 t.Errorf("Expected manifest in %v", res) 386 } 387 388 if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { 389 t.Errorf("unexpected output: %s", rel.Manifest) 390 } 391 392 if rel.Info.Description != "Install complete" { 393 t.Errorf("unexpected description: %s", rel.Info.Description) 394 } 395 } 396 397 func TestInstallReleaseWithNotesRendered(t *testing.T) { 398 c := helm.NewContext() 399 rs := rsFixture() 400 401 // TODO: Refactor this into a mock. 402 req := &services.InstallReleaseRequest{ 403 Namespace: "spaced", 404 Chart: &chart.Chart{ 405 Metadata: &chart.Metadata{Name: "hello"}, 406 Templates: []*chart.Template{ 407 {Name: "templates/hello", Data: []byte("hello: world")}, 408 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 409 {Name: "templates/NOTES.txt", Data: []byte(notesText + " {{.Release.Name}}")}, 410 }, 411 }, 412 } 413 res, err := rs.InstallRelease(c, req) 414 if err != nil { 415 t.Fatalf("Failed install: %s", err) 416 } 417 if res.Release.Name == "" { 418 t.Errorf("Expected release name.") 419 } 420 if res.Release.Namespace != "spaced" { 421 t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) 422 } 423 424 rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 425 if err != nil { 426 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 427 } 428 429 t.Logf("rel: %v", rel) 430 431 if len(rel.Hooks) != 1 { 432 t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) 433 } 434 if rel.Hooks[0].Manifest != manifestWithHook { 435 t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) 436 } 437 438 expectedNotes := fmt.Sprintf("%s %s", notesText, res.Release.Name) 439 if rel.Info.Status.Notes != expectedNotes { 440 t.Fatalf("Expected '%s', got '%s'", expectedNotes, rel.Info.Status.Notes) 441 } 442 443 if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { 444 t.Errorf("Expected event 0 is post install") 445 } 446 if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { 447 t.Errorf("Expected event 0 is pre-delete") 448 } 449 450 if len(res.Release.Manifest) == 0 { 451 t.Errorf("No manifest returned: %v", res.Release) 452 } 453 454 if len(rel.Manifest) == 0 { 455 t.Errorf("Expected manifest in %v", res) 456 } 457 458 if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { 459 t.Errorf("unexpected output: %s", rel.Manifest) 460 } 461 462 if rel.Info.Description != "Install complete" { 463 t.Errorf("unexpected description: %s", rel.Info.Description) 464 } 465 } 466 467 func TestInstallReleaseWithChartAndDependencyNotes(t *testing.T) { 468 c := helm.NewContext() 469 rs := rsFixture() 470 471 // TODO: Refactor this into a mock. 472 req := &services.InstallReleaseRequest{ 473 Namespace: "spaced", 474 Chart: &chart.Chart{ 475 Metadata: &chart.Metadata{Name: "hello"}, 476 Templates: []*chart.Template{ 477 {Name: "templates/hello", Data: []byte("hello: world")}, 478 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 479 {Name: "templates/NOTES.txt", Data: []byte(notesText)}, 480 }, 481 Dependencies: []*chart.Chart{ 482 { 483 Metadata: &chart.Metadata{Name: "hello"}, 484 Templates: []*chart.Template{ 485 {Name: "templates/hello", Data: []byte("hello: world")}, 486 {Name: "templates/hooks", Data: []byte(manifestWithHook)}, 487 {Name: "templates/NOTES.txt", Data: []byte(notesText + " child")}, 488 }, 489 }, 490 }, 491 }, 492 } 493 494 res, err := rs.InstallRelease(c, req) 495 if err != nil { 496 t.Fatalf("Failed install: %s", err) 497 } 498 if res.Release.Name == "" { 499 t.Errorf("Expected release name.") 500 } 501 502 rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 503 if err != nil { 504 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 505 } 506 507 t.Logf("rel: %v", rel) 508 509 if rel.Info.Status.Notes != notesText { 510 t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) 511 } 512 513 if rel.Info.Description != "Install complete" { 514 t.Errorf("unexpected description: %s", rel.Info.Description) 515 } 516 } 517 518 func TestInstallReleaseDryRun(t *testing.T) { 519 c := helm.NewContext() 520 rs := rsFixture() 521 522 req := &services.InstallReleaseRequest{ 523 Chart: chartStub(), 524 DryRun: true, 525 } 526 res, err := rs.InstallRelease(c, req) 527 if err != nil { 528 t.Errorf("Failed install: %s", err) 529 } 530 if res.Release.Name == "" { 531 t.Errorf("Expected release name.") 532 } 533 534 if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { 535 t.Errorf("unexpected output: %s", res.Release.Manifest) 536 } 537 538 if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world") { 539 t.Errorf("unexpected output: %s", res.Release.Manifest) 540 } 541 542 if !strings.Contains(res.Release.Manifest, "hello: Earth") { 543 t.Errorf("Should contain partial content. %s", res.Release.Manifest) 544 } 545 546 if strings.Contains(res.Release.Manifest, "hello: {{ template \"_planet\" . }}") { 547 t.Errorf("Should not contain partial templates itself. %s", res.Release.Manifest) 548 } 549 550 if strings.Contains(res.Release.Manifest, "empty") { 551 t.Errorf("Should not contain template data for an empty file. %s", res.Release.Manifest) 552 } 553 554 if _, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version); err == nil { 555 t.Errorf("Expected no stored release.") 556 } 557 558 if l := len(res.Release.Hooks); l != 1 { 559 t.Fatalf("Expected 1 hook, got %d", l) 560 } 561 562 if res.Release.Hooks[0].LastRun != nil { 563 t.Error("Expected hook to not be marked as run.") 564 } 565 566 if res.Release.Info.Description != "Dry run complete" { 567 t.Errorf("unexpected description: %s", res.Release.Info.Description) 568 } 569 } 570 571 func TestInstallReleaseNoHooks(t *testing.T) { 572 c := helm.NewContext() 573 rs := rsFixture() 574 rs.env.Releases.Create(releaseStub()) 575 576 req := &services.InstallReleaseRequest{ 577 Chart: chartStub(), 578 DisableHooks: true, 579 } 580 res, err := rs.InstallRelease(c, req) 581 if err != nil { 582 t.Errorf("Failed install: %s", err) 583 } 584 585 if hl := res.Release.Hooks[0].LastRun; hl != nil { 586 t.Errorf("Expected that no hooks were run. Got %d", hl) 587 } 588 } 589 590 func TestInstallReleaseFailedHooks(t *testing.T) { 591 c := helm.NewContext() 592 rs := rsFixture() 593 rs.env.Releases.Create(releaseStub()) 594 rs.env.KubeClient = newHookFailingKubeClient() 595 596 req := &services.InstallReleaseRequest{ 597 Chart: chartStub(), 598 } 599 res, err := rs.InstallRelease(c, req) 600 if err == nil { 601 t.Error("Expected failed install") 602 } 603 604 if hl := res.Release.Info.Status.Code; hl != release.Status_FAILED { 605 t.Errorf("Expected FAILED release. Got %d", hl) 606 } 607 } 608 609 func TestInstallReleaseReuseName(t *testing.T) { 610 c := helm.NewContext() 611 rs := rsFixture() 612 rel := releaseStub() 613 rel.Info.Status.Code = release.Status_DELETED 614 rs.env.Releases.Create(rel) 615 616 req := &services.InstallReleaseRequest{ 617 Chart: chartStub(), 618 ReuseName: true, 619 Name: rel.Name, 620 } 621 res, err := rs.InstallRelease(c, req) 622 if err != nil { 623 t.Fatalf("Failed install: %s", err) 624 } 625 626 if res.Release.Name != rel.Name { 627 t.Errorf("expected %q, got %q", rel.Name, res.Release.Name) 628 } 629 630 getreq := &services.GetReleaseStatusRequest{Name: rel.Name, Version: 0} 631 getres, err := rs.GetReleaseStatus(c, getreq) 632 if err != nil { 633 t.Errorf("Failed to retrieve release: %s", err) 634 } 635 if getres.Info.Status.Code != release.Status_DEPLOYED { 636 t.Errorf("Release status is %q", getres.Info.Status.Code) 637 } 638 } 639 640 func TestUpdateRelease(t *testing.T) { 641 c := helm.NewContext() 642 rs := rsFixture() 643 rel := releaseStub() 644 rs.env.Releases.Create(rel) 645 646 req := &services.UpdateReleaseRequest{ 647 Name: rel.Name, 648 Chart: &chart.Chart{ 649 Metadata: &chart.Metadata{Name: "hello"}, 650 Templates: []*chart.Template{ 651 {Name: "templates/hello", Data: []byte("hello: world")}, 652 {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, 653 }, 654 }, 655 } 656 res, err := rs.UpdateRelease(c, req) 657 if err != nil { 658 t.Fatalf("Failed updated: %s", err) 659 } 660 661 if res.Release.Name == "" { 662 t.Errorf("Expected release name.") 663 } 664 665 if res.Release.Name != rel.Name { 666 t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) 667 } 668 669 if res.Release.Namespace != rel.Namespace { 670 t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) 671 } 672 673 updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 674 if err != nil { 675 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 676 } 677 678 if len(updated.Hooks) != 1 { 679 t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) 680 } 681 if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { 682 t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) 683 } 684 685 if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { 686 t.Errorf("Expected event 0 to be post upgrade") 687 } 688 689 if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { 690 t.Errorf("Expected event 0 to be pre upgrade") 691 } 692 693 if len(res.Release.Manifest) == 0 { 694 t.Errorf("No manifest returned: %v", res.Release) 695 } 696 697 if res.Release.Config == nil { 698 t.Errorf("Got release without config: %#v", res.Release) 699 } else if res.Release.Config.Raw != rel.Config.Raw { 700 t.Errorf("Expected release values %q, got %q", rel.Config.Raw, res.Release.Config.Raw) 701 } 702 703 if len(updated.Manifest) == 0 { 704 t.Errorf("Expected manifest in %v", res) 705 } 706 707 if !strings.Contains(updated.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { 708 t.Errorf("unexpected output: %s", rel.Manifest) 709 } 710 711 if res.Release.Version != 2 { 712 t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) 713 } 714 715 edesc := "Upgrade complete" 716 if got := res.Release.Info.Description; got != edesc { 717 t.Errorf("Expected description %q, got %q", edesc, got) 718 } 719 } 720 func TestUpdateRelease_ResetValues(t *testing.T) { 721 c := helm.NewContext() 722 rs := rsFixture() 723 rel := releaseStub() 724 rs.env.Releases.Create(rel) 725 726 req := &services.UpdateReleaseRequest{ 727 Name: rel.Name, 728 Chart: &chart.Chart{ 729 Metadata: &chart.Metadata{Name: "hello"}, 730 Templates: []*chart.Template{ 731 {Name: "templates/hello", Data: []byte("hello: world")}, 732 {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, 733 }, 734 }, 735 ResetValues: true, 736 } 737 res, err := rs.UpdateRelease(c, req) 738 if err != nil { 739 t.Fatalf("Failed updated: %s", err) 740 } 741 // This should have been unset. Config: &chart.Config{Raw: `name: value`}, 742 if res.Release.Config != nil && res.Release.Config.Raw != "" { 743 t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) 744 } 745 } 746 747 func TestUpdateRelease_ReuseValues(t *testing.T) { 748 c := helm.NewContext() 749 rs := rsFixture() 750 rel := releaseStub() 751 rs.env.Releases.Create(rel) 752 753 req := &services.UpdateReleaseRequest{ 754 Name: rel.Name, 755 Chart: &chart.Chart{ 756 Metadata: &chart.Metadata{Name: "hello"}, 757 Templates: []*chart.Template{ 758 {Name: "templates/hello", Data: []byte("hello: world")}, 759 {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, 760 }, 761 // Since reuseValues is set, this should get ignored. 762 Values: &chart.Config{Raw: "foo: bar\n"}, 763 }, 764 Values: &chart.Config{Raw: "name2: val2"}, 765 ReuseValues: true, 766 } 767 res, err := rs.UpdateRelease(c, req) 768 if err != nil { 769 t.Fatalf("Failed updated: %s", err) 770 } 771 // This should have been overwritten with the old value. 772 expect := "name: value\n" 773 if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { 774 t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) 775 } 776 // This should have the newly-passed overrides. 777 expect = "name2: val2" 778 if res.Release.Config != nil && res.Release.Config.Raw != expect { 779 t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) 780 } 781 } 782 783 func TestUpdateRelease_ResetReuseValues(t *testing.T) { 784 // This verifies that when both reset and reuse are set, reset wins. 785 c := helm.NewContext() 786 rs := rsFixture() 787 rel := releaseStub() 788 rs.env.Releases.Create(rel) 789 790 req := &services.UpdateReleaseRequest{ 791 Name: rel.Name, 792 Chart: &chart.Chart{ 793 Metadata: &chart.Metadata{Name: "hello"}, 794 Templates: []*chart.Template{ 795 {Name: "templates/hello", Data: []byte("hello: world")}, 796 {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, 797 }, 798 }, 799 ResetValues: true, 800 ReuseValues: true, 801 } 802 res, err := rs.UpdateRelease(c, req) 803 if err != nil { 804 t.Fatalf("Failed updated: %s", err) 805 } 806 // This should have been unset. Config: &chart.Config{Raw: `name: value`}, 807 if res.Release.Config != nil && res.Release.Config.Raw != "" { 808 t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) 809 } 810 } 811 812 func TestUpdateReleaseFailure(t *testing.T) { 813 c := helm.NewContext() 814 rs := rsFixture() 815 rel := releaseStub() 816 rs.env.Releases.Create(rel) 817 rs.env.KubeClient = newUpdateFailingKubeClient() 818 819 req := &services.UpdateReleaseRequest{ 820 Name: rel.Name, 821 DisableHooks: true, 822 Chart: &chart.Chart{ 823 Metadata: &chart.Metadata{Name: "hello"}, 824 Templates: []*chart.Template{ 825 {Name: "templates/something", Data: []byte("hello: world")}, 826 }, 827 }, 828 } 829 830 res, err := rs.UpdateRelease(c, req) 831 if err == nil { 832 t.Error("Expected failed update") 833 } 834 835 if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_FAILED { 836 t.Errorf("Expected FAILED release. Got %d", updatedStatus) 837 } 838 839 edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client" 840 if got := res.Release.Info.Description; got != edesc { 841 t.Errorf("Expected description %q, got %q", edesc, got) 842 } 843 844 oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) 845 if err != nil { 846 t.Errorf("Expected to be able to get previous release") 847 } 848 if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { 849 t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) 850 } 851 } 852 853 func TestRollbackReleaseFailure(t *testing.T) { 854 c := helm.NewContext() 855 rs := rsFixture() 856 rel := releaseStub() 857 rs.env.Releases.Create(rel) 858 upgradedRel := upgradeReleaseVersion(rel) 859 rs.env.Releases.Update(rel) 860 rs.env.Releases.Create(upgradedRel) 861 862 req := &services.RollbackReleaseRequest{ 863 Name: rel.Name, 864 DisableHooks: true, 865 } 866 867 rs.env.KubeClient = newUpdateFailingKubeClient() 868 res, err := rs.RollbackRelease(c, req) 869 if err == nil { 870 t.Error("Expected failed rollback") 871 } 872 873 if targetStatus := res.Release.Info.Status.Code; targetStatus != release.Status_FAILED { 874 t.Errorf("Expected FAILED release. Got %v", targetStatus) 875 } 876 877 oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) 878 if err != nil { 879 t.Errorf("Expected to be able to get previous release") 880 } 881 if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { 882 t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) 883 } 884 } 885 886 func TestUpdateReleaseNoHooks(t *testing.T) { 887 c := helm.NewContext() 888 rs := rsFixture() 889 rel := releaseStub() 890 rs.env.Releases.Create(rel) 891 892 req := &services.UpdateReleaseRequest{ 893 Name: rel.Name, 894 DisableHooks: true, 895 Chart: &chart.Chart{ 896 Metadata: &chart.Metadata{Name: "hello"}, 897 Templates: []*chart.Template{ 898 {Name: "templates/hello", Data: []byte("hello: world")}, 899 {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, 900 }, 901 }, 902 } 903 904 res, err := rs.UpdateRelease(c, req) 905 if err != nil { 906 t.Fatalf("Failed updated: %s", err) 907 } 908 909 if hl := res.Release.Hooks[0].LastRun; hl != nil { 910 t.Errorf("Expected that no hooks were run. Got %d", hl) 911 } 912 913 } 914 915 func TestUpdateReleaseNoChanges(t *testing.T) { 916 c := helm.NewContext() 917 rs := rsFixture() 918 rel := releaseStub() 919 rs.env.Releases.Create(rel) 920 921 req := &services.UpdateReleaseRequest{ 922 Name: rel.Name, 923 DisableHooks: true, 924 Chart: rel.GetChart(), 925 } 926 927 _, err := rs.UpdateRelease(c, req) 928 if err != nil { 929 t.Fatalf("Failed updated: %s", err) 930 } 931 } 932 933 func TestRollbackReleaseNoHooks(t *testing.T) { 934 c := helm.NewContext() 935 rs := rsFixture() 936 rel := releaseStub() 937 rel.Hooks = []*release.Hook{ 938 { 939 Name: "test-cm", 940 Kind: "ConfigMap", 941 Path: "test-cm", 942 Manifest: manifestWithRollbackHooks, 943 Events: []release.Hook_Event{ 944 release.Hook_PRE_ROLLBACK, 945 release.Hook_POST_ROLLBACK, 946 }, 947 }, 948 } 949 rs.env.Releases.Create(rel) 950 upgradedRel := upgradeReleaseVersion(rel) 951 rs.env.Releases.Update(rel) 952 rs.env.Releases.Create(upgradedRel) 953 954 req := &services.RollbackReleaseRequest{ 955 Name: rel.Name, 956 DisableHooks: true, 957 } 958 959 res, err := rs.RollbackRelease(c, req) 960 if err != nil { 961 t.Fatalf("Failed rollback: %s", err) 962 } 963 964 if hl := res.Release.Hooks[0].LastRun; hl != nil { 965 t.Errorf("Expected that no hooks were run. Got %d", hl) 966 } 967 } 968 969 func TestRollbackWithReleaseVersion(t *testing.T) { 970 c := helm.NewContext() 971 rs := rsFixture() 972 rel := releaseStub() 973 rs.env.Releases.Create(rel) 974 upgradedRel := upgradeReleaseVersion(rel) 975 rs.env.Releases.Update(rel) 976 rs.env.Releases.Create(upgradedRel) 977 978 req := &services.RollbackReleaseRequest{ 979 Name: rel.Name, 980 DisableHooks: true, 981 Version: 1, 982 } 983 984 _, err := rs.RollbackRelease(c, req) 985 if err != nil { 986 t.Fatalf("Failed rollback: %s", err) 987 } 988 } 989 990 func TestRollbackRelease(t *testing.T) { 991 c := helm.NewContext() 992 rs := rsFixture() 993 rel := releaseStub() 994 rs.env.Releases.Create(rel) 995 upgradedRel := upgradeReleaseVersion(rel) 996 upgradedRel.Hooks = []*release.Hook{ 997 { 998 Name: "test-cm", 999 Kind: "ConfigMap", 1000 Path: "test-cm", 1001 Manifest: manifestWithRollbackHooks, 1002 Events: []release.Hook_Event{ 1003 release.Hook_PRE_ROLLBACK, 1004 release.Hook_POST_ROLLBACK, 1005 }, 1006 }, 1007 } 1008 1009 upgradedRel.Manifest = "hello world" 1010 rs.env.Releases.Update(rel) 1011 rs.env.Releases.Create(upgradedRel) 1012 1013 req := &services.RollbackReleaseRequest{ 1014 Name: rel.Name, 1015 } 1016 res, err := rs.RollbackRelease(c, req) 1017 if err != nil { 1018 t.Fatalf("Failed rollback: %s", err) 1019 } 1020 1021 if res.Release.Name == "" { 1022 t.Errorf("Expected release name.") 1023 } 1024 1025 if res.Release.Name != rel.Name { 1026 t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) 1027 } 1028 1029 if res.Release.Namespace != rel.Namespace { 1030 t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) 1031 } 1032 1033 if res.Release.Version != 3 { 1034 t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) 1035 } 1036 1037 updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 1038 if err != nil { 1039 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 1040 } 1041 1042 if len(updated.Hooks) != 2 { 1043 t.Fatalf("Expected 2 hooks, got %d", len(updated.Hooks)) 1044 } 1045 1046 if updated.Hooks[0].Manifest != manifestWithHook { 1047 t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) 1048 } 1049 1050 anotherUpgradedRelease := upgradeReleaseVersion(upgradedRel) 1051 rs.env.Releases.Update(upgradedRel) 1052 rs.env.Releases.Create(anotherUpgradedRelease) 1053 1054 res, err = rs.RollbackRelease(c, req) 1055 if err != nil { 1056 t.Fatalf("Failed rollback: %s", err) 1057 } 1058 1059 updated, err = rs.env.Releases.Get(res.Release.Name, res.Release.Version) 1060 if err != nil { 1061 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 1062 } 1063 1064 if len(updated.Hooks) != 1 { 1065 t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) 1066 } 1067 1068 if updated.Hooks[0].Manifest != manifestWithRollbackHooks { 1069 t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) 1070 } 1071 1072 if res.Release.Version != 4 { 1073 t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) 1074 } 1075 1076 if updated.Hooks[0].Events[0] != release.Hook_PRE_ROLLBACK { 1077 t.Errorf("Expected event 0 to be pre rollback") 1078 } 1079 1080 if updated.Hooks[0].Events[1] != release.Hook_POST_ROLLBACK { 1081 t.Errorf("Expected event 1 to be post rollback") 1082 } 1083 1084 if len(res.Release.Manifest) == 0 { 1085 t.Errorf("No manifest returned: %v", res.Release) 1086 } 1087 1088 if len(updated.Manifest) == 0 { 1089 t.Errorf("Expected manifest in %v", res) 1090 } 1091 1092 if !strings.Contains(updated.Manifest, "hello world") { 1093 t.Errorf("unexpected output: %s", rel.Manifest) 1094 } 1095 1096 if res.Release.Info.Description != "Rollback to 2" { 1097 t.Errorf("Expected rollback to 2, got %q", res.Release.Info.Description) 1098 } 1099 1100 } 1101 1102 func TestUninstallRelease(t *testing.T) { 1103 c := helm.NewContext() 1104 rs := rsFixture() 1105 rs.env.Releases.Create(releaseStub()) 1106 1107 req := &services.UninstallReleaseRequest{ 1108 Name: "angry-panda", 1109 } 1110 1111 res, err := rs.UninstallRelease(c, req) 1112 if err != nil { 1113 t.Fatalf("Failed uninstall: %s", err) 1114 } 1115 1116 if res.Release.Name != "angry-panda" { 1117 t.Errorf("Expected angry-panda, got %q", res.Release.Name) 1118 } 1119 1120 if res.Release.Info.Status.Code != release.Status_DELETED { 1121 t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) 1122 } 1123 1124 if res.Release.Hooks[0].LastRun.Seconds == 0 { 1125 t.Error("Expected LastRun to be greater than zero.") 1126 } 1127 1128 if res.Release.Info.Deleted.Seconds <= 0 { 1129 t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) 1130 } 1131 1132 if res.Release.Info.Description != "Deletion complete" { 1133 t.Errorf("Expected Deletion complete, got %q", res.Release.Info.Description) 1134 } 1135 } 1136 1137 func TestUninstallPurgeRelease(t *testing.T) { 1138 c := helm.NewContext() 1139 rs := rsFixture() 1140 rel := releaseStub() 1141 rs.env.Releases.Create(rel) 1142 upgradedRel := upgradeReleaseVersion(rel) 1143 rs.env.Releases.Update(rel) 1144 rs.env.Releases.Create(upgradedRel) 1145 1146 req := &services.UninstallReleaseRequest{ 1147 Name: "angry-panda", 1148 Purge: true, 1149 } 1150 1151 res, err := rs.UninstallRelease(c, req) 1152 if err != nil { 1153 t.Fatalf("Failed uninstall: %s", err) 1154 } 1155 1156 if res.Release.Name != "angry-panda" { 1157 t.Errorf("Expected angry-panda, got %q", res.Release.Name) 1158 } 1159 1160 if res.Release.Info.Status.Code != release.Status_DELETED { 1161 t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) 1162 } 1163 1164 if res.Release.Info.Deleted.Seconds <= 0 { 1165 t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) 1166 } 1167 rels, err := rs.GetHistory(helm.NewContext(), &services.GetHistoryRequest{Name: "angry-panda"}) 1168 if err != nil { 1169 t.Fatal(err) 1170 } 1171 if len(rels.Releases) != 0 { 1172 t.Errorf("Expected no releases in storage, got %d", len(rels.Releases)) 1173 } 1174 } 1175 1176 func TestUninstallPurgeDeleteRelease(t *testing.T) { 1177 c := helm.NewContext() 1178 rs := rsFixture() 1179 rs.env.Releases.Create(releaseStub()) 1180 1181 req := &services.UninstallReleaseRequest{ 1182 Name: "angry-panda", 1183 } 1184 1185 _, err := rs.UninstallRelease(c, req) 1186 if err != nil { 1187 t.Fatalf("Failed uninstall: %s", err) 1188 } 1189 1190 req2 := &services.UninstallReleaseRequest{ 1191 Name: "angry-panda", 1192 Purge: true, 1193 } 1194 1195 _, err2 := rs.UninstallRelease(c, req2) 1196 if err2 != nil && err2.Error() != "'angry-panda' has no deployed releases" { 1197 t.Errorf("Failed uninstall: %s", err2) 1198 } 1199 } 1200 1201 func TestUninstallReleaseWithKeepPolicy(t *testing.T) { 1202 c := helm.NewContext() 1203 rs := rsFixture() 1204 name := "angry-bunny" 1205 rs.env.Releases.Create(releaseWithKeepStub(name)) 1206 1207 req := &services.UninstallReleaseRequest{ 1208 Name: name, 1209 } 1210 1211 res, err := rs.UninstallRelease(c, req) 1212 if err != nil { 1213 t.Fatalf("Failed uninstall: %s", err) 1214 } 1215 1216 if res.Release.Name != name { 1217 t.Errorf("Expected angry-bunny, got %q", res.Release.Name) 1218 } 1219 1220 if res.Release.Info.Status.Code != release.Status_DELETED { 1221 t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) 1222 } 1223 1224 if res.Info == "" { 1225 t.Errorf("Expected response info to not be empty") 1226 } else { 1227 if !strings.Contains(res.Info, "[ConfigMap] test-cm-keep") { 1228 t.Errorf("unexpected output: %s", res.Info) 1229 } 1230 } 1231 } 1232 1233 func releaseWithKeepStub(rlsName string) *release.Release { 1234 ch := &chart.Chart{ 1235 Metadata: &chart.Metadata{ 1236 Name: "bunnychart", 1237 }, 1238 Templates: []*chart.Template{ 1239 {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, 1240 }, 1241 } 1242 1243 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 1244 return &release.Release{ 1245 Name: rlsName, 1246 Info: &release.Info{ 1247 FirstDeployed: &date, 1248 LastDeployed: &date, 1249 Status: &release.Status{Code: release.Status_DEPLOYED}, 1250 }, 1251 Chart: ch, 1252 Config: &chart.Config{Raw: `name: value`}, 1253 Version: 1, 1254 Manifest: manifestWithKeep, 1255 } 1256 } 1257 1258 func TestUninstallReleaseNoHooks(t *testing.T) { 1259 c := helm.NewContext() 1260 rs := rsFixture() 1261 rs.env.Releases.Create(releaseStub()) 1262 1263 req := &services.UninstallReleaseRequest{ 1264 Name: "angry-panda", 1265 DisableHooks: true, 1266 } 1267 1268 res, err := rs.UninstallRelease(c, req) 1269 if err != nil { 1270 t.Errorf("Failed uninstall: %s", err) 1271 } 1272 1273 // The default value for a protobuf timestamp is nil. 1274 if res.Release.Hooks[0].LastRun != nil { 1275 t.Errorf("Expected LastRun to be zero, got %d.", res.Release.Hooks[0].LastRun.Seconds) 1276 } 1277 } 1278 1279 func TestGetReleaseContent(t *testing.T) { 1280 c := helm.NewContext() 1281 rs := rsFixture() 1282 rel := releaseStub() 1283 if err := rs.env.Releases.Create(rel); err != nil { 1284 t.Fatalf("Could not store mock release: %s", err) 1285 } 1286 1287 res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name, Version: 1}) 1288 if err != nil { 1289 t.Errorf("Error getting release content: %s", err) 1290 } 1291 1292 if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name { 1293 t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Release.Chart.Metadata.Name) 1294 } 1295 } 1296 1297 func TestGetReleaseStatus(t *testing.T) { 1298 c := helm.NewContext() 1299 rs := rsFixture() 1300 rel := releaseStub() 1301 if err := rs.env.Releases.Create(rel); err != nil { 1302 t.Fatalf("Could not store mock release: %s", err) 1303 } 1304 1305 res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) 1306 if err != nil { 1307 t.Errorf("Error getting release content: %s", err) 1308 } 1309 1310 if res.Name != rel.Name { 1311 t.Errorf("Expected name %q, got %q", rel.Name, res.Name) 1312 } 1313 if res.Info.Status.Code != release.Status_DEPLOYED { 1314 t.Errorf("Expected %d, got %d", release.Status_DEPLOYED, res.Info.Status.Code) 1315 } 1316 } 1317 1318 func TestGetReleaseStatusDeleted(t *testing.T) { 1319 c := helm.NewContext() 1320 rs := rsFixture() 1321 rel := releaseStub() 1322 rel.Info.Status.Code = release.Status_DELETED 1323 if err := rs.env.Releases.Create(rel); err != nil { 1324 t.Fatalf("Could not store mock release: %s", err) 1325 } 1326 1327 res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) 1328 if err != nil { 1329 t.Fatalf("Error getting release content: %s", err) 1330 } 1331 1332 if res.Info.Status.Code != release.Status_DELETED { 1333 t.Errorf("Expected %d, got %d", release.Status_DELETED, res.Info.Status.Code) 1334 } 1335 } 1336 1337 func TestListReleases(t *testing.T) { 1338 rs := rsFixture() 1339 num := 7 1340 for i := 0; i < num; i++ { 1341 rel := releaseStub() 1342 rel.Name = fmt.Sprintf("rel-%d", i) 1343 if err := rs.env.Releases.Create(rel); err != nil { 1344 t.Fatalf("Could not store mock release: %s", err) 1345 } 1346 } 1347 1348 mrs := &mockListServer{} 1349 if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { 1350 t.Fatalf("Failed listing: %s", err) 1351 } 1352 1353 if len(mrs.val.Releases) != num { 1354 t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) 1355 } 1356 } 1357 1358 func TestListReleasesByStatus(t *testing.T) { 1359 rs := rsFixture() 1360 stubs := []*release.Release{ 1361 namedReleaseStub("kamal", release.Status_DEPLOYED), 1362 namedReleaseStub("astrolabe", release.Status_DELETED), 1363 namedReleaseStub("octant", release.Status_FAILED), 1364 namedReleaseStub("sextant", release.Status_UNKNOWN), 1365 } 1366 for _, stub := range stubs { 1367 if err := rs.env.Releases.Create(stub); err != nil { 1368 t.Fatalf("Could not create stub: %s", err) 1369 } 1370 } 1371 1372 tests := []struct { 1373 statusCodes []release.Status_Code 1374 names []string 1375 }{ 1376 { 1377 names: []string{"kamal"}, 1378 statusCodes: []release.Status_Code{release.Status_DEPLOYED}, 1379 }, 1380 { 1381 names: []string{"astrolabe"}, 1382 statusCodes: []release.Status_Code{release.Status_DELETED}, 1383 }, 1384 { 1385 names: []string{"kamal", "octant"}, 1386 statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, 1387 }, 1388 { 1389 names: []string{"kamal", "astrolabe", "octant", "sextant"}, 1390 statusCodes: []release.Status_Code{ 1391 release.Status_DEPLOYED, 1392 release.Status_DELETED, 1393 release.Status_FAILED, 1394 release.Status_UNKNOWN, 1395 }, 1396 }, 1397 } 1398 1399 for i, tt := range tests { 1400 mrs := &mockListServer{} 1401 if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { 1402 t.Fatalf("Failed listing %d: %s", i, err) 1403 } 1404 1405 if len(tt.names) != len(mrs.val.Releases) { 1406 t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) 1407 } 1408 1409 for _, name := range tt.names { 1410 found := false 1411 for _, rel := range mrs.val.Releases { 1412 if rel.Name == name { 1413 found = true 1414 } 1415 } 1416 if !found { 1417 t.Errorf("%d: Did not find name %q", i, name) 1418 } 1419 } 1420 } 1421 } 1422 1423 func TestListReleasesSort(t *testing.T) { 1424 rs := rsFixture() 1425 1426 // Put them in by reverse order so that the mock doesn't "accidentally" 1427 // sort. 1428 num := 7 1429 for i := num; i > 0; i-- { 1430 rel := releaseStub() 1431 rel.Name = fmt.Sprintf("rel-%d", i) 1432 if err := rs.env.Releases.Create(rel); err != nil { 1433 t.Fatalf("Could not store mock release: %s", err) 1434 } 1435 } 1436 1437 limit := 6 1438 mrs := &mockListServer{} 1439 req := &services.ListReleasesRequest{ 1440 Offset: "", 1441 Limit: int64(limit), 1442 SortBy: services.ListSort_NAME, 1443 } 1444 if err := rs.ListReleases(req, mrs); err != nil { 1445 t.Fatalf("Failed listing: %s", err) 1446 } 1447 1448 if len(mrs.val.Releases) != limit { 1449 t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) 1450 } 1451 1452 for i := 0; i < limit; i++ { 1453 n := fmt.Sprintf("rel-%d", i+1) 1454 if mrs.val.Releases[i].Name != n { 1455 t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) 1456 } 1457 } 1458 } 1459 1460 func TestListReleasesFilter(t *testing.T) { 1461 rs := rsFixture() 1462 names := []string{ 1463 "axon", 1464 "dendrite", 1465 "neuron", 1466 "neuroglia", 1467 "synapse", 1468 "nucleus", 1469 "organelles", 1470 } 1471 num := 7 1472 for i := 0; i < num; i++ { 1473 rel := releaseStub() 1474 rel.Name = names[i] 1475 if err := rs.env.Releases.Create(rel); err != nil { 1476 t.Fatalf("Could not store mock release: %s", err) 1477 } 1478 } 1479 1480 mrs := &mockListServer{} 1481 req := &services.ListReleasesRequest{ 1482 Offset: "", 1483 Limit: 64, 1484 Filter: "neuro[a-z]+", 1485 SortBy: services.ListSort_NAME, 1486 } 1487 if err := rs.ListReleases(req, mrs); err != nil { 1488 t.Fatalf("Failed listing: %s", err) 1489 } 1490 1491 if len(mrs.val.Releases) != 2 { 1492 t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) 1493 } 1494 1495 if mrs.val.Releases[0].Name != "neuroglia" { 1496 t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) 1497 } 1498 if mrs.val.Releases[1].Name != "neuron" { 1499 t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) 1500 } 1501 } 1502 1503 func TestReleasesNamespace(t *testing.T) { 1504 rs := rsFixture() 1505 1506 names := []string{ 1507 "axon", 1508 "dendrite", 1509 "neuron", 1510 "ribosome", 1511 } 1512 1513 namespaces := []string{ 1514 "default", 1515 "test123", 1516 "test123", 1517 "cerebellum", 1518 } 1519 num := 4 1520 for i := 0; i < num; i++ { 1521 rel := releaseStub() 1522 rel.Name = names[i] 1523 rel.Namespace = namespaces[i] 1524 if err := rs.env.Releases.Create(rel); err != nil { 1525 t.Fatalf("Could not store mock release: %s", err) 1526 } 1527 } 1528 1529 mrs := &mockListServer{} 1530 req := &services.ListReleasesRequest{ 1531 Offset: "", 1532 Limit: 64, 1533 Namespace: "test123", 1534 } 1535 1536 if err := rs.ListReleases(req, mrs); err != nil { 1537 t.Fatalf("Failed listing: %s", err) 1538 } 1539 1540 if len(mrs.val.Releases) != 2 { 1541 t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) 1542 } 1543 } 1544 1545 func TestRunReleaseTest(t *testing.T) { 1546 rs := rsFixture() 1547 rel := namedReleaseStub("nemo", release.Status_DEPLOYED) 1548 rs.env.Releases.Create(rel) 1549 1550 req := &services.TestReleaseRequest{Name: "nemo", Timeout: 2} 1551 err := rs.RunReleaseTest(req, mockRunReleaseTestServer{}) 1552 if err != nil { 1553 t.Fatalf("failed to run release tests on %s: %s", rel.Name, err) 1554 } 1555 } 1556 1557 func MockEnvironment() *environment.Environment { 1558 e := environment.New() 1559 e.Releases = storage.Init(driver.NewMemory()) 1560 e.KubeClient = &environment.PrintingKubeClient{Out: os.Stdout} 1561 return e 1562 } 1563 1564 func newUpdateFailingKubeClient() *updateFailingKubeClient { 1565 return &updateFailingKubeClient{ 1566 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 1567 } 1568 1569 } 1570 1571 type updateFailingKubeClient struct { 1572 environment.PrintingKubeClient 1573 } 1574 1575 func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { 1576 return errors.New("Failed update in kube client") 1577 } 1578 1579 func newHookFailingKubeClient() *hookFailingKubeClient { 1580 return &hookFailingKubeClient{ 1581 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 1582 } 1583 } 1584 1585 type hookFailingKubeClient struct { 1586 environment.PrintingKubeClient 1587 } 1588 1589 func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { 1590 return errors.New("Failed watch") 1591 } 1592 1593 type mockListServer struct { 1594 val *services.ListReleasesResponse 1595 } 1596 1597 func (l *mockListServer) Send(res *services.ListReleasesResponse) error { 1598 l.val = res 1599 return nil 1600 } 1601 1602 func (l *mockListServer) Context() context.Context { return helm.NewContext() } 1603 func (l *mockListServer) SendMsg(v interface{}) error { return nil } 1604 func (l *mockListServer) RecvMsg(v interface{}) error { return nil } 1605 func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } 1606 func (l *mockListServer) SetTrailer(m metadata.MD) {} 1607 func (l *mockListServer) SetHeader(m metadata.MD) error { return nil } 1608 1609 type mockRunReleaseTestServer struct { 1610 stream grpc.ServerStream 1611 } 1612 1613 func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error { 1614 return nil 1615 } 1616 func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error { return nil } 1617 func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil } 1618 func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} 1619 func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } 1620 func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } 1621 func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() }