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 }