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 }