From c1a2a939f94ec8fcdf753ebc24631a0a268a743c Mon Sep 17 00:00:00 2001 From: Calvin Leung Huang <1883212+calvn@users.noreply.github.com> Date: Mon, 21 Jun 2021 16:10:15 -0700 Subject: [PATCH] agent: restart template runner on retry for unlimited retries (#11775) * agent: restart template runner on retry for unlimited retries * template: log error message early * template: delegate retries back to template if param is set to true * agent: add and use the new template config stanza * agent: fix panic, fix existing tests * changelog: add changelog entry * agent: add tests for exit_on_retry_failure * agent: properly check on agent exit cases, add separate tests for missing key vs missing secrets * agent: add note on difference between missing key vs missing secret * docs: add docs for template_config * Update website/content/docs/agent/template-config.mdx Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> * Update website/content/docs/agent/template-config.mdx Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> * Update website/content/docs/agent/template-config.mdx Co-authored-by: Tom Proctor * Update website/content/docs/agent/template-config.mdx Co-authored-by: Tom Proctor * Update website/content/docs/agent/template-config.mdx Co-authored-by: Tom Proctor * docs: fix exit_on_retry_failure, fix Functionality section * docs: update interaction title * template: add internal note on behavior for persist case * docs: update agent, template, and template-config docs * docs: update agent docs on retry stanza * Apply suggestions from code review Co-authored-by: Jim Kalafut Co-authored-by: Theron Voran * Update changelog/11775.txt Co-authored-by: Brian Kassouf * agent/test: rename expectExit to expectExitFromError * agent/test: add check on early exits on the happy path * Update website/content/docs/agent/template-config.mdx Co-authored-by: Tom Proctor Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> Co-authored-by: Tom Proctor Co-authored-by: Jim Kalafut Co-authored-by: Theron Voran Co-authored-by: Brian Kassouf --- changelog/11775.txt | 9 + command/agent/cache/api_proxy.go | 4 + command/agent/config/config.go | 45 ++- command/agent/config/config_test.go | 53 +++ .../config-template_config-empty.hcl | 13 + .../test-fixtures/config-template_config.hcl | 15 + command/agent/template/template.go | 28 +- command/agent/template/template_test.go | 23 +- command/agent_test.go | 308 ++++++++++++++++++ website/content/docs/agent/index.mdx | 21 +- .../content/docs/agent/template-config.mdx | 54 +++ website/content/docs/agent/template.mdx | 17 +- website/data/docs-nav-data.json | 4 + 13 files changed, 571 insertions(+), 23 deletions(-) create mode 100644 changelog/11775.txt create mode 100644 command/agent/config/test-fixtures/config-template_config-empty.hcl create mode 100644 command/agent/config/test-fixtures/config-template_config.hcl create mode 100644 website/content/docs/agent/template-config.mdx diff --git a/changelog/11775.txt b/changelog/11775.txt new file mode 100644 index 000000000..be3735521 --- /dev/null +++ b/changelog/11775.txt @@ -0,0 +1,9 @@ +```release-note:change +agent: Errors in the template engine will no longer cause agent to exit unless +explicitly defined to do so. A new configuration parameter, +`exit_on_retry_failure`, within the new top-level stanza, `template_config`, can +be set to `true` in order to cause agent to exit. Note that for agent to exit if +`template.error_on_missing_key` is set to `true`, `exit_on_retry_failure` must +be also set to `true`. Otherwise, the template engine will log an error but then +restart its internal runner. +``` diff --git a/command/agent/cache/api_proxy.go b/command/agent/cache/api_proxy.go index 5ba7076d1..a4793239c 100644 --- a/command/agent/cache/api_proxy.go +++ b/command/agent/cache/api_proxy.go @@ -119,6 +119,10 @@ func (ap *APIProxy) Send(ctx context.Context, req *SendRequest) (*SendResponse, } client.SetToken(req.Token) + // Derive and set a logger for the client + clientLogger := ap.logger.Named("client") + client.SetLogger(clientLogger) + // http.Transport will transparently request gzip and decompress the response, but only if // the client doesn't manually set the header. Removing any Accept-Encoding header allows the // transparent compression to occur. diff --git a/command/agent/config/config.go b/command/agent/config/config.go index deceeb9f0..419d5e5f6 100644 --- a/command/agent/config/config.go +++ b/command/agent/config/config.go @@ -22,11 +22,12 @@ import ( type Config struct { *configutil.SharedConfig `hcl:"-"` - AutoAuth *AutoAuth `hcl:"auto_auth"` - ExitAfterAuth bool `hcl:"exit_after_auth"` - Cache *Cache `hcl:"cache"` - Vault *Vault `hcl:"vault"` - Templates []*ctconfig.TemplateConfig `hcl:"templates"` + AutoAuth *AutoAuth `hcl:"auto_auth"` + ExitAfterAuth bool `hcl:"exit_after_auth"` + Cache *Cache `hcl:"cache"` + Vault *Vault `hcl:"vault"` + TemplateConfig *TemplateConfig `hcl:"template_config"` + Templates []*ctconfig.TemplateConfig `hcl:"templates"` } func (c *Config) Prune() { @@ -116,6 +117,11 @@ type Sink struct { Config map[string]interface{} } +// TemplateConfig defines global behaviors around template +type TemplateConfig struct { + ExitOnRetryFailure bool `hcl:"exit_on_retry_failure"` +} + func NewConfig() *Config { return &Config{ SharedConfig: new(configutil.SharedConfig), @@ -179,6 +185,10 @@ func LoadConfig(path string) (*Config, error) { return nil, fmt.Errorf("error parsing 'cache':%w", err) } + if err := parseTemplateConfig(result, list); err != nil { + return nil, fmt.Errorf("error parsing 'template_config': %w", err) + } + if err := parseTemplates(result, list); err != nil { return nil, fmt.Errorf("error parsing 'template': %w", err) } @@ -553,6 +563,31 @@ func parseSinks(result *Config, list *ast.ObjectList) error { return nil } +func parseTemplateConfig(result *Config, list *ast.ObjectList) error { + name := "template_config" + + templateConfigList := list.Filter(name) + if len(templateConfigList.Items) == 0 { + return nil + } + + if len(templateConfigList.Items) > 1 { + return fmt.Errorf("at most one %q block is allowed", name) + } + + // Get our item + item := templateConfigList.Items[0] + + var cfg TemplateConfig + if err := hcl.DecodeObject(&cfg, item.Val); err != nil { + return err + } + + result.TemplateConfig = &cfg + + return nil +} + func parseTemplates(result *Config, list *ast.ObjectList) error { name := "template" diff --git a/command/agent/config/config_test.go b/command/agent/config/config_test.go index ac14d2965..005db39d6 100644 --- a/command/agent/config/config_test.go +++ b/command/agent/config/config_test.go @@ -535,6 +535,59 @@ func TestLoadConfigFile_AgentCache_PersistMissingType(t *testing.T) { } } +func TestLoadConfigFile_TemplateConfig(t *testing.T) { + + testCases := map[string]struct { + fixturePath string + expectedTemplateConfig TemplateConfig + }{ + "set-true": { + "./test-fixtures/config-template_config.hcl", + TemplateConfig{ + ExitOnRetryFailure: true, + }, + }, + "empty": { + "./test-fixtures/config-template_config-empty.hcl", + TemplateConfig{ + ExitOnRetryFailure: false, + }, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + config, err := LoadConfig(tc.fixturePath) + if err != nil { + t.Fatal(err) + } + + expected := &Config{ + SharedConfig: &configutil.SharedConfig{}, + Vault: &Vault{ + Address: "http://127.0.0.1:1111", + Retry: &Retry{ + NumRetries: 5, + }, + }, + TemplateConfig: &tc.expectedTemplateConfig, + Templates: []*ctconfig.TemplateConfig{ + { + Source: pointerutil.StringPtr("/path/on/disk/to/template.ctmpl"), + Destination: pointerutil.StringPtr("/path/on/disk/where/template/will/render.txt"), + }, + }, + } + + config.Prune() + if diff := deep.Equal(config, expected); diff != nil { + t.Fatal(diff) + } + }) + } + +} + // TestLoadConfigFile_Template tests template definitions in Vault Agent func TestLoadConfigFile_Template(t *testing.T) { testCases := map[string]struct { diff --git a/command/agent/config/test-fixtures/config-template_config-empty.hcl b/command/agent/config/test-fixtures/config-template_config-empty.hcl new file mode 100644 index 000000000..a4f5b3a09 --- /dev/null +++ b/command/agent/config/test-fixtures/config-template_config-empty.hcl @@ -0,0 +1,13 @@ +vault { + address = "http://127.0.0.1:1111" + retry { + num_retries = 5 + } +} + +template_config {} + +template { + source = "/path/on/disk/to/template.ctmpl" + destination = "/path/on/disk/where/template/will/render.txt" +} \ No newline at end of file diff --git a/command/agent/config/test-fixtures/config-template_config.hcl b/command/agent/config/test-fixtures/config-template_config.hcl new file mode 100644 index 000000000..c2dfea20a --- /dev/null +++ b/command/agent/config/test-fixtures/config-template_config.hcl @@ -0,0 +1,15 @@ +vault { + address = "http://127.0.0.1:1111" + retry { + num_retries = 5 + } +} + +template_config { + exit_on_retry_failure = true +} + +template { + source = "/path/on/disk/to/template.ctmpl" + destination = "/path/on/disk/where/template/will/render.txt" +} \ No newline at end of file diff --git a/command/agent/template/template.go b/command/agent/template/template.go index 9396d1f82..0b9c1e007 100644 --- a/command/agent/template/template.go +++ b/command/agent/template/template.go @@ -172,8 +172,20 @@ func (ts *Server) Run(ctx context.Context, incoming chan string, templates []*ct } case err := <-ts.runner.ErrCh: + ts.logger.Error("template server error", "error", err.Error()) ts.runner.StopImmediately() - return fmt.Errorf("template server: %w", err) + + // Return after stopping the runner if exit on retry failure was + // specified + if ts.config.AgentConfig.TemplateConfig != nil && ts.config.AgentConfig.TemplateConfig.ExitOnRetryFailure { + return fmt.Errorf("template server: %w", err) + } + + ts.runner, err = manager.NewRunner(runnerConfig, false) + if err != nil { + return fmt.Errorf("template server failed to create: %w", err) + } + go ts.runner.Start() case <-ts.runner.TemplateRenderedCh(): // A template has been rendered, figure out what to do @@ -253,6 +265,20 @@ func newRunnerConfig(sc *ServerConfig, templates ctconfig.TemplateConfigs) (*ctc // that need to be fixed for 1.7x/1.8 if sc.AgentConfig.Cache != nil && sc.AgentConfig.Cache.Persist != nil && len(sc.AgentConfig.Listeners) != 0 { attempts = 0 + + // If we don't want exit on template retry failure (i.e. unlimited + // retries), let consul-template handle retry and backoff logic. + // + // Note: This is a fixed value (12) that ends up being a multiplier to + // retry.num_retires (i.e. 12 * N total retries per runner restart). + // Since we are performing retries indefinitely this base number helps + // prevent agent from spamming Vault if retry.num_retries is set to a + // low value by forcing exponential backoff to be high towards the end + // of retries during the process. + if sc.AgentConfig.TemplateConfig != nil && !sc.AgentConfig.TemplateConfig.ExitOnRetryFailure { + attempts = ctconfig.DefaultRetryAttempts + } + scheme := "unix://" if sc.AgentConfig.Listeners[0].Type == "tcp" { scheme = "https://" diff --git a/command/agent/template/template_test.go b/command/agent/template/template_test.go index 5efa76900..af26f15e3 100644 --- a/command/agent/template/template_test.go +++ b/command/agent/template/template_test.go @@ -352,8 +352,9 @@ func TestServerRun(t *testing.T) { } testCases := map[string]struct { - templateMap map[string]*templateTest - expectError bool + templateMap map[string]*templateTest + expectError bool + exitOnRetryFailure bool }{ "simple": { templateMap: map[string]*templateTest{ @@ -363,7 +364,8 @@ func TestServerRun(t *testing.T) { }, }, }, - expectError: false, + expectError: false, + exitOnRetryFailure: false, }, "multiple": { templateMap: map[string]*templateTest{ @@ -403,7 +405,8 @@ func TestServerRun(t *testing.T) { }, }, }, - expectError: false, + expectError: false, + exitOnRetryFailure: false, }, "bad secret": { templateMap: map[string]*templateTest{ @@ -413,7 +416,8 @@ func TestServerRun(t *testing.T) { }, }, }, - expectError: true, + expectError: true, + exitOnRetryFailure: true, }, "missing key": { templateMap: map[string]*templateTest{ @@ -424,7 +428,8 @@ func TestServerRun(t *testing.T) { }, }, }, - expectError: true, + expectError: true, + exitOnRetryFailure: true, }, "permission denied": { templateMap: map[string]*templateTest{ @@ -434,7 +439,8 @@ func TestServerRun(t *testing.T) { }, }, }, - expectError: true, + expectError: true, + exitOnRetryFailure: true, }, } @@ -458,6 +464,9 @@ func TestServerRun(t *testing.T) { NumRetries: 3, }, }, + TemplateConfig: &config.TemplateConfig{ + ExitOnRetryFailure: tc.exitOnRetryFailure, + }, }, LogLevel: hclog.Trace, LogWriter: hclog.DefaultOutput, diff --git a/command/agent_test.go b/command/agent_test.go index 11440795f..9bcb29713 100644 --- a/command/agent_test.go +++ b/command/agent_test.go @@ -22,6 +22,7 @@ import ( vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/sdk/helper/logging" + "github.com/hashicorp/vault/sdk/helper/pointerutil" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault" "github.com/mitchellh/cli" @@ -1387,6 +1388,9 @@ vault { tls_skip_verify = true } %s +template_config { + exit_on_retry_failure = true +} `, methodConf, serverClient.Address(), retryConf, templateConfig) configPath := makeTempFile(t, "config.hcl", config) @@ -1691,3 +1695,307 @@ vault { }) } } + +func TestAgent_TemplateConfig_ExitOnRetryFailure(t *testing.T) { + //---------------------------------------------------- + // Start the server and agent + //---------------------------------------------------- + logger := logging.NewVaultLogger(hclog.Trace) + cluster := vault.NewTestCluster(t, + &vault.CoreConfig{ + // Logger: logger, + CredentialBackends: map[string]logical.Factory{ + "approle": credAppRole.Factory, + }, + LogicalBackends: map[string]logical.Factory{ + "kv": logicalKv.Factory, + }, + }, + &vault.TestClusterOptions{ + NumCores: 1, + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + + vault.TestWaitActive(t, cluster.Cores[0].Core) + serverClient := cluster.Cores[0].Client + + // Unset the environment variable so that agent picks up the right test + // cluster address + defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress)) + os.Unsetenv(api.EnvVaultAddress) + + autoAuthConfig, cleanup := prepAgentApproleKV(t, serverClient) + defer cleanup() + + err := serverClient.Sys().TuneMount("secret", api.MountConfigInput{ + Options: map[string]string{ + "version": "2", + }, + }) + if err != nil { + t.Fatal(err) + } + + _, err = serverClient.Logical().Write("secret/data/otherapp", map[string]interface{}{ + "data": map[string]interface{}{ + "username": "barstuff", + "password": "zap", + "cert": "something", + }, + }) + if err != nil { + t.Fatal(err) + } + + // make a temp directory to hold renders. Each test will create a temp dir + // inside this one + tmpDirRoot, err := ioutil.TempDir("", "agent-test-renders") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDirRoot) + + // Note that missing key is different from a non-existent secret. A missing + // key (2xx response with missing keys in the response map) can still yield + // a successful render unless error_on_missing_key is specified, whereas a + // missing secret (4xx response) always results in an error. + missingKeyTemplateContent := `{{- with secret "secret/otherapp"}}{"secret": "other", +{{- if .Data.data.foo}}"foo":"{{ .Data.data.foo}}"{{- end }}} +{{- end }}` + missingKeyTemplateRender := `{"secret": "other",}` + + badTemplateContent := `{{- with secret "secret/non-existent"}}{"secret": "other", +{{- if .Data.data.foo}}"foo":"{{ .Data.data.foo}}"{{- end }}} +{{- end }}` + + testCases := map[string]struct { + exitOnRetryFailure *bool + templateContents string + expectTemplateRender string + templateErrorOnMissingKey bool + expectError bool + expectExitFromError bool + }{ + "true, no template error": { + exitOnRetryFailure: pointerutil.BoolPtr(true), + templateContents: templateContents(0), + expectTemplateRender: templateRendered(0), + templateErrorOnMissingKey: false, + expectError: false, + expectExitFromError: false, + }, + "true, with non-existent secret": { + exitOnRetryFailure: pointerutil.BoolPtr(true), + templateContents: badTemplateContent, + expectTemplateRender: "", + templateErrorOnMissingKey: false, + expectError: true, + expectExitFromError: true, + }, + "true, with missing key": { + exitOnRetryFailure: pointerutil.BoolPtr(true), + templateContents: missingKeyTemplateContent, + expectTemplateRender: missingKeyTemplateRender, + templateErrorOnMissingKey: false, + expectError: false, + expectExitFromError: false, + }, + "true, with missing key, with error_on_missing_key": { + exitOnRetryFailure: pointerutil.BoolPtr(true), + templateContents: missingKeyTemplateContent, + expectTemplateRender: "", + templateErrorOnMissingKey: true, + expectError: true, + expectExitFromError: true, + }, + "false, no template error": { + exitOnRetryFailure: pointerutil.BoolPtr(false), + templateContents: templateContents(0), + expectTemplateRender: templateRendered(0), + templateErrorOnMissingKey: false, + expectError: false, + expectExitFromError: false, + }, + "false, with non-existent secret": { + exitOnRetryFailure: pointerutil.BoolPtr(false), + templateContents: badTemplateContent, + expectTemplateRender: "", + templateErrorOnMissingKey: false, + expectError: true, + expectExitFromError: false, + }, + "false, with missing key": { + exitOnRetryFailure: pointerutil.BoolPtr(false), + templateContents: missingKeyTemplateContent, + expectTemplateRender: missingKeyTemplateRender, + templateErrorOnMissingKey: false, + expectError: false, + expectExitFromError: false, + }, + "false, with missing key, with error_on_missing_key": { + exitOnRetryFailure: pointerutil.BoolPtr(false), + templateContents: missingKeyTemplateContent, + expectTemplateRender: missingKeyTemplateRender, + templateErrorOnMissingKey: true, + expectError: true, + expectExitFromError: false, + }, + "missing": { + exitOnRetryFailure: nil, + templateContents: templateContents(0), + expectTemplateRender: templateRendered(0), + templateErrorOnMissingKey: false, + expectError: false, + expectExitFromError: false, + }, + } + + for tcName, tc := range testCases { + t.Run(tcName, func(t *testing.T) { + // create temp dir for this test run + tmpDir, err := ioutil.TempDir(tmpDirRoot, tcName) + if err != nil { + t.Fatal(err) + } + + listenAddr := "127.0.0.1:18123" + listenConfig := fmt.Sprintf(` +listener "tcp" { + address = "%s" + tls_disable = true +} +`, listenAddr) + + var exitOnRetryFailure string + if tc.exitOnRetryFailure != nil { + exitOnRetryFailure = fmt.Sprintf("exit_on_retry_failure = %t", *tc.exitOnRetryFailure) + } + templateConfig := fmt.Sprintf(` +template_config = { + %s +} +`, exitOnRetryFailure) + + template := fmt.Sprintf(` +template { + contents = <([template][template]: ) - Specifies options used for templating Vault secrets to files. +- `template_config` ([template_config][template-config]: ) - Specifies templating engine behavior. + ### vault Stanza There can at most be one top level `vault` block and it has the following @@ -94,12 +96,17 @@ configuration entries: #### retry Stanza -The `vault` stanza may contain a `retry` stanza that controls how failing -Vault requests are handled, whether these requests are issued in order to render +The `vault` stanza may contain a `retry` stanza that controls how failing Vault +requests are handled, whether these requests are issued in order to render templates, or are proxied requests coming from the proxy cache subsystem. Auto-auth, however, has its own notion of retrying and is not affected by this section. +For requests from the templating engine, Agent will reset its retry counter and +perform retries again once all retries are exhausted. This means that templating +will retry on failures indefinitely unless `exit_after_retry_failure` from the +[`template_config`][template-config] stanza is set to `true`. + Here are the options for the `retry` stanza: - `num_retries` `(int: 12)` - Specify how many times a failing request will @@ -114,7 +121,13 @@ result codes: any 50x code except 501 ("not implemented"), as well as 412 stale read due to eventual consistency. Requests coming from the template subsystem are retried regardless of the failure. -Second, the backoff algorithm used to set the time between retries differs for +Second, templating retries may be performed by both the templating engine _and_ +the cache proxy if Agent [persistent +cache][persistent-cache] is enabled. This is due to the +fact that templating requests go through the cache proxy when persistence is +enabled. + +Third, the backoff algorithm used to set the time between retries differs for the template and cache subsystems. This is a technical limitation we hope to address in the future. @@ -198,7 +211,9 @@ template { [vault]: /docs/agent#vault-stanza [autoauth]: /docs/agent/autoauth [caching]: /docs/agent/caching +[persistent-cache]: /docs/agent/caching/persistent-caches [template]: /docs/agent/template +[template-config]: /docs/agent/template-config [listener]: /docs/agent#listener-stanza [listener_main]: /docs/configuration/listener/tcp [winsvc]: /docs/agent/winsvc diff --git a/website/content/docs/agent/template-config.mdx b/website/content/docs/agent/template-config.mdx new file mode 100644 index 000000000..645a847dc --- /dev/null +++ b/website/content/docs/agent/template-config.mdx @@ -0,0 +1,54 @@ +--- +layout: docs +page_title: Vault Agent Template Config +description: |- + Vault Agent's Template Config to set Templating Engine behavior +--- + +# Vault Agent Template Config + +Template Config configures Vault Agent behavior common to all `template` stanzas. + +For template-specific rendering configuration, refer to the parameters within the +[`template`](/docs/agent/template) stanza. + +## Functionality + +The `template_config` stanza configures overall default behavior for the +templating engine. Note that `template_config` can only be defined once, and is +different from the `template` stanza. Unlike `template` which focuses on where +and how a specific secret is rendered, `template_config` contains parameters +affecting how the templating engine as a whole behaves and its interaction with +the rest of Agent. This includes, but is not limited to, program exit behavior. +Other parameters that apply to the templating engine as a whole may be added +over time. + +### Interaction between `exit_on_retry_failure` and `error_on_missing_key` + +The parameter +[`error_on_missing_key`](/docs/agent/template#error_on_missing_key) can be +specified within the `template` stanza which determines if a template should +error when a key is missing in the secret. When `error_on_missing_key` is not +specified or set to `false` and the key to render is not in the secret's +response, the templating engine will ignore it (or render `""`) and +continue on with its rendering. + +If the desire is to have Agent fail and exit on a missing key, both +`template.error_on_missing_key` and `template_config.exit_on_retry_failure` must +be set to true. Otherwise, the templating engine will error and render to its +destination, but agent will not exit and will retry until the key exists or until +the process is terminated. + +Note that a missing key from a secret's response is different from a missing or +non-existent secret. The templating engine will always error if a secret is +missing, but will only error for a missing key if `error_on_missing_key` is set. +Whether Vault Agent will exit when the templating engine errors depends on the +value of `exit_on_retry_failure`. + +## Configuration + +The top level `template_config` block has the following configuration entries: + +- `exit_on_retry_failure` `(bool: false)` - This option configures Vault Agent +to exit after it has exhausted its number of template retry attempts due to +failures. diff --git a/website/content/docs/agent/template.mdx b/website/content/docs/agent/template.mdx index e2d350422..8f93d9101 100644 --- a/website/content/docs/agent/template.mdx +++ b/website/content/docs/agent/template.mdx @@ -13,17 +13,20 @@ description: >- Vault Agent's Template functionality allows Vault secrets to be rendered to files using [Consul Template markup](https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md). +For globally applicable templating engine configuration, refer to the parameters +within the [`template_config`](/docs/agent/template-config) stanza. + ## Functionality -The `template` stanza configures the templating engine in the Vault agent for rendering -secrets to files using Consul Template markup language. Multiple `template` stanzas -can be defined to render multiple files. +The `template` stanza configures the Vault agent for rendering secrets to files +using Consul Template markup language. Multiple `template` stanzas can be +defined to render multiple files. When the agent is started with templating enabled, it will attempt to acquire a -Vault token using the configured Method. On failure, it will back off for a short -while (including some randomness to help prevent thundering herd scenarios) and -retry. On success, secrets defined in the templates will be retrieved from Vault and -rendered locally. +Vault token using the configured auto-auth Method. On failure, it will back off +for a short while (including some randomness to help prevent thundering herd +scenarios) and retry. On success, secrets defined in the templates will be +retrieved from Vault and rendered locally. ## Configuration diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index ce745c6d5..bf2ed8258 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -825,6 +825,10 @@ "title": "Templates", "path": "agent/template" }, + { + "title": "Template Config", + "path": "agent/template-config" + }, { "title": "Windows service", "path": "agent/winsvc"