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