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 <tomhjp@users.noreply.github.com>

* Update website/content/docs/agent/template-config.mdx

Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com>

* Update website/content/docs/agent/template-config.mdx

Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com>

* 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 <jkalafut@hashicorp.com>
Co-authored-by: Theron Voran <tvoran@users.noreply.github.com>

* Update changelog/11775.txt

Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com>

* 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 <tomhjp@users.noreply.github.com>

Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>
Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com>
Co-authored-by: Jim Kalafut <jkalafut@hashicorp.com>
Co-authored-by: Theron Voran <tvoran@users.noreply.github.com>
Co-authored-by: Brian Kassouf <briankassouf@users.noreply.github.com>
This commit is contained in:
Calvin Leung Huang 2021-06-21 16:10:15 -07:00 committed by GitHub
parent 3ec17429e8
commit c1a2a939f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 571 additions and 23 deletions

9
changelog/11775.txt Normal file
View File

@ -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.
```

View File

@ -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.

View File

@ -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"

View File

@ -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 {

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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://"

View File

@ -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,

View File

@ -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 = <<EOF
%s
EOF
destination = "%s/render_0.json"
error_on_missing_key = %t
}
`, tc.templateContents, tmpDir, tc.templateErrorOnMissingKey)
config := fmt.Sprintf(`
# auto-auth stanza
%s
vault {
address = "%s"
tls_skip_verify = true
retry {
num_retries = 3
}
}
# listener stanza
%s
# template_config stanza
%s
# template stanza
%s
`, autoAuthConfig, serverClient.Address(), listenConfig, templateConfig, template)
configPath := makeTempFile(t, "config.hcl", config)
defer os.Remove(configPath)
// Start the agent
ui, cmd := testAgentCommand(t, logger)
cmd.startedCh = make(chan struct{})
// Channel to let verify() know to stop early if agent
// has exited
cmdRunDoneCh := make(chan struct{})
var exitedEarly bool
wg := &sync.WaitGroup{}
wg.Add(1)
var code int
go func() {
code = cmd.Run([]string{"-config", configPath})
close(cmdRunDoneCh)
wg.Done()
}()
verify := func() error {
t.Helper()
// We need to poll for a bit to give Agent time to render the
// templates. Without this this, the test will attempt to read
// the temp dir before Agent has had time to render and will
// likely fail the test
tick := time.Tick(1 * time.Second)
timeout := time.After(15 * time.Second)
var err error
for {
select {
case <-cmdRunDoneCh:
exitedEarly = true
return nil
case <-timeout:
return fmt.Errorf("timed out waiting for templates to render, last error: %w", err)
case <-tick:
}
// Check for files rendered in the directory and break
// early for shutdown if we do have all the files
// rendered
//----------------------------------------------------
// Perform the tests
//----------------------------------------------------
if numFiles := testListFiles(t, tmpDir, ".json"); numFiles != 1 {
err = fmt.Errorf("expected 1 template, got (%d)", numFiles)
continue
}
fileName := filepath.Join(tmpDir, "render_0.json")
var c []byte
c, err = ioutil.ReadFile(fileName)
if err != nil {
continue
}
if strings.TrimSpace(string(c)) != tc.expectTemplateRender {
err = fmt.Errorf("expected='%s', got='%s'", tc.expectTemplateRender, strings.TrimSpace(string(c)))
continue
}
return nil
}
}
err = verify()
close(cmd.ShutdownCh)
wg.Wait()
switch {
case (code != 0 || err != nil) && tc.expectError:
if exitedEarly != tc.expectExitFromError {
t.Fatalf("expected program exit due to error to be '%t', got '%t'", tc.expectExitFromError, exitedEarly)
}
case code == 0 && err == nil && !tc.expectError:
if exitedEarly {
t.Fatalf("did not expect program to exit before verify completes")
}
default:
if code != 0 {
t.Logf("output from agent:\n%s", ui.OutputWriter.String())
t.Logf("error from agent:\n%s", ui.ErrorWriter.String())
}
t.Fatalf("expectError=%v error=%v code=%d", tc.expectError, err, code)
}
})
}
}

View File

@ -55,6 +55,8 @@ These are the currently-available general configuration option:
- `template` <code>([template][template]: <optional\>)</code> - Specifies options used for templating Vault secrets to files.
- `template_config` <code>([template_config][template-config]: <optional\>)</code> - 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

View File

@ -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 `"<no value>"`) 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.

View File

@ -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

View File

@ -825,6 +825,10 @@
"title": "Templates",
"path": "agent/template"
},
{
"title": "Template Config",
"path": "agent/template-config"
},
{
"title": "Windows service",
"path": "agent/winsvc"