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