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