Last active
March 27, 2016 00:11
-
-
Save sparkprime/3f9887a77523a8b4094e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"github.com/Masterminds/sprig" | |
"github.com/cloudfoundry-incubator/candiedyaml" | |
"github.com/ghodss/yaml" | |
"io" | |
"strings" | |
"text/template" | |
) | |
// parseYAMLStream takes an encoded YAML stream and turns it into a slice of JSON-marshalable | |
// objects, one for each document in the stream. | |
func parseYAMLStream(in io.Reader) ([]interface{}, error) { | |
// Use candiedyaml because it's the only one that supports streams. | |
decoder := candiedyaml.NewDecoder(in) | |
var document interface{} | |
stream := []interface{}{} | |
for { | |
err := decoder.Decode(&document) | |
if err != nil { | |
if strings.Contains(err.Error(), "Expected document start at line") { | |
return stream, nil | |
} | |
return nil, err | |
} | |
// Now it's held in document but we have to do a bit of a dance to get it in a form that can | |
// be marshaled as JSON. Luckily, ghodss/yaml has code to help with this: | |
// 1) Marshal it // back to YAML string. | |
yamlBytes, err := candiedyaml.Marshal(document) | |
if err != nil { | |
return nil, err | |
} | |
var jsonObj interface{} | |
// 2) Use ghodss/yaml to unmarshal it into JSON-compatible data structures. | |
err = yaml.Unmarshal(yamlBytes, &jsonObj) | |
if err != nil { | |
return nil, err | |
} | |
stream = append(stream, jsonObj) | |
} | |
} | |
type file struct { | |
path string | |
content []byte | |
} | |
// TODO(dcunnin): Remove non-hermetic functions | |
// TODO(dcunnin): JSONSchema | |
// executeAllTemplates resolves the given files to a sequence of JSON-marshalable values. | |
func executeAllTemplates(files []file, params interface{}) ([]interface{}, error) { | |
resources := []interface{}{} | |
for _, file := range files { | |
name := file.path | |
content := file.content | |
tmpl := template.New(name).Funcs(sprig.TxtFuncMap()) | |
for _, otherFile := range files { | |
otherName := otherFile.path | |
otherContent := otherFile.content | |
if name == otherName { | |
continue | |
} | |
_, err := tmpl.Parse(string(otherContent)) | |
if err != nil { | |
return nil, err | |
} | |
} | |
// Have to put something in that resolves non-empty or Go templates get confused. | |
_, err := tmpl.Parse("# Content begins now") | |
if err != nil { | |
return nil, err | |
} | |
tmpl, err = tmpl.Parse(string(content)) | |
if err != nil { | |
return nil, err | |
} | |
generated := bytes.NewBuffer(nil) | |
err = tmpl.ExecuteTemplate(generated, name, params) | |
if err != nil { | |
return nil, err | |
} | |
stream, err := parseYAMLStream(generated) | |
if err != nil { | |
return nil, fmt.Errorf("%s\nContent:\n%s", err.Error(), generated) | |
} | |
for _, doc := range stream { | |
resources = append(resources, doc) | |
} | |
} | |
return resources, nil | |
} | |
func main() { | |
files := []file{ | |
{ | |
"templates/bar.tmpl", | |
[]byte(` | |
{{ template "banana" . }} | |
`), | |
}, | |
{ | |
"templates/base.tmpl", | |
[]byte(` | |
{{ define "apple" }} | |
name: Abby | |
kind: Apple | |
dbname: {{default "whatdb" .DatabaseName}} | |
{{ end }} | |
{{ define "banana" }} | |
name: Bobby | |
kind: Banana | |
dbname: {{default "whatdb" .DatabaseName}} | |
{{ end }} | |
`), | |
}, | |
{ | |
"templates/foo.tmpl", | |
[]byte(` | |
--- | |
foo: | |
bar: baz | |
--- | |
{{ template "apple" . }} | |
--- | |
{{ template "apple" . }} | |
... | |
`), | |
}, | |
{ | |
"templates/docs.txt", | |
[]byte(` | |
{{/* | |
File contains only a comment. Suitable for documentation within templates/ | |
*/}} | |
`), | |
}, | |
} | |
type ChartParams struct { | |
DatabaseName string | |
NumReplicas int | |
} | |
params := ChartParams{"mydb", 3} | |
resources, err := executeAllTemplates(files, params) | |
if err != nil { | |
panic(err) | |
} | |
for i, resource := range resources { | |
json_bytes, err := json.MarshalIndent(resource, "", " ") | |
if err != nil { | |
panic(err) | |
} | |
fmt.Printf("resource[%d]: %s\n", i, string(json_bytes)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
output: