From 22ac61469f54d5bd520f8cbed8e257c587795e3b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 22 Jun 2016 21:24:34 -0600 Subject: [PATCH 1/4] feat(*): add Values namespace to templates This adds the .Values namespace qualifier to all values --- cmd/tiller/release_server.go | 8 +++++-- .../examples/alpine/templates/alpine-pod.yaml | 4 ++-- docs/examples/nginx/templates/_helpers.tpl | 4 ++-- docs/examples/nginx/templates/configmap.yaml | 2 +- docs/examples/nginx/templates/deployment.yaml | 6 ++--- docs/examples/nginx/templates/svc.yaml | 2 +- pkg/chartutil/values.go | 3 ++- pkg/engine/engine.go | 22 ++++++++++++++++--- pkg/engine/engine_test.go | 16 ++++++++++---- 9 files changed, 48 insertions(+), 19 deletions(-) diff --git a/cmd/tiller/release_server.go b/cmd/tiller/release_server.go index 3f73129b8..bf398e436 100644 --- a/cmd/tiller/release_server.go +++ b/cmd/tiller/release_server.go @@ -238,13 +238,17 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea // Render the templates // TODO: Fix based on whether chart has `engine: SOMETHING` set. - vals, err := chartutil.CoalesceValues(req.Chart, req.Values, overrides) + //vals, err := chartutil.CoalesceValues(req.Chart, req.Values, overrides) + vals, err := chartutil.CoalesceValues(req.Chart, req.Values, nil) if err != nil { return nil, err } + overrides["Values"] = vals + renderer := s.engine(req.Chart) - files, err := renderer.Render(req.Chart, vals) + files, err := renderer.Render(req.Chart, overrides) + //files, err := renderer.Render(req.Chart, vals) if err != nil { return nil, err } diff --git a/docs/examples/alpine/templates/alpine-pod.yaml b/docs/examples/alpine/templates/alpine-pod.yaml index 2294cfd03..c15ab8efc 100644 --- a/docs/examples/alpine/templates/alpine-pod.yaml +++ b/docs/examples/alpine/templates/alpine-pod.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: Pod metadata: - name: "{{.Release.Name}}-{{.Chart.Name}}" + name: "{{.Release.Name}}-{{.Values.Name}}" labels: # The "heritage" label is used to track which tool deployed a given chart. # It is useful for admins who want to see what releases a particular tool @@ -19,7 +19,7 @@ spec: # called restartPolicy. If it is not found, it will use the default value. # {{default "Never" .restartPolicy}} is a slightly optimized version of the # more conventional syntax: {{.restartPolicy | default "Never"}} - restartPolicy: {{default "Never" .restartPolicy}} + restartPolicy: {{default "Never" .Values.restartPolicy}} containers: - name: waiter image: "alpine:3.3" diff --git a/docs/examples/nginx/templates/_helpers.tpl b/docs/examples/nginx/templates/_helpers.tpl index 1727e9b09..1af41a2e3 100644 --- a/docs/examples/nginx/templates/_helpers.tpl +++ b/docs/examples/nginx/templates/_helpers.tpl @@ -2,7 +2,7 @@ {{/* Expand the name of the chart. */}} -{{define "name"}}{{default "nginx" .nameOverride | trunc 24 }}{{end}} +{{define "name"}}{{default "nginx" .Values.nameOverride | trunc 24 }}{{end}} {{/* Create a default fully qualified app name. @@ -10,4 +10,4 @@ Create a default fully qualified app name. We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). */}} -{{define "fullname"}}{{.Release.Name}}-{{default "nginx" .nameOverride | trunc 24 }}{{end}} +{{define "fullname"}}{{.Release.Name}}-{{default "nginx" .Values.nameOverride | trunc 24 }}{{end}} diff --git a/docs/examples/nginx/templates/configmap.yaml b/docs/examples/nginx/templates/configmap.yaml index f6dc2011f..ec4b9e5c3 100644 --- a/docs/examples/nginx/templates/configmap.yaml +++ b/docs/examples/nginx/templates/configmap.yaml @@ -11,5 +11,5 @@ metadata: data: # When the config map is mounted as a volume, these will be created as # files. - index.html: {{ default "Hello" .index | quote }} + index.html: {{default "Hello" .Values.index | quote}} test.txt: test diff --git a/docs/examples/nginx/templates/deployment.yaml b/docs/examples/nginx/templates/deployment.yaml index f291aa44e..1443a5136 100644 --- a/docs/examples/nginx/templates/deployment.yaml +++ b/docs/examples/nginx/templates/deployment.yaml @@ -15,7 +15,7 @@ metadata: # This makes it easy to audit chart usage. chart: "{{.Chart.Name}}-{{.Chart.Version}}" spec: - replicas: {{ default 1 .replicaCount | quote }} + replicas: {{default 1 .Values.replicaCount | quote}} template: metadata: labels: @@ -28,8 +28,8 @@ spec: # is a nice option for the user. Especially in the strange cases like # nginx where the base distro is determined by the tag. Using :latest # is frowned upon, using :stable isn't that great either. - image: "{{default "nginx" .image}}:{{default "stable-alpine" .imageTag}}" - imagePullPolicy: {{default "IfNotPresent" .pullPolicy}} + image: "{{default "nginx" .Values.image}}:{{default "stable-alpine" .Values.imageTag}}" + imagePullPolicy: {{default "IfNotPresent" .Values.pullPolicy}} ports: - containerPort: 80 # This (and the volumes section below) mount the config map as a volume. diff --git a/docs/examples/nginx/templates/svc.yaml b/docs/examples/nginx/templates/svc.yaml index 62c55a759..dbbb3bb21 100644 --- a/docs/examples/nginx/templates/svc.yaml +++ b/docs/examples/nginx/templates/svc.yaml @@ -10,7 +10,7 @@ metadata: chart: "{{.Chart.Name}}-{{.Chart.Version}}" spec: ports: - - port: {{ default 80 .httpPort | quote }} + - port: {{default 80 .Values.httpPort | quote}} targetPort: 80 protocol: TCP name: http diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index 4a403a9ce..6aa1c5cd2 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -94,7 +94,8 @@ func tableLookup(v Values, simple string) (Values, error) { if !ok { return v, ErrNoTable } - vv, ok := v2.(map[string]interface{}) + //vv, ok := v2.(map[string]interface{}) + vv, ok := v2.(Values) if !ok { return vv, ErrNoTable } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 2499e1c77..84c46a638 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -19,6 +19,7 @@ package engine import ( "bytes" "fmt" + "log" "text/template" "github.com/Masterminds/sprig" @@ -71,6 +72,7 @@ func New() *Engine { func (e *Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { // Render the charts tmap := allTemplates(chrt, values) + fmt.Printf("%v", tmap) return e.render(tmap) } @@ -104,7 +106,7 @@ 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 { - // log.Printf("Exec %s with %v (%s)", file, tpls[file].vals, tpls[file].tpl) + log.Printf("Exec %s with %v (%s)", file, tpls[file].vals, tpls[file].tpl) if err := t.ExecuteTemplate(&buf, file, tpls[file].vals); err != nil { return map[string]string{}, fmt.Errorf("render error in %q: %s", file, err) } @@ -137,9 +139,23 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals char } 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. - tmp, err := parentVals.Table(c.Metadata.Name) + + var tmp chartutil.Values + vs, err := parentVals.Table("Values") + if err == nil { + tmp, err = vs.Table(c.Metadata.Name) + } else { + log.Printf(" *** COULD NOT FIND Values; using %s *** %q %v", c.Metadata.Name, err, parentVals) + tmp, err = parentVals.Table(c.Metadata.Name) + } + + //tmp, err := parentVals["Values"].(chartutil.Values).Table(c.Metadata.Name) if err == nil { - cvals = tmp + cvals = map[string]interface{}{ + "Values": tmp, + "Release": parentVals["Release"], + "Chart": c, + } } } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 661939d5c..39d8ab8c6 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -219,7 +219,7 @@ func TestRenderNestedValues(t *testing.T) { deepest := &chart.Chart{ Metadata: &chart.Metadata{Name: "deepest"}, Templates: []*chart.Template{ - {Name: deepestpath, Data: []byte(`And this same {{.what}} that smiles {{.global.when}}`)}, + {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, }, Values: &chart.Config{Raw: `what: "milkshake"`}, } @@ -227,7 +227,7 @@ func TestRenderNestedValues(t *testing.T) { inner := &chart.Chart{ Metadata: &chart.Metadata{Name: "herrick"}, Templates: []*chart.Template{ - {Name: innerpath, Data: []byte(`Old {{.who}} is still a-flyin'`)}, + {Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)}, }, Values: &chart.Config{Raw: `who: "Robert"`}, Dependencies: []*chart.Chart{deepest}, @@ -236,7 +236,7 @@ func TestRenderNestedValues(t *testing.T) { outer := &chart.Chart{ Metadata: &chart.Metadata{Name: "top"}, Templates: []*chart.Template{ - {Name: outerpath, Data: []byte(`Gather ye {{.what}} while ye may`)}, + {Name: outerpath, Data: []byte(`Gather ye {{.Values.what}} while ye may`)}, }, Values: &chart.Config{ Raw: ` @@ -258,11 +258,19 @@ global: when: to-day`, } - inject, err := chartutil.CoalesceValues(outer, &injValues, map[string]interface{}{}) + tmp, err := chartutil.CoalesceValues(outer, &injValues, map[string]interface{}{}) if err != nil { t.Fatalf("Failed to coalesce values: %s", err) } + inject := chartutil.Values{ + "Values": tmp, + "Chart": outer.Metadata, + "Release": chartutil.Values{ + "Name": "Robert", + }, + } + t.Logf("Calculated values: %v", inject) out, err := e.Render(outer, inject) From 90c46e896de13064d3c8d5b36e0cbf1a68cccd9b Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 23 Jun 2016 15:18:24 -0600 Subject: [PATCH 2/4] fix(chartutil): fix Table() method to test Values This makes the Table() method more flexible than the original version. It allows either a map[string]interface{} or a chartutil.Values to be treated as a table. --- pkg/chartutil/values.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index 6aa1c5cd2..3987c5a16 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -94,12 +94,18 @@ func tableLookup(v Values, simple string) (Values, error) { if !ok { return v, ErrNoTable } - //vv, ok := v2.(map[string]interface{}) - vv, ok := v2.(Values) - if !ok { - return vv, ErrNoTable + if vv, ok := v2.(map[string]interface{}); ok { + return vv, nil + } + + // This catches a case where a value is of type Values, but doesn't (for some + // reason) match the map[string]interface{}. This has been observed in the + // wild, and might be a result of a nil map of type Values. + if vv, ok := v2.(Values); ok { + return vv, nil } - return vv, nil + + return map[string]interface{}{}, ErrNoTable } // ReadValues will parse YAML byte data into a Values. From e00f774a5d6197a6ff57e040cb29f130de216bb0 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 23 Jun 2016 15:48:50 -0600 Subject: [PATCH 3/4] fix(engine): remove debugging output --- pkg/engine/engine.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 84c46a638..f5cd9df21 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -19,7 +19,6 @@ package engine import ( "bytes" "fmt" - "log" "text/template" "github.com/Masterminds/sprig" @@ -106,7 +105,6 @@ 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 { - log.Printf("Exec %s with %v (%s)", file, tpls[file].vals, tpls[file].tpl) if err := t.ExecuteTemplate(&buf, file, tpls[file].vals); err != nil { return map[string]string{}, fmt.Errorf("render error in %q: %s", file, err) } @@ -145,7 +143,6 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals char if err == nil { tmp, err = vs.Table(c.Metadata.Name) } else { - log.Printf(" *** COULD NOT FIND Values; using %s *** %q %v", c.Metadata.Name, err, parentVals) tmp, err = parentVals.Table(c.Metadata.Name) } @@ -159,7 +156,6 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals char } } - //log.Printf("racAllTpls values: %v", cvals) for _, child := range c.Dependencies { recAllTpls(child, templates, cvals, false) } From 225d3a8adcd96032ef98beb798f356ffb016fe5a Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 23 Jun 2016 16:06:53 -0600 Subject: [PATCH 4/4] test(engine): add test case for deep release passing --- cmd/tiller/release_server.go | 2 -- pkg/engine/engine_test.go | 8 +++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/tiller/release_server.go b/cmd/tiller/release_server.go index bf398e436..c422cb0a7 100644 --- a/cmd/tiller/release_server.go +++ b/cmd/tiller/release_server.go @@ -238,7 +238,6 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea // Render the templates // TODO: Fix based on whether chart has `engine: SOMETHING` set. - //vals, err := chartutil.CoalesceValues(req.Chart, req.Values, overrides) vals, err := chartutil.CoalesceValues(req.Chart, req.Values, nil) if err != nil { return nil, err @@ -248,7 +247,6 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea renderer := s.engine(req.Chart) files, err := renderer.Render(req.Chart, overrides) - //files, err := renderer.Render(req.Chart, vals) if err != nil { return nil, err } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 39d8ab8c6..e8c8e54cc 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -215,11 +215,13 @@ func TestRenderNestedValues(t *testing.T) { innerpath := "charts/inner/templates/inner.tpl" outerpath := "templates/outer.tpl" deepestpath := "charts/inner/charts/deepest/templates/deepest.tpl" + checkrelease := "charts/inner/charts/deepest/templates/release.tpl" deepest := &chart.Chart{ Metadata: &chart.Metadata{Name: "deepest"}, Templates: []*chart.Template{ {Name: deepestpath, Data: []byte(`And this same {{.Values.what}} that smiles {{.Values.global.when}}`)}, + {Name: checkrelease, Data: []byte(`Tomorrow will be {{default "happy" .Release.Name }}`)}, }, Values: &chart.Config{Raw: `what: "milkshake"`}, } @@ -267,7 +269,7 @@ global: "Values": tmp, "Chart": outer.Metadata, "Release": chartutil.Values{ - "Name": "Robert", + "Name": "dyin", }, } @@ -289,4 +291,8 @@ global: if out[deepestpath] != "And this same flower that smiles to-day" { t.Errorf("Unexpected deepest: %q", out[deepestpath]) } + + if out[checkrelease] != "Tomorrow will be dyin" { + t.Errorf("Unexpected release: %q", out[checkrelease]) + } }