release_server_test.go 24.66 KiB
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
package main
import (
	"errors"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"testing"
	"github.com/golang/protobuf/ptypes/timestamp"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
	"k8s.io/helm/cmd/tiller/environment"
	"k8s.io/helm/pkg/proto/hapi/chart"
	"k8s.io/helm/pkg/proto/hapi/release"
	"k8s.io/helm/pkg/proto/hapi/services"
	"k8s.io/helm/pkg/storage"
	"k8s.io/helm/pkg/storage/driver"
const notesText = "my notes here"
var manifestWithHook = `apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  annotations:
    "helm.sh/hook": post-install,pre-delete
data:
  name: value
var manifestWithUpgradeHooks = `apiVersion: v1
kind: ConfigMap
metadata:
  name: test-cm
  annotations:
    "helm.sh/hook": post-upgrade,pre-upgrade
data:
  name: value
func rsFixture() *releaseServer {
	return &releaseServer{
		env: mockEnvironment(),
// chartStub creates a fully stubbed out chart.
func chartStub() *chart.Chart {
	return &chart.Chart{
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
// TODO: This should be more complete. Metadata: &chart.Metadata{ Name: "hello", }, // This adds basic templates, partials, and hooks. Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, {Name: "goodbye", Data: []byte("goodbye: world")}, {Name: "empty", Data: []byte("")}, {Name: "with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)}, {Name: "partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)}, {Name: "hooks", Data: []byte(manifestWithHook)}, }, } } // releaseStub creates a release stub, complete with the chartStub as its chart. func releaseStub() *release.Release { return namedReleaseStub("angry-panda", release.Status_DEPLOYED) } func namedReleaseStub(name string, status release.Status_Code) *release.Release { date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} return &release.Release{ Name: name, Info: &release.Info{ FirstDeployed: &date, LastDeployed: &date, Status: &release.Status{Code: status}, }, Chart: chartStub(), Config: &chart.Config{Raw: `name = "value"`}, Version: 1, Hooks: []*release.Hook{ { Name: "test-cm", Kind: "ConfigMap", Path: "test-cm", Manifest: manifestWithHook, Events: []release.Hook_Event{ release.Hook_POST_INSTALL, release.Hook_PRE_DELETE, }, }, }, } } func TestGetVersionSet(t *testing.T) { rs := rsFixture() vs, err := rs.getVersionSet() if err != nil { t.Error(err) } if !vs.Has("v1") { t.Errorf("Expected supported versions to at least include v1.") } if vs.Has("nosuchversion/v1") { t.Error("Non-existent version is reported found.") } } func TestUniqName(t *testing.T) { rs := rsFixture() rel1 := releaseStub() rel2 := releaseStub() rel2.Name = "happy-panda" rel2.Info.Status.Code = release.Status_DELETED
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
rs.env.Releases.Create(rel1) rs.env.Releases.Create(rel2) tests := []struct { name string expect string reuse bool err bool }{ {"first", "first", false, false}, {"", "[a-z]+-[a-z]+", false, false}, {"angry-panda", "", false, true}, {"happy-panda", "", false, true}, {"happy-panda", "happy-panda", true, false}, {"hungry-hungry-hippos", "", true, true}, // Exceeds max name length } for _, tt := range tests { u, err := rs.uniqName(tt.name, tt.reuse) if err != nil { if tt.err { continue } t.Fatal(err) } if tt.err { t.Errorf("Expected an error for %q", tt.name) } if match, err := regexp.MatchString(tt.expect, u); err != nil { t.Fatal(err) } else if !match { t.Errorf("Expected %q to match %q", u, tt.expect) } } } func TestInstallRelease(t *testing.T) { c := context.Background() rs := rsFixture() // TODO: Refactor this into a mock. req := &services.InstallReleaseRequest{ Namespace: "spaced", Chart: &chart.Chart{ Metadata: &chart.Metadata{Name: "hello"}, Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, {Name: "hooks", Data: []byte(manifestWithHook)}, }, }, } res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) } if res.Release.Name == "" { t.Errorf("Expected release name.") } if res.Release.Namespace != "spaced" { t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) } rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) if err != nil { t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) } t.Logf("rel: %v", rel) if len(rel.Hooks) != 1 {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) } if rel.Hooks[0].Manifest != manifestWithHook { t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) } if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { t.Errorf("Expected event 0 is post install") } if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { t.Errorf("Expected event 0 is pre-delete") } if len(res.Release.Manifest) == 0 { t.Errorf("No manifest returned: %v", res.Release) } if len(rel.Manifest) == 0 { t.Errorf("Expected manifest in %v", res) } if !strings.Contains(rel.Manifest, "---\n# Source: hello/hello\nhello: world") { t.Errorf("unexpected output: %s", rel.Manifest) } } func TestInstallReleaseWithNotes(t *testing.T) { c := context.Background() rs := rsFixture() // TODO: Refactor this into a mock. req := &services.InstallReleaseRequest{ Namespace: "spaced", Chart: &chart.Chart{ Metadata: &chart.Metadata{Name: "hello"}, Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, {Name: "hooks", Data: []byte(manifestWithHook)}, {Name: "NOTES.txt", Data: []byte(notesText)}, }, }, } res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) } if res.Release.Name == "" { t.Errorf("Expected release name.") } if res.Release.Namespace != "spaced" { t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) } rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) if err != nil { t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) } t.Logf("rel: %v", rel) if len(rel.Hooks) != 1 { t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) } if rel.Hooks[0].Manifest != manifestWithHook { t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) } if rel.Info.Status.Notes != notesText { t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) }
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { t.Errorf("Expected event 0 is post install") } if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { t.Errorf("Expected event 0 is pre-delete") } if len(res.Release.Manifest) == 0 { t.Errorf("No manifest returned: %v", res.Release) } if len(rel.Manifest) == 0 { t.Errorf("Expected manifest in %v", res) } if !strings.Contains(rel.Manifest, "---\n# Source: hello/hello\nhello: world") { t.Errorf("unexpected output: %s", rel.Manifest) } } func TestInstallReleaseWithNotesRendered(t *testing.T) { c := context.Background() rs := rsFixture() // TODO: Refactor this into a mock. req := &services.InstallReleaseRequest{ Namespace: "spaced", Chart: &chart.Chart{ Metadata: &chart.Metadata{Name: "hello"}, Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, {Name: "hooks", Data: []byte(manifestWithHook)}, {Name: "NOTES.txt", Data: []byte(notesText + " {{.Release.Name}}")}, }, }, } res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) } if res.Release.Name == "" { t.Errorf("Expected release name.") } if res.Release.Namespace != "spaced" { t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) } rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) if err != nil { t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) } t.Logf("rel: %v", rel) if len(rel.Hooks) != 1 { t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) } if rel.Hooks[0].Manifest != manifestWithHook { t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) } expectedNotes := fmt.Sprintf("%s %s", notesText, res.Release.Name) if rel.Info.Status.Notes != expectedNotes { t.Fatalf("Expected '%s', got '%s'", expectedNotes, rel.Info.Status.Notes) } if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { t.Errorf("Expected event 0 is post install") }
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { t.Errorf("Expected event 0 is pre-delete") } if len(res.Release.Manifest) == 0 { t.Errorf("No manifest returned: %v", res.Release) } if len(rel.Manifest) == 0 { t.Errorf("Expected manifest in %v", res) } if !strings.Contains(rel.Manifest, "---\n# Source: hello/hello\nhello: world") { t.Errorf("unexpected output: %s", rel.Manifest) } } func TestInstallReleaseDryRun(t *testing.T) { c := context.Background() rs := rsFixture() req := &services.InstallReleaseRequest{ Chart: chartStub(), DryRun: true, } res, err := rs.InstallRelease(c, req) if err != nil { t.Errorf("Failed install: %s", err) } if res.Release.Name == "" { t.Errorf("Expected release name.") } if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/hello\nhello: world") { t.Errorf("unexpected output: %s", res.Release.Manifest) } if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/goodbye\ngoodbye: world") { t.Errorf("unexpected output: %s", res.Release.Manifest) } if !strings.Contains(res.Release.Manifest, "hello: Earth") { t.Errorf("Should contain partial content. %s", res.Release.Manifest) } if strings.Contains(res.Release.Manifest, "hello: {{ template \"_planet\" . }}") { t.Errorf("Should not contain partial templates itself. %s", res.Release.Manifest) } if strings.Contains(res.Release.Manifest, "empty") { t.Errorf("Should not contain template data for an empty file. %s", res.Release.Manifest) } if _, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version); err == nil { t.Errorf("Expected no stored release.") } if l := len(res.Release.Hooks); l != 1 { t.Fatalf("Expected 1 hook, got %d", l) } if res.Release.Hooks[0].LastRun != nil { t.Error("Expected hook to not be marked as run.") } } func TestInstallReleaseNoHooks(t *testing.T) { c := context.Background() rs := rsFixture() rs.env.Releases.Create(releaseStub())
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
req := &services.InstallReleaseRequest{ Chart: chartStub(), DisableHooks: true, } res, err := rs.InstallRelease(c, req) if err != nil { t.Errorf("Failed install: %s", err) } if hl := res.Release.Hooks[0].LastRun; hl != nil { t.Errorf("Expected that no hooks were run. Got %d", hl) } } func TestInstallReleaseFailedHooks(t *testing.T) { c := context.Background() rs := rsFixture() rs.env.Releases.Create(releaseStub()) rs.env.KubeClient = newHookFailingKubeClient() req := &services.InstallReleaseRequest{ Chart: chartStub(), } res, err := rs.InstallRelease(c, req) if err == nil { t.Error("Expected failed install") } if hl := res.Release.Info.Status.Code; hl != release.Status_FAILED { t.Errorf("Expected FAILED release. Got %d", hl) } } func TestInstallReleaseReuseName(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() rel.Info.Status.Code = release.Status_DELETED rs.env.Releases.Create(rel) req := &services.InstallReleaseRequest{ Chart: chartStub(), ReuseName: true, Name: rel.Name, } res, err := rs.InstallRelease(c, req) if err != nil { t.Fatalf("Failed install: %s", err) } if res.Release.Name != rel.Name { t.Errorf("expected %q, got %q", rel.Name, res.Release.Name) } getreq := &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1} getres, err := rs.GetReleaseStatus(c, getreq) if err != nil { t.Errorf("Failed to retrieve release: %s", err) } if getres.Info.Status.Code != release.Status_DEPLOYED { t.Errorf("Release status is %q", getres.Info.Status.Code) } } func TestUpdateRelease(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() rs.env.Releases.Create(rel)
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
req := &services.UpdateReleaseRequest{ Name: rel.Name, Chart: &chart.Chart{ Metadata: &chart.Metadata{Name: "hello"}, Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, {Name: "hooks", Data: []byte(manifestWithUpgradeHooks)}, }, }, } res, err := rs.UpdateRelease(c, req) if err != nil { t.Fatalf("Failed updated: %s", err) } if res.Release.Name == "" { t.Errorf("Expected release name.") } if res.Release.Name != rel.Name { t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) } if res.Release.Namespace != rel.Namespace { t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) } updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) if err != nil { t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) } if len(updated.Hooks) != 1 { t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) } if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) } if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { t.Errorf("Expected event 0 to be post upgrade") } if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { t.Errorf("Expected event 0 to be pre upgrade") } if len(res.Release.Manifest) == 0 { t.Errorf("No manifest returned: %v", res.Release) } if len(updated.Manifest) == 0 { t.Errorf("Expected manifest in %v", res) } if !strings.Contains(updated.Manifest, "---\n# Source: hello/hello\nhello: world") { t.Errorf("unexpected output: %s", rel.Manifest) } if res.Release.Version != 2 { t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) } } func TestUpdateReleaseNoHooks(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() rs.env.Releases.Create(rel)
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
req := &services.UpdateReleaseRequest{ Name: rel.Name, DisableHooks: true, Chart: &chart.Chart{ Metadata: &chart.Metadata{Name: "hello"}, Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, {Name: "hooks", Data: []byte(manifestWithUpgradeHooks)}, }, }, } res, err := rs.UpdateRelease(c, req) if err != nil { t.Fatalf("Failed updated: %s", err) } if hl := res.Release.Hooks[0].LastRun; hl != nil { t.Errorf("Expected that no hooks were run. Got %d", hl) } } func TestUpdateReleaseNoChanges(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() rs.env.Releases.Create(rel) req := &services.UpdateReleaseRequest{ Name: rel.Name, DisableHooks: true, Chart: rel.GetChart(), } _, err := rs.UpdateRelease(c, req) if err != nil { t.Fatalf("Failed updated: %s", err) } } func TestUninstallRelease(t *testing.T) { c := context.Background() rs := rsFixture() rs.env.Releases.Create(releaseStub()) req := &services.UninstallReleaseRequest{ Name: "angry-panda", } res, err := rs.UninstallRelease(c, req) if err != nil { t.Fatalf("Failed uninstall: %s", err) } if res.Release.Name != "angry-panda" { t.Errorf("Expected angry-panda, got %q", res.Release.Name) } if res.Release.Info.Status.Code != release.Status_DELETED { t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) } if res.Release.Hooks[0].LastRun.Seconds == 0 { t.Error("Expected LastRun to be greater than zero.") } if res.Release.Info.Deleted.Seconds <= 0 { t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds)
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
} } func TestUninstallPurgeRelease(t *testing.T) { c := context.Background() rs := rsFixture() rs.env.Releases.Create(releaseStub()) req := &services.UninstallReleaseRequest{ Name: "angry-panda", Purge: true, } res, err := rs.UninstallRelease(c, req) if err != nil { t.Fatalf("Failed uninstall: %s", err) } if res.Release.Name != "angry-panda" { t.Errorf("Expected angry-panda, got %q", res.Release.Name) } if res.Release.Info.Status.Code != release.Status_DELETED { t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) } if res.Release.Hooks[0].LastRun.Seconds == 0 { t.Error("Expected LastRun to be greater than zero.") } if res.Release.Info.Deleted.Seconds <= 0 { t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) } } func TestUninstallPurgeDeleteRelease(t *testing.T) { c := context.Background() rs := rsFixture() rs.env.Releases.Create(releaseStub()) req := &services.UninstallReleaseRequest{ Name: "angry-panda", } _, err := rs.UninstallRelease(c, req) if err != nil { t.Fatalf("Failed uninstall: %s", err) } req2 := &services.UninstallReleaseRequest{ Name: "angry-panda", Purge: true, } _, err2 := rs.UninstallRelease(c, req2) if err2 != nil && err2.Error() != "'angry-panda' has no deployed releases" { t.Errorf("Failed uninstall: %s", err2) } } func TestUninstallReleaseNoHooks(t *testing.T) { c := context.Background() rs := rsFixture() rs.env.Releases.Create(releaseStub()) req := &services.UninstallReleaseRequest{ Name: "angry-panda", DisableHooks: true, }
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
res, err := rs.UninstallRelease(c, req) if err != nil { t.Errorf("Failed uninstall: %s", err) } // The default value for a protobuf timestamp is nil. if res.Release.Hooks[0].LastRun != nil { t.Errorf("Expected LastRun to be zero, got %d.", res.Release.Hooks[0].LastRun.Seconds) } } func TestGetReleaseContent(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() if err := rs.env.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name, Version: 1}) if err != nil { t.Errorf("Error getting release content: %s", err) } if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name { t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Release.Chart.Metadata.Name) } } func TestGetReleaseStatus(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() if err := rs.env.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) if err != nil { t.Errorf("Error getting release content: %s", err) } if res.Info.Status.Code != release.Status_DEPLOYED { t.Errorf("Expected %d, got %d", release.Status_DEPLOYED, res.Info.Status.Code) } } func TestGetReleaseStatusDeleted(t *testing.T) { c := context.Background() rs := rsFixture() rel := releaseStub() rel.Info.Status.Code = release.Status_DELETED if err := rs.env.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) if err != nil { t.Fatalf("Error getting release content: %s", err) } if res.Info.Status.Code != release.Status_DELETED { t.Errorf("Expected %d, got %d", release.Status_DELETED, res.Info.Status.Code) } } func TestListReleases(t *testing.T) { rs := rsFixture() num := 7 for i := 0; i < num; i++ {
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
rel := releaseStub() rel.Name = fmt.Sprintf("rel-%d", i) if err := rs.env.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } } mrs := &mockListServer{} if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { t.Fatalf("Failed listing: %s", err) } if len(mrs.val.Releases) != num { t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) } } func TestListReleasesByStatus(t *testing.T) { rs := rsFixture() stubs := []*release.Release{ namedReleaseStub("kamal", release.Status_DEPLOYED), namedReleaseStub("astrolabe", release.Status_DELETED), namedReleaseStub("octant", release.Status_FAILED), namedReleaseStub("sextant", release.Status_UNKNOWN), } for _, stub := range stubs { if err := rs.env.Releases.Create(stub); err != nil { t.Fatalf("Could not create stub: %s", err) } } tests := []struct { statusCodes []release.Status_Code names []string }{ { names: []string{"kamal"}, statusCodes: []release.Status_Code{release.Status_DEPLOYED}, }, { names: []string{"astrolabe"}, statusCodes: []release.Status_Code{release.Status_DELETED}, }, { names: []string{"kamal", "octant"}, statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, }, { names: []string{"kamal", "astrolabe", "octant", "sextant"}, statusCodes: []release.Status_Code{ release.Status_DEPLOYED, release.Status_DELETED, release.Status_FAILED, release.Status_UNKNOWN, }, }, } for i, tt := range tests { mrs := &mockListServer{} if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { t.Fatalf("Failed listing %d: %s", i, err) } if len(tt.names) != len(mrs.val.Releases) { t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) } for _, name := range tt.names { found := false
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
for _, rel := range mrs.val.Releases { if rel.Name == name { found = true } } if !found { t.Errorf("%d: Did not find name %q", i, name) } } } } func TestListReleasesSort(t *testing.T) { rs := rsFixture() // Put them in by reverse order so that the mock doesn't "accidentally" // sort. num := 7 for i := num; i > 0; i-- { rel := releaseStub() rel.Name = fmt.Sprintf("rel-%d", i) if err := rs.env.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } } limit := 6 mrs := &mockListServer{} req := &services.ListReleasesRequest{ Offset: "", Limit: int64(limit), SortBy: services.ListSort_NAME, } if err := rs.ListReleases(req, mrs); err != nil { t.Fatalf("Failed listing: %s", err) } if len(mrs.val.Releases) != limit { t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) } for i := 0; i < limit; i++ { n := fmt.Sprintf("rel-%d", i+1) if mrs.val.Releases[i].Name != n { t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) } } } func TestListReleasesFilter(t *testing.T) { rs := rsFixture() names := []string{ "axon", "dendrite", "neuron", "neuroglia", "synapse", "nucleus", "organelles", } num := 7 for i := 0; i < num; i++ { rel := releaseStub() rel.Name = names[i] if err := rs.env.Releases.Create(rel); err != nil { t.Fatalf("Could not store mock release: %s", err) } } mrs := &mockListServer{}
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
req := &services.ListReleasesRequest{ Offset: "", Limit: 64, Filter: "neuro[a-z]+", SortBy: services.ListSort_NAME, } if err := rs.ListReleases(req, mrs); err != nil { t.Fatalf("Failed listing: %s", err) } if len(mrs.val.Releases) != 2 { t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) } if mrs.val.Releases[0].Name != "neuroglia" { t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) } if mrs.val.Releases[1].Name != "neuron" { t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) } } func mockEnvironment() *environment.Environment { e := environment.New() e.Releases = storage.Init(driver.NewMemory()) e.KubeClient = &environment.PrintingKubeClient{Out: os.Stdout} return e } func newHookFailingKubeClient() *hookFailingKubeClient { return &hookFailingKubeClient{ PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout}, } } type hookFailingKubeClient struct { environment.PrintingKubeClient } func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader) error { return errors.New("Failed watch") } type mockListServer struct { val *services.ListReleasesResponse } func (l *mockListServer) Send(res *services.ListReleasesResponse) error { l.val = res return nil } func (l *mockListServer) Context() context.Context { return context.TODO() } func (l *mockListServer) SendMsg(v interface{}) error { return nil } func (l *mockListServer) RecvMsg(v interface{}) error { return nil } func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } func (l *mockListServer) SetTrailer(m metadata.MD) {}