github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+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 TestEngine(t *testing.T) {
    31  	e := New()
    32  
    33  	// Forbidden because they allow access to the host OS.
    34  	forbidden := []string{"env", "expandenv"}
    35  	for _, f := range forbidden {
    36  		if _, ok := e.FuncMap[f]; ok {
    37  			t.Errorf("Forbidden function %s exists in FuncMap.", f)
    38  		}
    39  	}
    40  }
    41  
    42  func TestFuncMap(t *testing.T) {
    43  	fns := FuncMap()
    44  	forbidden := []string{"env", "expandenv"}
    45  	for _, f := range forbidden {
    46  		if _, ok := fns[f]; ok {
    47  			t.Errorf("Forbidden function %s exists in FuncMap.", f)
    48  		}
    49  	}
    50  
    51  	// Test for Engine-specific template functions.
    52  	expect := []string{"include", "required", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"}
    53  	for _, f := range expect {
    54  		if _, ok := fns[f]; !ok {
    55  			t.Errorf("Expected add-on function %q", f)
    56  		}
    57  	}
    58  }
    59  
    60  func TestRender(t *testing.T) {
    61  	c := &chart.Chart{
    62  		Metadata: &chart.Metadata{
    63  			Name:    "moby",
    64  			Version: "1.2.3",
    65  		},
    66  		Templates: []*chart.Template{
    67  			{Name: "templates/test1", Data: []byte("{{.outer | title }} {{.inner | title}}")},
    68  			{Name: "templates/test2", Data: []byte("{{.global.callme | lower }}")},
    69  			{Name: "templates/test3", Data: []byte("{{.noValue}}")},
    70  		},
    71  		Values: &chart.Config{
    72  			Raw: "outer: DEFAULT\ninner: DEFAULT",
    73  		},
    74  	}
    75  
    76  	vals := &chart.Config{
    77  		Raw: `
    78  outer: spouter
    79  inner: inn
    80  global:
    81    callme: Ishmael
    82  `}
    83  
    84  	e := New()
    85  	v, err := chartutil.CoalesceValues(c, vals)
    86  	if err != nil {
    87  		t.Fatalf("Failed to coalesce values: %s", err)
    88  	}
    89  	out, err := e.Render(c, v)
    90  	if err != nil {
    91  		t.Errorf("Failed to render templates: %s", err)
    92  	}
    93  
    94  	expect := "Spouter Inn"
    95  	if out["moby/templates/test1"] != expect {
    96  		t.Errorf("Expected %q, got %q", expect, out["test1"])
    97  	}
    98  
    99  	expect = "ishmael"
   100  	if out["moby/templates/test2"] != expect {
   101  		t.Errorf("Expected %q, got %q", expect, out["test2"])
   102  	}
   103  	expect = ""
   104  	if out["moby/templates/test3"] != expect {
   105  		t.Errorf("Expected %q, got %q", expect, out["test3"])
   106  	}
   107  
   108  	if _, err := e.Render(c, v); err != nil {
   109  		t.Errorf("Unexpected error: %s", err)
   110  	}
   111  }
   112  
   113  func TestRenderInternals(t *testing.T) {
   114  	// Test the internals of the rendering tool.
   115  	e := New()
   116  
   117  	vals := chartutil.Values{"Name": "one", "Value": "two"}
   118  	tpls := map[string]renderable{
   119  		"one": {tpl: `Hello {{title .Name}}`, vals: vals},
   120  		"two": {tpl: `Goodbye {{upper .Value}}`, vals: vals},
   121  		// Test whether a template can reliably reference another template
   122  		// without regard for ordering.
   123  		"three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals},
   124  	}
   125  
   126  	out, err := e.render(tpls)
   127  	if err != nil {
   128  		t.Fatalf("Failed template rendering: %s", err)
   129  	}
   130  
   131  	if len(out) != 3 {
   132  		t.Fatalf("Expected 3 templates, got %d", len(out))
   133  	}
   134  
   135  	if out["one"] != "Hello One" {
   136  		t.Errorf("Expected 'Hello One', got %q", out["one"])
   137  	}
   138  
   139  	if out["two"] != "Goodbye TWO" {
   140  		t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"])
   141  	}
   142  
   143  	if out["three"] != "Goodbye THREE" {
   144  		t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"])
   145  	}
   146  }
   147  
   148  func TestParallelRenderInternals(t *testing.T) {
   149  	// Make sure that we can use one Engine to run parallel template renders.
   150  	e := New()
   151  	var wg sync.WaitGroup
   152  	for i := 0; i < 20; i++ {
   153  		wg.Add(1)
   154  		go func(i int) {
   155  			fname := "my/file/name"
   156  			tt := fmt.Sprintf("expect-%d", i)
   157  			v := chartutil.Values{"val": tt}
   158  			tpls := map[string]renderable{fname: {tpl: `{{.val}}`, vals: v}}
   159  			out, err := e.render(tpls)
   160  			if err != nil {
   161  				t.Errorf("Failed to render %s: %s", tt, err)
   162  			}
   163  			if out[fname] != tt {
   164  				t.Errorf("Expected %q, got %q", tt, out[fname])
   165  			}
   166  			wg.Done()
   167  		}(i)
   168  	}
   169  	wg.Wait()
   170  }
   171  
   172  func TestAllTemplates(t *testing.T) {
   173  	ch1 := &chart.Chart{
   174  		Metadata: &chart.Metadata{Name: "ch1"},
   175  		Templates: []*chart.Template{
   176  			{Name: "templates/foo", Data: []byte("foo")},
   177  			{Name: "templates/bar", Data: []byte("bar")},
   178  		},
   179  		Dependencies: []*chart.Chart{
   180  			{
   181  				Metadata: &chart.Metadata{Name: "laboratory mice"},
   182  				Templates: []*chart.Template{
   183  					{Name: "templates/pinky", Data: []byte("pinky")},
   184  					{Name: "templates/brain", Data: []byte("brain")},
   185  				},
   186  				Dependencies: []*chart.Chart{{
   187  					Metadata: &chart.Metadata{Name: "same thing we do every night"},
   188  					Templates: []*chart.Template{
   189  						{Name: "templates/innermost", Data: []byte("innermost")},
   190  					}},
   191  				},
   192  			},
   193  		},
   194  	}
   195  
   196  	var v chartutil.Values
   197  	tpls := allTemplates(ch1, v)
   198  	if len(tpls) != 5 {
   199  		t.Errorf("Expected 5 charts, got %d", len(tpls))
   200  	}
   201  }
   202  
   203  func TestRenderDependency(t *testing.T) {
   204  	e := New()
   205  	deptpl := `{{define "myblock"}}World{{end}}`
   206  	toptpl := `Hello {{template "myblock"}}`
   207  	ch := &chart.Chart{
   208  		Metadata: &chart.Metadata{Name: "outerchart"},
   209  		Templates: []*chart.Template{
   210  			{Name: "templates/outer", Data: []byte(toptpl)},
   211  		},
   212  		Dependencies: []*chart.Chart{
   213  			{
   214  				Metadata: &chart.Metadata{Name: "innerchart"},
   215  				Templates: []*chart.Template{
   216  					{Name: "templates/inner", Data: []byte(deptpl)},
   217  				},
   218  			},
   219  		},
   220  	}
   221  
   222  	out, err := e.Render(ch, map[string]interface{}{})
   223  
   224  	if err != nil {
   225  		t.Fatalf("failed to render chart: %s", err)
   226  	}
   227  
   228  	if len(out) != 2 {
   229  		t.Errorf("Expected 2, got %d", len(out))
   230  	}
   231  
   232  	expect := "Hello World"
   233  	if out["outerchart/templates/outer"] != expect {
   234  		t.Errorf("Expected %q, got %q", expect, out["outer"])
   235  	}
   236  
   237  }
   238  
   239  func TestRenderNestedValues(t *testing.T) {
   240  	e := New()
   241  
   242  	innerpath := "templates/inner.tpl"
   243  	outerpath := "templates/outer.tpl"
   244  	// Ensure namespacing rules are working.
   245  	deepestpath := "templates/inner.tpl"
   246  	checkrelease := "templates/release.tpl"
   247  
   248  	deepest := &chart.Chart{
   249  		Metadata: &chart.Metadata{Name: "deepest"},
   250  		Templates: []*chart.Template{
   251  			{Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)},
   252  			{Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)},
   253  		},
   254  		Values: &chart.Config{Raw: `what: "milkshake"`},
   255  	}
   256  
   257  	inner := &chart.Chart{
   258  		Metadata: &chart.Metadata{Name: "herrick"},
   259  		Templates: []*chart.Template{
   260  			{Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)},
   261  		},
   262  		Values:       &chart.Config{Raw: `who: "Robert"`},
   263  		Dependencies: []*chart.Chart{deepest},
   264  	}
   265  
   266  	outer := &chart.Chart{
   267  		Metadata: &chart.Metadata{Name: "top"},
   268  		Templates: []*chart.Template{
   269  			{Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)},
   270  		},
   271  		Values: &chart.Config{
   272  			Raw: `
   273  what: stinkweed
   274  who: me
   275  herrick:
   276      who: time`,
   277  		},
   278  		Dependencies: []*chart.Chart{inner},
   279  	}
   280  
   281  	injValues := chart.Config{
   282  		Raw: `
   283  what: rosebuds
   284  herrick:
   285    deepest:
   286      what: flower
   287  global:
   288    when: to-day`,
   289  	}
   290  
   291  	tmp, err := chartutil.CoalesceValues(outer, &injValues)
   292  	if err != nil {
   293  		t.Fatalf("Failed to coalesce values: %s", err)
   294  	}
   295  
   296  	inject := chartutil.Values{
   297  		"Values": tmp,
   298  		"Chart":  outer.Metadata,
   299  		"Release": chartutil.Values{
   300  			"Name": "dyin",
   301  		},
   302  	}
   303  
   304  	t.Logf("Calculated values: %v", inject)
   305  
   306  	out, err := e.Render(outer, inject)
   307  	if err != nil {
   308  		t.Fatalf("failed to render templates: %s", err)
   309  	}
   310  
   311  	fullouterpath := "top/" + outerpath
   312  	if out[fullouterpath] != "Gather ye rosebuds while ye may" {
   313  		t.Errorf("Unexpected outer: %q", out[fullouterpath])
   314  	}
   315  
   316  	fullinnerpath := "top/charts/herrick/" + innerpath
   317  	if out[fullinnerpath] != "Old time is still a-flyin'" {
   318  		t.Errorf("Unexpected inner: %q", out[fullinnerpath])
   319  	}
   320  
   321  	fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath
   322  	if out[fulldeepestpath] != "And this same flower that smiles to-day" {
   323  		t.Errorf("Unexpected deepest: %q", out[fulldeepestpath])
   324  	}
   325  
   326  	fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease
   327  	if out[fullcheckrelease] != "Tomorrow will be dyin" {
   328  		t.Errorf("Unexpected release: %q", out[fullcheckrelease])
   329  	}
   330  }
   331  
   332  func TestRenderBuiltinValues(t *testing.T) {
   333  	inner := &chart.Chart{
   334  		Metadata: &chart.Metadata{Name: "Latium"},
   335  		Templates: []*chart.Template{
   336  			{Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   337  			{Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)},
   338  		},
   339  		Values:       &chart.Config{Raw: ``},
   340  		Dependencies: []*chart.Chart{},
   341  		Files: []*any.Any{
   342  			{TypeUrl: "author", Value: []byte("Virgil")},
   343  			{TypeUrl: "book/title.txt", Value: []byte("Aeneid")},
   344  		},
   345  	}
   346  
   347  	outer := &chart.Chart{
   348  		Metadata: &chart.Metadata{Name: "Troy"},
   349  		Templates: []*chart.Template{
   350  			{Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
   351  		},
   352  		Values:       &chart.Config{Raw: ``},
   353  		Dependencies: []*chart.Chart{inner},
   354  	}
   355  
   356  	inject := chartutil.Values{
   357  		"Values": &chart.Config{Raw: ""},
   358  		"Chart":  outer.Metadata,
   359  		"Release": chartutil.Values{
   360  			"Name": "Aeneid",
   361  		},
   362  	}
   363  
   364  	t.Logf("Calculated values: %v", outer)
   365  
   366  	out, err := New().Render(outer, inject)
   367  	if err != nil {
   368  		t.Fatalf("failed to render templates: %s", err)
   369  	}
   370  
   371  	expects := map[string]string{
   372  		"Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid",
   373  		"Troy/templates/Aeneas":                "Troy/templates/AeneasTroyAeneid",
   374  		"Troy/charts/Latium/templates/From":    "Virgil Aeneid",
   375  	}
   376  	for file, expect := range expects {
   377  		if out[file] != expect {
   378  			t.Errorf("Expected %q, got %q", expect, out[file])
   379  		}
   380  	}
   381  
   382  }
   383  
   384  func TestAlterFuncMap(t *testing.T) {
   385  	c := &chart.Chart{
   386  		Metadata: &chart.Metadata{Name: "conrad"},
   387  		Templates: []*chart.Template{
   388  			{Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)},
   389  			{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
   390  		},
   391  		Values:       &chart.Config{Raw: ``},
   392  		Dependencies: []*chart.Chart{},
   393  	}
   394  
   395  	v := chartutil.Values{
   396  		"Values": &chart.Config{Raw: ""},
   397  		"Chart":  c.Metadata,
   398  		"Release": chartutil.Values{
   399  			"Name": "Mistah Kurtz",
   400  		},
   401  	}
   402  
   403  	out, err := New().Render(c, v)
   404  	if err != nil {
   405  		t.Fatal(err)
   406  	}
   407  
   408  	expect := "  Mistah Kurtz - he dead."
   409  	if got := out["conrad/templates/quote"]; got != expect {
   410  		t.Errorf("Expected %q, got %q (%v)", expect, got, out)
   411  	}
   412  
   413  	reqChart := &chart.Chart{
   414  		Metadata: &chart.Metadata{Name: "conan"},
   415  		Templates: []*chart.Template{
   416  			{Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)},
   417  			{Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)},
   418  		},
   419  		Values:       &chart.Config{Raw: ``},
   420  		Dependencies: []*chart.Chart{},
   421  	}
   422  
   423  	reqValues := chartutil.Values{
   424  		"Values": chartutil.Values{
   425  			"who":   "us",
   426  			"bases": 2,
   427  		},
   428  		"Chart": reqChart.Metadata,
   429  		"Release": chartutil.Values{
   430  			"Name": "That 90s meme",
   431  		},
   432  	}
   433  
   434  	outReq, err := New().Render(reqChart, reqValues)
   435  	if err != nil {
   436  		t.Fatal(err)
   437  	}
   438  
   439  	expectStr := "All your base are belong to us"
   440  	if gotStr := outReq["conan/templates/quote"]; gotStr != expectStr {
   441  		t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, outReq)
   442  	}
   443  	expectNum := "All 2 of them!"
   444  	if gotNum := outReq["conan/templates/bases"]; gotNum != expectNum {
   445  		t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq)
   446  	}
   447  
   448  }