Skip to content

Commit

Permalink
Merge branch 'phemmer-template-error-fatal'
Browse files Browse the repository at this point in the history
  • Loading branch information
eikenb committed Feb 3, 2022
2 parents 3ea7d99 + 1f3d98f commit f9dd3bb
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 4 deletions.
16 changes: 15 additions & 1 deletion cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,11 @@ func (cli *CLI) ParseFlags(args []string) (
return nil
}), "template", "")

flags.Var((funcBoolVar)(func(b bool) error {
c.TemplateErrFatal = config.Bool(b)
return nil
}), "template-error-fatal", "")

flags.Var((funcVar)(func(s string) error {
c.Vault.Address = config.String(s)
return nil
Expand Down Expand Up @@ -618,6 +623,11 @@ func loadConfigs(paths []string, o *config.Config) (*config.Config, error) {
}

finalC = finalC.Merge(o)
if o.TemplateErrFatal != nil {
for _, tmpl := range *finalC.Templates {
tmpl.ErrFatal = o.TemplateErrFatal
}
}
finalC.Finalize()
return finalC, nil
}
Expand Down Expand Up @@ -790,7 +800,11 @@ Options:
attribute is supplied, the -syslog flag must also be supplied
-template=<template>
Adds a new template to watch on disk in the format 'in:out(:command)'
Adds a new template to watch on disk in the format 'in:out(:command)'
-template-error-fatal=<bool>
Control whether template errors cause consul-template to immediately exit.
This overrides the per-template setting.
-vault-addr=<address>
Sets the address of the Vault server
Expand Down
19 changes: 19 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ type Config struct {
// Templates is the list of templates.
Templates *TemplateConfigs `mapstructure:"template"`

// TemplateErrFatal determines whether template errors should cause the
// process to exit, or just log and continue.
TemplateErrFatal *bool `mapstructure:"template_error_fatal"`

// Vault is the configuration for connecting to a vault server.
Vault *VaultConfig `mapstructure:"vault"`

Expand Down Expand Up @@ -146,6 +150,10 @@ func (c *Config) Copy() *Config {
o.Templates = c.Templates.Copy()
}

if c.TemplateErrFatal != nil {
o.TemplateErrFatal = c.TemplateErrFatal
}

if c.Vault != nil {
o.Vault = c.Vault.Copy()
}
Expand Down Expand Up @@ -225,6 +233,10 @@ func (c *Config) Merge(o *Config) *Config {
r.Templates = r.Templates.Merge(o.Templates)
}

if o.TemplateErrFatal != nil {
r.TemplateErrFatal = o.TemplateErrFatal
}

if o.Vault != nil {
r.Vault = r.Vault.Merge(o.Vault)
}
Expand Down Expand Up @@ -429,6 +441,7 @@ func (c *Config) GoString() string {
"FileLog:%#v, "+
"Syslog:%#v, "+
"Templates:%#v, "+
"TemplateErrFatal:%#v"+
"Vault:%#v, "+
"Wait:%#v, "+
"Once:%#v, "+
Expand All @@ -446,6 +459,7 @@ func (c *Config) GoString() string {
c.FileLog,
c.Syslog,
c.Templates,
c.TemplateErrFatal,
c.Vault,
c.Wait,
c.Once,
Expand Down Expand Up @@ -561,6 +575,11 @@ func (c *Config) Finalize() {
if c.Templates == nil {
c.Templates = DefaultTemplateConfigs()
}
for _, tmpl := range *c.Templates {
if tmpl.ErrFatal == nil {
tmpl.ErrFatal = c.TemplateErrFatal
}
}
c.Templates.Finalize()

if c.Vault == nil {
Expand Down
16 changes: 16 additions & 0 deletions config/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ type TemplateConfig struct {
// to index a struct or map key that does not exist.
ErrMissingKey *bool `mapstructure:"error_on_missing_key"`

// ErrFatal determines whether template errors should cause the process to
// exit, or just log and continue.
ErrFatal *bool `mapstructure:"error_fatal"`

// Exec is the configuration for the command to run when the template renders
// successfully.
Exec *ExecConfig `mapstructure:"exec"`
Expand Down Expand Up @@ -137,6 +141,8 @@ func (c *TemplateConfig) Copy() *TemplateConfig {

o.ErrMissingKey = c.ErrMissingKey

o.ErrFatal = c.ErrFatal

if c.Exec != nil {
o.Exec = c.Exec.Copy()
}
Expand Down Expand Up @@ -211,6 +217,10 @@ func (c *TemplateConfig) Merge(o *TemplateConfig) *TemplateConfig {
r.ErrMissingKey = o.ErrMissingKey
}

if o.ErrFatal != nil {
r.ErrFatal = o.ErrFatal
}

if o.Exec != nil {
r.Exec = r.Exec.Merge(o.Exec)
}
Expand Down Expand Up @@ -281,6 +291,10 @@ func (c *TemplateConfig) Finalize() {
c.ErrMissingKey = Bool(false)
}

if c.ErrFatal == nil {
c.ErrFatal = Bool(true)
}

if c.Exec == nil {
c.Exec = DefaultExecConfig()
}
Expand Down Expand Up @@ -341,6 +355,7 @@ func (c *TemplateConfig) GoString() string {
"CreateDestDirs:%s, "+
"Destination:%s, "+
"ErrMissingKey:%s, "+
"ErrFatal:%s, "+
"Exec:%#v, "+
"Perms:%s, "+
"Source:%s, "+
Expand All @@ -357,6 +372,7 @@ func (c *TemplateConfig) GoString() string {
BoolGoString(c.CreateDestDirs),
StringGoString(c.Destination),
BoolGoString(c.ErrMissingKey),
BoolGoString(c.ErrFatal),
c.Exec,
FileModeGoString(c.Perms),
StringGoString(c.Source),
Expand Down
1 change: 1 addition & 0 deletions config/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ func TestTemplateConfig_Finalize(t *testing.T) {
CreateDestDirs: Bool(true),
Destination: String(""),
ErrMissingKey: Bool(false),
ErrFatal: Bool(true),
Exec: &ExecConfig{
Command: String(""),
Enabled: Bool(false),
Expand Down
9 changes: 9 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ block_query_wait = "60s"
# Valid options include (in order of verbosity): trace, debug, info, warn, err
log_level = "warn"
# This controls whether an error within a template will cause consul-template
# to immediately exit. This value can be overridden within each template
# configuration.
template_error_fatal = true
# This is the quiescence timers; it defines the minimum and maximum amount of
# time to wait for the cluster to reach a consistent state before rendering a
# template. This is useful to enable in systems that have a lot of flapping,
Expand Down Expand Up @@ -460,6 +465,10 @@ template {
# retrieving secrets from Vault.
error_on_missing_key = false
# This controls whether an error within the template will cause
# consul-template to immediately exit.
error_fatal = true
# This is the permission to render the file. If this option is left
# unspecified, Consul Template will attempt to match the permissions of the
# file that already exists at the destination path. If no file exists at that
Expand Down
32 changes: 29 additions & 3 deletions manager/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ type RenderEvent struct {
// been rendered we need to know if the event is triggered by quiesence
// and if we can skip evaluating it as a render event for those purposes
ForQuiescence bool

// Error contains the error encountered while rendering the template.
Error error
}

// NewRunner accepts a slice of TemplateConfigs and returns a pointer to the new
Expand Down Expand Up @@ -640,7 +643,8 @@ type templateRunCtx struct {
// template to run and a shared run context that allows sharing of information
// between templates. The run returns a potentially nil render event and any
// error that occured. The render event is nil in the case that the template has
// been already rendered and is a once template or if there is an error.
// been already rendered and is a once template or if there is an error and
// fatal errors are enabled.
func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (*RenderEvent, error) {
log.Printf("[DEBUG] (runner) checking template %s", tmpl.ID())

Expand Down Expand Up @@ -687,7 +691,23 @@ func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (*
Env: r.childEnv(),
})
if err != nil {
return nil, errors.Wrap(err, tmpl.Source())
if tmpl.ErrFatal() {
return nil, errors.Wrap(err, tmpl.Source())
}
log.Printf("[ERR] (runner) %s: %v", tmpl.Source(), err)
event.Error = err

if lastEvent != nil {
// Keep watching our dependencies so that we retry when they update.
for _, d := range lastEvent.UsedDeps.List() {
if _, ok := runCtx.depsMap[d.String()]; !ok {
runCtx.depsMap[d.String()] = d
}
}
event.UsedDeps = lastEvent.UsedDeps
}

return event, nil
}

// Grab the list of used and missing dependencies.
Expand Down Expand Up @@ -784,7 +804,12 @@ func (r *Runner) runTemplate(tmpl *template.Template, runCtx *templateRunCtx) (*
Gid: templateConfig.Gid,
})
if err != nil {
return nil, errors.Wrap(err, "error rendering "+templateConfig.Display())
if tmpl.ErrFatal() {
return nil, errors.Wrap(err, "error rendering "+templateConfig.Display())
}
log.Printf("[ERR] (runner) error rendering: %s: %v", templateConfig.Display(), err)
event.Error = err
return event, nil
}

renderTime := time.Now().UTC()
Expand Down Expand Up @@ -892,6 +917,7 @@ func (r *Runner) init() error {
Source: config.StringVal(ctmpl.Source),
Contents: config.StringVal(ctmpl.Contents),
ErrMissingKey: config.BoolVal(ctmpl.ErrMissingKey),
ErrFatal: config.BoolVal(ctmpl.ErrFatal),
LeftDelim: leftDelim,
RightDelim: rightDelim,
FunctionDenylist: ctmpl.FunctionDenylist,
Expand Down
14 changes: 14 additions & 0 deletions template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type Template struct {
// is indexed with a key that does not exist.
errMissingKey bool

// errFatal determines whether template errors should cause the process to
// exit, or just log and continue.
errFatal bool

// functionDenylist are functions not permitted to be executed
// when we render this template
functionDenylist []string
Expand All @@ -68,6 +72,10 @@ type NewTemplateInput struct {
// when a map is indexed with a key that does not exist.
ErrMissingKey bool

// ErrFatal determines whether template errors should cause the process to
// exit, or just log and continue.
ErrFatal bool

// LeftDelim and RightDelim are the template delimiters.
LeftDelim string
RightDelim string
Expand Down Expand Up @@ -104,6 +112,7 @@ func NewTemplate(i *NewTemplateInput) (*Template, error) {
t.leftDelim = i.LeftDelim
t.rightDelim = i.RightDelim
t.errMissingKey = i.ErrMissingKey
t.errFatal = i.ErrFatal
t.functionDenylist = i.FunctionDenylist
t.sandboxPath = i.SandboxPath

Expand Down Expand Up @@ -140,6 +149,11 @@ func (t *Template) Source() string {
return t.source
}

// ErrFatal indicates whether errors in this template should be fatal.
func (t *Template) ErrFatal() bool {
return t.errFatal
}

// ExecuteInput is used as input to the template's execute function.
type ExecuteInput struct {
// Brain is the brain where data for the template is stored.
Expand Down

0 comments on commit f9dd3bb

Please sign in to comment.