github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/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  	"fmt"
    21  	"reflect"
    22  	"regexp"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  
    27  	"helm.sh/helm/pkg/release"
    28  )
    29  
    30  type nameTemplateTestCase struct {
    31  	tpl              string
    32  	expected         string
    33  	expectedErrorStr string
    34  }
    35  
    36  func installAction(t *testing.T) *Install {
    37  	config := actionConfigFixture(t)
    38  	instAction := NewInstall(config)
    39  	instAction.Namespace = "spaced"
    40  	instAction.ReleaseName = "test-install-release"
    41  
    42  	return instAction
    43  }
    44  
    45  func TestInstallRelease(t *testing.T) {
    46  	is := assert.New(t)
    47  	instAction := installAction(t)
    48  	instAction.rawValues = map[string]interface{}{}
    49  	res, err := instAction.Run(buildChart())
    50  	if err != nil {
    51  		t.Fatalf("Failed install: %s", err)
    52  	}
    53  	is.Equal(res.Name, "test-install-release", "Expected release name.")
    54  	is.Equal(res.Namespace, "spaced")
    55  
    56  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
    57  	is.NoError(err)
    58  
    59  	is.Len(rel.Hooks, 1)
    60  	is.Equal(rel.Hooks[0].Manifest, manifestWithHook)
    61  	is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall)
    62  	is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete")
    63  
    64  	is.NotEqual(len(res.Manifest), 0)
    65  	is.NotEqual(len(rel.Manifest), 0)
    66  	is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
    67  	is.Equal(rel.Info.Description, "Install complete")
    68  }
    69  
    70  func TestInstallRelease_NoName(t *testing.T) {
    71  	instAction := installAction(t)
    72  	instAction.ReleaseName = ""
    73  	instAction.rawValues = map[string]interface{}{}
    74  	_, err := instAction.Run(buildChart())
    75  	if err == nil {
    76  		t.Fatal("expected failure when no name is specified")
    77  	}
    78  	assert.Contains(t, err.Error(), "name is required")
    79  }
    80  
    81  func TestInstallRelease_WithNotes(t *testing.T) {
    82  	is := assert.New(t)
    83  	instAction := installAction(t)
    84  	instAction.ReleaseName = "with-notes"
    85  	instAction.rawValues = map[string]interface{}{}
    86  	res, err := instAction.Run(buildChart(withNotes("note here")))
    87  	if err != nil {
    88  		t.Fatalf("Failed install: %s", err)
    89  	}
    90  
    91  	is.Equal(res.Name, "with-notes")
    92  	is.Equal(res.Namespace, "spaced")
    93  
    94  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
    95  	is.NoError(err)
    96  	is.Len(rel.Hooks, 1)
    97  	is.Equal(rel.Hooks[0].Manifest, manifestWithHook)
    98  	is.Equal(rel.Hooks[0].Events[0], release.HookPostInstall)
    99  	is.Equal(rel.Hooks[0].Events[1], release.HookPreDelete, "Expected event 0 is pre-delete")
   100  	is.NotEqual(len(res.Manifest), 0)
   101  	is.NotEqual(len(rel.Manifest), 0)
   102  	is.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
   103  	is.Equal(rel.Info.Description, "Install complete")
   104  
   105  	is.Equal(rel.Info.Notes, "note here")
   106  }
   107  
   108  func TestInstallRelease_WithNotesRendered(t *testing.T) {
   109  	is := assert.New(t)
   110  	instAction := installAction(t)
   111  	instAction.ReleaseName = "with-notes"
   112  	instAction.rawValues = map[string]interface{}{}
   113  	res, err := instAction.Run(buildChart(withNotes("got-{{.Release.Name}}")))
   114  	if err != nil {
   115  		t.Fatalf("Failed install: %s", err)
   116  	}
   117  
   118  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   119  	is.NoError(err)
   120  
   121  	expectedNotes := fmt.Sprintf("got-%s", res.Name)
   122  	is.Equal(expectedNotes, rel.Info.Notes)
   123  	is.Equal(rel.Info.Description, "Install complete")
   124  }
   125  
   126  func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) {
   127  	// Regression: Make sure that the child's notes don't override the parent's
   128  	is := assert.New(t)
   129  	instAction := installAction(t)
   130  	instAction.ReleaseName = "with-notes"
   131  	instAction.rawValues = map[string]interface{}{}
   132  	res, err := instAction.Run(buildChart(withNotes("parent"), withDependency(withNotes("child"))))
   133  	if err != nil {
   134  		t.Fatalf("Failed install: %s", err)
   135  	}
   136  
   137  	rel, err := instAction.cfg.Releases.Get(res.Name, res.Version)
   138  	is.Equal("with-notes", rel.Name)
   139  	is.NoError(err)
   140  	is.Equal("parent", rel.Info.Notes)
   141  	is.Equal(rel.Info.Description, "Install complete")
   142  }
   143  
   144  func TestInstallRelease_DryRun(t *testing.T) {
   145  	is := assert.New(t)
   146  	instAction := installAction(t)
   147  	instAction.DryRun = true
   148  	instAction.rawValues = map[string]interface{}{}
   149  	res, err := instAction.Run(buildChart(withSampleTemplates()))
   150  	if err != nil {
   151  		t.Fatalf("Failed install: %s", err)
   152  	}
   153  
   154  	is.Contains(res.Manifest, "---\n# Source: hello/templates/hello\nhello: world")
   155  	is.Contains(res.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world")
   156  	is.Contains(res.Manifest, "hello: Earth")
   157  	is.NotContains(res.Manifest, "hello: {{ template \"_planet\" . }}")
   158  	is.NotContains(res.Manifest, "empty")
   159  
   160  	_, err = instAction.cfg.Releases.Get(res.Name, res.Version)
   161  	is.Error(err)
   162  	is.Len(res.Hooks, 1)
   163  	is.True(res.Hooks[0].LastRun.IsZero(), "expect hook to not be marked as run")
   164  	is.Equal(res.Info.Description, "Dry run complete")
   165  }
   166  
   167  func TestInstallRelease_NoHooks(t *testing.T) {
   168  	is := assert.New(t)
   169  	instAction := installAction(t)
   170  	instAction.DisableHooks = true
   171  	instAction.ReleaseName = "no-hooks"
   172  	instAction.cfg.Releases.Create(releaseStub())
   173  
   174  	instAction.rawValues = map[string]interface{}{}
   175  	res, err := instAction.Run(buildChart())
   176  	if err != nil {
   177  		t.Fatalf("Failed install: %s", err)
   178  	}
   179  
   180  	is.True(res.Hooks[0].LastRun.IsZero(), "hooks should not run with no-hooks")
   181  }
   182  
   183  func TestInstallRelease_FailedHooks(t *testing.T) {
   184  	is := assert.New(t)
   185  	instAction := installAction(t)
   186  	instAction.ReleaseName = "failed-hooks"
   187  	instAction.cfg.KubeClient = newHookFailingKubeClient()
   188  
   189  	instAction.rawValues = map[string]interface{}{}
   190  	res, err := instAction.Run(buildChart())
   191  	is.Error(err)
   192  	is.Contains(res.Info.Description, "failed post-install")
   193  	is.Equal(res.Info.Status, release.StatusFailed)
   194  }
   195  
   196  func TestInstallRelease_ReplaceRelease(t *testing.T) {
   197  	is := assert.New(t)
   198  	instAction := installAction(t)
   199  	instAction.Replace = true
   200  
   201  	rel := releaseStub()
   202  	rel.Info.Status = release.StatusUninstalled
   203  	instAction.cfg.Releases.Create(rel)
   204  	instAction.ReleaseName = rel.Name
   205  
   206  	instAction.rawValues = map[string]interface{}{}
   207  	res, err := instAction.Run(buildChart())
   208  	is.NoError(err)
   209  
   210  	// This should have been auto-incremented
   211  	is.Equal(2, res.Version)
   212  	is.Equal(res.Name, rel.Name)
   213  
   214  	getres, err := instAction.cfg.Releases.Get(rel.Name, res.Version)
   215  	is.NoError(err)
   216  	is.Equal(getres.Info.Status, release.StatusDeployed)
   217  }
   218  
   219  func TestInstallRelease_KubeVersion(t *testing.T) {
   220  	is := assert.New(t)
   221  	instAction := installAction(t)
   222  	instAction.rawValues = map[string]interface{}{}
   223  	_, err := instAction.Run(buildChart(withKube(">=0.0.0")))
   224  	is.NoError(err)
   225  
   226  	// This should fail for a few hundred years
   227  	instAction.ReleaseName = "should-fail"
   228  	instAction.rawValues = map[string]interface{}{}
   229  	_, err = instAction.Run(buildChart(withKube(">=99.0.0")))
   230  	is.Error(err)
   231  	is.Contains(err.Error(), "chart requires kubernetesVersion")
   232  }
   233  
   234  func TestNameTemplate(t *testing.T) {
   235  	testCases := []nameTemplateTestCase{
   236  		// Just a straight up nop please
   237  		{
   238  			tpl:              "foobar",
   239  			expected:         "foobar",
   240  			expectedErrorStr: "",
   241  		},
   242  		// Random numbers at the end for fun & profit
   243  		{
   244  			tpl:              "foobar-{{randNumeric 6}}",
   245  			expected:         "foobar-[0-9]{6}$",
   246  			expectedErrorStr: "",
   247  		},
   248  		// Random numbers in the middle for fun & profit
   249  		{
   250  			tpl:              "foobar-{{randNumeric 4}}-baz",
   251  			expected:         "foobar-[0-9]{4}-baz$",
   252  			expectedErrorStr: "",
   253  		},
   254  		// No such function
   255  		{
   256  			tpl:              "foobar-{{randInt}}",
   257  			expected:         "",
   258  			expectedErrorStr: "function \"randInt\" not defined",
   259  		},
   260  		// Invalid template
   261  		{
   262  			tpl:              "foobar-{{",
   263  			expected:         "",
   264  			expectedErrorStr: "unexpected unclosed action",
   265  		},
   266  	}
   267  
   268  	for _, tc := range testCases {
   269  
   270  		n, err := TemplateName(tc.tpl)
   271  		if err != nil {
   272  			if tc.expectedErrorStr == "" {
   273  				t.Errorf("Was not expecting error, but got: %v", err)
   274  				continue
   275  			}
   276  			re, compErr := regexp.Compile(tc.expectedErrorStr)
   277  			if compErr != nil {
   278  				t.Errorf("Expected error string failed to compile: %v", compErr)
   279  				continue
   280  			}
   281  			if !re.MatchString(err.Error()) {
   282  				t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err)
   283  				continue
   284  			}
   285  		}
   286  		if err == nil && tc.expectedErrorStr != "" {
   287  			t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr)
   288  		}
   289  
   290  		if tc.expected != "" {
   291  			re, err := regexp.Compile(tc.expected)
   292  			if err != nil {
   293  				t.Errorf("Expected string failed to compile: %v", err)
   294  				continue
   295  			}
   296  			if !re.MatchString(n) {
   297  				t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n)
   298  			}
   299  		}
   300  	}
   301  }
   302  
   303  func TestMergeValues(t *testing.T) {
   304  	nestedMap := map[string]interface{}{
   305  		"foo": "bar",
   306  		"baz": map[string]string{
   307  			"cool": "stuff",
   308  		},
   309  	}
   310  	anotherNestedMap := map[string]interface{}{
   311  		"foo": "bar",
   312  		"baz": map[string]string{
   313  			"cool":    "things",
   314  			"awesome": "stuff",
   315  		},
   316  	}
   317  	flatMap := map[string]interface{}{
   318  		"foo": "bar",
   319  		"baz": "stuff",
   320  	}
   321  	anotherFlatMap := map[string]interface{}{
   322  		"testing": "fun",
   323  	}
   324  
   325  	testMap := mergeValues(flatMap, nestedMap)
   326  	equal := reflect.DeepEqual(testMap, nestedMap)
   327  	if !equal {
   328  		t.Errorf("Expected a nested map to overwrite a flat value. Expected: %v, got %v", nestedMap, testMap)
   329  	}
   330  
   331  	testMap = mergeValues(nestedMap, flatMap)
   332  	equal = reflect.DeepEqual(testMap, flatMap)
   333  	if !equal {
   334  		t.Errorf("Expected a flat value to overwrite a map. Expected: %v, got %v", flatMap, testMap)
   335  	}
   336  
   337  	testMap = mergeValues(nestedMap, anotherNestedMap)
   338  	equal = reflect.DeepEqual(testMap, anotherNestedMap)
   339  	if !equal {
   340  		t.Errorf("Expected a nested map to overwrite another nested map. Expected: %v, got %v", anotherNestedMap, testMap)
   341  	}
   342  
   343  	testMap = mergeValues(anotherFlatMap, anotherNestedMap)
   344  	expectedMap := map[string]interface{}{
   345  		"testing": "fun",
   346  		"foo":     "bar",
   347  		"baz": map[string]string{
   348  			"cool":    "things",
   349  			"awesome": "stuff",
   350  		},
   351  	}
   352  	equal = reflect.DeepEqual(testMap, expectedMap)
   353  	if !equal {
   354  		t.Errorf("Expected a map with different keys to merge properly with another map. Expected: %v, got %v", expectedMap, testMap)
   355  	}
   356  }