github.com/cloudposse/helm@v2.2.3+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 TestUpdateReleaseResetValues(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 TestUpdateReleaseFailure(t *testing.T) { 748 c := helm.NewContext() 749 rs := rsFixture() 750 rel := releaseStub() 751 rs.env.Releases.Create(rel) 752 rs.env.KubeClient = newUpdateFailingKubeClient() 753 754 req := &services.UpdateReleaseRequest{ 755 Name: rel.Name, 756 DisableHooks: true, 757 Chart: &chart.Chart{ 758 Metadata: &chart.Metadata{Name: "hello"}, 759 Templates: []*chart.Template{ 760 {Name: "templates/something", Data: []byte("hello: world")}, 761 }, 762 }, 763 } 764 765 res, err := rs.UpdateRelease(c, req) 766 if err == nil { 767 t.Error("Expected failed update") 768 } 769 770 if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_FAILED { 771 t.Errorf("Expected FAILED release. Got %d", updatedStatus) 772 } 773 774 edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client" 775 if got := res.Release.Info.Description; got != edesc { 776 t.Errorf("Expected description %q, got %q", edesc, got) 777 } 778 779 oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) 780 if err != nil { 781 t.Errorf("Expected to be able to get previous release") 782 } 783 if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { 784 t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) 785 } 786 } 787 788 func TestRollbackReleaseFailure(t *testing.T) { 789 c := helm.NewContext() 790 rs := rsFixture() 791 rel := releaseStub() 792 rs.env.Releases.Create(rel) 793 upgradedRel := upgradeReleaseVersion(rel) 794 rs.env.Releases.Update(rel) 795 rs.env.Releases.Create(upgradedRel) 796 797 req := &services.RollbackReleaseRequest{ 798 Name: rel.Name, 799 DisableHooks: true, 800 } 801 802 rs.env.KubeClient = newUpdateFailingKubeClient() 803 res, err := rs.RollbackRelease(c, req) 804 if err == nil { 805 t.Error("Expected failed rollback") 806 } 807 808 if targetStatus := res.Release.Info.Status.Code; targetStatus != release.Status_FAILED { 809 t.Errorf("Expected FAILED release. Got %v", targetStatus) 810 } 811 812 oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) 813 if err != nil { 814 t.Errorf("Expected to be able to get previous release") 815 } 816 if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { 817 t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) 818 } 819 } 820 821 func TestUpdateReleaseNoHooks(t *testing.T) { 822 c := helm.NewContext() 823 rs := rsFixture() 824 rel := releaseStub() 825 rs.env.Releases.Create(rel) 826 827 req := &services.UpdateReleaseRequest{ 828 Name: rel.Name, 829 DisableHooks: true, 830 Chart: &chart.Chart{ 831 Metadata: &chart.Metadata{Name: "hello"}, 832 Templates: []*chart.Template{ 833 {Name: "templates/hello", Data: []byte("hello: world")}, 834 {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, 835 }, 836 }, 837 } 838 839 res, err := rs.UpdateRelease(c, req) 840 if err != nil { 841 t.Fatalf("Failed updated: %s", err) 842 } 843 844 if hl := res.Release.Hooks[0].LastRun; hl != nil { 845 t.Errorf("Expected that no hooks were run. Got %d", hl) 846 } 847 848 } 849 850 func TestUpdateReleaseNoChanges(t *testing.T) { 851 c := helm.NewContext() 852 rs := rsFixture() 853 rel := releaseStub() 854 rs.env.Releases.Create(rel) 855 856 req := &services.UpdateReleaseRequest{ 857 Name: rel.Name, 858 DisableHooks: true, 859 Chart: rel.GetChart(), 860 } 861 862 _, err := rs.UpdateRelease(c, req) 863 if err != nil { 864 t.Fatalf("Failed updated: %s", err) 865 } 866 } 867 868 func TestRollbackReleaseNoHooks(t *testing.T) { 869 c := helm.NewContext() 870 rs := rsFixture() 871 rel := releaseStub() 872 rel.Hooks = []*release.Hook{ 873 { 874 Name: "test-cm", 875 Kind: "ConfigMap", 876 Path: "test-cm", 877 Manifest: manifestWithRollbackHooks, 878 Events: []release.Hook_Event{ 879 release.Hook_PRE_ROLLBACK, 880 release.Hook_POST_ROLLBACK, 881 }, 882 }, 883 } 884 rs.env.Releases.Create(rel) 885 upgradedRel := upgradeReleaseVersion(rel) 886 rs.env.Releases.Update(rel) 887 rs.env.Releases.Create(upgradedRel) 888 889 req := &services.RollbackReleaseRequest{ 890 Name: rel.Name, 891 DisableHooks: true, 892 } 893 894 res, err := rs.RollbackRelease(c, req) 895 if err != nil { 896 t.Fatalf("Failed rollback: %s", err) 897 } 898 899 if hl := res.Release.Hooks[0].LastRun; hl != nil { 900 t.Errorf("Expected that no hooks were run. Got %d", hl) 901 } 902 } 903 904 func TestRollbackWithReleaseVersion(t *testing.T) { 905 c := helm.NewContext() 906 rs := rsFixture() 907 rel := releaseStub() 908 rs.env.Releases.Create(rel) 909 upgradedRel := upgradeReleaseVersion(rel) 910 rs.env.Releases.Update(rel) 911 rs.env.Releases.Create(upgradedRel) 912 913 req := &services.RollbackReleaseRequest{ 914 Name: rel.Name, 915 DisableHooks: true, 916 Version: 1, 917 } 918 919 _, err := rs.RollbackRelease(c, req) 920 if err != nil { 921 t.Fatalf("Failed rollback: %s", err) 922 } 923 } 924 925 func TestRollbackRelease(t *testing.T) { 926 c := helm.NewContext() 927 rs := rsFixture() 928 rel := releaseStub() 929 rs.env.Releases.Create(rel) 930 upgradedRel := upgradeReleaseVersion(rel) 931 upgradedRel.Hooks = []*release.Hook{ 932 { 933 Name: "test-cm", 934 Kind: "ConfigMap", 935 Path: "test-cm", 936 Manifest: manifestWithRollbackHooks, 937 Events: []release.Hook_Event{ 938 release.Hook_PRE_ROLLBACK, 939 release.Hook_POST_ROLLBACK, 940 }, 941 }, 942 } 943 944 upgradedRel.Manifest = "hello world" 945 rs.env.Releases.Update(rel) 946 rs.env.Releases.Create(upgradedRel) 947 948 req := &services.RollbackReleaseRequest{ 949 Name: rel.Name, 950 } 951 res, err := rs.RollbackRelease(c, req) 952 if err != nil { 953 t.Fatalf("Failed rollback: %s", err) 954 } 955 956 if res.Release.Name == "" { 957 t.Errorf("Expected release name.") 958 } 959 960 if res.Release.Name != rel.Name { 961 t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) 962 } 963 964 if res.Release.Namespace != rel.Namespace { 965 t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) 966 } 967 968 if res.Release.Version != 3 { 969 t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) 970 } 971 972 updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) 973 if err != nil { 974 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 975 } 976 977 if len(updated.Hooks) != 2 { 978 t.Fatalf("Expected 2 hooks, got %d", len(updated.Hooks)) 979 } 980 981 if updated.Hooks[0].Manifest != manifestWithHook { 982 t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) 983 } 984 985 anotherUpgradedRelease := upgradeReleaseVersion(upgradedRel) 986 rs.env.Releases.Update(upgradedRel) 987 rs.env.Releases.Create(anotherUpgradedRelease) 988 989 res, err = rs.RollbackRelease(c, req) 990 if err != nil { 991 t.Fatalf("Failed rollback: %s", err) 992 } 993 994 updated, err = rs.env.Releases.Get(res.Release.Name, res.Release.Version) 995 if err != nil { 996 t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) 997 } 998 999 if len(updated.Hooks) != 1 { 1000 t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) 1001 } 1002 1003 if updated.Hooks[0].Manifest != manifestWithRollbackHooks { 1004 t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) 1005 } 1006 1007 if res.Release.Version != 4 { 1008 t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) 1009 } 1010 1011 if updated.Hooks[0].Events[0] != release.Hook_PRE_ROLLBACK { 1012 t.Errorf("Expected event 0 to be pre rollback") 1013 } 1014 1015 if updated.Hooks[0].Events[1] != release.Hook_POST_ROLLBACK { 1016 t.Errorf("Expected event 1 to be post rollback") 1017 } 1018 1019 if len(res.Release.Manifest) == 0 { 1020 t.Errorf("No manifest returned: %v", res.Release) 1021 } 1022 1023 if len(updated.Manifest) == 0 { 1024 t.Errorf("Expected manifest in %v", res) 1025 } 1026 1027 if !strings.Contains(updated.Manifest, "hello world") { 1028 t.Errorf("unexpected output: %s", rel.Manifest) 1029 } 1030 1031 if res.Release.Info.Description != "Rollback to 2" { 1032 t.Errorf("Expected rollback to 2, got %q", res.Release.Info.Description) 1033 } 1034 1035 } 1036 1037 func TestUninstallRelease(t *testing.T) { 1038 c := helm.NewContext() 1039 rs := rsFixture() 1040 rs.env.Releases.Create(releaseStub()) 1041 1042 req := &services.UninstallReleaseRequest{ 1043 Name: "angry-panda", 1044 } 1045 1046 res, err := rs.UninstallRelease(c, req) 1047 if err != nil { 1048 t.Fatalf("Failed uninstall: %s", err) 1049 } 1050 1051 if res.Release.Name != "angry-panda" { 1052 t.Errorf("Expected angry-panda, got %q", res.Release.Name) 1053 } 1054 1055 if res.Release.Info.Status.Code != release.Status_DELETED { 1056 t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) 1057 } 1058 1059 if res.Release.Hooks[0].LastRun.Seconds == 0 { 1060 t.Error("Expected LastRun to be greater than zero.") 1061 } 1062 1063 if res.Release.Info.Deleted.Seconds <= 0 { 1064 t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) 1065 } 1066 1067 if res.Release.Info.Description != "Deletion complete" { 1068 t.Errorf("Expected Deletion complete, got %q", res.Release.Info.Description) 1069 } 1070 } 1071 1072 func TestUninstallPurgeRelease(t *testing.T) { 1073 c := helm.NewContext() 1074 rs := rsFixture() 1075 rel := releaseStub() 1076 rs.env.Releases.Create(rel) 1077 upgradedRel := upgradeReleaseVersion(rel) 1078 rs.env.Releases.Update(rel) 1079 rs.env.Releases.Create(upgradedRel) 1080 1081 req := &services.UninstallReleaseRequest{ 1082 Name: "angry-panda", 1083 Purge: true, 1084 } 1085 1086 res, err := rs.UninstallRelease(c, req) 1087 if err != nil { 1088 t.Fatalf("Failed uninstall: %s", err) 1089 } 1090 1091 if res.Release.Name != "angry-panda" { 1092 t.Errorf("Expected angry-panda, got %q", res.Release.Name) 1093 } 1094 1095 if res.Release.Info.Status.Code != release.Status_DELETED { 1096 t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) 1097 } 1098 1099 if res.Release.Info.Deleted.Seconds <= 0 { 1100 t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) 1101 } 1102 rels, err := rs.GetHistory(helm.NewContext(), &services.GetHistoryRequest{Name: "angry-panda"}) 1103 if err != nil { 1104 t.Fatal(err) 1105 } 1106 if len(rels.Releases) != 0 { 1107 t.Errorf("Expected no releases in storage, got %d", len(rels.Releases)) 1108 } 1109 } 1110 1111 func TestUninstallPurgeDeleteRelease(t *testing.T) { 1112 c := helm.NewContext() 1113 rs := rsFixture() 1114 rs.env.Releases.Create(releaseStub()) 1115 1116 req := &services.UninstallReleaseRequest{ 1117 Name: "angry-panda", 1118 } 1119 1120 _, err := rs.UninstallRelease(c, req) 1121 if err != nil { 1122 t.Fatalf("Failed uninstall: %s", err) 1123 } 1124 1125 req2 := &services.UninstallReleaseRequest{ 1126 Name: "angry-panda", 1127 Purge: true, 1128 } 1129 1130 _, err2 := rs.UninstallRelease(c, req2) 1131 if err2 != nil && err2.Error() != "'angry-panda' has no deployed releases" { 1132 t.Errorf("Failed uninstall: %s", err2) 1133 } 1134 } 1135 1136 func TestUninstallReleaseWithKeepPolicy(t *testing.T) { 1137 c := helm.NewContext() 1138 rs := rsFixture() 1139 name := "angry-bunny" 1140 rs.env.Releases.Create(releaseWithKeepStub(name)) 1141 1142 req := &services.UninstallReleaseRequest{ 1143 Name: name, 1144 } 1145 1146 res, err := rs.UninstallRelease(c, req) 1147 if err != nil { 1148 t.Fatalf("Failed uninstall: %s", err) 1149 } 1150 1151 if res.Release.Name != name { 1152 t.Errorf("Expected angry-bunny, got %q", res.Release.Name) 1153 } 1154 1155 if res.Release.Info.Status.Code != release.Status_DELETED { 1156 t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) 1157 } 1158 1159 if res.Info == "" { 1160 t.Errorf("Expected response info to not be empty") 1161 } else { 1162 if !strings.Contains(res.Info, "[ConfigMap] test-cm-keep") { 1163 t.Errorf("unexpected output: %s", res.Info) 1164 } 1165 } 1166 } 1167 1168 func releaseWithKeepStub(rlsName string) *release.Release { 1169 ch := &chart.Chart{ 1170 Metadata: &chart.Metadata{ 1171 Name: "bunnychart", 1172 }, 1173 Templates: []*chart.Template{ 1174 {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, 1175 }, 1176 } 1177 1178 date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} 1179 return &release.Release{ 1180 Name: rlsName, 1181 Info: &release.Info{ 1182 FirstDeployed: &date, 1183 LastDeployed: &date, 1184 Status: &release.Status{Code: release.Status_DEPLOYED}, 1185 }, 1186 Chart: ch, 1187 Config: &chart.Config{Raw: `name: value`}, 1188 Version: 1, 1189 Manifest: manifestWithKeep, 1190 } 1191 } 1192 1193 func TestUninstallReleaseNoHooks(t *testing.T) { 1194 c := helm.NewContext() 1195 rs := rsFixture() 1196 rs.env.Releases.Create(releaseStub()) 1197 1198 req := &services.UninstallReleaseRequest{ 1199 Name: "angry-panda", 1200 DisableHooks: true, 1201 } 1202 1203 res, err := rs.UninstallRelease(c, req) 1204 if err != nil { 1205 t.Errorf("Failed uninstall: %s", err) 1206 } 1207 1208 // The default value for a protobuf timestamp is nil. 1209 if res.Release.Hooks[0].LastRun != nil { 1210 t.Errorf("Expected LastRun to be zero, got %d.", res.Release.Hooks[0].LastRun.Seconds) 1211 } 1212 } 1213 1214 func TestGetReleaseContent(t *testing.T) { 1215 c := helm.NewContext() 1216 rs := rsFixture() 1217 rel := releaseStub() 1218 if err := rs.env.Releases.Create(rel); err != nil { 1219 t.Fatalf("Could not store mock release: %s", err) 1220 } 1221 1222 res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name, Version: 1}) 1223 if err != nil { 1224 t.Errorf("Error getting release content: %s", err) 1225 } 1226 1227 if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name { 1228 t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Release.Chart.Metadata.Name) 1229 } 1230 } 1231 1232 func TestGetReleaseStatus(t *testing.T) { 1233 c := helm.NewContext() 1234 rs := rsFixture() 1235 rel := releaseStub() 1236 if err := rs.env.Releases.Create(rel); err != nil { 1237 t.Fatalf("Could not store mock release: %s", err) 1238 } 1239 1240 res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) 1241 if err != nil { 1242 t.Errorf("Error getting release content: %s", err) 1243 } 1244 1245 if res.Name != rel.Name { 1246 t.Errorf("Expected name %q, got %q", rel.Name, res.Name) 1247 } 1248 if res.Info.Status.Code != release.Status_DEPLOYED { 1249 t.Errorf("Expected %d, got %d", release.Status_DEPLOYED, res.Info.Status.Code) 1250 } 1251 } 1252 1253 func TestGetReleaseStatusDeleted(t *testing.T) { 1254 c := helm.NewContext() 1255 rs := rsFixture() 1256 rel := releaseStub() 1257 rel.Info.Status.Code = release.Status_DELETED 1258 if err := rs.env.Releases.Create(rel); err != nil { 1259 t.Fatalf("Could not store mock release: %s", err) 1260 } 1261 1262 res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) 1263 if err != nil { 1264 t.Fatalf("Error getting release content: %s", err) 1265 } 1266 1267 if res.Info.Status.Code != release.Status_DELETED { 1268 t.Errorf("Expected %d, got %d", release.Status_DELETED, res.Info.Status.Code) 1269 } 1270 } 1271 1272 func TestListReleases(t *testing.T) { 1273 rs := rsFixture() 1274 num := 7 1275 for i := 0; i < num; i++ { 1276 rel := releaseStub() 1277 rel.Name = fmt.Sprintf("rel-%d", i) 1278 if err := rs.env.Releases.Create(rel); err != nil { 1279 t.Fatalf("Could not store mock release: %s", err) 1280 } 1281 } 1282 1283 mrs := &mockListServer{} 1284 if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { 1285 t.Fatalf("Failed listing: %s", err) 1286 } 1287 1288 if len(mrs.val.Releases) != num { 1289 t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) 1290 } 1291 } 1292 1293 func TestListReleasesByStatus(t *testing.T) { 1294 rs := rsFixture() 1295 stubs := []*release.Release{ 1296 namedReleaseStub("kamal", release.Status_DEPLOYED), 1297 namedReleaseStub("astrolabe", release.Status_DELETED), 1298 namedReleaseStub("octant", release.Status_FAILED), 1299 namedReleaseStub("sextant", release.Status_UNKNOWN), 1300 } 1301 for _, stub := range stubs { 1302 if err := rs.env.Releases.Create(stub); err != nil { 1303 t.Fatalf("Could not create stub: %s", err) 1304 } 1305 } 1306 1307 tests := []struct { 1308 statusCodes []release.Status_Code 1309 names []string 1310 }{ 1311 { 1312 names: []string{"kamal"}, 1313 statusCodes: []release.Status_Code{release.Status_DEPLOYED}, 1314 }, 1315 { 1316 names: []string{"astrolabe"}, 1317 statusCodes: []release.Status_Code{release.Status_DELETED}, 1318 }, 1319 { 1320 names: []string{"kamal", "octant"}, 1321 statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, 1322 }, 1323 { 1324 names: []string{"kamal", "astrolabe", "octant", "sextant"}, 1325 statusCodes: []release.Status_Code{ 1326 release.Status_DEPLOYED, 1327 release.Status_DELETED, 1328 release.Status_FAILED, 1329 release.Status_UNKNOWN, 1330 }, 1331 }, 1332 } 1333 1334 for i, tt := range tests { 1335 mrs := &mockListServer{} 1336 if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { 1337 t.Fatalf("Failed listing %d: %s", i, err) 1338 } 1339 1340 if len(tt.names) != len(mrs.val.Releases) { 1341 t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) 1342 } 1343 1344 for _, name := range tt.names { 1345 found := false 1346 for _, rel := range mrs.val.Releases { 1347 if rel.Name == name { 1348 found = true 1349 } 1350 } 1351 if !found { 1352 t.Errorf("%d: Did not find name %q", i, name) 1353 } 1354 } 1355 } 1356 } 1357 1358 func TestListReleasesSort(t *testing.T) { 1359 rs := rsFixture() 1360 1361 // Put them in by reverse order so that the mock doesn't "accidentally" 1362 // sort. 1363 num := 7 1364 for i := num; i > 0; i-- { 1365 rel := releaseStub() 1366 rel.Name = fmt.Sprintf("rel-%d", i) 1367 if err := rs.env.Releases.Create(rel); err != nil { 1368 t.Fatalf("Could not store mock release: %s", err) 1369 } 1370 } 1371 1372 limit := 6 1373 mrs := &mockListServer{} 1374 req := &services.ListReleasesRequest{ 1375 Offset: "", 1376 Limit: int64(limit), 1377 SortBy: services.ListSort_NAME, 1378 } 1379 if err := rs.ListReleases(req, mrs); err != nil { 1380 t.Fatalf("Failed listing: %s", err) 1381 } 1382 1383 if len(mrs.val.Releases) != limit { 1384 t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) 1385 } 1386 1387 for i := 0; i < limit; i++ { 1388 n := fmt.Sprintf("rel-%d", i+1) 1389 if mrs.val.Releases[i].Name != n { 1390 t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) 1391 } 1392 } 1393 } 1394 1395 func TestListReleasesFilter(t *testing.T) { 1396 rs := rsFixture() 1397 names := []string{ 1398 "axon", 1399 "dendrite", 1400 "neuron", 1401 "neuroglia", 1402 "synapse", 1403 "nucleus", 1404 "organelles", 1405 } 1406 num := 7 1407 for i := 0; i < num; i++ { 1408 rel := releaseStub() 1409 rel.Name = names[i] 1410 if err := rs.env.Releases.Create(rel); err != nil { 1411 t.Fatalf("Could not store mock release: %s", err) 1412 } 1413 } 1414 1415 mrs := &mockListServer{} 1416 req := &services.ListReleasesRequest{ 1417 Offset: "", 1418 Limit: 64, 1419 Filter: "neuro[a-z]+", 1420 SortBy: services.ListSort_NAME, 1421 } 1422 if err := rs.ListReleases(req, mrs); err != nil { 1423 t.Fatalf("Failed listing: %s", err) 1424 } 1425 1426 if len(mrs.val.Releases) != 2 { 1427 t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) 1428 } 1429 1430 if mrs.val.Releases[0].Name != "neuroglia" { 1431 t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) 1432 } 1433 if mrs.val.Releases[1].Name != "neuron" { 1434 t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) 1435 } 1436 } 1437 1438 func TestReleasesNamespace(t *testing.T) { 1439 rs := rsFixture() 1440 1441 names := []string{ 1442 "axon", 1443 "dendrite", 1444 "neuron", 1445 "ribosome", 1446 } 1447 1448 namespaces := []string{ 1449 "default", 1450 "test123", 1451 "test123", 1452 "cerebellum", 1453 } 1454 num := 4 1455 for i := 0; i < num; i++ { 1456 rel := releaseStub() 1457 rel.Name = names[i] 1458 rel.Namespace = namespaces[i] 1459 if err := rs.env.Releases.Create(rel); err != nil { 1460 t.Fatalf("Could not store mock release: %s", err) 1461 } 1462 } 1463 1464 mrs := &mockListServer{} 1465 req := &services.ListReleasesRequest{ 1466 Offset: "", 1467 Limit: 64, 1468 Namespace: "test123", 1469 } 1470 1471 if err := rs.ListReleases(req, mrs); err != nil { 1472 t.Fatalf("Failed listing: %s", err) 1473 } 1474 1475 if len(mrs.val.Releases) != 2 { 1476 t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) 1477 } 1478 } 1479 1480 func TestRunReleaseTest(t *testing.T) { 1481 rs := rsFixture() 1482 rel := namedReleaseStub("nemo", release.Status_DEPLOYED) 1483 rs.env.Releases.Create(rel) 1484 1485 req := &services.TestReleaseRequest{Name: "nemo", Timeout: 2} 1486 err := rs.RunReleaseTest(req, mockRunReleaseTestServer{}) 1487 if err != nil { 1488 t.Fatalf("failed to run release tests on %s: %s", rel.Name, err) 1489 } 1490 } 1491 1492 func MockEnvironment() *environment.Environment { 1493 e := environment.New() 1494 e.Releases = storage.Init(driver.NewMemory()) 1495 e.KubeClient = &environment.PrintingKubeClient{Out: os.Stdout} 1496 return e 1497 } 1498 1499 func newUpdateFailingKubeClient() *updateFailingKubeClient { 1500 return &updateFailingKubeClient{ 1501 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 1502 } 1503 1504 } 1505 1506 type updateFailingKubeClient struct { 1507 environment.PrintingKubeClient 1508 } 1509 1510 func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { 1511 return errors.New("Failed update in kube client") 1512 } 1513 1514 func newHookFailingKubeClient() *hookFailingKubeClient { 1515 return &hookFailingKubeClient{ 1516 PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, 1517 } 1518 } 1519 1520 type hookFailingKubeClient struct { 1521 environment.PrintingKubeClient 1522 } 1523 1524 func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { 1525 return errors.New("Failed watch") 1526 } 1527 1528 type mockListServer struct { 1529 val *services.ListReleasesResponse 1530 } 1531 1532 func (l *mockListServer) Send(res *services.ListReleasesResponse) error { 1533 l.val = res 1534 return nil 1535 } 1536 1537 func (l *mockListServer) Context() context.Context { return helm.NewContext() } 1538 func (l *mockListServer) SendMsg(v interface{}) error { return nil } 1539 func (l *mockListServer) RecvMsg(v interface{}) error { return nil } 1540 func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } 1541 func (l *mockListServer) SetTrailer(m metadata.MD) {} 1542 func (l *mockListServer) SetHeader(m metadata.MD) error { return nil } 1543 1544 type mockRunReleaseTestServer struct { 1545 stream grpc.ServerStream 1546 } 1547 1548 func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error { 1549 return nil 1550 } 1551 func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error { return nil } 1552 func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil } 1553 func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} 1554 func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } 1555 func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } 1556 func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() }