Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose Consul template configuration parameters #11606

Merged
merged 16 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/11606.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:improvement
core: Expose consul-template configuration options at the client level for consul_retry,
vault_retry, max_stale, block_query_wait and wait. Expose per template configuration
for wait that will override the client level configuration. Add wait_bounds to
allow operators to constrain per-template overrides at the client level.
DerekStrickland marked this conversation as resolved.
Show resolved Hide resolved
```
29 changes: 29 additions & 0 deletions api/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,31 @@ func (a *TaskArtifact) Canonicalize() {
}
}

// WaitConfig is the Min/Max duration to wait for the Consul cluster to reach a
// consistent state before attempting to render Templates.
type WaitConfig struct {
Enabled *bool `mapstructure:"bool" hcl:"enabled"`
DerekStrickland marked this conversation as resolved.
Show resolved Hide resolved
Min *time.Duration `mapstructure:"min" hcl:"min"`
Max *time.Duration `mapstructure:"max" hcl:"max"`
}

func (wc *WaitConfig) Copy() *WaitConfig {
if wc == nil {
return nil
}

nwc := new(WaitConfig)
*nwc = *wc

return nwc
}

func (wc *WaitConfig) Canonicalize() {
if wc.Enabled == nil {
wc.Enabled = boolToPtr(false)
}
}

type Template struct {
SourcePath *string `mapstructure:"source" hcl:"source,optional"`
DestPath *string `mapstructure:"destination" hcl:"destination,optional"`
Expand All @@ -784,6 +809,7 @@ type Template struct {
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"`
}

