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