Skip to content

Commit

Permalink
config: Add WithCredentialCacheOptions for LoadDefaultConfig's LoadOp…
Browse files Browse the repository at this point in the history
…tions (#1523)

* config: add load option for CredentialCache

Adds a new member to the LoadOptions struct, CredentialsCacheOptions.
This member allows specifying a function that will be used to configure
the CredentialsCache. The CredentialsCacheOptions will only be used if
the configuration loader will wrap the underlying credential provider in
the CredentialsCache.

* fixup copy/paste in description
  • Loading branch information
jasdel committed Jan 7, 2022
1 parent 9ebb3ef commit 2d25393
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 10 deletions.
8 changes: 8 additions & 0 deletions .changelog/e8196bb044a34f06804817bb03c87bff.json
@@ -0,0 +1,8 @@
{
"id": "e8196bb0-44a3-4f06-8048-17bb03c87bff",
"type": "feature",
"description": "Add load option for CredentialCache. Adds a new member to the LoadOptions struct, CredentialsCacheOptions. This member allows specifying a function that will be used to configure the CredentialsCache. The CredentialsCacheOptions will only be used if the configuration loader will wrap the underlying credential provider in the CredentialsCache.",
"modules": [
"config"
]
}
2 changes: 2 additions & 0 deletions config/codegen/main.go
Expand Up @@ -26,6 +26,8 @@ var implAsserts = map[string][]string{
"regionProvider": {envConfigType, sharedConfigType, loadOptionsType, ec2IMDSRegionType},
"credentialsProviderProvider": {loadOptionsType},
"defaultRegionProvider": {loadOptionsType},
"credentialsCacheOptionsProvider": {loadOptionsType},
"processCredentialOptions": {loadOptionsType},
"ec2RoleCredentialOptionsProvider": {loadOptionsType},
"endpointCredentialOptionsProvider": {loadOptionsType},
"assumeRoleCredentialOptionsProvider": {loadOptionsType},
Expand Down
13 changes: 13 additions & 0 deletions config/example_test.go
Expand Up @@ -6,6 +6,7 @@ import (
"log"
"net/http"
"path/filepath"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
Expand All @@ -16,6 +17,18 @@ import (
smithyhttp "github.com/aws/smithy-go/transport/http"
)

func ExampleWithCredentialsCacheOptions() {
cfg, err := config.LoadDefaultConfig(context.TODO(),
config.WithCredentialsCacheOptions(func(o *aws.CredentialsCacheOptions) {
o.ExpiryWindow = 10 * time.Minute
}),
)
if err != nil {
log.Fatal(err)
}
_ = cfg
}

func ExampleWithSharedConfigProfile() {
cfg, err := config.LoadDefaultConfig(context.TODO(),
// Specify the shared configuration profile to load.
Expand Down
27 changes: 27 additions & 0 deletions config/load_options.go
Expand Up @@ -101,6 +101,10 @@ type LoadOptions struct {
// from the EC2 Metadata service
UseEC2IMDSRegion *UseEC2IMDSRegion

// CredentialsCacheOptions is a function for setting the
// aws.CredentialsCacheOptions
CredentialsCacheOptions func(*aws.CredentialsCacheOptions)

// ProcessCredentialOptions is a function for setting
// the processcreds.Options
ProcessCredentialOptions func(*processcreds.Options)
Expand Down Expand Up @@ -365,6 +369,29 @@ func WithCredentialsProvider(v aws.CredentialsProvider) LoadOptionsFunc {
}
}

// getCredentialsCacheOptionsProvider returns the wrapped function to set aws.CredentialsCacheOptions
func (o LoadOptions) getCredentialsCacheOptions(ctx context.Context) (func(*aws.CredentialsCacheOptions), bool, error) {
if o.CredentialsCacheOptions == nil {
return nil, false, nil
}

return o.CredentialsCacheOptions, true, nil
}

// WithCredentialsCacheOptions is a helper function to construct functional
// options that sets a function to modify the aws.CredentialsCacheOptions the
// aws.CredentialsCache will be configured with, if the CredentialsCache is used
// by the configuration loader.
//
// If multiple WithCredentialsCacheOptions calls are made, the last call
// overrides the previous call values.
func WithCredentialsCacheOptions(v func(*aws.CredentialsCacheOptions)) LoadOptionsFunc {
return func(o *LoadOptions) error {
o.CredentialsCacheOptions = v
return nil
}
}

// getProcessCredentialOptions returns the wrapped function to set processcreds.Options
func (o LoadOptions) getProcessCredentialOptions(ctx context.Context) (func(*processcreds.Options), bool, error) {
if o.ProcessCredentialOptions == nil {
Expand Down
22 changes: 22 additions & 0 deletions config/provider.go
Expand Up @@ -162,6 +162,28 @@ func getCredentialsProvider(ctx context.Context, configs configs) (p aws.Credent
return
}

// credentialsCacheOptionsProvider is an interface for retrieving a function for setting
// the aws.CredentialsCacheOptions.
type credentialsCacheOptionsProvider interface {
getCredentialsCacheOptions(ctx context.Context) (func(*aws.CredentialsCacheOptions), bool, error)
}

// getCredentialsCacheOptionsProvider is an interface for retrieving a function for setting
// the aws.CredentialsCacheOptions.
func getCredentialsCacheOptionsProvider(ctx context.Context, configs configs) (
f func(*aws.CredentialsCacheOptions), found bool, err error,
) {
for _, config := range configs {
if p, ok := config.(credentialsCacheOptionsProvider); ok {
f, found, err = p.getCredentialsCacheOptions(ctx)
if err != nil || found {
break
}
}
}
return
}

// processCredentialOptions is an interface for retrieving a function for setting
// the processcreds.Options.
type processCredentialOptions interface {
Expand Down
10 changes: 10 additions & 0 deletions config/provider_assert_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 41 additions & 10 deletions config/resolve_credentials.go
Expand Up @@ -59,16 +59,19 @@ func resolveCredentials(ctx context.Context, cfg *aws.Config, configs configs) e
//
// Config providers used:
// * credentialsProviderProvider
func resolveCredentialProvider(ctx context.Context, cfg *aws.Config, cfgs configs) (bool, error) {
credProvider, found, err := getCredentialsProvider(ctx, cfgs)
func resolveCredentialProvider(ctx context.Context, cfg *aws.Config, configs configs) (bool, error) {
credProvider, found, err := getCredentialsProvider(ctx, configs)
if err != nil {
return false, err
}
if !found {
return false, nil
}

cfg.Credentials = wrapWithCredentialsCache(credProvider)
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, credProvider)
if err != nil {
return false, err
}

return true, nil
}
Expand Down Expand Up @@ -105,7 +108,10 @@ func resolveCredentialChain(ctx context.Context, cfg *aws.Config, configs config
}

// Wrap the resolved provider in a cache so the SDK will cache credentials.
cfg.Credentials = wrapWithCredentialsCache(cfg.Credentials)
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, cfg.Credentials)
if err != nil {
return err
}

return nil
}
Expand Down Expand Up @@ -248,9 +254,12 @@ func resolveHTTPCredProvider(ctx context.Context, cfg *aws.Config, url, authToke

provider := endpointcreds.New(url, optFns...)

cfg.Credentials = wrapWithCredentialsCache(provider, func(options *aws.CredentialsCacheOptions) {
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider, func(options *aws.CredentialsCacheOptions) {
options.ExpiryWindow = 5 * time.Minute
})
if err != nil {
return err
}

return nil
}
Expand Down Expand Up @@ -296,9 +305,12 @@ func resolveEC2RoleCredentials(ctx context.Context, cfg *aws.Config, configs con

provider := ec2rolecreds.New(optFns...)

cfg.Credentials = wrapWithCredentialsCache(provider, func(options *aws.CredentialsCacheOptions) {
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider, func(options *aws.CredentialsCacheOptions) {
options.ExpiryWindow = 5 * time.Minute
})
if err != nil {
return err
}

return nil
}
Expand Down Expand Up @@ -430,12 +442,31 @@ func credsFromAssumeRole(ctx context.Context, cfg *aws.Config, sharedCfg *Shared
return nil
}

// wrapWithCredentialsCache will wrap provider with an aws.CredentialsCache with the provided options if the provider is not already a aws.CredentialsCache.
func wrapWithCredentialsCache(provider aws.CredentialsProvider, optFns ...func(options *aws.CredentialsCacheOptions)) aws.CredentialsProvider {
// wrapWithCredentialsCache will wrap provider with an aws.CredentialsCache
// with the provided options if the provider is not already a
// aws.CredentialsCache.
func wrapWithCredentialsCache(
ctx context.Context,
cfgs configs,
provider aws.CredentialsProvider,
optFns ...func(options *aws.CredentialsCacheOptions),
) (aws.CredentialsProvider, error) {
_, ok := provider.(*aws.CredentialsCache)
if ok {
return provider
return provider, nil
}

credCacheOptions, found, err := getCredentialsCacheOptionsProvider(ctx, cfgs)
if err != nil {
return nil, err
}

// force allocation of a new slice if the additional options are
// needed, to prevent overwriting the passed in slice of options.
optFns = optFns[:len(optFns):len(optFns)]
if found {
optFns = append(optFns, credCacheOptions)
}

return aws.NewCredentialsCache(provider, optFns...)
return aws.NewCredentialsCache(provider, optFns...), nil
}
19 changes: 19 additions & 0 deletions config/resolve_credentials_test.go
Expand Up @@ -452,3 +452,22 @@ func TestSharedConfigCredentialSource(t *testing.T) {
})
}
}

func TestResolveCredentialsCacheOptions(t *testing.T) {
var cfg aws.Config
var optionsFnCalled bool

err := resolveCredentials(context.Background(), &cfg, configs{LoadOptions{
CredentialsCacheOptions: func(o *aws.CredentialsCacheOptions) {
optionsFnCalled = true
o.ExpiryWindow = time.Minute * 5
},
}})
if err != nil {
t.Fatalf("expect no error, got %v", err)
}

if !optionsFnCalled {
t.Errorf("expect options to be called")
}
}

0 comments on commit 2d25393

Please sign in to comment.