diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index 3987c5a16..690162880 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -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 { diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 9d46b02c9..737e92abd 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -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 "" 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, } } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 28de0a779..b11603e4f 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -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]) + } + } + +}