github.com/darkowlzz/helm@v2.5.1-0.20171213183701-6707fe0468d4+incompatible/pkg/engine/engine_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 engine 18 19 import ( 20 "fmt" 21 "sync" 22 "testing" 23 24 "k8s.io/helm/pkg/chartutil" 25 "k8s.io/helm/pkg/proto/hapi/chart" 26 27 "github.com/golang/protobuf/ptypes/any" 28 ) 29 30 func TestSortTemplates(t *testing.T) { 31 tpls := map[string]renderable{ 32 "/mychart/templates/foo.tpl": {}, 33 "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {}, 34 "/mychart/templates/bar.tpl": {}, 35 "/mychart/templates/charts/foo/templates/bar.tpl": {}, 36 "/mychart/templates/_foo.tpl": {}, 37 "/mychart/templates/charts/foo/templates/foo.tpl": {}, 38 "/mychart/templates/charts/bar/templates/foo.tpl": {}, 39 } 40 got := sortTemplates(tpls) 41 if len(got) != len(tpls) { 42 t.Fatal("Sorted results are missing templates") 43 } 44 45 expect := []string{ 46 "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl", 47 "/mychart/templates/charts/foo/templates/foo.tpl", 48 "/mychart/templates/charts/foo/templates/bar.tpl", 49 "/mychart/templates/charts/bar/templates/foo.tpl", 50 "/mychart/templates/foo.tpl", 51 "/mychart/templates/bar.tpl", 52 "/mychart/templates/_foo.tpl", 53 } 54 for i, e := range expect { 55 if got[i] != e { 56 t.Errorf("expected %q, got %q at index %d\n\tExp: %#v\n\tGot: %#v", e, got[i], i, expect, got) 57 } 58 } 59 } 60 61 func TestEngine(t *testing.T) { 62 e := New() 63 64 // Forbidden because they allow access to the host OS. 65 forbidden := []string{"env", "expandenv"} 66 for _, f := range forbidden { 67 if _, ok := e.FuncMap[f]; ok { 68 t.Errorf("Forbidden function %s exists in FuncMap.", f) 69 } 70 } 71 } 72 73 func TestFuncMap(t *testing.T) { 74 fns := FuncMap() 75 forbidden := []string{"env", "expandenv"} 76 for _, f := range forbidden { 77 if _, ok := fns[f]; ok { 78 t.Errorf("Forbidden function %s exists in FuncMap.", f) 79 } 80 } 81 82 // Test for Engine-specific template functions. 83 expect := []string{"include", "required", "tpl", "toYaml", "fromYaml", "toToml", "toJson", "fromJson"} 84 for _, f := range expect { 85 if _, ok := fns[f]; !ok { 86 t.Errorf("Expected add-on function %q", f) 87 } 88 } 89 } 90 91 func TestRender(t *testing.T) { 92 c := &chart.Chart{ 93 Metadata: &chart.Metadata{ 94 Name: "moby", 95 Version: "1.2.3", 96 }, 97 Templates: []*chart.Template{ 98 {Name: "templates/test1", Data: []byte("{{.outer | title }} {{.inner | title}}")}, 99 {Name: "templates/test2", Data: []byte("{{.global.callme | lower }}")}, 100 {Name: "templates/test3", Data: []byte("{{.noValue}}")}, 101 }, 102 Values: &chart.Config{ 103 Raw: "outer: DEFAULT\ninner: DEFAULT", 104 }, 105 } 106 107 vals := &chart.Config{ 108 Raw: ` 109 outer: spouter 110 inner: inn 111 global: 112 callme: Ishmael 113 `} 114 115 e := New() 116 v, err := chartutil.CoalesceValues(c, vals) 117 if err != nil { 118 t.Fatalf("Failed to coalesce values: %s", err) 119 } 120 out, err := e.Render(c, v) 121 if err != nil { 122 t.Errorf("Failed to render templates: %s", err) 123 } 124 125 expect := "Spouter Inn" 126 if out["moby/templates/test1"] != expect { 127 t.Errorf("Expected %q, got %q", expect, out["test1"]) 128 } 129 130 expect = "ishmael" 131 if out["moby/templates/test2"] != expect { 132 t.Errorf("Expected %q, got %q", expect, out["test2"]) 133 } 134 expect = "" 135 if out["moby/templates/test3"] != expect { 136 t.Errorf("Expected %q, got %q", expect, out["test3"]) 137 } 138 139 if _, err := e.Render(c, v); err != nil { 140 t.Errorf("Unexpected error: %s", err) 141 } 142 } 143 144 func TestRenderInternals(t *testing.T) { 145 // Test the internals of the rendering tool. 146 e := New() 147 148 vals := chartutil.Values{"Name": "one", "Value": "two"} 149 tpls := map[string]renderable{ 150 "one": {tpl: `Hello {{title .Name}}`, vals: vals}, 151 "two": {tpl: `Goodbye {{upper .Value}}`, vals: vals}, 152 // Test whether a template can reliably reference another template 153 // without regard for ordering. 154 "three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals}, 155 } 156 157 out, err := e.render(tpls) 158 if err != nil { 159 t.Fatalf("Failed template rendering: %s", err) 160 } 161 162 if len(out) != 3 { 163 t.Fatalf("Expected 3 templates, got %d", len(out)) 164 } 165 166 if out["one"] != "Hello One" { 167 t.Errorf("Expected 'Hello One', got %q", out["one"]) 168 } 169 170 if out["two"] != "Goodbye TWO" { 171 t.Errorf("Expected 'Goodbye TWO'. got %q", out["two"]) 172 } 173 174 if out["three"] != "Goodbye THREE" { 175 t.Errorf("Expected 'Goodbye THREE'. got %q", out["two"]) 176 } 177 } 178 179 func TestParallelRenderInternals(t *testing.T) { 180 // Make sure that we can use one Engine to run parallel template renders. 181 e := New() 182 var wg sync.WaitGroup 183 for i := 0; i < 20; i++ { 184 wg.Add(1) 185 go func(i int) { 186 fname := "my/file/name" 187 tt := fmt.Sprintf("expect-%d", i) 188 v := chartutil.Values{"val": tt} 189 tpls := map[string]renderable{fname: {tpl: `{{.val}}`, vals: v}} 190 out, err := e.render(tpls) 191 if err != nil { 192 t.Errorf("Failed to render %s: %s", tt, err) 193 } 194 if out[fname] != tt { 195 t.Errorf("Expected %q, got %q", tt, out[fname]) 196 } 197 wg.Done() 198 }(i) 199 } 200 wg.Wait() 201 } 202 203 func TestAllTemplates(t *testing.T) { 204 ch1 := &chart.Chart{ 205 Metadata: &chart.Metadata{Name: "ch1"}, 206 Templates: []*chart.Template{ 207 {Name: "templates/foo", Data: []byte("foo")}, 208 {Name: "templates/bar", Data: []byte("bar")}, 209 }, 210 Dependencies: []*chart.Chart{ 211 { 212 Metadata: &chart.Metadata{Name: "laboratory mice"}, 213 Templates: []*chart.Template{ 214 {Name: "templates/pinky", Data: []byte("pinky")}, 215 {Name: "templates/brain", Data: []byte("brain")}, 216 }, 217 Dependencies: []*chart.Chart{{ 218 Metadata: &chart.Metadata{Name: "same thing we do every night"}, 219 Templates: []*chart.Template{ 220 {Name: "templates/innermost", Data: []byte("innermost")}, 221 }}, 222 }, 223 }, 224 }, 225 } 226 227 var v chartutil.Values 228 tpls := allTemplates(ch1, v) 229 if len(tpls) != 5 { 230 t.Errorf("Expected 5 charts, got %d", len(tpls)) 231 } 232 } 233 234 func TestRenderDependency(t *testing.T) { 235 e := New() 236 deptpl := `{{define "myblock"}}World{{end}}` 237 toptpl := `Hello {{template "myblock"}}` 238 ch := &chart.Chart{ 239 Metadata: &chart.Metadata{Name: "outerchart"}, 240 Templates: []*chart.Template{ 241 {Name: "templates/outer", Data: []byte(toptpl)}, 242 }, 243 Dependencies: []*chart.Chart{ 244 { 245 Metadata: &chart.Metadata{Name: "innerchart"}, 246 Templates: []*chart.Template{ 247 {Name: "templates/inner", Data: []byte(deptpl)}, 248 }, 249 }, 250 }, 251 } 252 253 out, err := e.Render(ch, map[string]interface{}{}) 254 255 if err != nil { 256 t.Fatalf("failed to render chart: %s", err) 257 } 258 259 if len(out) != 2 { 260 t.Errorf("Expected 2, got %d", len(out)) 261 } 262 263 expect := "Hello World" 264 if out["outerchart/templates/outer"] != expect { 265 t.Errorf("Expected %q, got %q", expect, out["outer"]) 266 } 267 268 } 269 270 func TestRenderNestedValues(t *testing.T) { 271 e := New() 272 273 innerpath := "templates/inner.tpl" 274 outerpath := "templates/outer.tpl" 275 // Ensure namespacing rules are working. 276 deepestpath := "templates/inner.tpl" 277 checkrelease := "templates/release.tpl" 278 279 deepest := &chart.Chart{ 280 Metadata: &chart.Metadata{Name: "deepest"}, 281 Templates: []*chart.Template{ 282 {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, 283 {Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)}, 284 }, 285 Values: &chart.Config{Raw: `what: "milkshake"`}, 286 } 287 288 inner := &chart.Chart{ 289 Metadata: &chart.Metadata{Name: "herrick"}, 290 Templates: []*chart.Template{ 291 {Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)}, 292 }, 293 Values: &chart.Config{Raw: `who: "Robert"`}, 294 Dependencies: []*chart.Chart{deepest}, 295 } 296 297 outer := &chart.Chart{ 298 Metadata: &chart.Metadata{Name: "top"}, 299 Templates: []*chart.Template{ 300 {Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)}, 301 }, 302 Values: &chart.Config{ 303 Raw: ` 304 what: stinkweed 305 who: me 306 herrick: 307 who: time`, 308 }, 309 Dependencies: []*chart.Chart{inner}, 310 } 311 312 injValues := chart.Config{ 313 Raw: ` 314 what: rosebuds 315 herrick: 316 deepest: 317 what: flower 318 global: 319 when: to-day`, 320 } 321 322 tmp, err := chartutil.CoalesceValues(outer, &injValues) 323 if err != nil { 324 t.Fatalf("Failed to coalesce values: %s", err) 325 } 326 327 inject := chartutil.Values{ 328 "Values": tmp, 329 "Chart": outer.Metadata, 330 "Release": chartutil.Values{ 331 "Name": "dyin", 332 }, 333 } 334 335 t.Logf("Calculated values: %v", inject) 336 337 out, err := e.Render(outer, inject) 338 if err != nil { 339 t.Fatalf("failed to render templates: %s", err) 340 } 341 342 fullouterpath := "top/" + outerpath 343 if out[fullouterpath] != "Gather ye rosebuds while ye may" { 344 t.Errorf("Unexpected outer: %q", out[fullouterpath]) 345 } 346 347 fullinnerpath := "top/charts/herrick/" + innerpath 348 if out[fullinnerpath] != "Old time is still a-flyin'" { 349 t.Errorf("Unexpected inner: %q", out[fullinnerpath]) 350 } 351 352 fulldeepestpath := "top/charts/herrick/charts/deepest/" + deepestpath 353 if out[fulldeepestpath] != "And this same flower that smiles to-day" { 354 t.Errorf("Unexpected deepest: %q", out[fulldeepestpath]) 355 } 356 357 fullcheckrelease := "top/charts/herrick/charts/deepest/" + checkrelease 358 if out[fullcheckrelease] != "Tomorrow will be dyin" { 359 t.Errorf("Unexpected release: %q", out[fullcheckrelease]) 360 } 361 } 362 363 func TestRenderBuiltinValues(t *testing.T) { 364 inner := &chart.Chart{ 365 Metadata: &chart.Metadata{Name: "Latium"}, 366 Templates: []*chart.Template{ 367 {Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, 368 {Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)}, 369 }, 370 Values: &chart.Config{Raw: ``}, 371 Dependencies: []*chart.Chart{}, 372 Files: []*any.Any{ 373 {TypeUrl: "author", Value: []byte("Virgil")}, 374 {TypeUrl: "book/title.txt", Value: []byte("Aeneid")}, 375 }, 376 } 377 378 outer := &chart.Chart{ 379 Metadata: &chart.Metadata{Name: "Troy"}, 380 Templates: []*chart.Template{ 381 {Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, 382 }, 383 Values: &chart.Config{Raw: ``}, 384 Dependencies: []*chart.Chart{inner}, 385 } 386 387 inject := chartutil.Values{ 388 "Values": &chart.Config{Raw: ""}, 389 "Chart": outer.Metadata, 390 "Release": chartutil.Values{ 391 "Name": "Aeneid", 392 }, 393 } 394 395 t.Logf("Calculated values: %v", outer) 396 397 out, err := New().Render(outer, inject) 398 if err != nil { 399 t.Fatalf("failed to render templates: %s", err) 400 } 401 402 expects := map[string]string{ 403 "Troy/charts/Latium/templates/Lavinia": "Troy/charts/Latium/templates/LaviniaLatiumAeneid", 404 "Troy/templates/Aeneas": "Troy/templates/AeneasTroyAeneid", 405 "Troy/charts/Latium/templates/From": "Virgil Aeneid", 406 } 407 for file, expect := range expects { 408 if out[file] != expect { 409 t.Errorf("Expected %q, got %q", expect, out[file]) 410 } 411 } 412 413 } 414 415 func TestAlterFuncMap(t *testing.T) { 416 c := &chart.Chart{ 417 Metadata: &chart.Metadata{Name: "conrad"}, 418 Templates: []*chart.Template{ 419 {Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)}, 420 {Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)}, 421 }, 422 Values: &chart.Config{Raw: ``}, 423 Dependencies: []*chart.Chart{}, 424 } 425 426 v := chartutil.Values{ 427 "Values": &chart.Config{Raw: ""}, 428 "Chart": c.Metadata, 429 "Release": chartutil.Values{ 430 "Name": "Mistah Kurtz", 431 }, 432 } 433 434 out, err := New().Render(c, v) 435 if err != nil { 436 t.Fatal(err) 437 } 438 439 expect := " Mistah Kurtz - he dead." 440 if got := out["conrad/templates/quote"]; got != expect { 441 t.Errorf("Expected %q, got %q (%v)", expect, got, out) 442 } 443 444 reqChart := &chart.Chart{ 445 Metadata: &chart.Metadata{Name: "conan"}, 446 Templates: []*chart.Template{ 447 {Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)}, 448 {Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)}, 449 }, 450 Values: &chart.Config{Raw: ``}, 451 Dependencies: []*chart.Chart{}, 452 } 453 454 reqValues := chartutil.Values{ 455 "Values": chartutil.Values{ 456 "who": "us", 457 "bases": 2, 458 }, 459 "Chart": reqChart.Metadata, 460 "Release": chartutil.Values{ 461 "Name": "That 90s meme", 462 }, 463 } 464 465 outReq, err := New().Render(reqChart, reqValues) 466 if err != nil { 467 t.Fatal(err) 468 } 469 470 expectStr := "All your base are belong to us" 471 if gotStr := outReq["conan/templates/quote"]; gotStr != expectStr { 472 t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, outReq) 473 } 474 expectNum := "All 2 of them!" 475 if gotNum := outReq["conan/templates/bases"]; gotNum != expectNum { 476 t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq) 477 } 478 479 tplChart := &chart.Chart{ 480 Metadata: &chart.Metadata{Name: "TplFunction"}, 481 Templates: []*chart.Template{ 482 {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)}, 483 }, 484 Values: &chart.Config{Raw: ``}, 485 Dependencies: []*chart.Chart{}, 486 } 487 488 tplValues := chartutil.Values{ 489 "Values": chartutil.Values{ 490 "value": "myvalue", 491 }, 492 "Chart": tplChart.Metadata, 493 "Release": chartutil.Values{ 494 "Name": "TestRelease", 495 }, 496 } 497 498 outTpl, err := New().Render(tplChart, tplValues) 499 if err != nil { 500 t.Fatal(err) 501 } 502 503 expectTplStr := "Evaluate tpl Value: myvalue" 504 if gotStrTpl := outTpl["TplFunction/templates/base"]; gotStrTpl != expectTplStr { 505 t.Errorf("Expected %q, got %q (%v)", expectTplStr, gotStrTpl, outTpl) 506 } 507 508 tplChartWithFunction := &chart.Chart{ 509 Metadata: &chart.Metadata{Name: "TplFunction"}, 510 Templates: []*chart.Template{ 511 {Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)}, 512 }, 513 Values: &chart.Config{Raw: ``}, 514 Dependencies: []*chart.Chart{}, 515 } 516 517 tplValuesWithFunction := chartutil.Values{ 518 "Values": chartutil.Values{ 519 "value": "myvalue", 520 }, 521 "Chart": tplChartWithFunction.Metadata, 522 "Release": chartutil.Values{ 523 "Name": "TestRelease", 524 }, 525 } 526 527 outTplWithFunction, err := New().Render(tplChartWithFunction, tplValuesWithFunction) 528 if err != nil { 529 t.Fatal(err) 530 } 531 532 expectTplStrWithFunction := "Evaluate tpl Value: \"myvalue\"" 533 if gotStrTplWithFunction := outTplWithFunction["TplFunction/templates/base"]; gotStrTplWithFunction != expectTplStrWithFunction { 534 t.Errorf("Expected %q, got %q (%v)", expectTplStrWithFunction, gotStrTplWithFunction, outTplWithFunction) 535 } 536 537 tplChartWithInclude := &chart.Chart{ 538 Metadata: &chart.Metadata{Name: "TplFunction"}, 539 Templates: []*chart.Template{ 540 {Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)}, 541 {Name: "templates/_partial", Data: []byte(`{{.Release.Name}}`)}, 542 }, 543 Values: &chart.Config{Raw: ``}, 544 Dependencies: []*chart.Chart{}, 545 } 546 tplValueWithInclude := chartutil.Values{ 547 "Values": chartutil.Values{ 548 "value": "myvalue", 549 }, 550 "Chart": tplChartWithInclude.Metadata, 551 "Release": chartutil.Values{ 552 "Name": "TestRelease", 553 }, 554 } 555 556 outTplWithInclude, err := New().Render(tplChartWithInclude, tplValueWithInclude) 557 if err != nil { 558 t.Fatal(err) 559 } 560 561 expectedTplStrWithInclude := "\"TestRelease\"" 562 if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude { 563 t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude) 564 } 565 566 }