diff --git a/pkg/chartutil/testdata/albatross/values.toml b/pkg/chartutil/testdata/albatross/values.toml deleted file mode 100644 index 0ef7eb2f9..000000000 --- a/pkg/chartutil/testdata/albatross/values.toml +++ /dev/null @@ -1 +0,0 @@ -albatross = "true" diff --git a/pkg/chartutil/testdata/albatross/values.yaml b/pkg/chartutil/testdata/albatross/values.yaml new file mode 100644 index 000000000..0acfa292f --- /dev/null +++ b/pkg/chartutil/testdata/albatross/values.yaml @@ -0,0 +1 @@ +albatross: "true" diff --git a/pkg/chartutil/testdata/coleridge.yaml b/pkg/chartutil/testdata/coleridge.yaml new file mode 100644 index 000000000..b6579628b --- /dev/null +++ b/pkg/chartutil/testdata/coleridge.yaml @@ -0,0 +1,12 @@ +poet: "Coleridge" +title: "Rime of the Ancient Mariner" +stanza: ["at", "length", "did", "cross", "an", "Albatross"] + +mariner: + with: "crossbow" + shot: "ALBATROSS" + +water: + water: + where: "everywhere" + nor: "any drop to drink" diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz index c908b2264..80c0a2502 100644 Binary files a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz and b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.toml b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml similarity index 100% rename from pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.toml rename to pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/values.toml b/pkg/chartutil/testdata/frobnitz/charts/alpine/values.toml deleted file mode 100644 index 504e6e1be..000000000 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/values.toml +++ /dev/null @@ -1,2 +0,0 @@ -# The pod name -name = "my-alpine" diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml new file mode 100644 index 000000000..6c2aab7ba --- /dev/null +++ b/pkg/chartutil/testdata/frobnitz/charts/alpine/values.yaml @@ -0,0 +1,2 @@ +# The pod name +name: "my-alpine" diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz index d4f19e624..3b6f987b3 100644 Binary files a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz/values.toml b/pkg/chartutil/testdata/frobnitz/values.toml deleted file mode 100644 index 6fc24051f..000000000 --- a/pkg/chartutil/testdata/frobnitz/values.toml +++ /dev/null @@ -1,6 +0,0 @@ -# A values file contains configuration. - -name = "Some Name" - -[section] -name = "Name in a section" diff --git a/pkg/chartutil/testdata/frobnitz/values.yaml b/pkg/chartutil/testdata/frobnitz/values.yaml new file mode 100644 index 000000000..61f501258 --- /dev/null +++ b/pkg/chartutil/testdata/frobnitz/values.yaml @@ -0,0 +1,6 @@ +# A values file contains configuration. + +name: "Some Name" + +section: + name: "Name in a section" diff --git a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz index ed3c1aee9..6795b2d9e 100644 Binary files a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz and b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/mariner/values.toml b/pkg/chartutil/testdata/mariner/values.yaml similarity index 53% rename from pkg/chartutil/testdata/mariner/values.toml rename to pkg/chartutil/testdata/mariner/values.yaml index 4a7bbf8e4..e1609e243 100644 --- a/pkg/chartutil/testdata/mariner/values.toml +++ b/pkg/chartutil/testdata/mariner/values.yaml @@ -1,4 +1,4 @@ # Default values for mariner. -# This is a TOML-formatted file. https://github.com/toml-lang/toml +# This is a YAML-formatted file. https://github.com/toml-lang/toml # Declare name/value pairs to be passed into your templates. -# name = "value" +# name: "value" diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go new file mode 100644 index 000000000..904b9f2de --- /dev/null +++ b/pkg/chartutil/values.go @@ -0,0 +1,81 @@ +package chartutil + +import ( + "errors" + "io" + "io/ioutil" + "strings" + + "github.com/ghodss/yaml" +) + +// ErrNoTable indicates that a chart does not have a matching table. +var ErrNoTable = errors.New("no table") + +// Values represents a collection of chart values. +type Values map[string]interface{} + +// Table gets a table (YAML subsection) from a Values object. +// +// The table is returned as a Values. +// +// Compound table names may be specified with dots: +// +// foo.bar +// +// The above will be evaluated as "The table bar inside the table +// foo". +// +// An ErrNoTable is returned if the table does not exist. +func (v Values) Table(name string) (Values, error) { + names := strings.Split(name, ".") + table := v + var err error + + for _, n := range names { + table, err = tableLookup(table, n) + if err != nil { + return table, err + } + } + return table, err +} + +// Encode writes serialized Values information to the given io.Writer. +func (v Values) Encode(w io.Writer) error { + //return yaml.NewEncoder(w).Encode(v) + out, err := yaml.Marshal(v) + if err != nil { + return err + } + _, err = w.Write(out) + return err +} + +func tableLookup(v Values, simple string) (Values, error) { + v2, ok := v[simple] + if !ok { + return v, ErrNoTable + } + vv, ok := v2.(map[string]interface{}) + if !ok { + return vv, ErrNoTable + } + return vv, nil +} + +// ReadValues will parse YAML byte data into a Values. +func ReadValues(data []byte) (Values, error) { + out := map[string]interface{}{} + err := yaml.Unmarshal(data, &out) + return out, err +} + +// ReadValuesFile will parse a YAML file into a Values. +func ReadValuesFile(filename string) (Values, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return map[string]interface{}{}, err + } + return ReadValues(data) +} diff --git a/pkg/chartutil/values_test.go b/pkg/chartutil/values_test.go new file mode 100644 index 000000000..6cf69e3f6 --- /dev/null +++ b/pkg/chartutil/values_test.go @@ -0,0 +1,141 @@ +package chartutil + +import ( + "bytes" + "fmt" + "testing" + "text/template" +) + +func TestReadValues(t *testing.T) { + doc := `# Test YAML parse +poet: "Coleridge" +title: "Rime of the Ancient Mariner" +stanza: + - "at" + - "length" + - "did" + - cross + - an + - Albatross + +mariner: + with: "crossbow" + shot: "ALBATROSS" + +water: + water: + where: "everywhere" + nor: "any drop to drink" +` + + data, err := ReadValues([]byte(doc)) + if err != nil { + t.Fatalf("Error parsing bytes: %s", err) + } + matchValues(t, data) +} + +func TestReadValuesFile(t *testing.T) { + data, err := ReadValuesFile("./testdata/coleridge.yaml") + if err != nil { + t.Fatalf("Error reading YAML file: %s", err) + } + matchValues(t, data) +} + +func ExampleValues() { + doc := ` +title: "Moby Dick" +chapter: + one: + title: "Loomings" + two: + title: "The Carpet-Bag" + three: + title: "The Spouter Inn" +` + d, err := ReadValues([]byte(doc)) + if err != nil { + panic(err) + } + ch1, err := d.Table("chapter.one") + if err != nil { + panic("could not find chapter one") + } + fmt.Print(ch1["title"]) + // Output: + // Loomings +} + +func TestTable(t *testing.T) { + doc := ` +title: "Moby Dick" +chapter: + one: + title: "Loomings" + two: + title: "The Carpet-Bag" + three: + title: "The Spouter Inn" +` + d, err := ReadValues([]byte(doc)) + if err != nil { + t.Fatalf("Failed to parse the White Whale: %s", err) + } + + if _, err := d.Table("title"); err == nil { + t.Fatalf("Title is not a table.") + } + + if _, err := d.Table("chapter"); err != nil { + t.Fatalf("Failed to get the chapter table: %s\n%v", err, d) + } + + if v, err := d.Table("chapter.one"); err != nil { + t.Errorf("Failed to get chapter.one: %s", err) + } else if v["title"] != "Loomings" { + t.Errorf("Unexpected title: %s", v["title"]) + } + + if _, err := d.Table("chapter.three"); err != nil { + t.Errorf("Chapter three is missing: %s\n%v", err, d) + } + + if _, err := d.Table("chapter.OneHundredThirtySix"); err == nil { + t.Errorf("I think you mean 'Epilogue'") + } +} + +func matchValues(t *testing.T, data map[string]interface{}) { + if data["poet"] != "Coleridge" { + t.Errorf("Unexpected poet: %s", data["poet"]) + } + + if o, err := ttpl("{{len .stanza}}", data); err != nil { + t.Errorf("len stanza: %s", err) + } else if o != "6" { + t.Errorf("Expected 6, got %s", o) + } + + if o, err := ttpl("{{.mariner.shot}}", data); err != nil { + t.Errorf(".mariner.shot: %s", err) + } else if o != "ALBATROSS" { + t.Errorf("Expected that mariner shot ALBATROSS") + } + + if o, err := ttpl("{{.water.water.where}}", data); err != nil { + t.Errorf(".water.water.where: %s", err) + } else if o != "everywhere" { + t.Errorf("Expected water water everywhere") + } +} + +func ttpl(tpl string, v map[string]interface{}) (string, error) { + var b bytes.Buffer + tt := template.Must(template.New("t").Parse(tpl)) + if err := tt.Execute(&b, v); err != nil { + return "", err + } + return b.String(), nil +}