Last active
March 2, 2024 14:54
-
-
Save dcwangmit01/e9e58dd123e0a8eb48c2f4e8ffa16d5a to your computer and use it in GitHub Desktop.
Self-templating with golang text/template and gomplate
This file contains hidden or 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
/* | |
Demo program illustrating usage of gomplate as a library. | |
This program will accept an input file and run it through the templating | |
engine as both the values dictionary as well as the template file. It will | |
repeat the process until the templating result stops changing, and print | |
the result to STDOUT. | |
This program add the gomplate datasources and functions to the golang | |
text/template engine. This requires the input to be have the gomplate | |
datasources and datasourceheaders to be set. | |
# Create the template-and-values.yaml file | |
cat > template-and-values.yaml << EOF | |
gomplate: | |
datasources: | |
- "file_obj=./test-person.json" | |
- "http_obj=https://httpbin.org/get" | |
datasourceheaders: [] | |
values: | |
a: foo | |
b: '[[ .values.a ]]bar' # foobar | |
c: '[[ .values.b ]]baz' # foobarbaz | |
d: 'Http datasource: [[ (ds "http_obj").headers.Host ]]' | |
e: 'File datasource: [[ (ds "file_obj").name ]]' | |
f: 'Func Test: [[ net.LookupIP "example.com" ]]' | |
EOF | |
# Create the test-person.json file | |
cat > test-person.json << EOF | |
{ | |
"name": "Dave" | |
} | |
EOF | |
# Compile the program | |
go build -o demo main.go | |
# Run the program | |
./demo template-and-values.yaml | |
*/ | |
package main | |
import ( | |
"bytes" | |
"errors" | |
"fmt" | |
"io/ioutil" | |
"os" | |
"text/template" | |
"github.com/hairyhenderson/gomplate" | |
"github.com/hairyhenderson/gomplate/data" | |
"github.com/ghodss/yaml" | |
) | |
type Values map[string]interface{} | |
func main() { | |
fileName := os.Args[1] | |
data, err := ioutil.ReadFile(fileName) | |
if err != nil { | |
fmt.Printf("Failed to read fileName %v", fileName) | |
} | |
contents := string(data) | |
renderedContents, err := selfRender(contents, true) | |
if err != nil { | |
fmt.Printf("Failed to selfRender %v", contents) | |
} | |
fmt.Print(renderedContents) | |
} | |
func convertInterfaceListToStringList(l []interface{}) ([]string) { | |
s := make([]string, len(l)) | |
for i, v := range l { | |
s[i] = fmt.Sprint(v) | |
} | |
return s | |
} | |
func selfRender(templateValuesStr string, enableGomplate bool) (string, error) { | |
/* | |
This function will accept an input string and run it through the | |
templating engine as both the values dictionary as well as the | |
template string. It will repeat the process until the templating | |
result stops changing. | |
*/ | |
lastRender := templateValuesStr | |
for i := 0; i < 10; i++ { | |
// Unmarshal the file as a values dict | |
vals := Values{} | |
err := yaml.Unmarshal([]byte(templateValuesStr), &vals) | |
if err != nil { panic(err) } | |
tmpl := template.New("SelfTemplate") | |
tmpl.Delims("[[", "]]") | |
if enableGomplate { | |
// Read the defined datasources and datasourcehaders | |
// from the values dict | |
dataSources := []string{} | |
dataSourceHeaders := []string{} | |
if _, ok := vals["gomplate"]; ok { | |
tmp := vals["gomplate"].(map[string]interface{}) | |
if _, ok := tmp["datasources"]; ok { | |
dataSources = convertInterfaceListToStringList( | |
tmp["datasources"].([]interface{})) | |
} | |
if _, ok := tmp["datasources"]; ok { | |
dataSourceHeaders = convertInterfaceListToStringList( | |
tmp["datasourceheaders"].([]interface{})) | |
} | |
} | |
// Access gomplate datasources and function library | |
d, err := data.NewData(dataSources, dataSourceHeaders) | |
if err != nil { panic(err) } | |
// Configure go/text/template to use gomplate | |
// datasources and function library. | |
tmpl.Option("missingkey=error") | |
tmpl.Funcs(gomplate.Funcs(d)) | |
} | |
// Run the the file through the tempating engine as both values | |
// file and template file | |
tmpl.Parse(string(templateValuesStr)) | |
if err != nil { panic(err) } | |
out := new(bytes.Buffer) | |
err = tmpl.Execute(out, vals) | |
if err != nil { panic(err) } | |
newRender := out.String() | |
if lastRender == newRender { | |
return newRender, nil // self-templating succeeded | |
} else { | |
lastRender = newRender | |
templateValuesStr = newRender | |
} | |
} | |
return templateValuesStr, errors.New("Self-templating failed") | |
} |
This file contains hidden or 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
$ ./demo template-and-values.yaml | |
gomplate: | |
datasources: | |
- "file_obj=./test-person.json" | |
- "http_obj=https://httpbin.org/get" | |
datasourceheaders: [] | |
values: | |
a: foo | |
b: 'foobar' # foobar | |
c: 'foobarbaz' # foobarbaz | |
d: 'Http datasource: httpbin.org' | |
e: 'File datasource: Dave' | |
f: 'Func Test: 10.114.236.103' |
This file contains hidden or 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
gomplate: | |
datasources: | |
- "file_obj=./test-person.json" | |
- "http_obj=https://httpbin.org/get" | |
datasourceheaders: [] | |
values: | |
a: foo | |
b: '[[ .values.a ]]bar' # foobar | |
c: '[[ .values.b ]]baz' # foobarbaz | |
d: 'Http datasource: [[ (ds "http_obj").headers.Host ]]' | |
e: 'File datasource: [[ (ds "file_obj").name ]]' | |
f: 'Func Test: [[ net.LookupIP "example.com" ]]' |
This file contains hidden or 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
{ | |
"name": "Dave" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment