github.com/zoumo/helm@v2.5.0+incompatible/pkg/engine/engine_test.go (about)

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