Allow absolute paths for template sources

This commit is contained in:
Alex Dadgar 2016-11-02 13:03:54 -07:00
parent b08f4e0b97
commit 2a5ac5e7ee
3 changed files with 92 additions and 4 deletions

View File

@ -18,6 +18,12 @@ import (
"github.com/hashicorp/nomad/nomad/structs"
)
const (
// hostSrcOption is the Client option that determines whether the template
// source may be from the host
hostSrcOption = "template.allow_host_source"
)
var (
// testRetryRate is used to speed up tests by setting consul-templates retry
// rate to something low
@ -319,7 +325,11 @@ func templateRunner(tmpls []*structs.Template, config *config.Config,
}
// Parse the templates
ctmplMapping := parseTemplateConfigs(tmpls, taskDir, taskEnv)
allowAbs := config.ReadBoolDefault(hostSrcOption, true)
ctmplMapping, err := parseTemplateConfigs(tmpls, taskDir, taskEnv, allowAbs)
if err != nil {
return nil, nil, err
}
// Set the config
flat := make([]*ctconf.ConfigTemplate, 0, len(ctmplMapping))
@ -349,7 +359,8 @@ func templateRunner(tmpls []*structs.Template, config *config.Config,
}
// parseTemplateConfigs converts the tasks templates into consul-templates
func parseTemplateConfigs(tmpls []*structs.Template, taskDir string, taskEnv *env.TaskEnvironment) map[ctconf.ConfigTemplate]*structs.Template {
func parseTemplateConfigs(tmpls []*structs.Template, taskDir string,
taskEnv *env.TaskEnvironment, allowAbs bool) (map[ctconf.ConfigTemplate]*structs.Template, error) {
// Build the task environment
// TODO Should be able to inject the Nomad env vars into Consul-template for
// rendering
@ -359,7 +370,15 @@ func parseTemplateConfigs(tmpls []*structs.Template, taskDir string, taskEnv *en
for _, tmpl := range tmpls {
var src, dest string
if tmpl.SourcePath != "" {
src = filepath.Join(taskDir, taskEnv.ReplaceEnv(tmpl.SourcePath))
if filepath.IsAbs(tmpl.SourcePath) {
if !allowAbs {
return nil, fmt.Errorf("Specifying absolute template paths disallowed by client config: %q", tmpl.SourcePath)
}
src = tmpl.SourcePath
} else {
src = filepath.Join(taskDir, taskEnv.ReplaceEnv(tmpl.SourcePath))
}
}
if tmpl.DestPath != "" {
dest = filepath.Join(taskDir, taskEnv.ReplaceEnv(tmpl.DestPath))
@ -376,7 +395,7 @@ func parseTemplateConfigs(tmpls []*structs.Template, taskDir string, taskEnv *en
ctmpls[ct] = tmpl
}
return ctmpls
return ctmpls, nil
}
// runnerConfig returns a consul-template runner configuration, setting the

View File

@ -2,6 +2,7 @@ package client
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -139,6 +140,13 @@ func (h *testHarness) start(t *testing.T) {
h.manager = manager
}
func (h *testHarness) startWithErr() error {
manager, err := NewTaskTemplateManager(h.mockHooks, h.templates,
h.config, h.vaultToken, h.taskDir, h.taskEnv)
h.manager = manager
return err
}
// stop is used to stop any running Vault or Consul server plus the task manager
func (h *testHarness) stop() {
if h.vault != nil {
@ -210,6 +218,59 @@ func TestTaskTemplateManager_Invalid(t *testing.T) {
}
}
func TestTaskTemplateManager_HostPath(t *testing.T) {
// Make a template that will render immediately and write it to a tmp file
f, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("Bad: %v", err)
}
defer f.Close()
defer os.Remove(f.Name())
content := "hello, world!"
if _, err := io.WriteString(f, content); err != nil {
t.Fatalf("Bad: %v", err)
}
file := "my.tmpl"
template := &structs.Template{
SourcePath: f.Name(),
DestPath: file,
ChangeMode: structs.TemplateChangeModeNoop,
}
harness := newTestHarness(t, []*structs.Template{template}, false, false)
harness.start(t)
defer harness.stop()
// Wait for the unblock
select {
case <-harness.mockHooks.UnblockCh:
case <-time.After(time.Duration(5*testutil.TestMultiplier()) * time.Second):
t.Fatalf("Task unblock should have been called")
}
// Check the file is there
path := filepath.Join(harness.taskDir, file)
raw, err := ioutil.ReadFile(path)
if err != nil {
t.Fatalf("Failed to read rendered template from %q: %v", path, err)
}
if s := string(raw); s != content {
t.Fatalf("Unexpected template data; got %q, want %q", s, content)
}
// Change the config to disallow host sources
harness = newTestHarness(t, []*structs.Template{template}, false, false)
harness.config.Options = map[string]string{
hostSrcOption: "false",
}
if err := harness.startWithErr(); err == nil || !strings.Contains(err.Error(), "absolute") {
t.Fatalf("Expected absolute template path disallowed: %v", err)
}
}
func TestTaskTemplateManager_Unblock_Static(t *testing.T) {
// Make a template that will render immediately
content := "hello, world!"

View File

@ -121,5 +121,13 @@ template {
}
```
### Client Configuration
The `template` block has the following [client configuration
options](/docs/agent/config.html#options):
* `template.allow_host_source` - Allows templates to specify their source
template as an absolute path referencing host directories. Defaults to `true`.
[ct]: https://github.com/hashicorp/consul-template "Consul Template by HashiCorp"
[artifact]: /docs/job-specification/artifact.html "Nomad artifact Job Specification"