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