func (r *LifetimeWatcher) doRenew() error {
	var nonRenewable bool
	var tokenMode bool
	var initLeaseDuration int
	var credString string
	var renewFunc func(string, int) (*Secret, error)
	switch {
	case r.secret.Auth != nil:
		tokenMode = true
		nonRenewable = !r.secret.Auth.Renewable
		initLeaseDuration = r.secret.Auth.LeaseDuration
		credString = r.secret.Auth.ClientToken
		renewFunc = r.client.Auth().Token().RenewTokenAsSelf
	default:
		nonRenewable = !r.secret.Renewable
		initLeaseDuration = r.secret.LeaseDuration
		credString = r.secret.LeaseID
		renewFunc = r.client.Sys().Renew
	}
	if credString == "" ||
		(nonRenewable && r.renewBehavior == RenewBehaviorErrorOnErrors) {
		return r.errLifetimeWatcherNotRenewable
	}
	initialTime := time.Now()
	priorDuration := time.Duration(initLeaseDuration) * time.Second
	r.calculateGrace(priorDuration)
	for {
		select {
		case <-r.stopCh:
			return nil
		default:
		}
		var leaseDuration time.Duration
		fallbackLeaseDuration := initialTime.Add(priorDuration).Sub(time.Now())
		switch {
		case nonRenewable || r.renewBehavior == RenewBehaviorRenewDisabled:
			leaseDuration = fallbackLeaseDuration
		default:
			renewal, err := renewFunc(credString, r.increment)
			if err != nil || renewal == nil || (tokenMode && renewal.Auth == nil) {
				if r.renewBehavior == RenewBehaviorErrorOnErrors {
					if err != nil {
						return err
					}
					if renewal == nil || (tokenMode && renewal.Auth == nil) {
						return r.errLifetimeWatcherNoSecretData
					}
				}
				leaseDuration = fallbackLeaseDuration
				break
			}
			select {
			case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
			default:
			}
			if ((tokenMode && !renewal.Auth.Renewable) || (!tokenMode && !renewal.Renewable)) &&
				r.renewBehavior == RenewBehaviorErrorOnErrors {
				return r.errLifetimeWatcherNotRenewable
			}
			newDuration := renewal.LeaseDuration
			if tokenMode {
				newDuration = renewal.Auth.LeaseDuration
			}
			leaseDuration = time.Duration(newDuration) * time.Second
		}
		if leaseDuration > priorDuration {
			r.calculateGrace(leaseDuration)
		}
		priorDuration = leaseDuration
		sleepDuration := time.Duration(float64(leaseDuration.Nanoseconds())*2/3 + float64(r.grace.Nanoseconds())/3)
		if leaseDuration <= r.grace || leaseDuration-sleepDuration <= r.grace {
			return nil
		}
		select {
		case <-r.stopCh:
			return nil
		case <-time.After(sleepDuration):
			continue
		}
	}
}
renewAuth is a helper for renewing authentication.
package api
import (
	"errors"
	"math/rand"
	"sync"
	"time"
)
var (
	ErrLifetimeWatcherMissingInput  = errors.New("missing input")
	ErrLifetimeWatcherMissingSecret = errors.New("missing secret")
	ErrLifetimeWatcherNotRenewable  = errors.New("secret is not renewable")
	ErrLifetimeWatcherNoSecretData  = errors.New("returned empty secret data")
	ErrRenewerMissingInput  = errors.New("missing input to renewer")
	ErrRenewerMissingSecret = errors.New("missing secret to renew")
	ErrRenewerNotRenewable  = errors.New("secret is not renewable")
	ErrRenewerNoSecretData  = errors.New("returned empty secret data")
	DefaultLifetimeWatcherRenewBuffer = 5
	DefaultRenewerRenewBuffer = 5
)
type RenewBehavior uint
const (
	RenewBehaviorIgnoreErrors RenewBehavior = iota
	RenewBehaviorRenewDisabled
	RenewBehaviorErrorOnErrors
)
type LifetimeWatcher struct {
	l sync.Mutex
	client        *Client
	secret        *Secret
	grace         time.Duration
	random        *rand.Rand
	increment     int
	doneCh        chan error
	renewCh       chan *RenewOutput
	renewBehavior RenewBehavior
	stopped bool
	stopCh  chan struct{}
	errLifetimeWatcherNotRenewable error
	errLifetimeWatcherNoSecretData error
}
type LifetimeWatcherInput struct {
	Secret *Secret
	Grace time.Duration
	Rand *rand.Rand
	RenewBuffer int
	Increment int
	RenewBehavior RenewBehavior
}
type RenewOutput struct {
	RenewedAt time.Time
	Secret *Secret
}
func (c *Client) NewLifetimeWatcher(i *LifetimeWatcherInput) (*LifetimeWatcher, error) {
	if i == nil {
		return nil, ErrLifetimeWatcherMissingInput
	}
	secret := i.Secret
	if secret == nil {
		return nil, ErrLifetimeWatcherMissingSecret
	}
	random := i.Rand
	if random == nil {
		random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
	}
	renewBuffer := i.RenewBuffer
	if renewBuffer == 0 {
		renewBuffer = DefaultLifetimeWatcherRenewBuffer
	}
	return &LifetimeWatcher{
		client:        c,
		secret:        secret,
		increment:     i.Increment,
		random:        random,
		doneCh:        make(chan error, 1),
		renewCh:       make(chan *RenewOutput, renewBuffer),
		renewBehavior: i.RenewBehavior,
		stopped: false,
		stopCh:  make(chan struct{}),
		errLifetimeWatcherNotRenewable: ErrLifetimeWatcherNotRenewable,
		errLifetimeWatcherNoSecretData: ErrLifetimeWatcherNoSecretData,
	}, nil
}
func (c *Client) NewRenewer(i *LifetimeWatcherInput) (*LifetimeWatcher, error) {
	if i == nil {
		return nil, ErrRenewerMissingInput
	}
	secret := i.Secret
	if secret == nil {
		return nil, ErrRenewerMissingSecret
	}
	renewer, err := c.NewLifetimeWatcher(i)
	if err != nil {
		return nil, err
	}
	renewer.renewBehavior = RenewBehaviorErrorOnErrors
	renewer.errLifetimeWatcherNotRenewable = ErrRenewerNotRenewable
	renewer.errLifetimeWatcherNoSecretData = ErrRenewerNoSecretData
	return renewer, err
}
func (r *LifetimeWatcher) DoneCh() <-chan error {
	return r.doneCh
}
func (r *LifetimeWatcher) RenewCh() <-chan *RenewOutput {
	return r.renewCh
}
func (r *LifetimeWatcher) Stop() {
	r.l.Lock()
	defer r.l.Unlock()
	if !r.stopped {
		close(r.stopCh)
		r.stopped = true
	}
}
func (r *LifetimeWatcher) Start() {
	r.doneCh <- r.doRenew()
}
func (r *LifetimeWatcher) Renew() {
	r.Start()
}