Commit 033dbfe7 authored by Matt Butcher's avatar Matt Butcher

feat(tiller): add Files map to templates

Templates can now access the non-template files in a chart by using
the '{{.Files}}' map inside of a template.

Relates to #950
parent bd5a9753
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package chartutil
import (
"github.com/golang/protobuf/ptypes/any"
)
// Files is a map of files in a chart that can be accessed from a template.
type Files map[string][]byte
// NewFiles creates a new Files from chart files.
// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
func NewFiles(from []*any.Any) Files {
files := map[string][]byte{}
for _, f := range from {
files[f.TypeUrl] = f.Value
}
return files
}
// Get a file by path.
//
// This is intended to be accessed from within a template, so a missed key returns
// an empty []byte.
func (f Files) Get(name string) []byte {
v, ok := f[name]
if !ok {
return []byte{}
}
return v
}
// GetString returns a string representation of the given file.
//
// This is a convenience for the otherwise cumbersome template logic
// for '{{.Files.Get "foo" | printf "%s"}}'.
func (f Files) GetString(name string) string {
return string(f.Get(name))
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package chartutil
import (
"testing"
"github.com/golang/protobuf/ptypes/any"
)
func TestNewFiles(t *testing.T) {
cases := []struct {
path, data string
}{
{"ship/captain.txt", "The Captain"},
{"ship/stowaway.txt", "Legatt"},
{"story/name.txt", "The Secret Sharer"},
{"story/author.txt", "Joseph Conrad"},
}
a := []*any.Any{}
for _, c := range cases {
a = append(a, &any.Any{TypeUrl: c.path, Value: []byte(c.data)})
}
files := NewFiles(a)
if len(files) != len(cases) {
t.Errorf("Expected len() = %d, got %d", len(cases), len(files))
}
for i, f := range cases {
if got := string(files.Get(f.path)); got != f.data {
t.Errorf("%d: expected %q, got %q", i, f.data, got)
}
if got := files.GetString(f.path); got != f.data {
t.Errorf("%d: expected %q, got %q", i, f.data, got)
}
}
}
...@@ -299,6 +299,7 @@ type ReleaseOptions struct { ...@@ -299,6 +299,7 @@ type ReleaseOptions struct {
// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files // ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files
func ToRenderValues(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOptions) (Values, error) { func ToRenderValues(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOptions) (Values, error) {
top := map[string]interface{}{ top := map[string]interface{}{
"Release": map[string]interface{}{ "Release": map[string]interface{}{
"Name": options.Name, "Name": options.Name,
...@@ -307,6 +308,7 @@ func ToRenderValues(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOp ...@@ -307,6 +308,7 @@ func ToRenderValues(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOp
"Service": "Tiller", "Service": "Tiller",
}, },
"Chart": chrt.Metadata, "Chart": chrt.Metadata,
"Files": NewFiles(chrt.Files),
} }
vals, err := CoalesceValues(chrt, chrtVals) vals, err := CoalesceValues(chrt, chrtVals)
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"testing" "testing"
"text/template" "text/template"
"github.com/golang/protobuf/ptypes/any"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/timeconv" "k8s.io/helm/pkg/timeconv"
) )
...@@ -93,6 +94,9 @@ where: ...@@ -93,6 +94,9 @@ where:
Values: &chart.Config{Raw: ""}, Values: &chart.Config{Raw: ""},
}, },
}, },
Files: []*any.Any{
{TypeUrl: "scheherazade/shahryar.txt", Value: []byte("1,001 Nights")},
},
} }
v := &chart.Config{Raw: overideValues} v := &chart.Config{Raw: overideValues}
...@@ -106,6 +110,18 @@ where: ...@@ -106,6 +110,18 @@ where:
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Ensure that the top-level values are all set.
if name := res["Chart"].(*chart.Metadata).Name; name != "test" {
t.Errorf("Expected chart name 'test', got %q", name)
}
if name := res["Release"].(map[string]interface{})["Name"]; fmt.Sprint(name) != "Seven Voyages" {
t.Errorf("Expected release name 'Seven Voyages', got %q", name)
}
if data := res["Files"].(Files)["scheherazade/shahryar.txt"]; string(data) != "1,001 Nights" {
t.Errorf("Expected file '1,001 Nights', got %q", string(data))
}
var vals Values var vals Values
vals = res["Values"].(Values) vals = res["Values"].(Values)
......
...@@ -170,6 +170,7 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals char ...@@ -170,6 +170,7 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals char
"Values": newVals, "Values": newVals,
"Release": parentVals["Release"], "Release": parentVals["Release"],
"Chart": c.Metadata, "Chart": c.Metadata,
"Files": chartutil.NewFiles(c.Files),
} }
} }
......
...@@ -23,6 +23,8 @@ import ( ...@@ -23,6 +23,8 @@ import (
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"github.com/golang/protobuf/ptypes/any"
) )
func TestEngine(t *testing.T) { func TestEngine(t *testing.T) {
...@@ -310,9 +312,14 @@ func TestRenderBuiltinValues(t *testing.T) { ...@@ -310,9 +312,14 @@ func TestRenderBuiltinValues(t *testing.T) {
Metadata: &chart.Metadata{Name: "Latium"}, Metadata: &chart.Metadata{Name: "Latium"},
Templates: []*chart.Template{ Templates: []*chart.Template{
{Name: "Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, {Name: "Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
{Name: "From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.GetString "book/title.txt"}}`)},
}, },
Values: &chart.Config{Raw: ``}, Values: &chart.Config{Raw: ``},
Dependencies: []*chart.Chart{}, Dependencies: []*chart.Chart{},
Files: []*any.Any{
{TypeUrl: "author", Value: []byte("Virgil")},
{TypeUrl: "book/title.txt", Value: []byte("Aeneid")},
},
} }
outer := &chart.Chart{ outer := &chart.Chart{
...@@ -342,6 +349,7 @@ func TestRenderBuiltinValues(t *testing.T) { ...@@ -342,6 +349,7 @@ func TestRenderBuiltinValues(t *testing.T) {
expects := map[string]string{ expects := map[string]string{
"Troy/charts/Latium/Lavinia": "Troy/charts/Latium/LaviniaLatiumAeneid", "Troy/charts/Latium/Lavinia": "Troy/charts/Latium/LaviniaLatiumAeneid",
"Troy/Aeneas": "Troy/AeneasTroyAeneid", "Troy/Aeneas": "Troy/AeneasTroyAeneid",
"Troy/charts/Latium/From": "Virgil Aeneid",
} }
for file, expect := range expects { for file, expect := range expects {
if out[file] != expect { if out[file] != expect {
......
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