github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/action/install_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 action
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"regexp"
    26  	"strings"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  
    33  	"github.com/stefanmcshane/helm/internal/test"
    34  	"github.com/stefanmcshane/helm/pkg/chart"
    35  	"github.com/stefanmcshane/helm/pkg/chartutil"
    36  	kubefake "github.com/stefanmcshane/helm/pkg/kube/fake"
    37  	"github.com/stefanmcshane/helm/pkg/release"
    38  	"github.com/stefanmcshane/helm/pkg/storage/driver"
    39  	helmtime "github.com/stefanmcshane/helm/pkg/time"
    40  )
    41  
    42  type nameTemplateTestCase struct {
    43  	tpl              string
    44  	expected         string
    45  	expectedErrorStr string
    46  }
    47  
    48  func installAction(t *testing.T) *Install {
    49  	config := actionConfigFixture(t)
    50  	instAction := NewInstall(config)
    51  	instAction.Namespace = "spaced"
    52  	instAction.ReleaseName = "test-install-release"
    53  
    54  	return instAction
    55  }
    56  
    57  func TestInstallRelease(t *testing.T) {
    58  	is := assert.New(t)
    59  	req := require.New(t)
    60  
    61  	instAction := installAction(t)
    62  	vals := map[string]interface{}{}
    63  	ctx, done := context.WithCancel(context.Background())
    64  	res, err := instAction.RunWithContext(ctx, buildChart(), vals)
    65  	if err != nil {
    66  		t.Fatalf("Failed install: %s", err)
    67  	}
    68  	is.Equal(res.Name, "test-install-release", "Expected release name.")
    69  	is.Equal(res.Namespace, "spaced")
    70  
    71  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
    72  	is.NoError(err)
    73  
    74  	is.Len(rel.Hooks, 1)
    75  	is.Equal(rel.Hooks[0].Manifest, manifestWithHook)
    76  	is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall)
    77  	is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete")
    78  
    79  	is.NotEqual(len(res.Manifest), 0)
    80  	is.NotEqual(len(rel.Manifest), 0)
    81  	is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
    82  	is.Equal(rel.Info.Description, "Install complete")
    83  
    84  	// Detecting previous bug where context termination after successful release
    85  	// caused release to fail.
    86  	done()
    87  	time.Sleep(time.Millisecond * 100)
    88  	lastRelease, err := instAction.cfg.Releases.Last(rel.Name)
    89  	req.NoError(err)
    90  	is.Equal(lastRelease.Info.Status, release.StatusDeployed)
    91  }
    92  
    93  func TestInstallReleaseWithValues(t *testing.T) {
    94  	is := assert.New(t)
    95  	instAction := installAction(t)
    96  	userVals := map[string]interface{}{
    97  		"nestedKey": map[string]interface{}{
    98  			"simpleKey": "simpleValue",
    99  		},
   100  	}
   101  	expectedUserValues := map[string]interface{}{
   102  		"nestedKey": map[string]interface{}{
   103  			"simpleKey": "simpleValue",
   104  		},
   105  	}
   106  	res, err := instAction.Run(buildChart(withSampleValues()), userVals)
   107  	if err != nil {
   108  		t.Fatalf("Failed install: %s", err)
   109  	}
   110  	is.Equal(res.Name, "test-install-release", "Expected release name.")
   111  	is.Equal(res.Namespace, "spaced")
   112  
   113  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   114  	is.NoError(err)
   115  
   116  	is.Len(rel.Hooks, 1)
   117  	is.Equal(rel.Hooks[0].Manifest, manifestWithHook)
   118  	is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall)
   119  	is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete")
   120  
   121  	is.NotEqual(len(res.Manifest), 0)
   122  	is.NotEqual(len(rel.Manifest), 0)
   123  	is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
   124  	is.Equal("Install complete", rel.Info.Description)
   125  	is.Equal(expectedUserValues, rel.Config)
   126  }
   127  
   128  func TestInstallReleaseClientOnly(t *testing.T) {
   129  	is := assert.New(t)
   130  	instAction := installAction(t)
   131  	instAction.ClientOnly = true
   132  	instAction.Run(buildChart(), nil) // disregard output
   133  
   134  	is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities)
   135  	is.Equal(instAction.cfg.KubeClient, &kubefake.PrintingKubeClient{Out: ioutil.Discard})
   136  }
   137  
   138  func TestInstallRelease_NoName(t *testing.T) {
   139  	instAction := installAction(t)
   140  	instAction.ReleaseName = ""
   141  	vals := map[string]interface{}{}
   142  	_, err := instAction.Run(buildChart(), vals)
   143  	if err == nil {
   144  		t.Fatal("expected failure when no name is specified")
   145  	}
   146  	assert.Contains(t, err.Error(), "no name provided")
   147  }
   148  
   149  func TestInstallRelease_WithNotes(t *testing.T) {
   150  	is := assert.New(t)
   151  	instAction := installAction(t)
   152  	instAction.ReleaseName = "with-notes"
   153  	vals := map[string]interface{}{}
   154  	res, err := instAction.Run(buildChart(withNotes("note here")), vals)
   155  	if err != nil {
   156  		t.Fatalf("Failed install: %s", err)
   157  	}
   158  
   159  	is.Equal(res.Name, "with-notes")
   160  	is.Equal(res.Namespace, "spaced")
   161  
   162  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   163  	is.NoError(err)
   164  	is.Len(rel.Hooks, 1)
   165  	is.Equal(rel.Hooks[0].Manifest, manifestWithHook)
   166  	is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall)
   167  	is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete")
   168  	is.NotEqual(len(res.Manifest), 0)
   169  	is.NotEqual(len(rel.Manifest), 0)
   170  	is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
   171  	is.Equal(rel.Info.Description, "Install complete")
   172  
   173  	is.Equal(rel.Info.Notes, "note here")
   174  }
   175  
   176  func TestInstallRelease_WithNotesRendered(t *testing.T) {
   177  	is := assert.New(t)
   178  	instAction := installAction(t)
   179  	instAction.ReleaseName = "with-notes"
   180  	vals := map[string]interface{}{}
   181  	res, err := instAction.Run(buildChart(withNotes("got-{{.Release.Name}}")), vals)
   182  	if err != nil {
   183  		t.Fatalf("Failed install: %s", err)
   184  	}
   185  
   186  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   187  	is.NoError(err)
   188  
   189  	expectedNotes := fmt.Sprintf("got-%s", res.Name)
   190  	is.Equal(expectedNotes, rel.Info.Notes)
   191  	is.Equal(rel.Info.Description, "Install complete")
   192  }
   193  
   194  func TestInstallRelease_WithChartAndDependencyParentNotes(t *testing.T) {
   195  	// Regression: Make sure that the child's notes don't override the parent's
   196  	is := assert.New(t)
   197  	instAction := installAction(t)
   198  	instAction.ReleaseName = "with-notes"
   199  	vals := map[string]interface{}{}
   200  	res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals)
   201  	if err != nil {
   202  		t.Fatalf("Failed install: %s", err)
   203  	}
   204  
   205  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   206  	is.Equal("with-notes", rel.Name)
   207  	is.NoError(err)
   208  	is.Equal("parent", rel.Info.Notes)
   209  	is.Equal(rel.Info.Description, "Install complete")
   210  }
   211  
   212  func TestInstallRelease_WithChartAndDependencyAllNotes(t *testing.T) {
   213  	// Regression: Make sure that the child's notes don't override the parent's
   214  	is := assert.New(t)
   215  	instAction := installAction(t)
   216  	instAction.ReleaseName = "with-notes"
   217  	instAction.SubNotes = true
   218  	vals := map[string]interface{}{}
   219  	res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))), vals)
   220  	if err != nil {
   221  		t.Fatalf("Failed install: %s", err)
   222  	}
   223  
   224  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   225  	is.Equal("with-notes", rel.Name)
   226  	is.NoError(err)
   227  	// test run can return as either 'parent\nchild' or 'child\nparent'
   228  	if !strings.Contains(rel.Info.Notes, "parent") && !strings.Contains(rel.Info.Notes, "child") {
   229  		t.Fatalf("Expected 'parent\nchild' or 'child\nparent', got '%s'", rel.Info.Notes)
   230  	}
   231  	is.Equal(rel.Info.Description, "Install complete")
   232  }
   233  
   234  func TestInstallRelease_DryRun(t *testing.T) {
   235  	is := assert.New(t)
   236  	instAction := installAction(t)
   237  	instAction.DryRun = true
   238  	vals := map[string]interface{}{}
   239  	res, err := instAction.Run(buildChart(withSampleTemplates()), vals)
   240  	if err != nil {
   241  		t.Fatalf("Failed install: %s", err)
   242  	}
   243  
   244  	is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
   245  	is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world")
   246  	is.Contains(res.Manifest, "hello: Earth")
   247  	is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}")
   248  	is.NotContains(res.Manifest, "empty")
   249  
   250  	_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
   251  	is.Error(err)
   252  	is.Len(res.Hooks, 1)
   253  	is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "expect hook to not be marked as run")
   254  	is.Equal(res.Info.Description, "Dry run complete")
   255  }
   256  
   257  // Regression test for #7955: Lookup must not connect to Kubernetes on a dry-run.
   258  func TestInstallRelease_DryRun_Lookup(t *testing.T) {
   259  	is := assert.New(t)
   260  	instAction := installAction(t)
   261  	instAction.DryRun = true
   262  	vals := map[string]interface{}{}
   263  
   264  	mockChart := buildChart(withSampleTemplates())
   265  	mockChart.Templates = append(mockChart.Templates, &chart.File{
   266  		Name: "templates/lookup",
   267  		Data: []byte(`goodbye: {{ lookup "v1" "Namespace" "" "___" }}`),
   268  	})
   269  
   270  	res, err := instAction.Run(mockChart, vals)
   271  	if err != nil {
   272  		t.Fatalf("Failed install: %s", err)
   273  	}
   274  
   275  	is.Contains(res.Manifest, "goodbye: map[]")
   276  }
   277  
   278  func TestInstallReleaseIncorrectTemplate_DryRun(t *testing.T) {
   279  	is := assert.New(t)
   280  	instAction := installAction(t)
   281  	instAction.DryRun = true
   282  	vals := map[string]interface{}{}
   283  	_, err := instAction.Run(buildChart(withSampleIncludingIncorrectTemplates()), vals)
   284  	expectedErr := "\"hello/templates/incorrect\" at <.Values.bad.doh>: nil pointer evaluating interface {}.doh"
   285  	if err == nil {
   286  		t.Fatalf("Install should fail containing error: %s", expectedErr)
   287  	}
   288  	if err != nil {
   289  		is.Contains(err.Error(), expectedErr)
   290  	}
   291  }
   292  
   293  func TestInstallRelease_NoHooks(t *testing.T) {
   294  	is := assert.New(t)
   295  	instAction := installAction(t)
   296  	instAction.DisableHooks = true
   297  	instAction.ReleaseName = "no-hooks"
   298  	instAction.cfg.Releases.Create(releaseStub())
   299  
   300  	vals := map[string]interface{}{}
   301  	res, err := instAction.Run(buildChart(), vals)
   302  	if err != nil {
   303  		t.Fatalf("Failed install: %s", err)
   304  	}
   305  
   306  	is.True(res.Hooks[0].LastRun.CompletedAt.IsZero(), "hooks should not run with no-hooks")
   307  }
   308  
   309  func TestInstallRelease_FailedHooks(t *testing.T) {
   310  	is := assert.New(t)
   311  	instAction := installAction(t)
   312  	instAction.ReleaseName = "failed-hooks"
   313  	failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   314  	failer.WatchUntilReadyError = fmt.Errorf("Failed watch")
   315  	instAction.cfg.KubeClient = failer
   316  
   317  	vals := map[string]interface{}{}
   318  	res, err := instAction.Run(buildChart(), vals)
   319  	is.Error(err)
   320  	is.Contains(res.Info.Description, "failed post-install")
   321  	is.Equal(release.StatusFailed, res.Info.Status)
   322  }
   323  
   324  func TestInstallRelease_ReplaceRelease(t *testing.T) {
   325  	is := assert.New(t)
   326  	instAction := installAction(t)
   327  	instAction.Replace = true
   328  
   329  	rel := releaseStub()
   330  	rel.Info.Status = release.StatusUninstalled
   331  	instAction.cfg.Releases.Create(rel)
   332  	instAction.ReleaseName = rel.Name
   333  
   334  	vals := map[string]interface{}{}
   335  	res, err := instAction.Run(buildChart(), vals)
   336  	is.NoError(err)
   337  
   338  	// This should have been auto-incremented
   339  	is.Equal(2, res.Version)
   340  	is.Equal(res.Name, rel.Name)
   341  
   342  	getres, err := instAction.cfg.Releases.Get(rel.Name, res.Version)
   343  	is.NoError(err)
   344  	is.Equal(getres.Info.Status, release.StatusDeployed)
   345  }
   346  
   347  func TestInstallRelease_KubeVersion(t *testing.T) {
   348  	is := assert.New(t)
   349  	instAction := installAction(t)
   350  	vals := map[string]interface{}{}
   351  	_, err := instAction.Run(buildChart(withKube(">=0.0.0")), vals)
   352  	is.NoError(err)
   353  
   354  	// This should fail for a few hundred years
   355  	instAction.ReleaseName = "should-fail"
   356  	vals = map[string]interface{}{}
   357  	_, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals)
   358  	is.Error(err)
   359  	is.Contains(err.Error(), "chart requires kubeVersion")
   360  }
   361  
   362  func TestInstallRelease_Wait(t *testing.T) {
   363  	is := assert.New(t)
   364  	instAction := installAction(t)
   365  	instAction.ReleaseName = "come-fail-away"
   366  	failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   367  	failer.WaitError = fmt.Errorf("I timed out")
   368  	instAction.cfg.KubeClient = failer
   369  	instAction.Wait = true
   370  	vals := map[string]interface{}{}
   371  
   372  	res, err := instAction.Run(buildChart(), vals)
   373  	is.Error(err)
   374  	is.Contains(res.Info.Description, "I timed out")
   375  	is.Equal(res.Info.Status, release.StatusFailed)
   376  }
   377  func TestInstallRelease_Wait_Interrupted(t *testing.T) {
   378  	is := assert.New(t)
   379  	instAction := installAction(t)
   380  	instAction.ReleaseName = "interrupted-release"
   381  	failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   382  	failer.WaitDuration = 10 * time.Second
   383  	instAction.cfg.KubeClient = failer
   384  	instAction.Wait = true
   385  	vals := map[string]interface{}{}
   386  
   387  	ctx := context.Background()
   388  	ctx, cancel := context.WithCancel(ctx)
   389  	time.AfterFunc(time.Second, cancel)
   390  
   391  	res, err := instAction.RunWithContext(ctx, buildChart(), vals)
   392  	is.Error(err)
   393  	is.Contains(res.Info.Description, "Release \"interrupted-release\" failed: context canceled")
   394  	is.Equal(res.Info.Status, release.StatusFailed)
   395  }
   396  func TestInstallRelease_WaitForJobs(t *testing.T) {
   397  	is := assert.New(t)
   398  	instAction := installAction(t)
   399  	instAction.ReleaseName = "come-fail-away"
   400  	failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   401  	failer.WaitError = fmt.Errorf("I timed out")
   402  	instAction.cfg.KubeClient = failer
   403  	instAction.Wait = true
   404  	instAction.WaitForJobs = true
   405  	vals := map[string]interface{}{}
   406  
   407  	res, err := instAction.Run(buildChart(), vals)
   408  	is.Error(err)
   409  	is.Contains(res.Info.Description, "I timed out")
   410  	is.Equal(res.Info.Status, release.StatusFailed)
   411  }
   412  
   413  func TestInstallRelease_Atomic(t *testing.T) {
   414  	is := assert.New(t)
   415  
   416  	t.Run("atomic uninstall succeeds", func(t *testing.T) {
   417  		instAction := installAction(t)
   418  		instAction.ReleaseName = "come-fail-away"
   419  		failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   420  		failer.WaitError = fmt.Errorf("I timed out")
   421  		instAction.cfg.KubeClient = failer
   422  		instAction.Atomic = true
   423  		vals := map[string]interface{}{}
   424  
   425  		res, err := instAction.Run(buildChart(), vals)
   426  		is.Error(err)
   427  		is.Contains(err.Error(), "I timed out")
   428  		is.Contains(err.Error(), "atomic")
   429  
   430  		// Now make sure it isn't in storage any more
   431  		_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
   432  		is.Error(err)
   433  		is.Equal(err, driver.ErrReleaseNotFound)
   434  	})
   435  
   436  	t.Run("atomic uninstall fails", func(t *testing.T) {
   437  		instAction := installAction(t)
   438  		instAction.ReleaseName = "come-fail-away-with-me"
   439  		failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   440  		failer.WaitError = fmt.Errorf("I timed out")
   441  		failer.DeleteError = fmt.Errorf("uninstall fail")
   442  		instAction.cfg.KubeClient = failer
   443  		instAction.Atomic = true
   444  		vals := map[string]interface{}{}
   445  
   446  		_, err := instAction.Run(buildChart(), vals)
   447  		is.Error(err)
   448  		is.Contains(err.Error(), "I timed out")
   449  		is.Contains(err.Error(), "uninstall fail")
   450  		is.Contains(err.Error(), "an error occurred while uninstalling the release")
   451  	})
   452  }
   453  func TestInstallRelease_Atomic_Interrupted(t *testing.T) {
   454  
   455  	is := assert.New(t)
   456  	instAction := installAction(t)
   457  	instAction.ReleaseName = "interrupted-release"
   458  	failer := instAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
   459  	failer.WaitDuration = 10 * time.Second
   460  	instAction.cfg.KubeClient = failer
   461  	instAction.Atomic = true
   462  	vals := map[string]interface{}{}
   463  
   464  	ctx := context.Background()
   465  	ctx, cancel := context.WithCancel(ctx)
   466  	time.AfterFunc(time.Second, cancel)
   467  
   468  	res, err := instAction.RunWithContext(ctx, buildChart(), vals)
   469  	is.Error(err)
   470  	is.Contains(err.Error(), "context canceled")
   471  	is.Contains(err.Error(), "atomic")
   472  	is.Contains(err.Error(), "uninstalled")
   473  
   474  	// Now make sure it isn't in storage any more
   475  	_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
   476  	is.Error(err)
   477  	is.Equal(err, driver.ErrReleaseNotFound)
   478  
   479  }
   480  func TestNameTemplate(t *testing.T) {
   481  	testCases := []nameTemplateTestCase{
   482  		// Just a straight up nop please
   483  		{
   484  			tpl:              "foobar",
   485  			expected:         "foobar",
   486  			expectedErrorStr: "",
   487  		},
   488  		// Random numbers at the end for fun & profit
   489  		{
   490  			tpl:              "foobar-{{randNumeric 6}}",
   491  			expected:         "foobar-[0-9]{6}$",
   492  			expectedErrorStr: "",
   493  		},
   494  		// Random numbers in the middle for fun & profit
   495  		{
   496  			tpl:              "foobar-{{randNumeric 4}}-baz",
   497  			expected:         "foobar-[0-9]{4}-baz$",
   498  			expectedErrorStr: "",
   499  		},
   500  		// No such function
   501  		{
   502  			tpl:              "foobar-{{randInteger}}",
   503  			expected:         "",
   504  			expectedErrorStr: "function \"randInteger\" not defined",
   505  		},
   506  		// Invalid template
   507  		{
   508  			tpl:              "foobar-{{",
   509  			expected:         "",
   510  			expectedErrorStr: "template: name-template:1: unclosed action",
   511  		},
   512  	}
   513  
   514  	for _, tc := range testCases {
   515  
   516  		n, err := TemplateName(tc.tpl)
   517  		if err != nil {
   518  			if tc.expectedErrorStr == "" {
   519  				t.Errorf("Was not expecting error, but got: %v", err)
   520  				continue
   521  			}
   522  			re, compErr := regexp.Compile(tc.expectedErrorStr)
   523  			if compErr != nil {
   524  				t.Errorf("Expected error string failed to compile: %v", compErr)
   525  				continue
   526  			}
   527  			if !re.MatchString(err.Error()) {
   528  				t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err)
   529  				continue
   530  			}
   531  		}
   532  		if err == nil && tc.expectedErrorStr != "" {
   533  			t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr)
   534  		}
   535  
   536  		if tc.expected != "" {
   537  			re, err := regexp.Compile(tc.expected)
   538  			if err != nil {
   539  				t.Errorf("Expected string failed to compile: %v", err)
   540  				continue
   541  			}
   542  			if !re.MatchString(n) {
   543  				t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n)
   544  			}
   545  		}
   546  	}
   547  }
   548  
   549  func TestInstallReleaseOutputDir(t *testing.T) {
   550  	is := assert.New(t)
   551  	instAction := installAction(t)
   552  	vals := map[string]interface{}{}
   553  
   554  	dir := t.TempDir()
   555  
   556  	instAction.OutputDir = dir
   557  
   558  	_, err := instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals)
   559  	if err != nil {
   560  		t.Fatalf("Failed install: %s", err)
   561  	}
   562  
   563  	_, err = os.Stat(filepath.Join(dir, "hello/templates/goodbye"))
   564  	is.NoError(err)
   565  
   566  	_, err = os.Stat(filepath.Join(dir, "hello/templates/hello"))
   567  	is.NoError(err)
   568  
   569  	_, err = os.Stat(filepath.Join(dir, "hello/templates/with-partials"))
   570  	is.NoError(err)
   571  
   572  	_, err = os.Stat(filepath.Join(dir, "hello/templates/rbac"))
   573  	is.NoError(err)
   574  
   575  	test.AssertGoldenFile(t, filepath.Join(dir, "hello/templates/rbac"), "rbac.txt")
   576  
   577  	_, err = os.Stat(filepath.Join(dir, "hello/templates/empty"))
   578  	is.True(os.IsNotExist(err))
   579  }
   580  
   581  func TestInstallOutputDirWithReleaseName(t *testing.T) {
   582  	is := assert.New(t)
   583  	instAction := installAction(t)
   584  	vals := map[string]interface{}{}
   585  
   586  	dir := t.TempDir()
   587  
   588  	instAction.OutputDir = dir
   589  	instAction.UseReleaseName = true
   590  	instAction.ReleaseName = "madra"
   591  
   592  	newDir := filepath.Join(dir, instAction.ReleaseName)
   593  
   594  	_, err := instAction.Run(buildChart(withSampleTemplates(), withMultipleManifestTemplate()), vals)
   595  	if err != nil {
   596  		t.Fatalf("Failed install: %s", err)
   597  	}
   598  
   599  	_, err = os.Stat(filepath.Join(newDir, "hello/templates/goodbye"))
   600  	is.NoError(err)
   601  
   602  	_, err = os.Stat(filepath.Join(newDir, "hello/templates/hello"))
   603  	is.NoError(err)
   604  
   605  	_, err = os.Stat(filepath.Join(newDir, "hello/templates/with-partials"))
   606  	is.NoError(err)
   607  
   608  	_, err = os.Stat(filepath.Join(newDir, "hello/templates/rbac"))
   609  	is.NoError(err)
   610  
   611  	test.AssertGoldenFile(t, filepath.Join(newDir, "hello/templates/rbac"), "rbac.txt")
   612  
   613  	_, err = os.Stat(filepath.Join(newDir, "hello/templates/empty"))
   614  	is.True(os.IsNotExist(err))
   615  }
   616  
   617  func TestNameAndChart(t *testing.T) {
   618  	is := assert.New(t)
   619  	instAction := installAction(t)
   620  	chartName := "./foo"
   621  
   622  	name, chrt, err := instAction.NameAndChart([]string{chartName})
   623  	if err != nil {
   624  		t.Fatal(err)
   625  	}
   626  	is.Equal(instAction.ReleaseName, name)
   627  	is.Equal(chartName, chrt)
   628  
   629  	instAction.GenerateName = true
   630  	_, _, err = instAction.NameAndChart([]string{"foo", chartName})
   631  	if err == nil {
   632  		t.Fatal("expected an error")
   633  	}
   634  	is.Equal("cannot set --generate-name and also specify a name", err.Error())
   635  
   636  	instAction.GenerateName = false
   637  	instAction.NameTemplate = "{{ . }}"
   638  	_, _, err = instAction.NameAndChart([]string{"foo", chartName})
   639  	if err == nil {
   640  		t.Fatal("expected an error")
   641  	}
   642  	is.Equal("cannot set --name-template and also specify a name", err.Error())
   643  
   644  	instAction.NameTemplate = ""
   645  	instAction.ReleaseName = ""
   646  	_, _, err = instAction.NameAndChart([]string{chartName})
   647  	if err == nil {
   648  		t.Fatal("expected an error")
   649  	}
   650  	is.Equal("must either provide a name or specify --generate-name", err.Error())
   651  
   652  	instAction.NameTemplate = ""
   653  	instAction.ReleaseName = ""
   654  	_, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"})
   655  	if err == nil {
   656  		t.Fatal("expected an error")
   657  	}
   658  	is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error())
   659  }
   660  
   661  func TestNameAndChartGenerateName(t *testing.T) {
   662  	is := assert.New(t)
   663  	instAction := installAction(t)
   664  
   665  	instAction.ReleaseName = ""
   666  	instAction.GenerateName = true
   667  
   668  	tests := []struct {
   669  		Name         string
   670  		Chart        string
   671  		ExpectedName string
   672  	}{
   673  		{
   674  			"local filepath",
   675  			"./chart",
   676  			fmt.Sprintf("chart-%d", helmtime.Now().Unix()),
   677  		},
   678  		{
   679  			"dot filepath",
   680  			".",
   681  			fmt.Sprintf("chart-%d", helmtime.Now().Unix()),
   682  		},
   683  		{
   684  			"empty filepath",
   685  			"",
   686  			fmt.Sprintf("chart-%d", helmtime.Now().Unix()),
   687  		},
   688  		{
   689  			"packaged chart",
   690  			"chart.tgz",
   691  			fmt.Sprintf("chart-%d", helmtime.Now().Unix()),
   692  		},
   693  		{
   694  			"packaged chart with .tar.gz extension",
   695  			"chart.tar.gz",
   696  			fmt.Sprintf("chart-%d", helmtime.Now().Unix()),
   697  		},
   698  		{
   699  			"packaged chart with local extension",
   700  			"./chart.tgz",
   701  			fmt.Sprintf("chart-%d", helmtime.Now().Unix()),
   702  		},
   703  	}
   704  
   705  	for _, tc := range tests {
   706  		tc := tc
   707  		t.Run(tc.Name, func(t *testing.T) {
   708  			t.Parallel()
   709  
   710  			name, chrt, err := instAction.NameAndChart([]string{tc.Chart})
   711  			if err != nil {
   712  				t.Fatal(err)
   713  			}
   714  
   715  			is.Equal(tc.ExpectedName, name)
   716  			is.Equal(tc.Chart, chrt)
   717  		})
   718  	}
   719  }