chain.go raw

   1  package credentials
   2  
   3  import (
   4  	"errors"
   5  	"fmt"
   6  )
   7  
   8  var (
   9  	ErrNoValidCredentialProviders = errors.New("no valid credential providers")
  10  )
  11  
  12  // A ChainProvider will search for a provider which returns credentials
  13  // and cache that provider until Retrieve is called again.
  14  type ChainProvider struct {
  15  	Providers []Provider
  16  	current   Provider
  17  }
  18  
  19  // NewChainCredentials returns a pointer to a new Credentials object
  20  // wrapping a chain of providers.
  21  func NewChainCredentials(providers []Provider) *Credentials {
  22  	return NewCredentials(&ChainProvider{
  23  		Providers: append([]Provider{}, providers...),
  24  	})
  25  }
  26  
  27  // Retrieve returns the first provider in the chain that succeeds,
  28  // or error if no provider returned.
  29  //
  30  // If a provider is found it will be cached and any calls to IsExpired()
  31  // will return the expired state of the cached provider.
  32  func (c *ChainProvider) Retrieve() (Value, error) {
  33  	var errs = ErrNoValidCredentialProviders
  34  
  35  	for _, p := range c.Providers {
  36  		creds, err := p.Retrieve()
  37  		if err == nil {
  38  			c.current = p
  39  			return creds, nil
  40  		}
  41  
  42  		errs = fmt.Errorf("%v: %w", errs, err)
  43  	}
  44  	c.current = nil
  45  
  46  	return Value{}, fmt.Errorf("chain provider: %w", errs)
  47  }
  48  
  49  // IsExpired will returned the expired state of the currently cached provider
  50  // if there is one.  If there is no current provider, true will be returned.
  51  func (c *ChainProvider) IsExpired() bool {
  52  	if c.current != nil {
  53  		return c.current.IsExpired()
  54  	}
  55  
  56  	return true
  57  }
  58