From be959a4e634eb070cbff44424fc32f6a7961cbe2 Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Thu, 25 May 2017 17:13:33 -0700 Subject: [PATCH] Fix and test multi-env-template loading --- client/consul_template.go | 63 ++++++++++++++------------ client/consul_template_test.go | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 29 deletions(-) diff --git a/client/consul_template.go b/client/consul_template.go index e054acdad..aec36a0d2 100644 --- a/client/consul_template.go +++ b/client/consul_template.go @@ -196,12 +196,12 @@ WAIT: } // Read environment variables from env templates - for _, t := range tm.templates { - if err := loadTemplateEnv(envBuilder, taskDir, t); err != nil { - tm.hook.Kill("consul-template", err.Error(), true) - return - } + envMap, err := loadTemplateEnv(tm.templates, taskDir) + if err != nil { + tm.hook.Kill("consul-template", err.Error(), true) + return } + envBuilder.SetTemplateEnv(envMap) allRenderedTime = time.Now() tm.hook.UnblockStart("consul-template") @@ -253,13 +253,15 @@ WAIT: return } - for _, tmpl := range tmpls { - // Read environment variables from templates - if err := loadTemplateEnv(envBuilder, taskDir, tmpl); err != nil { - tm.hook.Kill("consul-template", err.Error(), true) - return - } + // Read environment variables from templates + envMap, err := loadTemplateEnv(tmpls, taskDir) + if err != nil { + tm.hook.Kill("consul-template", err.Error(), true) + return + } + envBuilder.SetTemplateEnv(envMap) + for _, tmpl := range tmpls { switch tmpl.ChangeMode { case structs.TemplateChangeModeSignal: signals[tmpl.ChangeSignal] = struct{}{} @@ -508,26 +510,29 @@ func runnerConfig(config *config.Config, vaultToken string) (*ctconf.Config, err return conf, nil } -// loadTemplateEnv loads task environment variables from templates. -func loadTemplateEnv(builder *env.Builder, taskDir string, t *structs.Template) error { - if !t.Envvars { - return nil - } - f, err := os.Open(filepath.Join(taskDir, t.DestPath)) - if err != nil { - return fmt.Errorf("error opening env template: %v", err) - } - defer f.Close() +// loadTemplateEnv loads task environment variables from all templates. +func loadTemplateEnv(tmpls []*structs.Template, taskDir string) (map[string]string, error) { + all := make(map[string]string, 50) + for _, t := range tmpls { + if !t.Envvars { + continue + } + f, err := os.Open(filepath.Join(taskDir, t.DestPath)) + if err != nil { + return nil, fmt.Errorf("error opening env template: %v", err) + } + defer f.Close() - // Parse environment fil - vars, err := parseEnvFile(f) - if err != nil { - return fmt.Errorf("error parsing env template %q: %v", t.DestPath, err) + // Parse environment fil + vars, err := parseEnvFile(f) + if err != nil { + return nil, fmt.Errorf("error parsing env template %q: %v", t.DestPath, err) + } + for k, v := range vars { + all[k] = v + } } - - // Set the environment variables - builder.SetTemplateEnv(vars) - return nil + return all, nil } // parseEnvFile and return a map of the environment variables suitable for diff --git a/client/consul_template_test.go b/client/consul_template_test.go index d1b9b4174..9cbed9e75 100644 --- a/client/consul_template_test.go +++ b/client/consul_template_test.go @@ -953,3 +953,83 @@ ANYTHING-goes=Spaces are=ok! t.Errorf("expected ANYTHING_GOES='Spaces are ok!' but found %q", env["ANYTHING_goes"]) } } + +// TestTaskTemplateManager_Env_Missing asserts the core env +// template processing function returns errors when files don't exist +func TestTaskTemplateManager_Env_Missing(t *testing.T) { + d, err := ioutil.TempDir("", "ct_env_missing") + if err != nil { + t.Fatalf("err: %v", err) + } + defer os.RemoveAll(d) + + // Fake writing the file so we don't have to run the whole template manager + err = ioutil.WriteFile(filepath.Join(d, "exists.env"), []byte("FOO=bar\n"), 0644) + if err != nil { + t.Fatalf("error writing template file: %v", err) + } + + templates := []*structs.Template{ + { + EmbeddedTmpl: "FOO=bar\n", + DestPath: "exists.env", + Envvars: true, + }, + { + EmbeddedTmpl: "WHAT=ever\n", + DestPath: "missing.env", + Envvars: true, + }, + } + + if vars, err := loadTemplateEnv(templates, d); err == nil { + t.Fatalf("expected an error but instead got env vars: %#v", vars) + } +} + +// TestTaskTemplateManager_Env_Multi asserts the core env +// template processing function returns combined env vars from multiple +// templates correctly. +func TestTaskTemplateManager_Env_Multi(t *testing.T) { + d, err := ioutil.TempDir("", "ct_env_missing") + if err != nil { + t.Fatalf("err: %v", err) + } + defer os.RemoveAll(d) + + // Fake writing the files so we don't have to run the whole template manager + err = ioutil.WriteFile(filepath.Join(d, "zzz.env"), []byte("FOO=bar\nSHARED=nope\n"), 0644) + if err != nil { + t.Fatalf("error writing template file 1: %v", err) + } + err = ioutil.WriteFile(filepath.Join(d, "aaa.env"), []byte("BAR=foo\nSHARED=yup\n"), 0644) + if err != nil { + t.Fatalf("error writing template file 2: %v", err) + } + + // Templates will get loaded in order (not alpha sorted) + templates := []*structs.Template{ + { + DestPath: "zzz.env", + Envvars: true, + }, + { + DestPath: "aaa.env", + Envvars: true, + }, + } + + vars, err := loadTemplateEnv(templates, d) + if err != nil { + t.Fatalf("expected an error but instead got env vars: %#v", vars) + } + if vars["FOO"] != "bar" { + t.Error("expected FOO=bar but found %q", vars["FOO"]) + } + if vars["BAR"] != "foo" { + t.Error("expected BAR=foo but found %q", vars["BAR"]) + } + if vars["SHARED"] != "yup" { + t.Error("expected FOO=bar but found %q", vars["yup"]) + } +}