github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/pkg/engine/engine_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 engine
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  
    25  	"helm.sh/helm/pkg/chart"
    26  	"helm.sh/helm/pkg/chartutil"
    27  )
    28  
    29  func TestSortTemplates(t *testing.T) {
    30  	tpls := map[string]renderable{
    31  		"/mychart/templates/foo.tpl":                                 {},
    32  		"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {},
    33  		"/mychart/templates/bar.tpl":                                 {},
    34  		"/mychart/templates/charts/foo/templates/bar.tpl":            {},
    35  		"/mychart/templates/_foo.tpl":                                {},
    36  		"/mychart/templates/charts/foo/templates/foo.tpl":            {},
    37  		"/mychart/templates/charts/bar/templates/foo.tpl":            {},
    38  	}
    39  	got := sortTemplates(tpls)
    40  	if len(got) != len(tpls) {
    41  		t.Fatal("Sorted results are missing templates")
    42  	}
    43  
    44  	expect := []string{
    45  		"/mychart/templates/charts/foo/charts/bar/templates/foo.tpl",
    46  		"/mychart/templates/charts/foo/templates/foo.tpl",
    47  		"/mychart/templates/charts/foo/templates/bar.tpl",
    48  		"/mychart/templates/charts/bar/templates/foo.tpl",
    49  		"/mychart/templates/foo.tpl",
    50  		"/mychart/templates/bar.tpl",
    51  		"/mychart/templates/_foo.tpl",
    52  	}
    53  	for i, e := range expect {
    54  		if got[i] != e {
    55  			t.Fatalf("\n\tExp:\n%s\n\tGot:\n%s",
    56  				strings.Join(expect, "\n"),
    57  				strings.Join(got, "\n"),
    58  			)
    59  		}
    60  	}
    61  }
    62  
    63  func TestFuncMap(t *testing.T) {
    64  	fns := funcMap()
    65  	forbidden := []string{"env", "expandenv"}
    66  	for _, f := range forbidden {
    67  		if _, ok := fns[f]; ok {
    68  			t.Errorf("Forbidden function %s exists in FuncMap.", f)
    69  		}
    70  	}
    71  
    72  	// Test for Engine-specific template functions.
    73  	expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"}
    74  	for _, f := range expect {
    75  		if _, ok := fns[f]; !ok {
    76  			t.Errorf("Expected add-on function %q", f)
    77  		}
    78  	}
    79  }
    80  
    81  func TestRender(t *testing.T) {
    82  	c := &chart.Chart{
    83  		Metadata: &chart.Metadata{
    84  			Name:    "moby",
    85  			Version: "1.2.3",
    86  		},
    87  		Templates: []*chart.File{
    88  			{Name: "templates/test1", Data: []byte("{{.Values.outer | title }} {{.Values.inner | title}}")},
    89  			{Name: "templates/test2", Data: []byte("{{.Values.global.callme | lower }}")},
    90  			{Name: "templates/test3", Data: []byte("{{.noValue}}")},
    91  			{Name: "templates/test4", Data: []byte("{{toJson .Values}}")},
    92  		},
    93  		Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"},
    94  	}
    95  
    96  	vals := map[string]interface{}{
    97  		"Values": map[string]interface{}{
    98  			"outer": "spouter",
    99  			"inner": "inn",
   100  			"global": map[string]interface{}{
   101  				"callme": "Ishmael",
   102  			},
   103  		},
   104  	}
   105  
   106  	v, err := chartutil.CoalesceValues(c, vals)
   107  	if err != nil {
   108  		t.Fatalf("Failed to coalesce values: %s", err)
   109  	}
   110  	out, err := Render(c, v)
   111  	if err != nil {
   112  		t.Errorf("Failed to render templates: %s", err)
   113  	}
   114  
   115  	expect := map[string]string{
   116  		"moby/templates/test1": "Spouter Inn",
   117  		"moby/templates/test2": "ishmael",
   118  		"moby/templates/test3": "",
   119  		"moby/templates/test4": `{"global":{"callme":"Ishmael"},"inner":"inn","outer":"spouter"}`,
   120  	}
   121  
   122  	for name, data := range expect {
   123  		if out[name] != data {
   124  			t.Errorf("Expected %q, got %q", data, out[name])
   125  		}
   126  	}
   127  }
   128  
   129  func TestRenderInternals(t *testing.T) {
   130  	// Test the internals of the rendering tool.
   131  
   132  	vals := chartutil.Values{"Name": "one", "Value": "two"}
   133  	tpls := map[string]renderable{
   134  		"one": {tpl: `Hello {{title .Name}}`, vals: vals},
   135  		"two": {tpl: `Goodbye {{upper .Value}}`, vals: vals},
   136  		// Test whether a template can reliably reference another template
   137  		// without regard for ordering.
   138  		"three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals},
   139  	}
   140  
   141  	out, err := new(Engine).render(tpls)
   142  	if err != nil {
   143  		t.Fatalf("Failed template rendering: %s", err)
   144  	}
   145  
   146  	if len(out) != 3 {
   147  		t.Fatalf("Expected 3 templates, got %d", len(out))
   148  	}
   149  
   150  	if out["one"] != "Hello One" {
   151  		t.Errorf("Expected 'Hello One', got %q", out["one"])
   152  	}
   153  
   154  	if out["two"] != "Goodbye TWO" {
   155  		t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"])
   156  	}
   157  
   158  	if out["three"] != "Goodbye THREE" {
   159  		t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"])
   160  	}
   161  }
   162  
   163  func TestParallelRenderInternals(t *testing.T) {
   164  	// Make sure that we can use one Engine to run parallel template renders.
   165  	e := new(Engine)
   166  	var wg sync.WaitGroup
   167  	for i := 0; i < 20; i++ {
   168  		wg.Add(1)
   169  		go func(i int) {
   170  			tt := fmt.Sprintf("expect-%d", i)
   171  			tpls := map[string]renderable{
   172  				"t": {
   173  					tpl:  `{{.val}}`,
   174  					vals: map[string]interface{}{"val": tt},
   175  				},
   176  			}
   177  			out, err := e.render(tpls)
   178  			if err != nil {
   179  				t.Errorf("Failed to render %s: %s", tt, err)
   180  			}
   181  			if out["t"] != tt {
   182  				t.Errorf("Expected %q, got %q", tt, out["t"])
   183  			}
   184  			wg.Done()
   185  		}(i)
   186  	}
   187  	wg.Wait()
   188  }
   189  
   190  func TestRenderErrors(t *testing.T) {
   191  	vals := chartutil.Values{"Values": map[string]interface{}{}}
   192  
   193  	tplsMissingRequired := map[string]renderable{
   194  		"missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals},
   195  	}
   196  	_, err := new(Engine).render(tplsMissingRequired)
   197  	if err == nil {
   198  		t.Fatalf("Expected failures while rendering: %s", err)
   199  	}
   200  	expected := `render error at (missing_required:1:2): foo is required`
   201  	if err.Error() != expected {
   202  		t.Errorf("Expected '%s', got %q", expected, err.Error())
   203  	}
   204  
   205  	tplsUndefinedFunction := map[string]renderable{
   206  		"undefined_function": {tpl: `{{foo}}`, vals: vals},
   207  	}
   208  	_, err = new(Engine).render(tplsUndefinedFunction)
   209  	if err == nil {
   210  		t.Fatalf("Expected failures while rendering: %s", err)
   211  	}
   212  	expected = `render error at (undefined_function:1): function "foo" not defined`
   213  	if err.Error() != expected {
   214  		t.Errorf("Expected '%s', got %q", expected, err.Error())
   215  	}
   216  }
   217  func TestAllTemplates(t *testing.T) {
   218  	ch1 := &chart.Chart{
   219  		Metadata: &chart.Metadata{Name: "ch1"},
   220  		Templates: []*chart.File{
   221  			{Name: "templates/foo", Data: []byte("foo")},
   222  			{Name: "templates/bar", Data: []byte("bar")},
   223  		},
   224  	}
   225  	dep1 := &chart.Chart{
   226  		Metadata: &chart.Metadata{Name: "laboratory mice"},
   227  		Templates: []*chart.File{
   228  			{Name: "templates/pinky", Data: []byte("pinky")},
   229  			{Name: "templates/brain", Data: []byte("brain")},
   230  		},
   231  	}
   232  	ch1.AddDependency(dep1)
   233  
   234  	dep2 := &chart.Chart{
   235  		Metadata: &chart.Metadata{Name: "same thing we do every night"},
   236  		Templates: []*chart.File{
   237  			{Name: "templates/innermost", Data: []byte("innermost")},
   238  		},
   239  	}
   240  	dep1.AddDependency(dep2)
   241  
   242  	tpls := allTemplates(ch1, chartutil.Values{})
   243  	if len(tpls) != 5 {
   244  		t.Errorf("Expected 5 charts, got %d", len(tpls))
   245  	}
   246  }
   247  
   248  func TestRenderDependency(t *testing.T) {
   249  	deptpl := `{{define "myblock"}}World{{end}}`
   250  	toptpl := `Hello {{template "myblock"}}`
   251  	ch := &chart.Chart{
   252  		Metadata: &chart.Metadata{Name: "outerchart"},
   253  		Templates: []*chart.File{
   254  			{Name: "templates/outer", Data: []byte(toptpl)},
   255  		},
   256  	}
   257  	ch.AddDependency(&chart.Chart{
   258  		Metadata: &chart.Metadata{Name: "innerchart"},
   259  		Templates: []*chart.File{
   260  			{Name: "templates/inner", Data: []byte(deptpl)},
   261  		},
   262  	})
   263  
   264  	out, err := Render(ch, map[string]interface{}{})
   265  	if err != nil {
   266  		t.Fatalf("failed to render chart: %s", err)
   267  	}
   268  
   269  	if len(out) != 2 {
   270  		t.Errorf("Expected 2, got %d", len(out))
   271  	}
   272  
   273  	expect := "Hello World"
   274  	if out["outerchart/templates/outer"] != expect {
   275  		t.Errorf("Expected %q, got %q", expect, out["outer"])
   276  	}
   277  
   278  }
   279  
   280  func TestRenderNestedValues(t *testing.T) {
   281  	innerpath := "templates/inner.tpl"
   282  	outerpath := "templates/outer.tpl"
   283  	// Ensure namespacing rules are working.
   284  	deepestpath := "templates/inner.tpl"
   285  	checkrelease := "templates/release.tpl"
   286  
   287  	deepest := &chart.Chart{
   288  		Metadata: &chart.Metadata{Name: "deepest"},
   289  		Templates: []*chart.File{
   290  			{Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)},
   291  			{Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)},
   292  		},
   293  		Values: map[string]interface{}{"what": "milkshake"},
   294  	}
   295  
   296  	inner := &chart.Chart{
   297  		Metadata: &chart.Metadata{Name: "herrick"},
   298  		Templates: []*chart.File{
   299  			{Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)},
   300  		},
   301  		Values: map[string]interface{}{"who": "Robert"},
   302  	}
   303  	inner.AddDependency(deepest)
   304  
   305  	outer := &chart.Chart{
   306  		Metadata: &chart.Metadata{Name: "top"},
   307  		Templates: []*chart.File{
   308  			{Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)},
   309  		},
   310  		Values: map[string]interface{}{
   311  			"what": "stinkweed",
   312  			"who":  "me",
   313  			"herrick": map[string]interface{}{
   314  				"who": "time",
   315  			},
   316  		},
   317  	}
   318  	outer.AddDependency(inner)
   319  
   320  	injValues := map[string]interface{}{
   321  		"what": "rosebuds",
   322  		"herrick": map[string]interface{}{
   323  			"deepest": map[string]interface{}{
   324  				"what": "flower",
   325  			},
   326  		},
   327  		"global": map[string]interface{}{
   328  			"when": "to-day",
   329  		},
   330  	}
   331  
   332  	tmp, err := chartutil.CoalesceValues(outer, injValues)
   333  	if err != nil {
   334  		t.Fatalf("Failed to coalesce values: %s", err)
   335  	}
   336  
   337  	inject := chartutil.Values{
   338  		"Values": tmp,
   339  		"Chart":  outer.Metadata,
   340  		"Release": chartutil.Values{
   341  			"Name": "dyin",
   342  		},
   343  	}
   344  
   345  	t.Logf("Calculated values: %v", inject)
   346  
   347  	out, err := Render(outer, inject)
   348  	if err != nil {
   349  		t.Fatalf("failed to render templates: %s", err)
   350  	}
   351  
   352  	fullouterpath := "top/" + outerpath
   353  	if out[fullouterpath] != "Gather ye rosebuds while ye may" {
   354  		t.Errorf("Unexpected outer: %q", out[fullouterpath])
   355  	}
   356  
   357  	fullinnerpath := "top/charts/herrick/" + innerpath
   358  	if out[fullinnerpath] != "Old time is still a-flyin'" {
   359  		t.Errorf("Unexpected inner: %q", out[fullinnerpath])
   360  	}
   361  
   362  	fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath
   363  	if out[fulldeepestpath] != "And this same flower that smiles to-day" {
   364  		t.Errorf("Unexpected deepest: %q", out[fulldeepestpath])
   365  	}
   366  
   367  	fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease
   368  	if out[fullcheckrelease] != "Tomorrow will be dyin" {
   369  		t.Errorf("Unexpected release: %q", out[fullcheckrelease])
   370  	}
   371  }
   372  
   373  func TestRenderBuiltinValues(t *testing.T) {
   374  	inner := &chart.Chart{
   375  		Metadata: &chart.Metadata{Name: "Latium"},
   376  		Templates: []*chart.File{
   377  			{Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   378  			{Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)},
   379  		},
   380  		Files: []*chart.File{
   381  			{Name: "author", Data: []byte("Virgil")},
   382  			{Name: "book/title.txt", Data: []byte("Aeneid")},
   383  		},
   384  	}
   385  
   386  	outer := &chart.Chart{
   387  		Metadata: &chart.Metadata{Name: "Troy"},
   388  		Templates: []*chart.File{
   389  			{Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   390  		},
   391  	}
   392  	outer.AddDependency(inner)
   393  
   394  	inject := chartutil.Values{
   395  		"Values": "",
   396  		"Chart":  outer.Metadata,
   397  		"Release": chartutil.Values{
   398  			"Name": "Aeneid",
   399  		},
   400  	}
   401  
   402  	t.Logf("Calculated values: %v", outer)
   403  
   404  	out, err := Render(outer, inject)
   405  	if err != nil {
   406  		t.Fatalf("failed to render templates: %s", err)
   407  	}
   408  
   409  	expects := map[string]string{
   410  		"Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid",
   411  		"Troy/templates/Aeneas":                "Troy/templates/AeneasTroyAeneid",
   412  		"Troy/charts/Latium/templates/From":    "Virgil Aeneid",
   413  	}
   414  	for file, expect := range expects {
   415  		if out[file] != expect {
   416  			t.Errorf("Expected %q, got %q", expect, out[file])
   417  		}
   418  	}
   419  
   420  }
   421  
   422  func TestAlterFuncMap_include(t *testing.T) {
   423  	c := &chart.Chart{
   424  		Metadata: &chart.Metadata{Name: "conrad"},
   425  		Templates: []*chart.File{
   426  			{Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)},
   427  			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
   428  		},
   429  	}
   430  
   431  	v := chartutil.Values{
   432  		"Values": "",
   433  		"Chart":  c.Metadata,
   434  		"Release": chartutil.Values{
   435  			"Name": "Mistah Kurtz",
   436  		},
   437  	}
   438  
   439  	out, err := Render(c, v)
   440  	if err != nil {
   441  		t.Fatal(err)
   442  	}
   443  
   444  	expect := "  Mistah Kurtz - he dead."
   445  	if got := out["conrad/templates/quote"]; got != expect {
   446  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   447  	}
   448  }
   449  
   450  func TestAlterFuncMap_require(t *testing.T) {
   451  	c := &chart.Chart{
   452  		Metadata: &chart.Metadata{Name: "conan"},
   453  		Templates: []*chart.File{
   454  			{Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)},
   455  			{Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)},
   456  		},
   457  	}
   458  
   459  	v := chartutil.Values{
   460  		"Values": chartutil.Values{
   461  			"who":   "us",
   462  			"bases": 2,
   463  		},
   464  		"Chart": c.Metadata,
   465  		"Release": chartutil.Values{
   466  			"Name": "That 90s meme",
   467  		},
   468  	}
   469  
   470  	out, err := Render(c, v)
   471  	if err != nil {
   472  		t.Fatal(err)
   473  	}
   474  
   475  	expectStr := "All your base are belong to us"
   476  	if gotStr := out["conan/templates/quote"]; gotStr != expectStr {
   477  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out)
   478  	}
   479  	expectNum := "All 2 of them!"
   480  	if gotNum := out["conan/templates/bases"]; gotNum != expectNum {
   481  		t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out)
   482  	}
   483  }
   484  
   485  func TestAlterFuncMap_tpl(t *testing.T) {
   486  	c := &chart.Chart{
   487  		Metadata: &chart.Metadata{Name: "TplFunction"},
   488  		Templates: []*chart.File{
   489  			{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)},
   490  		},
   491  	}
   492  
   493  	v := chartutil.Values{
   494  		"Values": chartutil.Values{
   495  			"value": "myvalue",
   496  		},
   497  		"Chart": c.Metadata,
   498  		"Release": chartutil.Values{
   499  			"Name": "TestRelease",
   500  		},
   501  	}
   502  
   503  	out, err := Render(c, v)
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  
   508  	expect := "Evaluate tpl Value: myvalue"
   509  	if got := out["TplFunction/templates/base"]; got != expect {
   510  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   511  	}
   512  }
   513  
   514  func TestAlterFuncMap_tplfunc(t *testing.T) {
   515  	c := &chart.Chart{
   516  		Metadata: &chart.Metadata{Name: "TplFunction"},
   517  		Templates: []*chart.File{
   518  			{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)},
   519  		},
   520  	}
   521  
   522  	v := chartutil.Values{
   523  		"Values": chartutil.Values{
   524  			"value": "myvalue",
   525  		},
   526  		"Chart": c.Metadata,
   527  		"Release": chartutil.Values{
   528  			"Name": "TestRelease",
   529  		},
   530  	}
   531  
   532  	out, err := Render(c, v)
   533  	if err != nil {
   534  		t.Fatal(err)
   535  	}
   536  
   537  	expect := "Evaluate tpl Value: \"myvalue\""
   538  	if got := out["TplFunction/templates/base"]; got != expect {
   539  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   540  	}
   541  }
   542  
   543  func TestAlterFuncMap_tplinclude(t *testing.T) {
   544  	c := &chart.Chart{
   545  		Metadata: &chart.Metadata{Name: "TplFunction"},
   546  		Templates: []*chart.File{
   547  			{Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` .  | quote }}" .}}`)},
   548  			{Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)},
   549  		},
   550  	}
   551  	v := chartutil.Values{
   552  		"Values": chartutil.Values{
   553  			"value": "myvalue",
   554  		},
   555  		"Chart": c.Metadata,
   556  		"Release": chartutil.Values{
   557  			"Name": "TestRelease",
   558  		},
   559  	}
   560  
   561  	out, err := Render(c, v)
   562  	if err != nil {
   563  		t.Fatal(err)
   564  	}
   565  
   566  	expect := "\"TplFunction/templates/base\""
   567  	if got := out["TplFunction/templates/base"]; got != expect {
   568  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   569  	}
   570  
   571  }