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 }