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 }