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

     1  /*
     2  Copyright The Helm Authors.
     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  	"io/ioutil"
    24  	"os"
    25  	"regexp"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/ghodss/yaml"
    30  	"github.com/golang/protobuf/ptypes/timestamp"
    31  	"github.com/technosophos/moniker"
    32  	"golang.org/x/net/context"
    33  	"google.golang.org/grpc/metadata"
    34  	v1 "k8s.io/api/core/v1"
    35  	"k8s.io/cli-runtime/pkg/resource"
    36  	"k8s.io/client-go/kubernetes/fake"
    37  
    38  	"k8s.io/helm/pkg/helm"
    39  	"k8s.io/helm/pkg/hooks"
    40  	"k8s.io/helm/pkg/kube"
    41  	"k8s.io/helm/pkg/proto/hapi/chart"
    42  	"k8s.io/helm/pkg/proto/hapi/release"
    43  	"k8s.io/helm/pkg/proto/hapi/services"
    44  	"k8s.io/helm/pkg/storage"
    45  	"k8s.io/helm/pkg/storage/driver"
    46  	"k8s.io/helm/pkg/tiller/environment"
    47  )
    48  
    49  const notesText = "my notes here"
    50  
    51  var manifestWithHook = `kind: ConfigMap
    52  metadata:
    53    name: test-cm
    54    annotations:
    55      "helm.sh/hook": post-install,pre-delete
    56  data:
    57    name: value`
    58  
    59  var manifestWithCRDHook = `
    60  apiVersion: apiextensions.k8s.io/v1beta1
    61  kind: CustomResourceDefinition
    62  metadata:
    63    name: crontabs.stable.example.com
    64    annotations:
    65      "helm.sh/hook": crd-install
    66  spec:
    67    group: stable.example.com
    68    version: v1
    69    scope: Namespaced
    70    names:
    71      plural: crontabs
    72      singular: crontab
    73      kind: CronTab
    74      shortNames:
    75      - ct
    76  `
    77  
    78  var manifestWithTestHook = `kind: Pod
    79  metadata:
    80    name: finding-nemo,
    81    annotations:
    82      "helm.sh/hook": test-success
    83  spec:
    84    containers:
    85    - name: nemo-test
    86      image: fake-image
    87      cmd: fake-command
    88  `
    89  
    90  var manifestWithKeep = `kind: ConfigMap
    91  metadata:
    92    name: test-cm-keep-a
    93    annotations:
    94      "helm.sh/resource-policy": keep
    95  data:
    96    name: value
    97  `
    98  
    99  var manifestWithKeepEmpty = `kind: ConfigMap
   100  metadata:
   101    name: test-cm-keep-b
   102    annotations:
   103      "helm.sh/resource-policy": ""
   104  data:
   105    name: value
   106  `
   107  
   108  var manifestWithUpgradeHooks = `kind: ConfigMap
   109  metadata:
   110    name: test-cm
   111    annotations:
   112      "helm.sh/hook": post-upgrade,pre-upgrade
   113  data:
   114    name: value`
   115  
   116  var manifestWithRollbackHooks = `kind: ConfigMap
   117  metadata:
   118    name: test-cm
   119    annotations:
   120      "helm.sh/hook": post-rollback,pre-rollback
   121  data:
   122    name: value
   123  `
   124  
   125  type chartOptions struct {
   126  	*chart.Chart
   127  }
   128  
   129  type chartOption func(*chartOptions)
   130  
   131  func rsFixture() *ReleaseServer {
   132  	return NewReleaseServer(MockEnvironment(), fake.NewSimpleClientset(), false)
   133  }
   134  
   135  func buildChart(opts ...chartOption) *chart.Chart {
   136  	c := &chartOptions{
   137  		Chart: &chart.Chart{
   138  			// TODO: This should be more complete.
   139  			Metadata: &chart.Metadata{
   140  				Name: "hello",
   141  			},
   142  			// This adds a basic template and hooks.
   143  			Templates: []*chart.Template{
   144  				{Name: "templates/hello", Data: []byte("hello: world")},
   145  				{Name: "templates/hooks", Data: []byte(manifestWithHook)},
   146  			},
   147  		},
   148  	}
   149  
   150  	for _, opt := range opts {
   151  		opt(c)
   152  	}
   153  
   154  	return c.Chart
   155  }
   156  
   157  func withKube(version string) chartOption {
   158  	return func(opts *chartOptions) {
   159  		opts.Metadata.KubeVersion = version
   160  	}
   161  }
   162  
   163  func withTiller(version string) chartOption {
   164  	return func(opts *chartOptions) {
   165  		opts.Metadata.TillerVersion = version
   166  	}
   167  }
   168  
   169  func withDependency(dependencyOpts ...chartOption) chartOption {
   170  	return func(opts *chartOptions) {
   171  		opts.Dependencies = append(opts.Dependencies, buildChart(dependencyOpts...))
   172  	}
   173  }
   174  
   175  func withNotes(notes string) chartOption {
   176  	return func(opts *chartOptions) {
   177  		opts.Templates = append(opts.Templates, &chart.Template{
   178  			Name: "templates/NOTES.txt",
   179  			Data: []byte(notes),
   180  		})
   181  	}
   182  }
   183  
   184  func withSampleTemplates() chartOption {
   185  	return func(opts *chartOptions) {
   186  		sampleTemplates := []*chart.Template{
   187  			// This adds basic templates and partials.
   188  			{Name: "templates/goodbye", Data: []byte("goodbye: world")},
   189  			{Name: "templates/empty", Data: []byte("")},
   190  			{Name: "templates/with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)},
   191  			{Name: "templates/partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)},
   192  		}
   193  		opts.Templates = append(opts.Templates, sampleTemplates...)
   194  	}
   195  }
   196  
   197  type installOptions struct {
   198  	*services.InstallReleaseRequest
   199  }
   200  
   201  type installOption func(*installOptions)
   202  
   203  func withName(name string) installOption {
   204  	return func(opts *installOptions) {
   205  		opts.Name = name
   206  	}
   207  }
   208  
   209  func withDryRun() installOption {
   210  	return func(opts *installOptions) {
   211  		opts.DryRun = true
   212  	}
   213  }
   214  
   215  func withDisabledHooks() installOption {
   216  	return func(opts *installOptions) {
   217  		opts.DisableHooks = true
   218  	}
   219  }
   220  
   221  func withReuseName() installOption {
   222  	return func(opts *installOptions) {
   223  		opts.ReuseName = true
   224  	}
   225  }
   226  
   227  func withChart(chartOpts ...chartOption) installOption {
   228  	return func(opts *installOptions) {
   229  		opts.Chart = buildChart(chartOpts...)
   230  	}
   231  }
   232  
   233  func withSubNotes() installOption {
   234  	return func(opts *installOptions) {
   235  		opts.SubNotes = true
   236  	}
   237  }
   238  
   239  func installRequest(opts ...installOption) *services.InstallReleaseRequest {
   240  	reqOpts := &installOptions{
   241  		&services.InstallReleaseRequest{
   242  			Namespace: "spaced",
   243  			Chart:     buildChart(),
   244  		},
   245  	}
   246  
   247  	for _, opt := range opts {
   248  		opt(reqOpts)
   249  	}
   250  
   251  	return reqOpts.InstallReleaseRequest
   252  }
   253  
   254  // chartStub creates a fully stubbed out chart.
   255  func chartStub() *chart.Chart {
   256  	return buildChart(withSampleTemplates())
   257  }
   258  
   259  // releaseStub creates a release stub, complete with the chartStub as its chart.
   260  func releaseStub() *release.Release {
   261  	return namedReleaseStub("angry-panda", release.Status_DEPLOYED)
   262  }
   263  
   264  func namedReleaseStub(name string, status release.Status_Code) *release.Release {
   265  	date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0}
   266  	return &release.Release{
   267  		Name: name,
   268  		Info: &release.Info{
   269  			FirstDeployed: &date,
   270  			LastDeployed:  &date,
   271  			Status:        &release.Status{Code: status},
   272  			Description:   "Named Release Stub",
   273  		},
   274  		Chart:   chartStub(),
   275  		Config:  &chart.Config{Raw: `name: value`},
   276  		Version: 1,
   277  		Hooks: []*release.Hook{
   278  			{
   279  				Name:     "test-cm",
   280  				Kind:     "ConfigMap",
   281  				Path:     "test-cm",
   282  				Manifest: manifestWithHook,
   283  				Events: []release.Hook_Event{
   284  					release.Hook_POST_INSTALL,
   285  					release.Hook_PRE_DELETE,
   286  				},
   287  			},
   288  			{
   289  				Name:     "finding-nemo",
   290  				Kind:     "Pod",
   291  				Path:     "finding-nemo",
   292  				Manifest: manifestWithTestHook,
   293  				Events: []release.Hook_Event{
   294  					release.Hook_RELEASE_TEST_SUCCESS,
   295  				},
   296  			},
   297  		},
   298  	}
   299  }
   300  
   301  func upgradeReleaseVersion(rel *release.Release) *release.Release {
   302  	date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0}
   303  
   304  	rel.Info.Status.Code = release.Status_SUPERSEDED
   305  	return &release.Release{
   306  		Name: rel.Name,
   307  		Info: &release.Info{
   308  			FirstDeployed: rel.Info.FirstDeployed,
   309  			LastDeployed:  &date,
   310  			Status:        &release.Status{Code: release.Status_DEPLOYED},
   311  		},
   312  		Chart:   rel.Chart,
   313  		Config:  rel.Config,
   314  		Version: rel.Version + 1,
   315  	}
   316  }
   317  
   318  func TestValidName(t *testing.T) {
   319  	for name, valid := range map[string]error{
   320  		"nina pinta santa-maria": errInvalidName,
   321  		"nina-pinta-santa-maria": nil,
   322  		"-nina":                  errInvalidName,
   323  		"pinta-":                 errInvalidName,
   324  		"santa-maria":            nil,
   325  		"niƱa":                   errInvalidName,
   326  		"...":                    errInvalidName,
   327  		"pinta...":               errInvalidName,
   328  		"santa...maria":          nil,
   329  		"":                       errMissingRelease,
   330  		" ":                      errInvalidName,
   331  		".nina.":                 errInvalidName,
   332  		"nina.pinta":             nil,
   333  		"abcdefghi-abcdefghi-abcdefghi-abcdefghi-abcdefghi-abcd": errInvalidName,
   334  	} {
   335  		if valid != validateReleaseName(name) {
   336  			t.Errorf("Expected %q to be %t", name, valid)
   337  		}
   338  	}
   339  }
   340  
   341  func TestGetAllVersionSet(t *testing.T) {
   342  	rs := rsFixture()
   343  	vs, err := GetAllVersionSet(rs.clientset.Discovery())
   344  	if err != nil {
   345  		t.Error(err)
   346  	}
   347  	if !vs.Has("v1") {
   348  		t.Errorf("Expected supported versions to at least include v1.")
   349  	}
   350  	if vs.Has("nosuchversion/v1") {
   351  		t.Error("Non-existent version is reported found.")
   352  	}
   353  }
   354  
   355  func TestGetVersionSet(t *testing.T) {
   356  	rs := rsFixture()
   357  	vs, err := GetVersionSet(rs.clientset.Discovery())
   358  	if err != nil {
   359  		t.Error(err)
   360  	}
   361  	if !vs.Has("v1") {
   362  		t.Errorf("Expected supported versions to at least include v1.")
   363  	}
   364  	if vs.Has("nosuchversion/v1") {
   365  		t.Error("Non-existent version is reported found.")
   366  	}
   367  }
   368  
   369  func TestUniqName(t *testing.T) {
   370  	rs := rsFixture()
   371  
   372  	rel1 := releaseStub()
   373  	rel2 := releaseStub()
   374  	rel2.Name = "happy-panda"
   375  	rel2.Info.Status.Code = release.Status_DELETED
   376  
   377  	rs.env.Releases.Create(rel1)
   378  	rs.env.Releases.Create(rel2)
   379  
   380  	tests := []struct {
   381  		name   string
   382  		expect string
   383  		reuse  bool
   384  		err    bool
   385  	}{
   386  		{"first", "first", false, false},
   387  		{"", "[a-z]+-[a-z]+", false, false},
   388  		{"angry-panda", "", false, true},
   389  		{"happy-panda", "", false, true},
   390  		{"happy-panda", "happy-panda", true, false},
   391  		{"hungry-hungry-hungry-hungry-hungry-hungry-hungry-hungry-hippos", "", true, true}, // Exceeds max name length
   392  	}
   393  
   394  	for _, tt := range tests {
   395  		u, err := rs.uniqName(tt.name, tt.reuse)
   396  		if err != nil {
   397  			if tt.err {
   398  				continue
   399  			}
   400  			t.Fatal(err)
   401  		}
   402  		if tt.err {
   403  			t.Errorf("Expected an error for %q", tt.name)
   404  		}
   405  		if match, err := regexp.MatchString(tt.expect, u); err != nil {
   406  			t.Fatal(err)
   407  		} else if !match {
   408  			t.Errorf("Expected %q to match %q", u, tt.expect)
   409  		}
   410  	}
   411  }
   412  
   413  type fakeNamer struct {
   414  	name string
   415  }
   416  
   417  func NewFakeNamer(nam string) moniker.Namer {
   418  	return &fakeNamer{
   419  		name: nam,
   420  	}
   421  }
   422  
   423  func (f *fakeNamer) Name() string {
   424  	return f.NameSep(" ")
   425  }
   426  
   427  func (f *fakeNamer) NameSep(sep string) string {
   428  	return f.name
   429  }
   430  
   431  func TestCreateUniqueName(t *testing.T) {
   432  	rs := rsFixture()
   433  
   434  	rel1 := releaseStub()
   435  	rel1.Name = "happy-panda"
   436  
   437  	rs.env.Releases.Create(rel1)
   438  
   439  	tests := []struct {
   440  		name   string
   441  		expect string
   442  		err    bool
   443  	}{
   444  		{"happy-panda", "ERROR", true},
   445  		{"wobbly-octopus", "[a-z]+-[a-z]+", false},
   446  	}
   447  
   448  	for _, tt := range tests {
   449  		m := NewFakeNamer(tt.name)
   450  		u, err := rs.createUniqName(m)
   451  		if err != nil {
   452  			if tt.err {
   453  				continue
   454  			}
   455  			t.Fatal(err)
   456  		}
   457  		if tt.err {
   458  			t.Errorf("Expected an error for %q", tt.name)
   459  		}
   460  		if match, err := regexp.MatchString(tt.expect, u); err != nil {
   461  			t.Fatal(err)
   462  		} else if !match {
   463  			t.Errorf("Expected %q to match %q", u, tt.expect)
   464  		}
   465  	}
   466  
   467  }
   468  
   469  func releaseWithKeepStub(rlsName string) *release.Release {
   470  	ch := &chart.Chart{
   471  		Metadata: &chart.Metadata{
   472  			Name: "bunnychart",
   473  		},
   474  		Templates: []*chart.Template{
   475  			{Name: "templates/configmap-keep-a", Data: []byte(manifestWithKeep)},
   476  			{Name: "templates/configmap-keep-b", Data: []byte(manifestWithKeepEmpty)},
   477  		},
   478  	}
   479  
   480  	date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0}
   481  	rl := &release.Release{
   482  		Name: rlsName,
   483  		Info: &release.Info{
   484  			FirstDeployed: &date,
   485  			LastDeployed:  &date,
   486  			Status:        &release.Status{Code: release.Status_DEPLOYED},
   487  		},
   488  		Chart:   ch,
   489  		Config:  &chart.Config{Raw: `name: value`},
   490  		Version: 1,
   491  	}
   492  
   493  	helm.RenderReleaseMock(rl, false)
   494  
   495  	return rl
   496  }
   497  
   498  func MockEnvironment() *environment.Environment {
   499  	e := environment.New()
   500  	e.Releases = storage.Init(driver.NewMemory())
   501  	e.KubeClient = &environment.PrintingKubeClient{Out: ioutil.Discard}
   502  	return e
   503  }
   504  
   505  func newUpdateFailingKubeClient() *updateFailingKubeClient {
   506  	return &updateFailingKubeClient{
   507  		PrintingKubeClient: environment.PrintingKubeClient{Out: os.Stdout},
   508  	}
   509  
   510  }
   511  
   512  type updateFailingKubeClient struct {
   513  	environment.PrintingKubeClient
   514  }
   515  
   516  func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
   517  	return u.UpdateWithOptions(namespace, originalReader, modifiedReader, kube.UpdateOptions{
   518  		Force:      force,
   519  		Recreate:   recreate,
   520  		Timeout:    timeout,
   521  		ShouldWait: shouldWait,
   522  	})
   523  }
   524  
   525  func (u *updateFailingKubeClient) UpdateWithOptions(namespace string, originalReader, modifiedReader io.Reader, opts kube.UpdateOptions) error {
   526  	return errors.New("Failed update in kube client")
   527  }
   528  
   529  func newHookFailingKubeClient() *hookFailingKubeClient {
   530  	return &hookFailingKubeClient{
   531  		PrintingKubeClient: environment.PrintingKubeClient{Out: ioutil.Discard},
   532  	}
   533  }
   534  
   535  type hookFailingKubeClient struct {
   536  	environment.PrintingKubeClient
   537  }
   538  
   539  func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error {
   540  	return errors.New("Failed watch")
   541  }
   542  
   543  func newDeleteFailingKubeClient() *deleteFailingKubeClient {
   544  	return &deleteFailingKubeClient{
   545  		PrintingKubeClient: environment.PrintingKubeClient{Out: ioutil.Discard},
   546  	}
   547  }
   548  
   549  type deleteFailingKubeClient struct {
   550  	environment.PrintingKubeClient
   551  }
   552  
   553  func (d *deleteFailingKubeClient) Delete(ns string, r io.Reader) error {
   554  	return kube.ErrNoObjectsVisited
   555  }
   556  
   557  func (d *deleteFailingKubeClient) DeleteWithTimeout(ns string, r io.Reader, timeout int64, shouldWait bool) error {
   558  	return kube.ErrNoObjectsVisited
   559  }
   560  
   561  type mockListServer struct {
   562  	val *services.ListReleasesResponse
   563  }
   564  
   565  func (l *mockListServer) Send(res *services.ListReleasesResponse) error {
   566  	l.val = res
   567  	return nil
   568  }
   569  
   570  func (l *mockListServer) Context() context.Context       { return helm.NewContext() }
   571  func (l *mockListServer) SendMsg(v interface{}) error    { return nil }
   572  func (l *mockListServer) RecvMsg(v interface{}) error    { return nil }
   573  func (l *mockListServer) SendHeader(m metadata.MD) error { return nil }
   574  func (l *mockListServer) SetTrailer(m metadata.MD)       {}
   575  func (l *mockListServer) SetHeader(m metadata.MD) error  { return nil }
   576  
   577  type mockRunReleaseTestServer struct{}
   578  
   579  func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error {
   580  	return nil
   581  }
   582  func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error  { return nil }
   583  func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil }
   584  func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD)       {}
   585  func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error    { return nil }
   586  func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error    { return nil }
   587  func (rs mockRunReleaseTestServer) Context() context.Context {
   588  	return helm.NewContext()
   589  }
   590  
   591  type mockHooksManifest struct {
   592  	Metadata struct {
   593  		Name        string
   594  		Annotations map[string]string
   595  	}
   596  }
   597  type mockHooksKubeClient struct {
   598  	Resources map[string]*mockHooksManifest
   599  }
   600  
   601  var errResourceExists = errors.New("resource already exists")
   602  
   603  func (kc *mockHooksKubeClient) makeManifest(r io.Reader) (*mockHooksManifest, error) {
   604  	b, err := ioutil.ReadAll(r)
   605  	if err != nil {
   606  		return nil, err
   607  	}
   608  
   609  	manifest := &mockHooksManifest{}
   610  	err = yaml.Unmarshal(b, manifest)
   611  	if err != nil {
   612  		return nil, err
   613  	}
   614  
   615  	return manifest, nil
   616  }
   617  func (kc *mockHooksKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error {
   618  	manifest, err := kc.makeManifest(r)
   619  	if err != nil {
   620  		return err
   621  	}
   622  
   623  	if _, hasKey := kc.Resources[manifest.Metadata.Name]; hasKey {
   624  		return errResourceExists
   625  	}
   626  
   627  	kc.Resources[manifest.Metadata.Name] = manifest
   628  
   629  	return nil
   630  }
   631  func (kc *mockHooksKubeClient) Get(ns string, r io.Reader) (string, error) {
   632  	return "", nil
   633  }
   634  func (kc *mockHooksKubeClient) Delete(ns string, r io.Reader) error {
   635  	return kc.DeleteWithTimeout(ns, r, 0, false)
   636  }
   637  func (kc *mockHooksKubeClient) DeleteWithTimeout(ns string, r io.Reader, timeout int64, shouldWait bool) error {
   638  	manifest, err := kc.makeManifest(r)
   639  	if err != nil {
   640  		return err
   641  	}
   642  
   643  	delete(kc.Resources, manifest.Metadata.Name)
   644  
   645  	return nil
   646  }
   647  func (kc *mockHooksKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error {
   648  	paramManifest, err := kc.makeManifest(r)
   649  	if err != nil {
   650  		return err
   651  	}
   652  
   653  	manifest, hasManifest := kc.Resources[paramManifest.Metadata.Name]
   654  	if !hasManifest {
   655  		return fmt.Errorf("mockHooksKubeClient.WatchUntilReady: no such resource %s found", paramManifest.Metadata.Name)
   656  	}
   657  
   658  	if manifest.Metadata.Annotations["mockHooksKubeClient/Emulate"] == "hook-failed" {
   659  		return fmt.Errorf("mockHooksKubeClient.WatchUntilReady: hook-failed")
   660  	}
   661  
   662  	return nil
   663  }
   664  func (kc *mockHooksKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
   665  	return nil
   666  }
   667  func (kc *mockHooksKubeClient) UpdateWithOptions(ns string, currentReader, modifiedReader io.Reader, opts kube.UpdateOptions) error {
   668  	return nil
   669  }
   670  func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) {
   671  	return []*resource.Info{}, nil
   672  }
   673  func (kc *mockHooksKubeClient) BuildUnstructured(ns string, reader io.Reader) (kube.Result, error) {
   674  	return []*resource.Info{}, nil
   675  }
   676  func (kc *mockHooksKubeClient) Validate(ns string, reader io.Reader) error {
   677  	return nil
   678  }
   679  func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) {
   680  	return v1.PodUnknown, nil
   681  }
   682  func (kc *mockHooksKubeClient) GetPodLogs(name, namespace string) (io.ReadCloser, error) {
   683  	return nil, nil
   684  }
   685  
   686  func (kc *mockHooksKubeClient) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error {
   687  	return nil
   688  }
   689  
   690  func deletePolicyStub(kubeClient *mockHooksKubeClient) *ReleaseServer {
   691  	e := environment.New()
   692  	e.Releases = storage.Init(driver.NewMemory())
   693  	e.KubeClient = kubeClient
   694  
   695  	clientset := fake.NewSimpleClientset()
   696  	return &ReleaseServer{
   697  		ReleaseModule: &LocalReleaseModule{
   698  			clientset: clientset,
   699  		},
   700  		env:       e,
   701  		clientset: clientset,
   702  		Log:       func(_ string, _ ...interface{}) {},
   703  	}
   704  }
   705  
   706  func deletePolicyHookStub(hookName string, extraAnnotations map[string]string, DeletePolicies []release.Hook_DeletePolicy) *release.Hook {
   707  	extraAnnotationsStr := ""
   708  	for k, v := range extraAnnotations {
   709  		extraAnnotationsStr += fmt.Sprintf("    \"%s\": \"%s\"\n", k, v)
   710  	}
   711  
   712  	return &release.Hook{
   713  		Name: hookName,
   714  		Kind: "Job",
   715  		Path: hookName,
   716  		Manifest: fmt.Sprintf(`kind: Job
   717  metadata:
   718    name: %s
   719    annotations:
   720      "helm.sh/hook": pre-install,pre-upgrade
   721  %sdata:
   722  name: value`, hookName, extraAnnotationsStr),
   723  		Events: []release.Hook_Event{
   724  			release.Hook_PRE_INSTALL,
   725  			release.Hook_PRE_UPGRADE,
   726  		},
   727  		DeletePolicies: DeletePolicies,
   728  	}
   729  }
   730  
   731  func execHookShouldSucceed(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string) error {
   732  	err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600)
   733  	if err != nil {
   734  		return fmt.Errorf("expected hook %s to be successful: %s", hook.Name, err)
   735  	}
   736  	return nil
   737  }
   738  
   739  func execHookShouldFail(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string) error {
   740  	err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600)
   741  	if err == nil {
   742  		return fmt.Errorf("expected hook %s to be failed", hook.Name)
   743  	}
   744  	return nil
   745  }
   746  
   747  func execHookShouldFailWithError(rs *ReleaseServer, hook *release.Hook, releaseName string, namespace string, hookType string, expectedError error) error {
   748  	err := rs.execHook([]*release.Hook{hook}, releaseName, namespace, hookType, 600)
   749  	if err != expectedError {
   750  		return fmt.Errorf("expected hook %s to fail with error %v, got %v", hook.Name, expectedError, err)
   751  	}
   752  	return nil
   753  }
   754  
   755  type deletePolicyContext struct {
   756  	ReleaseServer *ReleaseServer
   757  	ReleaseName   string
   758  	Namespace     string
   759  	HookName      string
   760  	KubeClient    *mockHooksKubeClient
   761  }
   762  
   763  func newDeletePolicyContext() *deletePolicyContext {
   764  	kubeClient := &mockHooksKubeClient{
   765  		Resources: make(map[string]*mockHooksManifest),
   766  	}
   767  
   768  	return &deletePolicyContext{
   769  		KubeClient:    kubeClient,
   770  		ReleaseServer: deletePolicyStub(kubeClient),
   771  		ReleaseName:   "flying-carp",
   772  		Namespace:     "river",
   773  		HookName:      "migration-job",
   774  	}
   775  }
   776  
   777  func TestSuccessfulHookWithoutDeletePolicy(t *testing.T) {
   778  	ctx := newDeletePolicyContext()
   779  	hook := deletePolicyHookStub(ctx.HookName, nil, nil)
   780  
   781  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   782  	if err != nil {
   783  		t.Error(err)
   784  	}
   785  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   786  		t.Errorf("expected resource %s to be created by kube client", hook.Name)
   787  	}
   788  }
   789  
   790  func TestFailedHookWithoutDeletePolicy(t *testing.T) {
   791  	ctx := newDeletePolicyContext()
   792  	hook := deletePolicyHookStub(ctx.HookName,
   793  		map[string]string{"mockHooksKubeClient/Emulate": "hook-failed"},
   794  		nil,
   795  	)
   796  
   797  	err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   798  	if err != nil {
   799  		t.Error(err)
   800  	}
   801  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   802  		t.Errorf("expected resource %s to be created by kube client", hook.Name)
   803  	}
   804  }
   805  
   806  func TestSuccessfulHookWithSucceededDeletePolicy(t *testing.T) {
   807  	ctx := newDeletePolicyContext()
   808  	hook := deletePolicyHookStub(ctx.HookName,
   809  		map[string]string{"helm.sh/hook-delete-policy": "hook-succeeded"},
   810  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED},
   811  	)
   812  
   813  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   814  	if err != nil {
   815  		t.Error(err)
   816  	}
   817  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
   818  		t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name)
   819  	}
   820  }
   821  
   822  func TestSuccessfulHookWithFailedDeletePolicy(t *testing.T) {
   823  	ctx := newDeletePolicyContext()
   824  	hook := deletePolicyHookStub(ctx.HookName,
   825  		map[string]string{"helm.sh/hook-delete-policy": "hook-failed"},
   826  		[]release.Hook_DeletePolicy{release.Hook_FAILED},
   827  	)
   828  
   829  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   830  	if err != nil {
   831  		t.Error(err)
   832  	}
   833  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   834  		t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name)
   835  	}
   836  }
   837  
   838  func TestFailedHookWithSucceededDeletePolicy(t *testing.T) {
   839  	ctx := newDeletePolicyContext()
   840  
   841  	hook := deletePolicyHookStub(ctx.HookName,
   842  		map[string]string{
   843  			"mockHooksKubeClient/Emulate": "hook-failed",
   844  			"helm.sh/hook-delete-policy":  "hook-succeeded",
   845  		},
   846  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED},
   847  	)
   848  
   849  	err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   850  	if err != nil {
   851  		t.Error(err)
   852  	}
   853  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   854  		t.Errorf("expected resource %s to be existing after hook failed", hook.Name)
   855  	}
   856  }
   857  
   858  func TestFailedHookWithFailedDeletePolicy(t *testing.T) {
   859  	ctx := newDeletePolicyContext()
   860  
   861  	hook := deletePolicyHookStub(ctx.HookName,
   862  		map[string]string{
   863  			"mockHooksKubeClient/Emulate": "hook-failed",
   864  			"helm.sh/hook-delete-policy":  "hook-failed",
   865  		},
   866  		[]release.Hook_DeletePolicy{release.Hook_FAILED},
   867  	)
   868  
   869  	err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   870  	if err != nil {
   871  		t.Error(err)
   872  	}
   873  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
   874  		t.Errorf("expected resource %s to be unexisting after hook failed", hook.Name)
   875  	}
   876  }
   877  
   878  func TestSuccessfulHookWithSuccededOrFailedDeletePolicy(t *testing.T) {
   879  	ctx := newDeletePolicyContext()
   880  
   881  	hook := deletePolicyHookStub(ctx.HookName,
   882  		map[string]string{
   883  			"helm.sh/hook-delete-policy": "hook-succeeded,hook-failed",
   884  		},
   885  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_FAILED},
   886  	)
   887  
   888  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   889  	if err != nil {
   890  		t.Error(err)
   891  	}
   892  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
   893  		t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name)
   894  	}
   895  }
   896  
   897  func TestFailedHookWithSuccededOrFailedDeletePolicy(t *testing.T) {
   898  	ctx := newDeletePolicyContext()
   899  
   900  	hook := deletePolicyHookStub(ctx.HookName,
   901  		map[string]string{
   902  			"mockHooksKubeClient/Emulate": "hook-failed",
   903  			"helm.sh/hook-delete-policy":  "hook-succeeded,hook-failed",
   904  		},
   905  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_FAILED},
   906  	)
   907  
   908  	err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   909  	if err != nil {
   910  		t.Error(err)
   911  	}
   912  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
   913  		t.Errorf("expected resource %s to be unexisting after hook failed", hook.Name)
   914  	}
   915  }
   916  
   917  func TestHookAlreadyExists(t *testing.T) {
   918  	ctx := newDeletePolicyContext()
   919  
   920  	hook := deletePolicyHookStub(ctx.HookName, nil, nil)
   921  
   922  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   923  	if err != nil {
   924  		t.Error(err)
   925  	}
   926  
   927  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   928  		t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name)
   929  	}
   930  
   931  	err = execHookShouldFailWithError(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade, errResourceExists)
   932  	if err != nil {
   933  		t.Error(err)
   934  	}
   935  
   936  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   937  		t.Errorf("expected resource %s to be existing after already exists error", hook.Name)
   938  	}
   939  }
   940  
   941  func TestHookDeletingWithBeforeHookCreationDeletePolicy(t *testing.T) {
   942  	ctx := newDeletePolicyContext()
   943  
   944  	hook := deletePolicyHookStub(ctx.HookName,
   945  		map[string]string{"helm.sh/hook-delete-policy": "before-hook-creation"},
   946  		[]release.Hook_DeletePolicy{release.Hook_BEFORE_HOOK_CREATION},
   947  	)
   948  
   949  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   950  	if err != nil {
   951  		t.Error(err)
   952  	}
   953  
   954  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   955  		t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name)
   956  	}
   957  
   958  	err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade)
   959  	if err != nil {
   960  		t.Error(err)
   961  	}
   962  
   963  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
   964  		t.Errorf("expected resource %s to be existing after hook succeeded", hook.Name)
   965  	}
   966  }
   967  
   968  func TestSuccessfulHookWithMixedDeletePolicies(t *testing.T) {
   969  	ctx := newDeletePolicyContext()
   970  
   971  	hook := deletePolicyHookStub(ctx.HookName,
   972  		map[string]string{
   973  			"helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation",
   974  		},
   975  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION},
   976  	)
   977  
   978  	err := execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
   979  	if err != nil {
   980  		t.Error(err)
   981  	}
   982  
   983  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
   984  		t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name)
   985  	}
   986  
   987  	err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade)
   988  	if err != nil {
   989  		t.Error(err)
   990  	}
   991  
   992  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
   993  		t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name)
   994  	}
   995  }
   996  
   997  func TestFailedHookWithMixedDeletePolicies(t *testing.T) {
   998  	ctx := newDeletePolicyContext()
   999  
  1000  	hook := deletePolicyHookStub(ctx.HookName,
  1001  		map[string]string{
  1002  			"mockHooksKubeClient/Emulate": "hook-failed",
  1003  			"helm.sh/hook-delete-policy":  "hook-succeeded,before-hook-creation",
  1004  		},
  1005  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION},
  1006  	)
  1007  
  1008  	err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
  1009  	if err != nil {
  1010  		t.Error(err)
  1011  	}
  1012  
  1013  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
  1014  		t.Errorf("expected resource %s to be existing after hook failed", hook.Name)
  1015  	}
  1016  
  1017  	err = execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade)
  1018  	if err != nil {
  1019  		t.Error(err)
  1020  	}
  1021  
  1022  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
  1023  		t.Errorf("expected resource %s to be existing after hook failed", hook.Name)
  1024  	}
  1025  }
  1026  
  1027  func TestFailedThenSuccessfulHookWithMixedDeletePolicies(t *testing.T) {
  1028  	ctx := newDeletePolicyContext()
  1029  
  1030  	hook := deletePolicyHookStub(ctx.HookName,
  1031  		map[string]string{
  1032  			"mockHooksKubeClient/Emulate": "hook-failed",
  1033  			"helm.sh/hook-delete-policy":  "hook-succeeded,before-hook-creation",
  1034  		},
  1035  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION},
  1036  	)
  1037  
  1038  	err := execHookShouldFail(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreInstall)
  1039  	if err != nil {
  1040  		t.Error(err)
  1041  	}
  1042  
  1043  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; !hasResource {
  1044  		t.Errorf("expected resource %s to be existing after hook failed", hook.Name)
  1045  	}
  1046  
  1047  	hook = deletePolicyHookStub(ctx.HookName,
  1048  		map[string]string{
  1049  			"helm.sh/hook-delete-policy": "hook-succeeded,before-hook-creation",
  1050  		},
  1051  		[]release.Hook_DeletePolicy{release.Hook_SUCCEEDED, release.Hook_BEFORE_HOOK_CREATION},
  1052  	)
  1053  
  1054  	err = execHookShouldSucceed(ctx.ReleaseServer, hook, ctx.ReleaseName, ctx.Namespace, hooks.PreUpgrade)
  1055  	if err != nil {
  1056  		t.Error(err)
  1057  	}
  1058  
  1059  	if _, hasResource := ctx.KubeClient.Resources[hook.Name]; hasResource {
  1060  		t.Errorf("expected resource %s to be unexisting after hook succeeded", hook.Name)
  1061  	}
  1062  }