template: error on missing key (#15141)
* Support error_on_missing_value for templates * Update docs for template stanza
This commit is contained in:
parent
d7aa37a5c9
commit
79c4478f5b
|
@ -0,0 +1,5 @@
|
|||
```release-note:improvement
|
||||
template: Expose per-template configuration for `error_on_missing_key`. This allows jobspec authors to specify that a
|
||||
template should fail if it references a struct or map key that does not exist. The default value is false and should be
|
||||
fully backward compatible.
|
||||
```
|
|
@ -758,30 +758,32 @@ func TestJobs_Canonicalize(t *testing.T) {
|
|||
LogConfig: DefaultLogConfig(),
|
||||
Templates: []*Template{
|
||||
{
|
||||
SourcePath: pointerOf(""),
|
||||
DestPath: pointerOf("local/file.yml"),
|
||||
EmbeddedTmpl: pointerOf("---"),
|
||||
ChangeMode: pointerOf("restart"),
|
||||
ChangeSignal: pointerOf(""),
|
||||
Splay: pointerOf(5 * time.Second),
|
||||
Perms: pointerOf("0644"),
|
||||
LeftDelim: pointerOf("{{"),
|
||||
RightDelim: pointerOf("}}"),
|
||||
Envvars: pointerOf(false),
|
||||
VaultGrace: pointerOf(time.Duration(0)),
|
||||
SourcePath: pointerOf(""),
|
||||
DestPath: pointerOf("local/file.yml"),
|
||||
EmbeddedTmpl: pointerOf("---"),
|
||||
ChangeMode: pointerOf("restart"),
|
||||
ChangeSignal: pointerOf(""),
|
||||
Splay: pointerOf(5 * time.Second),
|
||||
Perms: pointerOf("0644"),
|
||||
LeftDelim: pointerOf("{{"),
|
||||
RightDelim: pointerOf("}}"),
|
||||
Envvars: pointerOf(false),
|
||||
VaultGrace: pointerOf(time.Duration(0)),
|
||||
ErrMissingKey: pointerOf(false),
|
||||
},
|
||||
{
|
||||
SourcePath: pointerOf(""),
|
||||
DestPath: pointerOf("local/file.env"),
|
||||
EmbeddedTmpl: pointerOf("FOO=bar\n"),
|
||||
ChangeMode: pointerOf("restart"),
|
||||
ChangeSignal: pointerOf(""),
|
||||
Splay: pointerOf(5 * time.Second),
|
||||
Perms: pointerOf("0644"),
|
||||
LeftDelim: pointerOf("{{"),
|
||||
RightDelim: pointerOf("}}"),
|
||||
Envvars: pointerOf(true),
|
||||
VaultGrace: pointerOf(time.Duration(0)),
|
||||
SourcePath: pointerOf(""),
|
||||
DestPath: pointerOf("local/file.env"),
|
||||
EmbeddedTmpl: pointerOf("FOO=bar\n"),
|
||||
ChangeMode: pointerOf("restart"),
|
||||
ChangeSignal: pointerOf(""),
|
||||
Splay: pointerOf(5 * time.Second),
|
||||
Perms: pointerOf("0644"),
|
||||
LeftDelim: pointerOf("{{"),
|
||||
RightDelim: pointerOf("}}"),
|
||||
Envvars: pointerOf(true),
|
||||
VaultGrace: pointerOf(time.Duration(0)),
|
||||
ErrMissingKey: pointerOf(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
35
api/tasks.go
35
api/tasks.go
|
@ -832,21 +832,22 @@ func (ch *ChangeScript) Canonicalize() {
|
|||
}
|
||||
|
||||
type Template struct {
|
||||
SourcePath *string `mapstructure:"source" hcl:"source,optional"`
|
||||
DestPath *string `mapstructure:"destination" hcl:"destination,optional"`
|
||||
EmbeddedTmpl *string `mapstructure:"data" hcl:"data,optional"`
|
||||
ChangeMode *string `mapstructure:"change_mode" hcl:"change_mode,optional"`
|
||||
ChangeScript *ChangeScript `mapstructure:"change_script" hcl:"change_script,block"`
|
||||
ChangeSignal *string `mapstructure:"change_signal" hcl:"change_signal,optional"`
|
||||
Splay *time.Duration `mapstructure:"splay" hcl:"splay,optional"`
|
||||
Perms *string `mapstructure:"perms" hcl:"perms,optional"`
|
||||
Uid *int `mapstructure:"uid" hcl:"uid,optional"`
|
||||
Gid *int `mapstructure:"gid" hcl:"gid,optional"`
|
||||
LeftDelim *string `mapstructure:"left_delimiter" hcl:"left_delimiter,optional"`
|
||||
RightDelim *string `mapstructure:"right_delimiter" hcl:"right_delimiter,optional"`
|
||||
Envvars *bool `mapstructure:"env" hcl:"env,optional"`
|
||||
VaultGrace *time.Duration `mapstructure:"vault_grace" hcl:"vault_grace,optional"`
|
||||
Wait *WaitConfig `mapstructure:"wait" hcl:"wait,block"`
|
||||
SourcePath *string `mapstructure:"source" hcl:"source,optional"`
|
||||
DestPath *string `mapstructure:"destination" hcl:"destination,optional"`
|
||||
EmbeddedTmpl *string `mapstructure:"data" hcl:"data,optional"`
|
||||
ChangeMode *string `mapstructure:"change_mode" hcl:"change_mode,optional"`
|
||||
ChangeScript *ChangeScript `mapstructure:"change_script" hcl:"change_script,block"`
|
||||
ChangeSignal *string `mapstructure:"change_signal" hcl:"change_signal,optional"`
|
||||
Splay *time.Duration `mapstructure:"splay" hcl:"splay,optional"`
|
||||
Perms *string `mapstructure:"perms" hcl:"perms,optional"`
|
||||
Uid *int `mapstructure:"uid" hcl:"uid,optional"`
|
||||
Gid *int `mapstructure:"gid" hcl:"gid,optional"`
|
||||
LeftDelim *string `mapstructure:"left_delimiter" hcl:"left_delimiter,optional"`
|
||||
RightDelim *string `mapstructure:"right_delimiter" hcl:"right_delimiter,optional"`
|
||||
Envvars *bool `mapstructure:"env" hcl:"env,optional"`
|
||||
VaultGrace *time.Duration `mapstructure:"vault_grace" hcl:"vault_grace,optional"`
|
||||
Wait *WaitConfig `mapstructure:"wait" hcl:"wait,block"`
|
||||
ErrMissingKey *bool `mapstructure:"error_on_missing_key" hcl:"error_on_missing_key,optional"`
|
||||
}
|
||||
|
||||
func (tmpl *Template) Canonicalize() {
|
||||
|
@ -890,7 +891,9 @@ func (tmpl *Template) Canonicalize() {
|
|||
if tmpl.Envvars == nil {
|
||||
tmpl.Envvars = pointerOf(false)
|
||||
}
|
||||
|
||||
if tmpl.ErrMissingKey == nil {
|
||||
tmpl.ErrMissingKey = pointerOf(false)
|
||||
}
|
||||
//COMPAT(0.12) VaultGrace is deprecated and unused as of Vault 0.5
|
||||
if tmpl.VaultGrace == nil {
|
||||
tmpl.VaultGrace = pointerOf(time.Duration(0))
|
||||
|
|
|
@ -694,6 +694,7 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.Templa
|
|||
ct.Contents = &tmpl.EmbeddedTmpl
|
||||
ct.LeftDelim = &tmpl.LeftDelim
|
||||
ct.RightDelim = &tmpl.RightDelim
|
||||
ct.ErrMissingKey = &tmpl.ErrMissingKey
|
||||
ct.FunctionDenylist = config.ClientConfig.TemplateConfig.FunctionDenylist
|
||||
if sandboxEnabled {
|
||||
ct.SandboxPath = &config.TaskDir
|
||||
|
|
|
@ -2471,6 +2471,46 @@ func TestTaskTemplateManager_Template_Wait_Set(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestTaskTemplateManager_Template_ErrMissingKey_Set asserts that all template level
|
||||
// configuration is accurately mapped from the template to the TaskTemplateManager's
|
||||
// template config.
|
||||
func TestTaskTemplateManager_Template_ErrMissingKey_Set(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
c := config.DefaultConfig()
|
||||
c.Node = mock.Node()
|
||||
|
||||
alloc := mock.Alloc()
|
||||
|
||||
ttmConfig := &TaskTemplateManagerConfig{
|
||||
ClientConfig: c,
|
||||
VaultToken: "token",
|
||||
EnvBuilder: taskenv.NewBuilder(c.Node, alloc, alloc.Job.TaskGroups[0].Tasks[0], c.Region),
|
||||
Templates: []*structs.Template{
|
||||
{
|
||||
EmbeddedTmpl: "test-false",
|
||||
ErrMissingKey: false,
|
||||
},
|
||||
{
|
||||
EmbeddedTmpl: "test-true",
|
||||
ErrMissingKey: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
templateMapping, err := parseTemplateConfigs(ttmConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
for k, tmpl := range templateMapping {
|
||||
if tmpl.EmbeddedTmpl == "test-false" {
|
||||
require.False(t, *k.ErrMissingKey)
|
||||
}
|
||||
if tmpl.EmbeddedTmpl == "test-true" {
|
||||
require.True(t, *k.ErrMissingKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestTaskTemplateManager_writeToFile_Disabled asserts the consul-template function
|
||||
// writeToFile is disabled by default.
|
||||
func TestTaskTemplateManager_writeToFile_Disabled(t *testing.T) {
|
||||
|
|
|
@ -1222,21 +1222,22 @@ func ApiTaskToStructsTask(job *structs.Job, group *structs.TaskGroup,
|
|||
for _, template := range apiTask.Templates {
|
||||
structsTask.Templates = append(structsTask.Templates,
|
||||
&structs.Template{
|
||||
SourcePath: *template.SourcePath,
|
||||
DestPath: *template.DestPath,
|
||||
EmbeddedTmpl: *template.EmbeddedTmpl,
|
||||
ChangeMode: *template.ChangeMode,
|
||||
ChangeSignal: *template.ChangeSignal,
|
||||
ChangeScript: apiChangeScriptToStructsChangeScript(template.ChangeScript),
|
||||
Splay: *template.Splay,
|
||||
Perms: *template.Perms,
|
||||
Uid: template.Uid,
|
||||
Gid: template.Gid,
|
||||
LeftDelim: *template.LeftDelim,
|
||||
RightDelim: *template.RightDelim,
|
||||
Envvars: *template.Envvars,
|
||||
VaultGrace: *template.VaultGrace,
|
||||
Wait: apiWaitConfigToStructsWaitConfig(template.Wait),
|
||||
SourcePath: *template.SourcePath,
|
||||
DestPath: *template.DestPath,
|
||||
EmbeddedTmpl: *template.EmbeddedTmpl,
|
||||
ChangeMode: *template.ChangeMode,
|
||||
ChangeSignal: *template.ChangeSignal,
|
||||
ChangeScript: apiChangeScriptToStructsChangeScript(template.ChangeScript),
|
||||
Splay: *template.Splay,
|
||||
Perms: *template.Perms,
|
||||
Uid: template.Uid,
|
||||
Gid: template.Gid,
|
||||
LeftDelim: *template.LeftDelim,
|
||||
RightDelim: *template.RightDelim,
|
||||
Envvars: *template.Envvars,
|
||||
VaultGrace: *template.VaultGrace,
|
||||
Wait: apiWaitConfigToStructsWaitConfig(template.Wait),
|
||||
ErrMissingKey: *template.ErrMissingKey,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2747,6 +2747,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
|||
Min: pointer.Of(5 * time.Second),
|
||||
Max: pointer.Of(10 * time.Second),
|
||||
},
|
||||
ErrMissingKey: pointer.Of(true),
|
||||
},
|
||||
},
|
||||
DispatchPayload: &api.DispatchPayloadConfig{
|
||||
|
@ -3160,6 +3161,7 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
|||
Min: pointer.Of(5 * time.Second),
|
||||
Max: pointer.Of(10 * time.Second),
|
||||
},
|
||||
ErrMissingKey: true,
|
||||
},
|
||||
},
|
||||
DispatchPayload: &structs.DispatchPayloadConfig{
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/hcl/hcl/ast"
|
||||
"github.com/hashicorp/nomad/api"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
|
@ -458,6 +459,8 @@ func parseTemplates(result *[]*api.Template, list *ast.ObjectList) error {
|
|||
"splay",
|
||||
"env",
|
||||
"vault_grace", //COMPAT(0.12) not used; emits warning in 0.11.
|
||||
"wait",
|
||||
"error_on_missing_key",
|
||||
}
|
||||
if err := checkHCLKeys(o.Val, valid); err != nil {
|
||||
return err
|
||||
|
@ -470,9 +473,12 @@ func parseTemplates(result *[]*api.Template, list *ast.ObjectList) error {
|
|||
delete(m, "change_script") // change_script is its own object
|
||||
|
||||
templ := &api.Template{
|
||||
ChangeMode: stringToPtr("restart"),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
ChangeMode: stringToPtr("restart"),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Uid: pointer.Of(-1),
|
||||
Gid: pointer.Of(-1),
|
||||
ErrMissingKey: pointer.Of(false),
|
||||
}
|
||||
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
|
|
|
@ -372,14 +372,17 @@ func TestParse(t *testing.T) {
|
|||
},
|
||||
Templates: []*api.Template{
|
||||
{
|
||||
SourcePath: stringToPtr("foo"),
|
||||
DestPath: stringToPtr("foo"),
|
||||
ChangeMode: stringToPtr("foo"),
|
||||
ChangeSignal: stringToPtr("foo"),
|
||||
Splay: timeToPtr(10 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Envvars: boolToPtr(true),
|
||||
VaultGrace: timeToPtr(33 * time.Second),
|
||||
SourcePath: stringToPtr("foo"),
|
||||
DestPath: stringToPtr("foo"),
|
||||
ChangeMode: stringToPtr("foo"),
|
||||
ChangeSignal: stringToPtr("foo"),
|
||||
Splay: timeToPtr(10 * time.Second),
|
||||
Perms: stringToPtr("0644"),
|
||||
Envvars: boolToPtr(true),
|
||||
Uid: intToPtr(-1),
|
||||
Gid: intToPtr(-1),
|
||||
VaultGrace: timeToPtr(33 * time.Second),
|
||||
ErrMissingKey: boolToPtr(true),
|
||||
},
|
||||
{
|
||||
SourcePath: stringToPtr("bar"),
|
||||
|
@ -391,12 +394,13 @@ func TestParse(t *testing.T) {
|
|||
Timeout: timeToPtr(5 * time.Second),
|
||||
FailOnError: boolToPtr(false),
|
||||
},
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("777"),
|
||||
Uid: intToPtr(1001),
|
||||
Gid: intToPtr(20),
|
||||
LeftDelim: stringToPtr("--"),
|
||||
RightDelim: stringToPtr("__"),
|
||||
Splay: timeToPtr(5 * time.Second),
|
||||
Perms: stringToPtr("777"),
|
||||
Uid: intToPtr(1001),
|
||||
Gid: intToPtr(20),
|
||||
LeftDelim: stringToPtr("--"),
|
||||
RightDelim: stringToPtr("__"),
|
||||
ErrMissingKey: boolToPtr(false),
|
||||
},
|
||||
},
|
||||
Leader: true,
|
||||
|
|
|
@ -305,13 +305,14 @@ job "binstore-storagelocker" {
|
|||
}
|
||||
|
||||
template {
|
||||
source = "foo"
|
||||
destination = "foo"
|
||||
change_mode = "foo"
|
||||
change_signal = "foo"
|
||||
splay = "10s"
|
||||
env = true
|
||||
vault_grace = "33s"
|
||||
source = "foo"
|
||||
destination = "foo"
|
||||
change_mode = "foo"
|
||||
change_signal = "foo"
|
||||
splay = "10s"
|
||||
env = true
|
||||
vault_grace = "33s"
|
||||
error_on_missing_key = true
|
||||
}
|
||||
|
||||
template {
|
||||
|
|
|
@ -111,6 +111,9 @@ func normalizeTemplates(templates []*api.Template) {
|
|||
if t.Splay == nil {
|
||||
t.Splay = pointer.Of(5 * time.Second)
|
||||
}
|
||||
if t.ErrMissingKey == nil {
|
||||
t.ErrMissingKey = pointer.Of(false)
|
||||
}
|
||||
normalizeChangeScript(t.ChangeScript)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1052,3 +1052,19 @@ func TestWaitConfig(t *testing.T) {
|
|||
require.Equal(t, 5*time.Second, *tmpl.Wait.Min)
|
||||
require.Equal(t, 60*time.Second, *tmpl.Wait.Max)
|
||||
}
|
||||
|
||||
func TestErrMissingKey(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
hclBytes, err := os.ReadFile("test-fixtures/template-err-missing-key.hcl")
|
||||
require.NoError(t, err)
|
||||
job, err := ParseWithConfig(&ParseConfig{
|
||||
Path: "test-fixtures/template-err-missing-key.hcl",
|
||||
Body: hclBytes,
|
||||
AllowFS: false,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
tmpl := job.TaskGroups[0].Tasks[0].Templates[0]
|
||||
require.NotNil(t, tmpl)
|
||||
require.NotNil(t, tmpl.ErrMissingKey)
|
||||
require.True(t, *tmpl.ErrMissingKey)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
job "example" {
|
||||
group "group" {
|
||||
task "task" {
|
||||
template {
|
||||
error_on_missing_key = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7070,6 +7070,7 @@ func TestTaskDiff(t *testing.T) {
|
|||
Min: pointer.Of(5 * time.Second),
|
||||
Max: pointer.Of(5 * time.Second),
|
||||
},
|
||||
ErrMissingKey: false,
|
||||
},
|
||||
{
|
||||
SourcePath: "foo2",
|
||||
|
@ -7113,6 +7114,7 @@ func TestTaskDiff(t *testing.T) {
|
|||
Min: pointer.Of(5 * time.Second),
|
||||
Max: pointer.Of(10 * time.Second),
|
||||
},
|
||||
ErrMissingKey: true,
|
||||
},
|
||||
{
|
||||
SourcePath: "foo3",
|
||||
|
@ -7134,6 +7136,7 @@ func TestTaskDiff(t *testing.T) {
|
|||
Min: pointer.Of(5 * time.Second),
|
||||
Max: pointer.Of(10 * time.Second),
|
||||
},
|
||||
ErrMissingKey: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -7150,6 +7153,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "baz",
|
||||
New: "baz new",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeEdited,
|
||||
Name: "ErrMissingKey",
|
||||
Old: "false",
|
||||
New: "true",
|
||||
},
|
||||
},
|
||||
Objects: []*ObjectDiff{
|
||||
{
|
||||
|
@ -7200,6 +7209,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "",
|
||||
New: "false",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeAdded,
|
||||
Name: "ErrMissingKey",
|
||||
Old: "",
|
||||
New: "true",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeAdded,
|
||||
Name: "Gid",
|
||||
|
@ -7330,6 +7345,12 @@ func TestTaskDiff(t *testing.T) {
|
|||
Old: "true",
|
||||
New: "",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeDeleted,
|
||||
Name: "ErrMissingKey",
|
||||
Old: "false",
|
||||
New: "",
|
||||
},
|
||||
{
|
||||
Type: DiffTypeDeleted,
|
||||
Name: "Gid",
|
||||
|
|
|
@ -7739,6 +7739,10 @@ type Template struct {
|
|||
|
||||
// WaitConfig is used to override the global WaitConfig on a per-template basis
|
||||
Wait *WaitConfig
|
||||
|
||||
// ErrMissingKey is used to control how the template behaves when attempting
|
||||
// to index a struct or map key that does not exist.
|
||||
ErrMissingKey bool
|
||||
}
|
||||
|
||||
// DefaultTemplate returns a default template.
|
||||
|
|
|
@ -837,6 +837,25 @@ func TestTasksUpdated(t *testing.T) {
|
|||
j28 := j27.Copy()
|
||||
j28.TaskGroups[0].Tasks[0].CSIPluginConfig.Type = "monolith"
|
||||
require.True(t, tasksUpdated(j27, j28, name))
|
||||
|
||||
// Compare identical Template ErrMissingKey
|
||||
j29 := mock.Job()
|
||||
j29.TaskGroups[0].Tasks[0].Templates = []*structs.Template{
|
||||
{
|
||||
ErrMissingKey: false,
|
||||
},
|
||||
}
|
||||
j30 := mock.Job()
|
||||
j30.TaskGroups[0].Tasks[0].Templates = []*structs.Template{
|
||||
{
|
||||
ErrMissingKey: false,
|
||||
},
|
||||
}
|
||||
require.False(t, tasksUpdated(j29, j30, name))
|
||||
|
||||
// Compare changed Template ErrMissingKey
|
||||
j30.TaskGroups[0].Tasks[0].Templates[0].ErrMissingKey = true
|
||||
require.True(t, tasksUpdated(j29, j30, name))
|
||||
}
|
||||
|
||||
func TestTasksUpdated_connectServiceUpdated(t *testing.T) {
|
||||
|
|
|
@ -34,10 +34,10 @@ job "docs" {
|
|||
|
||||
Nomad utilizes [Go template][gt] and a tool called [Consul Template][ct], which
|
||||
adds a set of new functions that can be used to retrieve data from Consul and
|
||||
Vault. Since Nomad v0.5.3, the template can reference [Nomad's runtime
|
||||
environment variables][env], and since Nomad v0.5.6, the template can reference
|
||||
[Node attributes and metadata][nodevars]. Since Nomad v0.6.0, templates can be
|
||||
read as environment variables.
|
||||
Vault. Nomad templates can reference [Nomad's runtime
|
||||
environment variables][env], [node attributes and metadata][nodevars],
|
||||
[Nomad service registrations][ct_api_nsvc], and [Nomad variables][nvars].
|
||||
Templates can also be used to provide environment variables to your workload.
|
||||
|
||||
For a full list of the API template functions, please refer to the [Consul
|
||||
Template documentation][ct_api]. For a an introduction to Go templates, please
|
||||
|
@ -47,7 +47,7 @@ refer to the [Learn Go Template Syntax][gt_learn] guide.
|
|||
|
||||
- `change_mode` `(string: "restart")` - Specifies the behavior Nomad should take
|
||||
if the rendered template changes. Nomad will always write the new contents of
|
||||
the template to the specified destination. The possible values below describe
|
||||
the template to the specified destination. The following possible values describe
|
||||
Nomad's action after writing the template to disk.
|
||||
|
||||
- `"noop"` - take no action (continue running the task)
|
||||
|
@ -59,7 +59,7 @@ refer to the [Learn Go Template Syntax][gt_learn] guide.
|
|||
string like `"SIGUSR1"` or `"SIGINT"`. This option is required if the
|
||||
`change_mode` is `signal`.
|
||||
|
||||
- `change_script` <code>([ChangeScript][]: nil)</code> - Configures the script
|
||||
- `change_script` <code>([`ChangeScript`][]: nil)</code> - Configures the script
|
||||
triggered on template change. This option is required if the `change_mode` is
|
||||
`script`.
|
||||
|
||||
|
@ -76,12 +76,22 @@ refer to the [Learn Go Template Syntax][gt_learn] guide.
|
|||
task drivers, see the [Filesystem internals] documentation.
|
||||
|
||||
- `env` `(bool: false)` - Specifies the template should be read back in as
|
||||
environment variables for the task ([see below](#environment-variables)). To
|
||||
environment variables for the task ([example](#environment-variables)). To
|
||||
update the environment on changes, you must set `change_mode` to
|
||||
`restart`. Setting `env` when the `change_mode` is `signal` will return a
|
||||
validation error. Setting `env` when the `change_mode` is `noop` is
|
||||
permitted but will not update the environment variables in the task.
|
||||
|
||||
- `error_on_missing_key` `(bool: false)` - Specifies how the template behaves
|
||||
when attempting to index a map key that does not exist in the map.
|
||||
|
||||
- When `true`, the template engine will return an error, which will cause the
|
||||
task to fail.
|
||||
|
||||
- When `false`, the template engine will do nothing and continue executing the
|
||||
template. If printed, the result of the index operation is the string
|
||||
"<no value\>".
|
||||
|
||||
- `left_delimiter` `(string: "{{")` - Specifies the left delimiter to use in the
|
||||
template. The default is "{{" for some templates, it may be easier to use a
|
||||
different delimiter that does not conflict with the output file itself.
|
||||
|
@ -90,7 +100,7 @@ refer to the [Learn Go Template Syntax][gt_learn] guide.
|
|||
File permissions are given as octal of the Unix file permissions `rwxrwxrwx`.
|
||||
|
||||
- `uid` `(int: nil)` - Specifies the rendered template owner's user ID. If
|
||||
negative or not specified (`nil`) the ID of the Nomad agent user wil be used.
|
||||
negative or not specified (`nil`) the ID of the Nomad agent user will be used.
|
||||
|
||||
~> **Caveat:** Works only on Unix-based systems. Be careful when using
|
||||
containerized drivers, such as `docker` or `podman`, as groups and users
|
||||
|
@ -114,7 +124,7 @@ refer to the [Learn Go Template Syntax][gt_learn] guide.
|
|||
One of `source` or `data` must be specified, but not both. This source can
|
||||
optionally be fetched using an [`artifact`][artifact] resource. This template
|
||||
must exist on the machine prior to starting the task; it is not possible to
|
||||
reference a template inside a Docker container, for example.
|
||||
reference a template that's source is inside a Docker container, for example.
|
||||
|
||||
- `splay` `(string: "5s")` - Specifies a random amount of time to wait between
|
||||
0 ms and the given splay value before invoking the change mode. This is
|
||||
|
@ -129,7 +139,8 @@ refer to the [Learn Go Template Syntax][gt_learn] guide.
|
|||
can be overridden by the [`client.template.wait_bounds`]. If the template
|
||||
configuration has a `min` lower than `client.template.wait_bounds.min` or a `max`
|
||||
greater than `client.template.wait_bounds.max`, the client's bounds will be enforced,
|
||||
and the template `wait` will be adjusted before being sent to `consul-template`.
|
||||
and the template `wait` will be adjusted before being sent to the template
|
||||
engine.
|
||||
|
||||
```hcl
|
||||
wait {
|
||||
|
@ -192,7 +203,8 @@ template {
|
|||
|
||||
### Node Variables
|
||||
|
||||
As of Nomad v0.5.6 it is possible to access the Node's attributes and metadata.
|
||||
Use the `env` function to access the Node's attributes and metadata inside a
|
||||
template.
|
||||
|
||||
```hcl
|
||||
template {
|
||||
|
@ -209,10 +221,10 @@ template {
|
|||
|
||||
### Environment Variables
|
||||
|
||||
Since v0.6.0 templates may be used to create environment variables for tasks.
|
||||
Env templates work exactly like other templates except once the templates are
|
||||
written, they are parsed as `KEY=value` pairs. Those key value pairs are
|
||||
included in the task's environment.
|
||||
Templates may be used to create environment variables for tasks. These templates
|
||||
work exactly like other templates except once the templates are written, they
|
||||
are parsed as `KEY=value` pairs. Those key value pairs are included in the
|
||||
task's environment.
|
||||
|
||||
For example the following template stanza:
|
||||
|
||||
|
@ -234,13 +246,13 @@ EOH
|
|||
The task's environment would then have environment variables like the
|
||||
following:
|
||||
|
||||
```
|
||||
```text
|
||||
LOG_LEVEL=DEBUG
|
||||
API_KEY=12345678-1234-1234-1234-1234-123456789abc
|
||||
```
|
||||
|
||||
This allows [12factor app](https://12factor.net/config) style environment
|
||||
variable based configuration while keeping all of the familiar features and
|
||||
variable based configuration while keeping all the familiar features and
|
||||
semantics of Nomad templates.
|
||||
|
||||
Secrets or certificates may contain a wide variety of characters such as
|
||||
|
@ -248,19 +260,19 @@ newlines, quotes, and backslashes which may be difficult to quote or escape
|
|||
properly.
|
||||
|
||||
Whenever a templated variable may include special characters, use the `toJSON`
|
||||
function to ensure special characters are properly parsed by Nomad:
|
||||
function to ensure special characters are properly parsed by Nomad.
|
||||
|
||||
```
|
||||
```hcl
|
||||
CERT_PEM={{ file "path/to/cert.pem" | toJSON }}
|
||||
```
|
||||
|
||||
The parser will read the JSON string, so the `$CERT_PEM` environment variable
|
||||
will be identical to the contents of the file.
|
||||
|
||||
Likewise when evaluating a password that may contain quotes or `#`, use the
|
||||
`toJSON` function to ensure Nomad passes the password to task unchanged:
|
||||
Likewise, when evaluating a password that may contain quotes or `#`, use the
|
||||
`toJSON` function to ensure Nomad passes the password to the task unchanged.
|
||||
|
||||
```
|
||||
```hcl
|
||||
# Passwords may contain any character including special characters like:
|
||||
# \"'#
|
||||
# Use toJSON to ensure Nomad passes them to the environment unchanged.
|
||||
|
@ -278,7 +290,7 @@ filesystem isolation (such as `raw_exec`) or drivers that build a chroot in
|
|||
the task working directory (such as `exec`) can have templates rendered to
|
||||
arbitrary paths in the task. But task drivers such as `docker` can only access
|
||||
templates rendered into the `NOMAD_ALLOC_DIR`, `NOMAD_TASK_DIR`, or
|
||||
`NOMAD_SECRETS_DIR`. To workaround this restriction, you can create a mount
|
||||
`NOMAD_SECRETS_DIR`. To work around this restriction, you can create a mount
|
||||
from the template `destination` to another location in the task.
|
||||
|
||||
```hcl
|
||||
|
@ -380,9 +392,9 @@ The list functions return a slice of `NomadVarMeta` type:
|
|||
|
||||
```golang
|
||||
type NomadVarMeta struct {
|
||||
Namespace, Path string
|
||||
CreateIndex, ModifyIndex uint64
|
||||
CreateTime, ModifyTime nanoTime
|
||||
Namespace, Path string
|
||||
CreateIndex, ModifyIndex uint64
|
||||
CreateTime, ModifyTime nanoTime
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -391,7 +403,7 @@ prints it out as a formatted date/time value. It also has a `Time` method that
|
|||
can be used to retrieve a Go [`time.Time`] for further manipulation.
|
||||
|
||||
`NomadVarMeta` objects print their `Path` value when used as a string. For
|
||||
example, these two template blocks produce identical output:
|
||||
example, these two template blocks produce identical output.
|
||||
|
||||
```hcl
|
||||
template {
|
||||
|
@ -427,7 +439,7 @@ EOH
|
|||
}
|
||||
```
|
||||
|
||||
By default the `nomadVarList` will list variables in the same namespace as the
|
||||
By default, the `nomadVarList` will list variables in the same namespace as the
|
||||
task. The path filter can change the namespace by adding a suffix separated by
|
||||
the `@` character:
|
||||
|
||||
|
@ -445,7 +457,6 @@ EOH
|
|||
The `nomadVarListSafe` function works identically to `nomadVarList`, but refuses
|
||||
to render the template if the variable list query returns blank/empty data.
|
||||
|
||||
|
||||
#### `nomadVar`
|
||||
|
||||
These functions can be used to a read Nomad variable, assuming the task has
|
||||
|
@ -454,12 +465,12 @@ path does not exist or the caller does not have access to it, the `template`
|
|||
renderer will block until the path is available. To avoid blocking, wrap
|
||||
`nomadVar` calls with [`nomadVarExists`](#nomadvarexists).
|
||||
|
||||
The `nomadVar` function returns a map of of `string` to `NomadVarItems`
|
||||
The `nomadVar` function returns a map of `string` to `NomadVarItems`
|
||||
structs. Each member of the map corresponds to a member of the variable's
|
||||
`Items` collection.
|
||||
|
||||
For example, given a variable exists at the path `nomad/jobs/redis`, with a
|
||||
single key/value pair in its Items collection — `maxconns`:`15`
|
||||
single key/value pair in its Items collection—`maxconns`:`15`
|
||||
|
||||
```hcl
|
||||
template {
|
||||
|
@ -478,11 +489,15 @@ renders
|
|||
Each map value also contains helper methods to get the variable metadata and a
|
||||
link to the parent variable:
|
||||
|
||||
* `Keys`: produces a sorted list of keys to this `NomadVarItems` map.
|
||||
* `Values`: produces a key-sorted list.
|
||||
* `Tuples`: produces a key-sorted list of K,V tuple structs.
|
||||
* `Metadata`: returns this collection's parent metadata as a `NomadVarMeta`
|
||||
* `Parent`: returns a parent object that has a Metadata field referring to the
|
||||
- `Keys`: produces a sorted list of keys to this `NomadVarItems` map.
|
||||
|
||||
- `Values`: produces a key-sorted list.
|
||||
|
||||
- `Tuples`: produces a key-sorted list of K,V tuple structs.
|
||||
|
||||
- `Metadata`: returns this collection's parent metadata as a `NomadVarMeta`
|
||||
|
||||
- `Parent`: returns a parent object that has a Metadata field referring to the
|
||||
`NomadVarMeta` and an Items field that refers to this `NomadVarItems` object.
|
||||
|
||||
For example, given a variable exists at the path `nomad/jobs/redis`, you could
|
||||
|
@ -501,9 +516,9 @@ EOH
|
|||
}
|
||||
```
|
||||
|
||||
By default the `nomadVar` will read a variable in the same namespace as the
|
||||
task. The path filter can change the namespace by adding a suffix separated by
|
||||
the `@` character:
|
||||
By default, the `nomadVar` function reads a variable in the same namespace as
|
||||
the task. The path filter can change the namespace by adding a suffix separated
|
||||
by the `@` character.
|
||||
|
||||
```hcl
|
||||
template {
|
||||
|
@ -602,11 +617,11 @@ When generating PKI certificates with Vault, the certificate, private key, and
|
|||
any intermediate certs are all returned as part of the same API call. Most
|
||||
software requires these files be placed in separate files on the system.
|
||||
|
||||
~> **Note**: `generate_lease` must be set to `true` (non-default) on the Vault PKI
|
||||
role.<br /><br /> Failure to do so will cause the template to frequently render a new
|
||||
certificate, approximately every minute. This creates a significant number of
|
||||
certificates to be expired in Vault and could ultimately lead to Vault performance
|
||||
impacts and failures.
|
||||
~> **Note**: `generate_lease` must be set to `true` (non-default) on the Vault
|
||||
PKI role.<br /><br /> Failure to do so will cause the template to frequently
|
||||
render a new certificate, approximately every minute. This creates a significant
|
||||
number of certificates to be expired in Vault and could ultimately lead to Vault
|
||||
performance impacts and failures.
|
||||
|
||||
#### As individual files
|
||||
|
||||
|
@ -711,8 +726,9 @@ Additionally, when using the Vault v2 API, the Vault policies applied to your
|
|||
Nomad jobs will need to grant permissions to `read` under `secret/data/...`
|
||||
rather than `secret/...`.
|
||||
|
||||
Similar to KV API v1, if the name of a secret includes the `-` character, you
|
||||
must access it by index. This secret was set using `vault kv put secret/app db-password=somepassword`.
|
||||
Like KV API v1, if the name of a secret includes the `-` character, you must
|
||||
access it by index. This secret was set using
|
||||
`vault kv put secret/app db-password=somepassword`.
|
||||
|
||||
```hcl
|
||||
template {
|
||||
|
@ -728,15 +744,15 @@ The `template` block has the following [client configuration
|
|||
options](/docs/configuration/client#options):
|
||||
|
||||
- `function_denylist` `([]string: ["plugin"])` - Specifies a list of template
|
||||
rendering functions that should be disallowed in job specs. By default the
|
||||
rendering functions that should be disallowed in job specs. By default, the
|
||||
`plugin` function is disallowed as it allows running arbitrary commands on
|
||||
the host as root (unless Nomad is configured to run as a non-root user).
|
||||
|
||||
- `disable_file_sandbox` `(bool: false)` - Allows templates access to arbitrary
|
||||
files on the client host via the `file` function. By default templates can
|
||||
files on the client host via the `file` function. By default, templates can
|
||||
access files only within the [task working directory].
|
||||
|
||||
[changescript]: /docs/job-specification/change_script 'Nomad change_script Job Specification'
|
||||
[`changescript`]: /docs/job-specification/change_script 'Nomad change_script Job Specification'
|
||||
[ct]: https://github.com/hashicorp/consul-template 'Consul Template by HashiCorp'
|
||||
[ct_api]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md 'Consul Template API by HashiCorp'
|
||||
[ct_api_connect]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md#connect 'Consul Template API by HashiCorp - connect'
|
||||
|
@ -744,6 +760,8 @@ options](/docs/configuration/client#options):
|
|||
[ct_api_ls]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md#ls 'Consul Template API by HashiCorp - ls'
|
||||
[ct_api_service]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md#service 'Consul Template API by HashiCorp - service'
|
||||
[ct_api_services]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md#services 'Consul Template API by HashiCorp - services'
|
||||
[ct_api_nsvc]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md#nomadService 'Consul Template API by HashiCorp - nomadService'
|
||||
[nvars]: /docs/concepts/variablesr 'Nomad Variables'
|
||||
[ct_api_tree]: https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md#tree 'Consul Template API by HashiCorp - tree'
|
||||
[gt]: https://pkg.go.dev/text/template 'Go template package'
|
||||
[gt_learn]: https://learn.hashicorp.com/tutorials/nomad/go-template-syntax
|
||||
|
|
Loading…
Reference in New Issue