func (r *Renewer) renewLease() error {
	if !r.secret.Renewable || r.secret.LeaseID == "" {
		return ErrRenewerNotRenewable
	}
	priorDuration := time.Duration(r.secret.LeaseDuration) * time.Second
	r.calculateGrace(priorDuration)
	client, leaseID := r.client, r.secret.LeaseID
	for {
		select {
		case <-r.stopCh:
			return nil
		default:
		}
		renewal, err := client.Sys().Renew(leaseID, r.increment)
		if err != nil {
			return err
		}
		select {
		case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
		default:
		}
		if renewal == nil {
			return ErrRenewerNoSecretData
		}
		if !renewal.Renewable {
			return ErrRenewerNotRenewable
		}
		leaseDuration := time.Duration(renewal.LeaseDuration) * 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
		}
	}
}
renewLease is a helper for renewing a lease.
package api
import (
	"errors"
	"math/rand"
	"sync"
	"time"
)
var (
	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")
	DefaultRenewerRenewBuffer = 5
)
type Renewer struct {
	l sync.Mutex
	client    *Client
	secret    *Secret
	grace     time.Duration
	random    *rand.Rand
	increment int
	doneCh    chan error
	renewCh   chan *RenewOutput
	stopped bool
	stopCh  chan struct{}
}
type RenewerInput struct {
	Secret *Secret
	Grace time.Duration
	Rand *rand.Rand
	RenewBuffer int
	Increment int
}
type RenewOutput struct {
	RenewedAt time.Time
	Secret *Secret
}
func (c *Client) NewRenewer(i *RenewerInput) (*Renewer, error) {
	if i == nil {
		return nil, ErrRenewerMissingInput
	}
	secret := i.Secret
	if secret == nil {
		return nil, ErrRenewerMissingSecret
	}
	random := i.Rand
	if random == nil {
		random = rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
	}
	renewBuffer := i.RenewBuffer
	if renewBuffer == 0 {
		renewBuffer = DefaultRenewerRenewBuffer
	}
	return &Renewer{
		client:    c,
		secret:    secret,
		increment: i.Increment,
		random:    random,
		doneCh:    make(chan error, 1),
		renewCh:   make(chan *RenewOutput, renewBuffer),
		stopped: false,
		stopCh:  make(chan struct{}),
	}, nil
}
func (r *Renewer) DoneCh() <-chan error {
	return r.doneCh
}
func (r *Renewer) RenewCh() <-chan *RenewOutput {
	return r.renewCh
}
func (r *Renewer) Stop() {
	r.l.Lock()
	if !r.stopped {
		close(r.stopCh)
		r.stopped = true
	}
	r.l.Unlock()
}
func (r *Renewer) Renew() {
	var result error
	if r.secret.Auth != nil {
		result = r.renewAuth()
	} else {
		result = r.renewLease()
	}
	r.doneCh <- result
}
func (r *Renewer) renewAuth() error {
	if !r.secret.Auth.Renewable || r.secret.Auth.ClientToken == "" {
		return ErrRenewerNotRenewable
	}
	priorDuration := time.Duration(r.secret.Auth.LeaseDuration) * time.Second
	r.calculateGrace(priorDuration)
	client, token := r.client, r.secret.Auth.ClientToken
	for {
		select {
		case <-r.stopCh:
			return nil
		default:
		}
		renewal, err := client.Auth().Token().RenewTokenAsSelf(token, r.increment)
		if err != nil {
			return err
		}
		select {
		case r.renewCh <- &RenewOutput{time.Now().UTC(), renewal}:
		default:
		}
		if renewal == nil || renewal.Auth == nil {
			return ErrRenewerNoSecretData
		}
		if !renewal.Auth.Renewable {
			return ErrRenewerNotRenewable
		}
		leaseDuration := time.Duration(renewal.Auth.LeaseDuration) * 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
		}
	}
}