From 16a1041d2e45c76bee11c43eb5cfa761089d22a5 Mon Sep 17 00:00:00 2001 From: Chris Goller <goller@gmail.com> Date: Tue, 19 Dec 2017 15:06:24 -0600 Subject: [PATCH] Add env var golang templates to filestore rendering --- filestore/dashboards.go | 3 +- filestore/environ.go | 24 ++++++++++++++ filestore/environ_test.go | 29 +++++++++++++++++ filestore/templates.go | 28 ++++++++++++++++ filestore/templates_test.go | 64 +++++++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 filestore/environ.go create mode 100644 filestore/environ_test.go create mode 100644 filestore/templates.go create mode 100644 filestore/templates_test.go diff --git a/filestore/dashboards.go b/filestore/dashboards.go index 6a73a1d11..9b8fc6578 100644 --- a/filestore/dashboards.go +++ b/filestore/dashboards.go @@ -47,10 +47,11 @@ func dashboardFile(dir string, dashboard chronograf.Dashboard) string { } func load(name string, resource interface{}) error { - octets, err := ioutil.ReadFile(name) + octets, err := templatedFromEnv(name) if err != nil { return fmt.Errorf("resource %s not found", name) } + return json.Unmarshal(octets, resource) } diff --git a/filestore/environ.go b/filestore/environ.go new file mode 100644 index 000000000..091e179e8 --- /dev/null +++ b/filestore/environ.go @@ -0,0 +1,24 @@ +package filestore + +import ( + "os" + "strings" +) + +var env map[string]string + +// environ returns a map of all environment variables in the running process +func environ() map[string]string { + if env == nil { + env = make(map[string]string) + envVars := os.Environ() + for _, envVar := range envVars { + kv := strings.SplitN(envVar, "=", 2) + if len(kv) != 2 { + continue + } + env[kv[0]] = kv[1] + } + } + return env +} diff --git a/filestore/environ_test.go b/filestore/environ_test.go new file mode 100644 index 000000000..689484806 --- /dev/null +++ b/filestore/environ_test.go @@ -0,0 +1,29 @@ +package filestore + +import ( + "os" + "testing" +) + +func Test_environ(t *testing.T) { + tests := []struct { + name string + key string + value string + }{ + { + name: "environment variable is returned", + key: "CHRONOGRAF_TEST_ENVIRON", + value: "howdy", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + os.Setenv(tt.key, tt.value) + got := environ() + if v, ok := got[tt.key]; !ok || v != tt.value { + t.Errorf("environ() = %v, want %v", v, tt.value) + } + }) + } +} diff --git a/filestore/templates.go b/filestore/templates.go new file mode 100644 index 000000000..fc0e1ffc4 --- /dev/null +++ b/filestore/templates.go @@ -0,0 +1,28 @@ +package filestore + +import ( + "bytes" + "html/template" +) + +// templated returns all files templated using data +func templated(data interface{}, filenames ...string) ([]byte, error) { + t, err := template.ParseFiles(filenames...) + if err != nil { + return nil, err + } + var b bytes.Buffer + // If a key in the file exists but is not in the data we + // immediately fail with a missing key error + err = t.Option("missingkey=error").Execute(&b, data) + if err != nil { + return nil, err + } + + return b.Bytes(), nil +} + +// templatedFromEnv returns all files templated against environment variables +func templatedFromEnv(filenames ...string) ([]byte, error) { + return templated(environ(), filenames...) +} diff --git a/filestore/templates_test.go b/filestore/templates_test.go new file mode 100644 index 000000000..5d5b82f5d --- /dev/null +++ b/filestore/templates_test.go @@ -0,0 +1,64 @@ +package filestore + +import ( + "io/ioutil" + "os" + "reflect" + "testing" +) + +func Test_templated(t *testing.T) { + tests := []struct { + name string + content []string + data interface{} + want []byte + wantErr bool + }{ + { + name: "files with templates are rendered correctly", + content: []string{ + "{{ .MYVAR }}", + }, + data: map[string]string{ + "MYVAR": "howdy", + }, + want: []byte("howdy"), + }, + { + name: "missing key gives an error", + content: []string{ + "{{ .MYVAR }}", + }, + wantErr: true, + }, + { + name: "no files make me an error!", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + filenames := make([]string, len(tt.content)) + for i, c := range tt.content { + f, err := ioutil.TempFile("", "") + if err != nil { + t.Fatal(err) + } + if _, err := f.Write([]byte(c)); err != nil { + t.Fatal(err) + } + filenames[i] = f.Name() + defer os.Remove(f.Name()) + } + got, err := templated(tt.data, filenames...) + if (err != nil) != tt.wantErr { + t.Errorf("templated() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("templated() = %v, want %v", got, tt.want) + } + }) + } +}