func (tmpl *Template) Canonicalize() {
Expand Down Expand Up @@ -824,6 +850,9 @@ func (tmpl *Template) Canonicalize() {
if tmpl.Envvars == nil {
tmpl.Envvars = boolToPtr(false)
}
if tmpl.Wait != nil {
tmpl.Wait.Canonicalize()
}

//COMPAT(0.12) VaultGrace is deprecated and unused as of Vault 0.5
if tmpl.VaultGrace == nil {
Expand Down
104 changes: 104 additions & 0 deletions api/tasks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,110 @@ func TestTask_Canonicalize_TaskLifecycle(t *testing.T) {
}
}

func TestTask_Template_WaitConfig_Canonicalize_and_Copy(t *testing.T) {
taskWithWait := func(wc *WaitConfig) *Task {
return &Task{
Templates: []*Template{
{
Wait: wc,
},
},
}
}

testCases := []struct {
name string
canonicalized *WaitConfig
copied *WaitConfig
task *Task
}{
{
name: "all-fields",
task: taskWithWait(&WaitConfig{
Enabled: boolToPtr(true),
Min: timeToPtr(5),
Max: timeToPtr(10),
}),
canonicalized: &WaitConfig{
Enabled: boolToPtr(true),
Min: timeToPtr(5),
Max: timeToPtr(10),
},
copied: &WaitConfig{
Enabled: boolToPtr(true),
Min: timeToPtr(5),
Max: timeToPtr(10),
},
},
{
name: "no-fields",
task: taskWithWait(&WaitConfig{}),
canonicalized: &WaitConfig{
Enabled: boolToPtr(false),
Min: nil,
Max: nil,
},
copied: &WaitConfig{
Enabled: nil,
Min: nil,
Max: nil,
},
},
{
name: "enabled-only",
task: taskWithWait(&WaitConfig{
Enabled: boolToPtr(true),
}),
canonicalized: &WaitConfig{
Enabled: boolToPtr(true),
},
copied: &WaitConfig{
Enabled: boolToPtr(true),
},
},
{
name: "min-only",
task: taskWithWait(&WaitConfig{
Min: timeToPtr(5),
}),
canonicalized: &WaitConfig{
Enabled: boolToPtr(false),
Min: timeToPtr(5),
},
copied: &WaitConfig{
Min: timeToPtr(5),
},
},
{
name: "max-only",
task: taskWithWait(&WaitConfig{
Max: timeToPtr(10),
}),
canonicalized: &WaitConfig{
Enabled: boolToPtr(false),
Max: timeToPtr(10),
},
copied: &WaitConfig{
Max: timeToPtr(10),
},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tg := &TaskGroup{
Name: stringToPtr("foo"),
}
j := &Job{
ID: stringToPtr("test"),
}
require.Equal(t, tc.copied, tc.task.Templates[0].Wait.Copy())
tc.task.Canonicalize(tg, j)
require.Equal(t, tc.canonicalized, tc.task.Templates[0].Wait)
})
}
}

// Ensures no regression on https://github.com/hashicorp/nomad/issues/3132
func TestTaskGroup_Canonicalize_Update(t *testing.T) {
// Job with an Empty() Update
Expand Down
94 changes: 91 additions & 3 deletions client/allocrunner/taskrunner/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func (tm *TaskTemplateManager) Stop() {

// run is the long lived loop that handles errors and templates being rendered
func (tm *TaskTemplateManager) run() {
// Runner is nil if there is no templates
// Runner is nil if there are no templates
if tm.runner == nil {
// Unblock the start if there is nothing to do
close(tm.config.UnblockCh)
Expand Down Expand Up @@ -602,6 +602,14 @@ func parseTemplateConfigs(config *TaskTemplateManagerConfig) (map[*ctconf.Templa
ct.SandboxPath = &config.TaskDir
}

if tmpl.Wait != nil {
ct.Wait = &ctconf.WaitConfig{
Enabled: tmpl.Wait.Enabled,
Min: tmpl.Wait.Min,
Max: tmpl.Wait.Max,
}
}

// Set the permissions
if tmpl.Perms != "" {
v, err := strconv.ParseUint(tmpl.Perms, 8, 12)
Expand Down Expand Up @@ -635,13 +643,68 @@ func newRunnerConfig(config *TaskTemplateManagerConfig,
}
conf.Templates = &flat

// TODO: Code review this. It seems that this setting is only used to force
// errors in a specific test scenarios. We need to rethink implementation.
DerekStrickland marked this conversation as resolved.
Show resolved Hide resolved
// Force faster retries
if config.retryRate != 0 {
rate := config.retryRate
conf.Consul.Retry.Backoff = &rate
}

// Setup the Consul config
// Set the amount of time to do a blocking query for.
if cc.TemplateConfig.BlockQueryWaitTime != nil {
conf.BlockQueryWaitTime = cc.TemplateConfig.BlockQueryWaitTime
}

// Set the stale-read threshold to allow queries to be served by followers
// if the last replicated data is within this bound.
if cc.TemplateConfig.MaxStale != nil {
conf.MaxStale = cc.TemplateConfig.MaxStale
}

// Set the minimum and maximum amount of time to wait for the cluster to reach
// a consistent state before rendering a template.
if cc.TemplateConfig.Wait != nil {
// If somehow the WaitConfig wasn't set correctly upstream, return an error.
var err error
err = cc.TemplateConfig.Wait.Validate()
if err != nil {
return nil, err
}
conf.Wait, err = cc.TemplateConfig.Wait.ToConsulTemplate()
if err != nil {
return nil, err
}
}

// Make sure any template specific configuration set by the job author is within
// the bounds set by the operator.
if cc.TemplateConfig.WaitBounds != nil && *cc.TemplateConfig.WaitBounds.Enabled {
// If somehow the WaitBounds weren't set correctly upstream, return an error.
err := cc.TemplateConfig.WaitBounds.Validate()
if err != nil {
return nil, err
}

// Check and override with bounds
for _, tmpl := range *conf.Templates {
if tmpl.Wait == nil || !*tmpl.Wait.Enabled {
continue
}
if cc.TemplateConfig.WaitBounds.Min != nil {
if tmpl.Wait.Min != nil && *tmpl.Wait.Min < *cc.TemplateConfig.WaitBounds.Min {
tmpl.Wait.Min = &*cc.TemplateConfig.WaitBounds.Min
}
}
if cc.TemplateConfig.WaitBounds.Max != nil {
if tmpl.Wait.Max != nil && *tmpl.Wait.Max > *cc.TemplateConfig.WaitBounds.Max {
tmpl.Wait.Max = &*cc.TemplateConfig.WaitBounds.Max
}
}
}
}

// Set up the Consul config
if cc.ConsulConfig != nil {
conf.Consul.Address = &cc.ConsulConfig.Addr
conf.Consul.Token = &cc.ConsulConfig.Token
Expand Down Expand Up @@ -675,6 +738,19 @@ func newRunnerConfig(config *TaskTemplateManagerConfig,
Password: &parts[1],
}
}

// Set the user-specified Consul RetryConfig
if cc.TemplateConfig.ConsulRetry != nil {
var err error
err = cc.TemplateConfig.ConsulRetry.Validate()
if err != nil {
return nil, err
}
conf.Consul.Retry, err = cc.TemplateConfig.ConsulRetry.ToConsulTemplate()
if err != nil {
return nil, err
}
}
}

// Get the Consul namespace from job/group config. This is the higher level
Expand All @@ -683,7 +759,7 @@ func newRunnerConfig(config *TaskTemplateManagerConfig,
conf.Consul.Namespace = &config.ConsulNamespace
}

// Setup the Vault config
// Set up the Vault config
// Always set these to ensure nothing is picked up from the environment
emptyStr := ""
conf.Vault.RenewToken = helper.BoolToPtr(false)
Expand Down Expand Up @@ -724,6 +800,18 @@ func newRunnerConfig(config *TaskTemplateManagerConfig,
ServerName: &emptyStr,
}
}

// Set the user-specified Vault RetryConfig
if cc.TemplateConfig.VaultRetry != nil {
var err error
if err = cc.TemplateConfig.VaultRetry.Validate(); err != nil {
return nil, err
}
conf.Vault.Retry, err = cc.TemplateConfig.VaultRetry.ToConsulTemplate()
if err != nil {
return nil, err
}
}
}

conf.Finalize()
Expand Down
Loading