github.com/amundsenjunior/helm@v2.8.0-rc.1.0.20180119233529-2b92431476e1+incompatible/pkg/chartutil/values_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 chartutil
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"testing"
    24  	"text/template"
    25  
    26  	"github.com/golang/protobuf/ptypes/any"
    27  
    28  	kversion "k8s.io/apimachinery/pkg/version"
    29  	"k8s.io/helm/pkg/proto/hapi/chart"
    30  	"k8s.io/helm/pkg/timeconv"
    31  	"k8s.io/helm/pkg/version"
    32  )
    33  
    34  func TestReadValues(t *testing.T) {
    35  	doc := `# Test YAML parse
    36  poet: "Coleridge"
    37  title: "Rime of the Ancient Mariner"
    38  stanza:
    39    - "at"
    40    - "length"
    41    - "did"
    42    - cross
    43    - an
    44    - Albatross
    45  
    46  mariner:
    47    with: "crossbow"
    48    shot: "ALBATROSS"
    49  
    50  water:
    51    water:
    52      where: "everywhere"
    53      nor: "any drop to drink"
    54  `
    55  
    56  	data, err := ReadValues([]byte(doc))
    57  	if err != nil {
    58  		t.Fatalf("Error parsing bytes: %s", err)
    59  	}
    60  	matchValues(t, data)
    61  
    62  	tests := []string{`poet: "Coleridge"`, "# Just a comment", ""}
    63  
    64  	for _, tt := range tests {
    65  		data, err = ReadValues([]byte(tt))
    66  		if err != nil {
    67  			t.Fatalf("Error parsing bytes (%s): %s", tt, err)
    68  		}
    69  		if data == nil {
    70  			t.Errorf(`YAML string "%s" gave a nil map`, tt)
    71  		}
    72  	}
    73  }
    74  
    75  func TestToRenderValuesCaps(t *testing.T) {
    76  
    77  	chartValues := `
    78  name: al Rashid
    79  where:
    80    city: Basrah
    81    title: caliph
    82  `
    83  	overideValues := `
    84  name: Haroun
    85  where:
    86    city: Baghdad
    87    date: 809 CE
    88  `
    89  
    90  	c := &chart.Chart{
    91  		Metadata:  &chart.Metadata{Name: "test"},
    92  		Templates: []*chart.Template{},
    93  		Values:    &chart.Config{Raw: chartValues},
    94  		Dependencies: []*chart.Chart{
    95  			{
    96  				Metadata: &chart.Metadata{Name: "where"},
    97  				Values:   &chart.Config{Raw: ""},
    98  			},
    99  		},
   100  		Files: []*any.Any{
   101  			{TypeUrl: "scheherazade/shahryar.txt", Value: []byte("1,001 Nights")},
   102  		},
   103  	}
   104  	v := &chart.Config{Raw: overideValues}
   105  
   106  	o := ReleaseOptions{
   107  		Name:      "Seven Voyages",
   108  		Time:      timeconv.Now(),
   109  		Namespace: "al Basrah",
   110  		IsInstall: true,
   111  		Revision:  5,
   112  	}
   113  
   114  	caps := &Capabilities{
   115  		APIVersions:   DefaultVersionSet,
   116  		TillerVersion: version.GetVersionProto(),
   117  		KubeVersion:   &kversion.Info{Major: "1"},
   118  	}
   119  
   120  	res, err := ToRenderValuesCaps(c, v, o, caps)
   121  	if err != nil {
   122  		t.Fatal(err)
   123  	}
   124  
   125  	// Ensure that the top-level values are all set.
   126  	if name := res["Chart"].(*chart.Metadata).Name; name != "test" {
   127  		t.Errorf("Expected chart name 'test', got %q", name)
   128  	}
   129  	relmap := res["Release"].(map[string]interface{})
   130  	if name := relmap["Name"]; name.(string) != "Seven Voyages" {
   131  		t.Errorf("Expected release name 'Seven Voyages', got %q", name)
   132  	}
   133  	if rev := relmap["Revision"]; rev.(int) != 5 {
   134  		t.Errorf("Expected release revision %d, got %q", 5, rev)
   135  	}
   136  	if relmap["IsUpgrade"].(bool) {
   137  		t.Error("Expected upgrade to be false.")
   138  	}
   139  	if !relmap["IsInstall"].(bool) {
   140  		t.Errorf("Expected install to be true.")
   141  	}
   142  	if data := res["Files"].(Files)["scheherazade/shahryar.txt"]; string(data) != "1,001 Nights" {
   143  		t.Errorf("Expected file '1,001 Nights', got %q", string(data))
   144  	}
   145  	if !res["Capabilities"].(*Capabilities).APIVersions.Has("v1") {
   146  		t.Error("Expected Capabilities to have v1 as an API")
   147  	}
   148  	if res["Capabilities"].(*Capabilities).TillerVersion.SemVer == "" {
   149  		t.Error("Expected Capabilities to have a Tiller version")
   150  	}
   151  	if res["Capabilities"].(*Capabilities).KubeVersion.Major != "1" {
   152  		t.Error("Expected Capabilities to have a Kube version")
   153  	}
   154  
   155  	var vals Values
   156  	vals = res["Values"].(Values)
   157  
   158  	if vals["name"] != "Haroun" {
   159  		t.Errorf("Expected 'Haroun', got %q (%v)", vals["name"], vals)
   160  	}
   161  	where := vals["where"].(map[string]interface{})
   162  	expects := map[string]string{
   163  		"city":  "Baghdad",
   164  		"date":  "809 CE",
   165  		"title": "caliph",
   166  	}
   167  	for field, expect := range expects {
   168  		if got := where[field]; got != expect {
   169  			t.Errorf("Expected %q, got %q (%v)", expect, got, where)
   170  		}
   171  	}
   172  }
   173  
   174  func TestReadValuesFile(t *testing.T) {
   175  	data, err := ReadValuesFile("./testdata/coleridge.yaml")
   176  	if err != nil {
   177  		t.Fatalf("Error reading YAML file: %s", err)
   178  	}
   179  	matchValues(t, data)
   180  }
   181  
   182  func ExampleValues() {
   183  	doc := `
   184  title: "Moby Dick"
   185  chapter:
   186    one:
   187      title: "Loomings"
   188    two:
   189      title: "The Carpet-Bag"
   190    three:
   191      title: "The Spouter Inn"
   192  `
   193  	d, err := ReadValues([]byte(doc))
   194  	if err != nil {
   195  		panic(err)
   196  	}
   197  	ch1, err := d.Table("chapter.one")
   198  	if err != nil {
   199  		panic("could not find chapter one")
   200  	}
   201  	fmt.Print(ch1["title"])
   202  	// Output:
   203  	// Loomings
   204  }
   205  
   206  func TestTable(t *testing.T) {
   207  	doc := `
   208  title: "Moby Dick"
   209  chapter:
   210    one:
   211      title: "Loomings"
   212    two:
   213      title: "The Carpet-Bag"
   214    three:
   215      title: "The Spouter Inn"
   216  `
   217  	d, err := ReadValues([]byte(doc))
   218  	if err != nil {
   219  		t.Fatalf("Failed to parse the White Whale: %s", err)
   220  	}
   221  
   222  	if _, err := d.Table("title"); err == nil {
   223  		t.Fatalf("Title is not a table.")
   224  	}
   225  
   226  	if _, err := d.Table("chapter"); err != nil {
   227  		t.Fatalf("Failed to get the chapter table: %s\n%v", err, d)
   228  	}
   229  
   230  	if v, err := d.Table("chapter.one"); err != nil {
   231  		t.Errorf("Failed to get chapter.one: %s", err)
   232  	} else if v["title"] != "Loomings" {
   233  		t.Errorf("Unexpected title: %s", v["title"])
   234  	}
   235  
   236  	if _, err := d.Table("chapter.three"); err != nil {
   237  		t.Errorf("Chapter three is missing: %s\n%v", err, d)
   238  	}
   239  
   240  	if _, err := d.Table("chapter.OneHundredThirtySix"); err == nil {
   241  		t.Errorf("I think you mean 'Epilogue'")
   242  	}
   243  }
   244  
   245  func matchValues(t *testing.T, data map[string]interface{}) {
   246  	if data["poet"] != "Coleridge" {
   247  		t.Errorf("Unexpected poet: %s", data["poet"])
   248  	}
   249  
   250  	if o, err := ttpl("{{len .stanza}}", data); err != nil {
   251  		t.Errorf("len stanza: %s", err)
   252  	} else if o != "6" {
   253  		t.Errorf("Expected 6, got %s", o)
   254  	}
   255  
   256  	if o, err := ttpl("{{.mariner.shot}}", data); err != nil {
   257  		t.Errorf(".mariner.shot: %s", err)
   258  	} else if o != "ALBATROSS" {
   259  		t.Errorf("Expected that mariner shot ALBATROSS")
   260  	}
   261  
   262  	if o, err := ttpl("{{.water.water.where}}", data); err != nil {
   263  		t.Errorf(".water.water.where: %s", err)
   264  	} else if o != "everywhere" {
   265  		t.Errorf("Expected water water everywhere")
   266  	}
   267  }
   268  
   269  func ttpl(tpl string, v map[string]interface{}) (string, error) {
   270  	var b bytes.Buffer
   271  	tt := template.Must(template.New("t").Parse(tpl))
   272  	if err := tt.Execute(&b, v); err != nil {
   273  		return "", err
   274  	}
   275  	return b.String(), nil
   276  }
   277  
   278  // ref: http://www.yaml.org/spec/1.2/spec.html#id2803362
   279  var testCoalesceValuesYaml = `
   280  top: yup
   281  bottom: null
   282  right: Null
   283  left: NULL
   284  front: ~
   285  back: ""
   286  
   287  global:
   288    name: Ishmael
   289    subject: Queequeg
   290    nested:
   291      boat: true
   292  
   293  pequod:
   294    global:
   295      name: Stinky
   296      harpooner: Tashtego
   297      nested:
   298        boat: false
   299        sail: true
   300    ahab:
   301      scope: whale
   302  `
   303  
   304  func TestCoalesceValues(t *testing.T) {
   305  	tchart := "testdata/moby"
   306  	c, err := LoadDir(tchart)
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  
   311  	tvals := &chart.Config{Raw: testCoalesceValuesYaml}
   312  
   313  	v, err := CoalesceValues(c, tvals)
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  	j, _ := json.MarshalIndent(v, "", "  ")
   318  	t.Logf("Coalesced Values: %s", string(j))
   319  
   320  	tests := []struct {
   321  		tpl    string
   322  		expect string
   323  	}{
   324  		{"{{.top}}", "yup"},
   325  		{"{{.back}}", ""},
   326  		{"{{.name}}", "moby"},
   327  		{"{{.global.name}}", "Ishmael"},
   328  		{"{{.global.subject}}", "Queequeg"},
   329  		{"{{.global.harpooner}}", "<no value>"},
   330  		{"{{.pequod.name}}", "pequod"},
   331  		{"{{.pequod.ahab.name}}", "ahab"},
   332  		{"{{.pequod.ahab.scope}}", "whale"},
   333  		{"{{.pequod.ahab.global.name}}", "Ishmael"},
   334  		{"{{.pequod.ahab.global.subject}}", "Queequeg"},
   335  		{"{{.pequod.ahab.global.harpooner}}", "Tashtego"},
   336  		{"{{.pequod.global.name}}", "Ishmael"},
   337  		{"{{.pequod.global.subject}}", "Queequeg"},
   338  		{"{{.spouter.global.name}}", "Ishmael"},
   339  		{"{{.spouter.global.harpooner}}", "<no value>"},
   340  
   341  		{"{{.global.nested.boat}}", "true"},
   342  		{"{{.pequod.global.nested.boat}}", "true"},
   343  		{"{{.spouter.global.nested.boat}}", "true"},
   344  		{"{{.pequod.global.nested.sail}}", "true"},
   345  		{"{{.spouter.global.nested.sail}}", "<no value>"},
   346  	}
   347  
   348  	for _, tt := range tests {
   349  		if o, err := ttpl(tt.tpl, v); err != nil || o != tt.expect {
   350  			t.Errorf("Expected %q to expand to %q, got %q", tt.tpl, tt.expect, o)
   351  		}
   352  	}
   353  
   354  	nullKeys := []string{"bottom", "right", "left", "front"}
   355  	for _, nullKey := range nullKeys {
   356  		if _, ok := v[nullKey]; ok {
   357  			t.Errorf("Expected key %q to be removed, still present", nullKey)
   358  		}
   359  	}
   360  }
   361  
   362  func TestCoalesceTables(t *testing.T) {
   363  	dst := map[string]interface{}{
   364  		"name": "Ishmael",
   365  		"address": map[string]interface{}{
   366  			"street": "123 Spouter Inn Ct.",
   367  			"city":   "Nantucket",
   368  		},
   369  		"details": map[string]interface{}{
   370  			"friends": []string{"Tashtego"},
   371  		},
   372  		"boat": "pequod",
   373  	}
   374  	src := map[string]interface{}{
   375  		"occupation": "whaler",
   376  		"address": map[string]interface{}{
   377  			"state":  "MA",
   378  			"street": "234 Spouter Inn Ct.",
   379  		},
   380  		"details": "empty",
   381  		"boat": map[string]interface{}{
   382  			"mast": true,
   383  		},
   384  	}
   385  
   386  	// What we expect is that anything in dst overrides anything in src, but that
   387  	// otherwise the values are coalesced.
   388  	coalesceTables(dst, src)
   389  
   390  	if dst["name"] != "Ishmael" {
   391  		t.Errorf("Unexpected name: %s", dst["name"])
   392  	}
   393  	if dst["occupation"] != "whaler" {
   394  		t.Errorf("Unexpected occupation: %s", dst["occupation"])
   395  	}
   396  
   397  	addr, ok := dst["address"].(map[string]interface{})
   398  	if !ok {
   399  		t.Fatal("Address went away.")
   400  	}
   401  
   402  	if addr["street"].(string) != "123 Spouter Inn Ct." {
   403  		t.Errorf("Unexpected address: %v", addr["street"])
   404  	}
   405  
   406  	if addr["city"].(string) != "Nantucket" {
   407  		t.Errorf("Unexpected city: %v", addr["city"])
   408  	}
   409  
   410  	if addr["state"].(string) != "MA" {
   411  		t.Errorf("Unexpected state: %v", addr["state"])
   412  	}
   413  
   414  	if det, ok := dst["details"].(map[string]interface{}); !ok {
   415  		t.Fatalf("Details is the wrong type: %v", dst["details"])
   416  	} else if _, ok := det["friends"]; !ok {
   417  		t.Error("Could not find your friends. Maybe you don't have any. :-(")
   418  	}
   419  
   420  	if dst["boat"].(string) != "pequod" {
   421  		t.Errorf("Expected boat string, got %v", dst["boat"])
   422  	}
   423  }
   424  func TestPathValue(t *testing.T) {
   425  	doc := `
   426  title: "Moby Dick"
   427  chapter:
   428    one:
   429      title: "Loomings"
   430    two:
   431      title: "The Carpet-Bag"
   432    three:
   433      title: "The Spouter Inn"
   434  `
   435  	d, err := ReadValues([]byte(doc))
   436  	if err != nil {
   437  		t.Fatalf("Failed to parse the White Whale: %s", err)
   438  	}
   439  
   440  	if v, err := d.PathValue("chapter.one.title"); err != nil {
   441  		t.Errorf("Got error instead of title: %s\n%v", err, d)
   442  	} else if v != "Loomings" {
   443  		t.Errorf("No error but got wrong value for title: %s\n%v", err, d)
   444  	}
   445  	if _, err := d.PathValue("chapter.one.doesntexist"); err == nil {
   446  		t.Errorf("Non-existent key should return error: %s\n%v", err, d)
   447  	}
   448  	if _, err := d.PathValue("chapter.doesntexist.one"); err == nil {
   449  		t.Errorf("Non-existent key in middle of path should return error: %s\n%v", err, d)
   450  	}
   451  	if _, err := d.PathValue(""); err == nil {
   452  		t.Error("Asking for the value from an empty path should yield an error")
   453  	}
   454  	if v, err := d.PathValue("title"); err == nil {
   455  		if v != "Moby Dick" {
   456  			t.Errorf("Failed to return values for root key title")
   457  		}
   458  	}
   459  }