github.com/cloudposse/helm@v2.2.3+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  	"k8s.io/helm/pkg/proto/hapi/chart"
    29  	"k8s.io/helm/pkg/timeconv"
    30  	"k8s.io/helm/pkg/version"
    31  	kversion "k8s.io/kubernetes/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  var testCoalesceValuesYaml = `
   279  top: yup
   280  
   281  global:
   282    name: Ishmael
   283    subject: Queequeg
   284    nested:
   285      boat: true
   286  
   287  pequod:
   288    global:
   289      name: Stinky
   290      harpooner: Tashtego
   291      nested:
   292        boat: false
   293        sail: true
   294    ahab:
   295      scope: whale
   296  `
   297  
   298  func TestCoalesceValues(t *testing.T) {
   299  	tchart := "testdata/moby"
   300  	c, err := LoadDir(tchart)
   301  	if err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	tvals := &chart.Config{Raw: testCoalesceValuesYaml}
   306  
   307  	v, err := CoalesceValues(c, tvals)
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  	j, _ := json.MarshalIndent(v, "", "  ")
   312  	t.Logf("Coalesced Values: %s", string(j))
   313  
   314  	tests := []struct {
   315  		tpl    string
   316  		expect string
   317  	}{
   318  		{"{{.top}}", "yup"},
   319  		{"{{.name}}", "moby"},
   320  		{"{{.global.name}}", "Ishmael"},
   321  		{"{{.global.subject}}", "Queequeg"},
   322  		{"{{.global.harpooner}}", "<no value>"},
   323  		{"{{.pequod.name}}", "pequod"},
   324  		{"{{.pequod.ahab.name}}", "ahab"},
   325  		{"{{.pequod.ahab.scope}}", "whale"},
   326  		{"{{.pequod.ahab.global.name}}", "Ishmael"},
   327  		{"{{.pequod.ahab.global.subject}}", "Queequeg"},
   328  		{"{{.pequod.ahab.global.harpooner}}", "Tashtego"},
   329  		{"{{.pequod.global.name}}", "Ishmael"},
   330  		{"{{.pequod.global.subject}}", "Queequeg"},
   331  		{"{{.spouter.global.name}}", "Ishmael"},
   332  		{"{{.spouter.global.harpooner}}", "<no value>"},
   333  
   334  		{"{{.global.nested.boat}}", "true"},
   335  		{"{{.pequod.global.nested.boat}}", "true"},
   336  		{"{{.spouter.global.nested.boat}}", "true"},
   337  		{"{{.pequod.global.nested.sail}}", "true"},
   338  		{"{{.spouter.global.nested.sail}}", "<no value>"},
   339  	}
   340  
   341  	for _, tt := range tests {
   342  		if o, err := ttpl(tt.tpl, v); err != nil || o != tt.expect {
   343  			t.Errorf("Expected %q to expand to %q, got %q", tt.tpl, tt.expect, o)
   344  		}
   345  	}
   346  }
   347  
   348  func TestCoalesceTables(t *testing.T) {
   349  	dst := map[string]interface{}{
   350  		"name": "Ishmael",
   351  		"address": map[string]interface{}{
   352  			"street": "123 Spouter Inn Ct.",
   353  			"city":   "Nantucket",
   354  		},
   355  		"details": map[string]interface{}{
   356  			"friends": []string{"Tashtego"},
   357  		},
   358  		"boat": "pequod",
   359  	}
   360  	src := map[string]interface{}{
   361  		"occupation": "whaler",
   362  		"address": map[string]interface{}{
   363  			"state":  "MA",
   364  			"street": "234 Spouter Inn Ct.",
   365  		},
   366  		"details": "empty",
   367  		"boat": map[string]interface{}{
   368  			"mast": true,
   369  		},
   370  	}
   371  
   372  	// What we expect is that anything in dst overrides anything in src, but that
   373  	// otherwise the values are coalesced.
   374  	coalesceTables(dst, src)
   375  
   376  	if dst["name"] != "Ishmael" {
   377  		t.Errorf("Unexpected name: %s", dst["name"])
   378  	}
   379  	if dst["occupation"] != "whaler" {
   380  		t.Errorf("Unexpected occupation: %s", dst["occupation"])
   381  	}
   382  
   383  	addr, ok := dst["address"].(map[string]interface{})
   384  	if !ok {
   385  		t.Fatal("Address went away.")
   386  	}
   387  
   388  	if addr["street"].(string) != "123 Spouter Inn Ct." {
   389  		t.Errorf("Unexpected address: %v", addr["street"])
   390  	}
   391  
   392  	if addr["city"].(string) != "Nantucket" {
   393  		t.Errorf("Unexpected city: %v", addr["city"])
   394  	}
   395  
   396  	if addr["state"].(string) != "MA" {
   397  		t.Errorf("Unexpected state: %v", addr["state"])
   398  	}
   399  
   400  	if det, ok := dst["details"].(map[string]interface{}); !ok {
   401  		t.Fatalf("Details is the wrong type: %v", dst["details"])
   402  	} else if _, ok := det["friends"]; !ok {
   403  		t.Error("Could not find your friends. Maybe you don't have any. :-(")
   404  	}
   405  
   406  	if dst["boat"].(string) != "pequod" {
   407  		t.Errorf("Expected boat string, got %v", dst["boat"])
   408  	}
   409  }
   410  func TestPathValue(t *testing.T) {
   411  	doc := `
   412  title: "Moby Dick"
   413  chapter:
   414    one:
   415      title: "Loomings"
   416    two:
   417      title: "The Carpet-Bag"
   418    three:
   419      title: "The Spouter Inn"
   420  `
   421  	d, err := ReadValues([]byte(doc))
   422  	if err != nil {
   423  		t.Fatalf("Failed to parse the White Whale: %s", err)
   424  	}
   425  
   426  	if v, err := d.PathValue("chapter.one.title"); err != nil {
   427  		t.Errorf("Got error instead of title: %s\n%v", err, d)
   428  	} else if v != "Loomings" {
   429  		t.Errorf("No error but got wrong value for title: %s\n%v", err, d)
   430  	}
   431  	if _, err := d.PathValue("chapter.one.doesntexist"); err == nil {
   432  		t.Errorf("Non-existent key should return error: %s\n%v", err, d)
   433  	}
   434  	if _, err := d.PathValue("chapter.doesntexist.one"); err == nil {
   435  		t.Errorf("Non-existent key in middle of path should return error: %s\n%v", err, d)
   436  	}
   437  	if v, err := d.PathValue("title"); err == nil {
   438  		if v != "Moby Dick" {
   439  			t.Errorf("Failed to return values for root key title")
   440  		}
   441  	}
   442  
   443  }