Commit 532f03ec authored by Matt Butcher's avatar Matt Butcher

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
parent ab4e20c2
......@@ -17,7 +17,7 @@ limitations under the License.
package chartutil
import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
......@@ -28,7 +28,7 @@ import (
)
// 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.
const GlobalKey = "global"
......@@ -92,7 +92,7 @@ func (v Values) Encode(w io.Writer) error {
func tableLookup(v Values, simple string) (Values, error) {
v2, ok := v[simple]
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 {
return vv, nil
......@@ -105,7 +105,8 @@ func tableLookup(v Values, simple string) (Values, error) {
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.
......@@ -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
// the values destined for its dependencies.
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
// the passed-in values are in the same namespace as the parent chart.
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))
var buf bytes.Buffer
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)
}
// 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
// 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
// scope.
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 this is the top of the rendering tree, assume that parentVals
// is already resolved to the authoritative values.
cvals = parentVals
} else if c.Metadata != nil && c.Metadata.Name != "" {
// An error indicates that the table doesn't exist. So we leave it as
// an empty map.
var tmp chartutil.Values
vs, err := parentVals.Table("Values")
if err == nil {
tmp, err = vs.Table(c.Metadata.Name)
} else {
tmp, err = parentVals.Table(c.Metadata.Name)
// If there is a {{.Values.ThisChart}} in the parent metadata,
// copy that into the {{.Values}} for this template.
newVals := chartutil.Values{}
if vs, err := parentVals.Table("Values"); err == nil {
if tmp, err := vs.Table(c.Metadata.Name); err == nil {
newVals = tmp
}
}
//tmp, err := parentVals["Values"].(chartutil.Values).Table(c.Metadata.Name)
if err == nil {
cvals = map[string]interface{}{
"Values": tmp,
"Release": parentVals["Release"],
"Chart": c,
}
cvals = map[string]interface{}{
"Values": newVals,
"Release": parentVals["Release"],
"Chart": c.Metadata,
}
}
......
......@@ -154,18 +154,21 @@ func TestParallelRenderInternals(t *testing.T) {
func TestAllTemplates(t *testing.T) {
ch1 := &chart.Chart{
Metadata: &chart.Metadata{Name: "ch1"},
Templates: []*chart.Template{
{Name: "foo", Data: []byte("foo")},
{Name: "bar", Data: []byte("bar")},
},
Dependencies: []*chart.Chart{
{
Metadata: &chart.Metadata{Name: "laboratory mice"},
Templates: []*chart.Template{
{Name: "pinky", Data: []byte("pinky")},
{Name: "brain", Data: []byte("brain")},
},
Dependencies: []*chart.Chart{
{Templates: []*chart.Template{
Dependencies: []*chart.Chart{{
Metadata: &chart.Metadata{Name: "same thing we do every night"},
Templates: []*chart.Template{
{Name: "innermost", Data: []byte("innermost")},
}},
},
......@@ -301,3 +304,49 @@ global:
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])
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment