feat(tiller): add .Template object in templates

This allows templates to access information about the template file.
Right now, the template can only access the .Template.Name, which is the
chart-relative path to the current template.

Closes #894
pull/902/head
Matt Butcher 9 years ago
parent ab4e20c278
commit 532f03ec78

@ -17,7 +17,7 @@ limitations under the License.
package chartutil package chartutil
import ( import (
"errors" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -28,7 +28,7 @@ import (
) )
// ErrNoTable indicates that a chart does not have a matching table. // ErrNoTable indicates that a chart does not have a matching table.
var ErrNoTable = errors.New("no table") type ErrNoTable error
// GlobalKey is the name of the Values key that is used for storing global vars. // GlobalKey is the name of the Values key that is used for storing global vars.
const GlobalKey = "global" const GlobalKey = "global"
@ -92,7 +92,7 @@ func (v Values) Encode(w io.Writer) error {
func tableLookup(v Values, simple string) (Values, error) { func tableLookup(v Values, simple string) (Values, error) {
v2, ok := v[simple] v2, ok := v[simple]
if !ok { if !ok {
return v, ErrNoTable return v, ErrNoTable(fmt.Errorf("no table named %q (%v)", simple, v))
} }
if vv, ok := v2.(map[string]interface{}); ok { if vv, ok := v2.(map[string]interface{}); ok {
return vv, nil return vv, nil
@ -105,7 +105,8 @@ func tableLookup(v Values, simple string) (Values, error) {
return vv, nil return vv, nil
} }
return map[string]interface{}{}, ErrNoTable var e ErrNoTable = fmt.Errorf("no table named %q", simple)
return map[string]interface{}{}, e
} }
// ReadValues will parse YAML byte data into a Values. // ReadValues will parse YAML byte data into a Values.
@ -138,7 +139,7 @@ func ReadValuesFile(filename string) (Values, error) {
// - A chart has access to all of the variables for it, as well as all of // - A chart has access to all of the variables for it, as well as all of
// the values destined for its dependencies. // the values destined for its dependencies.
func CoalesceValues(chrt *chart.Chart, vals *chart.Config, overrides map[string]interface{}) (Values, error) { func CoalesceValues(chrt *chart.Chart, vals *chart.Config, overrides map[string]interface{}) (Values, error) {
var cvals Values cvals := Values{}
// Parse values if not nil. We merge these at the top level because // Parse values if not nil. We merge these at the top level because
// the passed-in values are in the same namespace as the parent chart. // the passed-in values are in the same namespace as the parent chart.
if vals != nil { if vals != nil {

@ -115,9 +115,13 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) {
rendered := make(map[string]string, len(files)) rendered := make(map[string]string, len(files))
var buf bytes.Buffer var buf bytes.Buffer
for _, file := range files { for _, file := range files {
if err := t.ExecuteTemplate(&buf, file, tpls[file].vals); err != nil { // At render time, add information about the template that is being rendered.
vals := tpls[file].vals
vals["Template"] = map[string]interface{}{"Name": file}
if err := t.ExecuteTemplate(&buf, file, vals); err != nil {
return map[string]string{}, fmt.Errorf("render error in %q: %s", file, err) return map[string]string{}, fmt.Errorf("render error in %q: %s", file, err)
} }
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero) // Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
// is set. Since missing=error will never get here, we do not need to handle // is set. Since missing=error will never get here, we do not need to handle
// the Strict case. // the Strict case.
@ -142,30 +146,27 @@ func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
// As it recurses, it also sets the values to be appropriate for the template // As it recurses, it also sets the values to be appropriate for the template
// scope. // scope.
func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals chartutil.Values, top bool) { func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals chartutil.Values, top bool) {
var cvals chartutil.Values // This should never evaluate to a nil map. That will cause problems when
// values are appended later.
cvals := chartutil.Values{}
if top { if top {
// If this is the top of the rendering tree, assume that parentVals // If this is the top of the rendering tree, assume that parentVals
// is already resolved to the authoritative values. // is already resolved to the authoritative values.
cvals = parentVals cvals = parentVals
} else if c.Metadata != nil && c.Metadata.Name != "" { } else if c.Metadata != nil && c.Metadata.Name != "" {
// An error indicates that the table doesn't exist. So we leave it as // If there is a {{.Values.ThisChart}} in the parent metadata,
// an empty map. // copy that into the {{.Values}} for this template.
newVals := chartutil.Values{}
var tmp chartutil.Values if vs, err := parentVals.Table("Values"); err == nil {
vs, err := parentVals.Table("Values") if tmp, err := vs.Table(c.Metadata.Name); err == nil {
if err == nil { newVals = tmp
tmp, err = vs.Table(c.Metadata.Name) }
} else {
tmp, err = parentVals.Table(c.Metadata.Name)
} }
//tmp, err := parentVals["Values"].(chartutil.Values).Table(c.Metadata.Name)
if err == nil {
cvals = map[string]interface{}{ cvals = map[string]interface{}{
"Values": tmp, "Values": newVals,
"Release": parentVals["Release"], "Release": parentVals["Release"],
"Chart": c, "Chart": c.Metadata,
}
} }
} }

@ -154,18 +154,21 @@ func TestParallelRenderInternals(t *testing.T) {
func TestAllTemplates(t *testing.T) { func TestAllTemplates(t *testing.T) {
ch1 := &chart.Chart{ ch1 := &chart.Chart{
Metadata: &chart.Metadata{Name: "ch1"},
Templates: []*chart.Template{ Templates: []*chart.Template{
{Name: "foo", Data: []byte("foo")}, {Name: "foo", Data: []byte("foo")},
{Name: "bar", Data: []byte("bar")}, {Name: "bar", Data: []byte("bar")},
}, },
Dependencies: []*chart.Chart{ Dependencies: []*chart.Chart{
{ {
Metadata: &chart.Metadata{Name: "laboratory mice"},
Templates: []*chart.Template{ Templates: []*chart.Template{
{Name: "pinky", Data: []byte("pinky")}, {Name: "pinky", Data: []byte("pinky")},
{Name: "brain", Data: []byte("brain")}, {Name: "brain", Data: []byte("brain")},
}, },
Dependencies: []*chart.Chart{ Dependencies: []*chart.Chart{{
{Templates: []*chart.Template{ Metadata: &chart.Metadata{Name: "same thing we do every night"},
Templates: []*chart.Template{
{Name: "innermost", Data: []byte("innermost")}, {Name: "innermost", Data: []byte("innermost")},
}}, }},
}, },
@ -301,3 +304,49 @@ global:
t.Errorf("Unexpected release: %q", out[checkrelease]) t.Errorf("Unexpected release: %q", out[checkrelease])
} }
} }
func TestRenderBuiltinValues(t *testing.T) {
inner := &chart.Chart{
Metadata: &chart.Metadata{Name: "Latium"},
Templates: []*chart.Template{
{Name: "Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
},
Values: &chart.Config{Raw: ``},
Dependencies: []*chart.Chart{},
}
outer := &chart.Chart{
Metadata: &chart.Metadata{Name: "Troy"},
Templates: []*chart.Template{
{Name: "Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
},
Values: &chart.Config{Raw: ``},
Dependencies: []*chart.Chart{inner},
}
inject := chartutil.Values{
"Values": &chart.Config{Raw: ""},
"Chart": outer.Metadata,
"Release": chartutil.Values{
"Name": "Aeneid",
},
}
t.Logf("Calculated values: %v", outer)
out, err := New().Render(outer, inject)
if err != nil {
t.Fatalf("failed to render templates: %s", err)
}
expects := map[string]string{
"Lavinia": "LaviniaLatiumAeneid",
"Aeneas": "AeneasTroyAeneid",
}
for file, expect := range expects {
if out[file] != expect {
t.Errorf("Expected %q, got %q", expect, out[file])
}
}
}

Loading…
Cancel
Save