Update consul-template

This commit is contained in:
Alex Dadgar 2017-08-01 09:24:23 -07:00
parent e0b48831b2
commit 8df67563c7
12 changed files with 407 additions and 241 deletions

View file

@ -230,7 +230,7 @@ func Parse(s string) (*Config, error) {
})
// FlattenFlatten keys belonging to the templates. We cannot do this above
// because it is an array of tmeplates.
// because it is an array of templates.
if templates, ok := parsed["template"].([]map[string]interface{}); ok {
for _, template := range templates {
flattenKeys(template, []string{

View file

@ -11,7 +11,7 @@ const (
// DefaultVaultGrace is the default grace period before which to read a new
// secret from Vault. If a lease is due to expire in 5 minutes, Consul
// Template will read a new secret at that time minus this value.
DefaultVaultGrace = 15 * time.Second
DefaultVaultGrace = 5 * time.Minute
// DefaultVaultRenewToken is the default value for if the Vault token should
// be renewed.

View file

@ -9,3 +9,5 @@ var ErrStopped = errors.New("dependency stopped")
// ErrContinue is a special error which says to continue (retry) on error.
var ErrContinue = errors.New("dependency continue")
var ErrLeaseExpired = errors.New("lease expired or is not renewable")

View file

@ -1,15 +1,23 @@
package dependency
import "time"
import (
"log"
"math/rand"
"time"
"github.com/hashicorp/vault/api"
)
var (
// VaultDefaultLeaseDuration is the default lease duration in seconds.
VaultDefaultLeaseDuration = 5 * time.Minute
)
// Secret is a vault secret.
// Secret is the structure returned for every secret within Vault.
type Secret struct {
RequestID string
// The request ID that generated this response
RequestID string
LeaseID string
LeaseDuration int
Renewable bool
@ -17,23 +25,171 @@ type Secret struct {
// Data is the actual contents of the secret. The format of the data
// is arbitrary and up to the secret backend.
Data map[string]interface{}
// Warnings contains any warnings related to the operation. These
// are not issues that caused the command to fail, but that the
// client should be aware of.
Warnings []string
// Auth, if non-nil, means that there was authentication information
// attached to this response.
Auth *SecretAuth
// WrapInfo, if non-nil, means that the initial response was wrapped in the
// cubbyhole of the given token (which has a TTL of the given number of
// seconds)
WrapInfo *SecretWrapInfo
}
// leaseDurationOrDefault returns a value or the default lease duration.
func leaseDurationOrDefault(d int) int {
if d == 0 {
return int(VaultDefaultLeaseDuration.Nanoseconds() / 1000000000)
}
return d
// SecretAuth is the structure containing auth information if we have it.
type SecretAuth struct {
ClientToken string
Accessor string
Policies []string
Metadata map[string]string
LeaseDuration int
Renewable bool
}
// vaultRenewDuration accepts a given renew duration (lease duration) and
// returns the cooresponding time.Duration. If the duration is 0 (not provided),
// this falls back to the VaultDefaultLeaseDuration.
func vaultRenewDuration(d int) time.Duration {
dur := time.Duration(d/2.0) * time.Second
if dur == 0 {
dur = VaultDefaultLeaseDuration
}
return dur
// SecretWrapInfo contains wrapping information if we have it. If what is
// contained is an authentication token, the accessor for the token will be
// available in WrappedAccessor.
type SecretWrapInfo struct {
Token string
TTL int
CreationTime time.Time
WrappedAccessor string
}
// vaultRenewDuration accepts a secret and returns the recommended amount of
// time to sleep.
func vaultRenewDuration(s *Secret) time.Duration {
// Handle whether this is an auth or a regular secret.
base := s.LeaseDuration
if s.Auth != nil && s.Auth.LeaseDuration > 0 {
base = s.Auth.LeaseDuration
}
// Ensure we have a lease duration, since sometimes this can be zero.
if base <= 0 {
base = int(VaultDefaultLeaseDuration.Seconds())
}
// Convert to float seconds.
sleep := float64(time.Duration(base) * time.Second)
// Renew at 1/3 the remaining lease. This will give us an opportunity to retry
// at least one more time should the first renewal fail.
sleep = sleep / 3.0
// Use a randomness so many clients do not hit Vault simultaneously.
sleep = sleep * (rand.Float64() + 1) / 2.0
return time.Duration(sleep)
}
// printVaultWarnings prints warnings for a given dependency.
func printVaultWarnings(d Dependency, warnings []string) {
for _, w := range warnings {
log.Printf("[WARN] %s: %s", d, w)
}
}
// vaultSecretRenewable determines if the given secret is renewable.
func vaultSecretRenewable(s *Secret) bool {
if s.Auth != nil {
return s.Auth.Renewable
}
return s.Renewable
}
// transformSecret transforms an api secret into our secret. This does not deep
// copy underlying deep data structures, so it's not safe to modify the vault
// secret as that may modify the data in the transformed secret.
func transformSecret(theirs *api.Secret) *Secret {
var ours Secret
updateSecret(&ours, theirs)
return &ours
}
// updateSecret updates our secret with the new data from the api, careful to
// not overwrite missing data. Renewals don't include the original secret, and
// we don't want to delete that data accidentially.
func updateSecret(ours *Secret, theirs *api.Secret) {
if theirs.RequestID != "" {
ours.RequestID = theirs.RequestID
}
if theirs.LeaseID != "" {
ours.LeaseID = theirs.LeaseID
}
if theirs.LeaseDuration != 0 {
ours.LeaseDuration = theirs.LeaseDuration
}
if theirs.Renewable {
ours.Renewable = theirs.Renewable
}
if len(theirs.Data) != 0 {
ours.Data = theirs.Data
}
if len(theirs.Warnings) != 0 {
ours.Warnings = theirs.Warnings
}
if theirs.Auth != nil {
if ours.Auth == nil {
ours.Auth = &SecretAuth{}
}
if theirs.Auth.ClientToken != "" {
ours.Auth.ClientToken = theirs.Auth.ClientToken
}
if theirs.Auth.Accessor != "" {
ours.Auth.Accessor = theirs.Auth.Accessor
}
if len(theirs.Auth.Policies) != 0 {
ours.Auth.Policies = theirs.Auth.Policies
}
if len(theirs.Auth.Metadata) != 0 {
ours.Auth.Metadata = theirs.Auth.Metadata
}
if theirs.Auth.LeaseDuration != 0 {
ours.Auth.LeaseDuration = theirs.Auth.LeaseDuration
}
if theirs.Auth.Renewable {
ours.Auth.Renewable = theirs.Auth.Renewable
}
}
if theirs.WrapInfo != nil {
if ours.WrapInfo == nil {
ours.WrapInfo = &SecretWrapInfo{}
}
if theirs.WrapInfo.Token != "" {
ours.WrapInfo.Token = theirs.WrapInfo.Token
}
if theirs.WrapInfo.TTL != 0 {
ours.WrapInfo.TTL = theirs.WrapInfo.TTL
}
if !theirs.WrapInfo.CreationTime.IsZero() {
ours.WrapInfo.CreationTime = theirs.WrapInfo.CreationTime
}
if theirs.WrapInfo.WrappedAccessor != "" {
ours.WrapInfo.WrappedAccessor = theirs.WrapInfo.WrappedAccessor
}
}
}

View file

@ -7,6 +7,7 @@ import (
"strings"
"time"
"github.com/hashicorp/vault/api"
"github.com/pkg/errors"
)
@ -21,6 +22,9 @@ type VaultReadQuery struct {
path string
secret *Secret
// vaultSecret is the actual Vault secret which we are renewing
vaultSecret *api.Secret
}
// NewVaultReadQuery creates a new datacenter dependency.
@ -47,71 +51,68 @@ func (d *VaultReadQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interfac
opts = opts.Merge(&QueryOptions{})
// If this is not the first query and we have a lease duration, sleep until we
// try to renew.
if opts.WaitIndex != 0 && d.secret != nil && d.secret.LeaseDuration != 0 {
dur := vaultRenewDuration(d.secret.LeaseDuration)
if d.secret != nil {
if vaultSecretRenewable(d.secret) {
log.Printf("[TRACE] %s: starting renewer", d)
log.Printf("[TRACE] %s: long polling for %s", d, dur)
renewer, err := clients.Vault().NewRenewer(&api.RenewerInput{
Grace: opts.VaultGrace,
Secret: d.vaultSecret,
})
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
go renewer.Renew()
defer renewer.Stop()
select {
case <-d.stopCh:
return nil, nil, ErrStopped
case <-time.After(dur):
RENEW:
for {
select {
case err := <-renewer.DoneCh():
if err != nil {
log.Printf("[WARN] %s: failed to renew: %s", d, err)
}
log.Printf("[WARN] %s: renewer returned (maybe the lease expired)", d)
break RENEW
case renewal := <-renewer.RenewCh():
log.Printf("[TRACE] %s: successfully renewed", d)
printVaultWarnings(d, renewal.Secret.Warnings)
updateSecret(d.secret, renewal.Secret)
case <-d.stopCh:
return nil, nil, ErrStopped
}
}
} else {
// The secret isn't renewable, probably the generic secret backend.
dur := vaultRenewDuration(d.secret)
if dur < opts.VaultGrace {
log.Printf("[TRACE] %s: remaining lease %s is less than grace, skipping sleep", d, dur)
} else {
log.Printf("[TRACE] %s: secret is not renewable, sleeping for %s", d, dur)
select {
case <-time.After(dur):
// The lease is almost expired, it's time to request a new one.
case <-d.stopCh:
return nil, nil, ErrStopped
}
}
}
}
// Attempt to renew the secret. If we do not have a secret or if that secret
// is not renewable, we will attempt a (re-)read later.
if d.secret != nil && d.secret.LeaseID != "" && d.secret.Renewable {
log.Printf("[TRACE] %s: PUT %s", d, &url.URL{
Path: "/v1/sys/renew/" + d.secret.LeaseID,
RawQuery: opts.String(),
})
renewal, err := clients.Vault().Sys().Renew(d.secret.LeaseID, 0)
if err == nil {
log.Printf("[TRACE] %s: successfully renewed %s", d, d.secret.LeaseID)
// Print any warnings
d.printWarnings(renewal.Warnings)
secret := &Secret{
RequestID: renewal.RequestID,
LeaseID: renewal.LeaseID,
LeaseDuration: d.secret.LeaseDuration,
Renewable: renewal.Renewable,
Data: d.secret.Data,
}
// For some older versions of Vault, the renewal did not include the
// remaining lease duration, so just use the original lease duration,
// because it's the best we can do.
if renewal.LeaseDuration != 0 {
secret.LeaseDuration = renewal.LeaseDuration
}
d.secret = secret
// If the remaining time on the lease is less than or equal to our
// configured grace period, generate a new credential now. This will help
// minimize downtime, since Vault will revoke credentials immediately
// when their maximum TTL expires.
remaining := time.Duration(d.secret.LeaseDuration) * time.Second
if remaining <= opts.VaultGrace {
log.Printf("[DEBUG] %s: remaining lease (%s) < grace (%s), acquiring new",
d, remaining, opts.VaultGrace)
return d.readSecret(clients, opts)
}
return respWithMetadata(secret)
}
// The renewal failed for some reason.
log.Printf("[WARN] %s: failed to renew %s: %s", d, d.secret.LeaseID, err)
// We don't have a secret, or the prior renewal failed
vaultSecret, err := d.readSecret(clients, opts)
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
// If we got this far, we either didn't have a secret to renew, the secret was
// not renewable, or the renewal failed, so attempt a fresh read.
return d.readSecret(clients, opts)
// Print any warnings
printVaultWarnings(d, vaultSecret.Warnings)
// Create the cloned secret which will be exposed to the template.
d.vaultSecret = vaultSecret
d.secret = transformSecret(vaultSecret)
return respWithMetadata(d.secret)
}
// CanShare returns if this dependency is shareable.
@ -134,38 +135,17 @@ func (d *VaultReadQuery) Type() Type {
return TypeVault
}
func (d *VaultReadQuery) printWarnings(warnings []string) {
for _, w := range warnings {
log.Printf("[WARN] %s: %s", d, w)
}
}
func (d *VaultReadQuery) readSecret(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
func (d *VaultReadQuery) readSecret(clients *ClientSet, opts *QueryOptions) (*api.Secret, error) {
log.Printf("[TRACE] %s: GET %s", d, &url.URL{
Path: "/v1/" + d.path,
RawQuery: opts.String(),
})
vaultSecret, err := clients.Vault().Logical().Read(d.path)
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
return nil, errors.Wrap(err, d.String())
}
// The secret could be nil if it does not exist.
if vaultSecret == nil {
return nil, nil, fmt.Errorf("%s: no secret exists at %s", d, d.path)
return nil, fmt.Errorf("no secret exists at %s", d.path)
}
// Print any warnings.
d.printWarnings(vaultSecret.Warnings)
// Create our cloned secret.
secret := &Secret{
LeaseID: vaultSecret.LeaseID,
LeaseDuration: leaseDurationOrDefault(vaultSecret.LeaseDuration),
Renewable: vaultSecret.Renewable,
Data: vaultSecret.Data,
}
d.secret = secret
return respWithMetadata(secret)
return vaultSecret, nil
}

View file

@ -2,9 +2,9 @@ package dependency
import (
"log"
"net/url"
"time"
"github.com/hashicorp/vault/api"
"github.com/pkg/errors"
)
@ -15,16 +15,24 @@ var (
// VaultTokenQuery is the dependency to Vault for a secret
type VaultTokenQuery struct {
stopCh chan struct{}
leaseID string
leaseDuration int
stopCh chan struct{}
secret *Secret
vaultSecret *api.Secret
}
// NewVaultTokenQuery creates a new dependency.
func NewVaultTokenQuery() (*VaultTokenQuery, error) {
func NewVaultTokenQuery(token string) (*VaultTokenQuery, error) {
vaultSecret := &api.Secret{
Auth: &api.SecretAuth{
ClientToken: token,
Renewable: true,
LeaseDuration: 1,
},
}
return &VaultTokenQuery{
stopCh: make(chan struct{}, 1),
stopCh: make(chan struct{}, 1),
vaultSecret: vaultSecret,
secret: transformSecret(vaultSecret),
}, nil
}
@ -38,44 +46,53 @@ func (d *VaultTokenQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interfa
opts = opts.Merge(&QueryOptions{})
log.Printf("[TRACE] %s: GET %s", d, &url.URL{
Path: "/v1/auth/token/renew-self",
RawQuery: opts.String(),
})
if vaultSecretRenewable(d.secret) {
log.Printf("[TRACE] %s: starting renewer", d)
// If this is not the first query and we have a lease duration, sleep until we
// try to renew.
if opts.WaitIndex != 0 && d.leaseDuration != 0 {
dur := vaultRenewDuration(d.leaseDuration)
renewer, err := clients.Vault().NewRenewer(&api.RenewerInput{
Grace: opts.VaultGrace,
Secret: d.vaultSecret,
})
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
go renewer.Renew()
defer renewer.Stop()
log.Printf("[TRACE] %s: long polling for %s", d, dur)
select {
case <-d.stopCh:
return nil, nil, ErrStopped
case <-time.After(dur):
RENEW:
for {
select {
case err := <-renewer.DoneCh():
if err != nil {
log.Printf("[WARN] %s: failed to renew: %s", d, err)
}
log.Printf("[WARN] %s: renewer returned (maybe the lease expired)", d)
break RENEW
case renewal := <-renewer.RenewCh():
log.Printf("[TRACE] %s: successfully renewed", d)
printVaultWarnings(d, renewal.Secret.Warnings)
updateSecret(d.secret, renewal.Secret)
case <-d.stopCh:
return nil, nil, ErrStopped
}
}
}
token, err := clients.Vault().Auth().Token().RenewSelf(0)
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
// The secret isn't renewable, probably the generic secret backend.
dur := vaultRenewDuration(d.secret)
if dur < opts.VaultGrace {
log.Printf("[TRACE] %s: remaining lease %s is less than grace, skipping sleep", d, dur)
} else {
log.Printf("[TRACE] %s: token is not renewable, sleeping for %s", d, dur)
select {
case <-time.After(dur):
// The lease is almost expired, it's time to request a new one.
case <-d.stopCh:
return nil, nil, ErrStopped
}
}
// Create our cloned secret
secret := &Secret{
LeaseID: token.LeaseID,
LeaseDuration: token.Auth.LeaseDuration,
Renewable: token.Auth.Renewable,
Data: token.Data,
}
d.leaseID = secret.LeaseID
d.leaseDuration = secret.LeaseDuration
log.Printf("[DEBUG] %s: renewed token", d)
return respWithMetadata(secret)
return nil, nil, ErrLeaseExpired
}
// CanShare returns if this dependency is shareable.

View file

@ -10,6 +10,7 @@ import (
"strings"
"time"
"github.com/hashicorp/vault/api"
"github.com/pkg/errors"
)
@ -26,6 +27,9 @@ type VaultWriteQuery struct {
data map[string]interface{}
dataHash string
secret *Secret
// vaultSecret is the actual Vault secret which we are renewing
vaultSecret *api.Secret
}
// NewVaultWriteQuery creates a new datacenter dependency.
@ -54,70 +58,68 @@ func (d *VaultWriteQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interfa
opts = opts.Merge(&QueryOptions{})
// If this is not the first query and we have a lease duration, sleep until we
// try to renew.
if opts.WaitIndex != 0 && d.secret != nil && d.secret.LeaseDuration != 0 {
dur := vaultRenewDuration(d.secret.LeaseDuration)
if d.secret != nil {
if vaultSecretRenewable(d.secret) {
log.Printf("[TRACE] %s: starting renewer", d)
log.Printf("[TRACE] %s: long polling for %s", d, dur)
renewer, err := clients.Vault().NewRenewer(&api.RenewerInput{
Grace: opts.VaultGrace,
Secret: d.vaultSecret,
})
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
go renewer.Renew()
defer renewer.Stop()
select {
case <-d.stopCh:
return nil, nil, ErrStopped
case <-time.After(dur):
RENEW:
for {
select {
case err := <-renewer.DoneCh():
if err != nil {
log.Printf("[WARN] %s: failed to renew: %s", d, err)
}
log.Printf("[WARN] %s: renewer returned (maybe the lease expired)", d)
break RENEW
case renewal := <-renewer.RenewCh():
log.Printf("[TRACE] %s: successfully renewed", d)
printVaultWarnings(d, renewal.Secret.Warnings)
updateSecret(d.secret, renewal.Secret)
case <-d.stopCh:
return nil, nil, ErrStopped
}
}
} else {
// The secret isn't renewable, probably the generic secret backend.
dur := vaultRenewDuration(d.secret)
if dur < opts.VaultGrace {
log.Printf("[TRACE] %s: remaining lease %s is less than grace, skipping sleep", d, dur)
} else {
log.Printf("[TRACE] %s: secret is not renewable, sleeping for %s", d, dur)
select {
case <-time.After(dur):
// The lease is almost expired, it's time to request a new one.
case <-d.stopCh:
return nil, nil, ErrStopped
}
}
}
}
// Attempt to renew the secret. If we do not have a secret or if that secret
// is not renewable, we will attempt a (re-)write later.
if d.secret != nil && d.secret.LeaseID != "" && d.secret.Renewable {
log.Printf("[TRACE] %s: PUT %s", d, &url.URL{
Path: "/v1/sys/renew/" + d.secret.LeaseID,
RawQuery: opts.String(),
})
renewal, err := clients.Vault().Sys().Renew(d.secret.LeaseID, 0)
if err == nil {
log.Printf("[TRACE] %s: successfully renewed %s", d, d.secret.LeaseID)
// Print any warnings
d.printWarnings(renewal.Warnings)
secret := &Secret{
RequestID: renewal.RequestID,
LeaseID: renewal.LeaseID,
Renewable: renewal.Renewable,
Data: d.secret.Data,
}
// For some older versions of Vault, the renewal did not include the
// remaining lease duration, so just use the original lease duration,
// because it's the best we can do.
if renewal.LeaseDuration != 0 {
secret.LeaseDuration = renewal.LeaseDuration
}
d.secret = secret
// If the remaining time on the lease is less than or equal to our
// configured grace period, generate a new credential now. This will help
// minimize downtime, since Vault will revoke credentials immediately
// when their maximum TTL expires.
remaining := time.Duration(d.secret.LeaseDuration) * time.Second
if remaining <= opts.VaultGrace {
log.Printf("[DEBUG] %s: remaining lease (%s) < grace (%s), acquiring new",
d, remaining, opts.VaultGrace)
return d.writeSecret(clients, opts)
}
return respWithMetadata(secret)
}
// The renewal failed for some reason.
log.Printf("[WARN] %s: failed to renew %s: %s", d, d.secret.LeaseID, err)
// We don't have a secret, or the prior renewal failed
vaultSecret, err := d.writeSecret(clients, opts)
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
}
// If we got this far, we either didn't have a secret to renew, the secret was
// not renewable, or the renewal failed, so attempt a fresh write.
return d.writeSecret(clients, opts)
// Print any warnings
printVaultWarnings(d, vaultSecret.Warnings)
// Create the cloned secret which will be exposed to the template.
d.vaultSecret = vaultSecret
d.secret = transformSecret(vaultSecret)
return respWithMetadata(d.secret)
}
// CanShare returns if this dependency is shareable.
@ -164,7 +166,7 @@ func (d *VaultWriteQuery) printWarnings(warnings []string) {
}
}
func (d *VaultWriteQuery) writeSecret(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
func (d *VaultWriteQuery) writeSecret(clients *ClientSet, opts *QueryOptions) (*api.Secret, error) {
log.Printf("[TRACE] %s: PUT %s", d, &url.URL{
Path: "/v1/" + d.path,
RawQuery: opts.String(),
@ -172,27 +174,11 @@ func (d *VaultWriteQuery) writeSecret(clients *ClientSet, opts *QueryOptions) (i
vaultSecret, err := clients.Vault().Logical().Write(d.path, d.data)
if err != nil {
return nil, nil, errors.Wrap(err, d.String())
return nil, errors.Wrap(err, d.String())
}
// The secret could be nil if it does not exist.
if vaultSecret == nil {
return nil, nil, fmt.Errorf("%s: no secret exists at %s", d, d.path)
return nil, fmt.Errorf("no secret exists at %s", d.path)
}
// Print any warnings.
for _, w := range vaultSecret.Warnings {
log.Printf("[WARN] %s: %s", d, w)
}
// Create our cloned secret.
secret := &Secret{
LeaseID: vaultSecret.LeaseID,
LeaseDuration: leaseDurationOrDefault(vaultSecret.LeaseDuration),
Renewable: vaultSecret.Renewable,
Data: vaultSecret.Data,
}
d.secret = secret
return respWithMetadata(secret)
return vaultSecret, nil
}

View file

@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
)
// RenderInput is used as input to the render function.
type RenderInput struct {
Backup bool
Contents []byte
@ -20,9 +21,22 @@ type RenderInput struct {
Perms os.FileMode
}
// RenderResult is returned and stored. It contains the status of the render
// operationg.
type RenderResult struct {
DidRender bool
// DidRender indicates if the template rendered to disk. This will be false in
// the event of an error, but it will also be false in dry mode or when the
// template on disk matches the new result.
DidRender bool
// WouldRender indicates if the template would have rendered to disk. This
// will return false in the event of an error, but will return true in dry
// mode or when the template on disk matches the new result.
WouldRender bool
// Contents are the actual contents of the resulting template from the render
// operation.
Contents []byte
}
// Render atomically renders a file contents to disk, returning a result of
@ -37,6 +51,7 @@ func Render(i *RenderInput) (*RenderResult, error) {
return &RenderResult{
DidRender: false,
WouldRender: true,
Contents: existing,
}, nil
}
@ -51,6 +66,7 @@ func Render(i *RenderInput) (*RenderResult, error) {
return &RenderResult{
DidRender: true,
WouldRender: true,
Contents: existing,
}, nil
}

View file

@ -42,8 +42,7 @@ type Runner struct {
dry, once bool
// outStream and errStream are the io.Writer streams where the runner will
// write information. These streams can be set using the SetOutStream()
// and SetErrStream() functions.
// write information.
// inStream is the ioReader where the runner will read information.
outStream, errStream io.Writer
@ -118,6 +117,9 @@ type RenderEvent struct {
// Template is the template attempting to be rendered.
Template *template.Template
// Contents is the raw, rendered contents from the template.
Contents []byte
// TemplateConfigs is the list of template configs that correspond to this
// template.
TemplateConfigs []*config.TemplateConfig
@ -643,6 +645,9 @@ func (r *Runner) Run() error {
event.DidRender = true
event.LastDidRender = renderTime
// Update the contents
event.Contents = result.Contents
// Record that at least one template was rendered.
renderedAny = true
@ -1175,6 +1180,7 @@ func newWatcher(c *config.Config, clients *dep.ClientSet, once bool) (*watch.Wat
RetryFuncDefault: nil,
RetryFuncVault: watch.RetryFunc(c.Vault.Retry.RetryFunc()),
VaultGrace: config.TimeDurationVal(c.Vault.Grace),
VaultToken: config.StringVal(c.Vault.Token),
})
if err != nil {
return nil, errors.Wrap(err, "runner")

View file

@ -150,7 +150,7 @@ func (v *View) poll(viewCh chan<- *View, errCh chan<- error) {
// example, Consul make have an outage, but when it returns, the view
// is unchanged. We have to reset the counter retries, but not update the
// actual template.
log.Printf("[TRACE] view %s successful contact, resetting retries", v.dependency)
log.Printf("[TRACE] (view) %s successful contact, resetting retries", v.dependency)
retries = 0
goto WAIT
case err := <-fetchErrCh:

View file

@ -62,6 +62,9 @@ type NewWatcherInput struct {
// RenewVault indicates if this watcher should renew Vault tokens.
RenewVault bool
// VaultToken is the vault token to renew.
VaultToken string
// RetryFuncs specify the different ways to retry based on the upstream.
RetryFuncConsul RetryFunc
RetryFuncDefault RetryFunc
@ -90,7 +93,7 @@ func NewWatcher(i *NewWatcherInput) (*Watcher, error) {
// Start a watcher for the Vault renew if that config was specified
if i.RenewVault {
vt, err := dep.NewVaultTokenQuery()
vt, err := dep.NewVaultTokenQuery(i.VaultToken)
if err != nil {
return nil, errors.Wrap(err, "watcher")
}

36
vendor/vendor.json vendored
View file

@ -611,44 +611,44 @@
{
"checksumSHA1": "Nu2j1GusM7ZH0uYrGzqr1K7yH7I=",
"path": "github.com/hashicorp/consul-template/child",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "QWcGW3wELSp/YsOVzCW02oEYR7c=",
"checksumSHA1": "lemUzh6uQDMxuvTT/BREYdGcS0U=",
"path": "github.com/hashicorp/consul-template/config",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "mV7yjHpIfO4yRAdQaBlAqdGDKO8=",
"checksumSHA1": "WVZ+pqn/HLLXjj+Tj5ZZvD7w6r0=",
"path": "github.com/hashicorp/consul-template/dependency",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "ZTlPhrxNzME75A4ydXM88TFt3Qs=",
"checksumSHA1": "Cu8hIII8Z6FAuunFI/jXPLl0nQA=",
"path": "github.com/hashicorp/consul-template/manager",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "oskgb0WteBKOItG8NNDduM7E/D0=",
"path": "github.com/hashicorp/consul-template/signals",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "zSvJlNfZS3fCRlFaZ7r9Q+N17T8=",
"path": "github.com/hashicorp/consul-template/template",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "85W96Fo50FmrMaba7Dk12aDfwWs=",
"checksumSHA1": "b4+Y+02pY2Y5620F9ALzKg8Zmdw=",
"path": "github.com/hashicorp/consul-template/watch",
"revision": "ecbc27c1922fed2f562e7fb63e1ad24e818fa60e",
"revisionTime": "2017-07-05T14:04:00Z"
"revision": "7b3f45039cf3ad1a758683fd3eebb1cc72affa06",
"revisionTime": "2017-08-01T00:58:49Z"
},
{
"checksumSHA1": "jfELEMRhiTcppZmRH+ZwtkVS5Uw=",