github.com/cloudposse/helm@v2.2.3+incompatible/pkg/tiller/release_server_test.go (about)

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