1 package config
2 3 import (
4 "context"
5 "os"
6 7 "github.com/aws/aws-sdk-go-v2/aws"
8 )
9 10 // defaultAWSConfigResolvers are a slice of functions that will resolve external
11 // configuration values into AWS configuration values.
12 //
13 // This will setup the AWS configuration's Region,
14 var defaultAWSConfigResolvers = []awsConfigResolver{
15 // Resolves the default configuration the SDK's aws.Config will be
16 // initialized with.
17 resolveDefaultAWSConfig,
18 19 // Sets the logger to be used. Could be user provided logger, and client
20 // logging mode.
21 resolveLogger,
22 resolveClientLogMode,
23 24 // Sets the HTTP client and configuration to use for making requests using
25 // the HTTP transport.
26 resolveHTTPClient,
27 resolveCustomCABundle,
28 29 // Sets the endpoint resolving behavior the API Clients will use for making
30 // requests to. Clients default to their own clients this allows overrides
31 // to be specified. The resolveEndpointResolver option is deprecated, but
32 // we still need to set it for backwards compatibility on config
33 // construction.
34 resolveEndpointResolver,
35 resolveEndpointResolverWithOptions,
36 37 // Sets the retry behavior API clients will use within their retry attempt
38 // middleware. Defaults to unset, allowing API clients to define their own
39 // retry behavior.
40 resolveRetryer,
41 42 // Sets the region the API Clients should use for making requests to.
43 resolveRegion,
44 resolveEC2IMDSRegion,
45 resolveDefaultRegion,
46 47 // Sets the additional set of middleware stack mutators that will custom
48 // API client request pipeline middleware.
49 resolveAPIOptions,
50 51 // Resolves the DefaultsMode that should be used by SDK clients. If this
52 // mode is set to DefaultsModeAuto.
53 //
54 // Comes after HTTPClient and CustomCABundle to ensure the HTTP client is
55 // configured if provided before invoking IMDS if mode is auto. Comes
56 // before resolving credentials so that those subsequent clients use the
57 // configured auto mode.
58 resolveDefaultsModeOptions,
59 60 // Sets the resolved credentials the API clients will use for
61 // authentication. Provides the SDK's default credential chain.
62 //
63 // Should probably be the last step in the resolve chain to ensure that all
64 // other configurations are resolved first in case downstream credentials
65 // implementations depend on or can be configured with earlier resolved
66 // configuration options.
67 resolveCredentials,
68 69 // Sets the resolved bearer authentication token API clients will use for
70 // httpBearerAuth authentication scheme.
71 resolveBearerAuthToken,
72 73 // Sets the sdk app ID if present in env var or shared config profile
74 resolveAppID,
75 76 resolveBaseEndpoint,
77 78 // Sets the DisableRequestCompression if present in env var or shared config profile
79 resolveDisableRequestCompression,
80 81 // Sets the RequestMinCompressSizeBytes if present in env var or shared config profile
82 resolveRequestMinCompressSizeBytes,
83 84 // Sets the AccountIDEndpointMode if present in env var or shared config profile
85 resolveAccountIDEndpointMode,
86 87 // Sets the RequestChecksumCalculation if present in env var or shared config profile
88 resolveRequestChecksumCalculation,
89 90 // Sets the ResponseChecksumValidation if present in env var or shared config profile
91 resolveResponseChecksumValidation,
92 93 resolveInterceptors,
94 95 resolveAuthSchemePreference,
96 97 // Sets the ServiceOptions if present in LoadOptions
98 resolveServiceOptions,
99 }
100 101 // A Config represents a generic configuration value or set of values. This type
102 // will be used by the AWSConfigResolvers to extract
103 //
104 // General the Config type will use type assertion against the Provider interfaces
105 // to extract specific data from the Config.
106 type Config interface{}
107 108 // A loader is used to load external configuration data and returns it as
109 // a generic Config type.
110 //
111 // The loader should return an error if it fails to load the external configuration
112 // or the configuration data is malformed, or required components missing.
113 type loader func(context.Context, configs) (Config, error)
114 115 // An awsConfigResolver will extract configuration data from the configs slice
116 // using the provider interfaces to extract specific functionality. The extracted
117 // configuration values will be written to the AWS Config value.
118 //
119 // The resolver should return an error if it it fails to extract the data, the
120 // data is malformed, or incomplete.
121 type awsConfigResolver func(ctx context.Context, cfg *aws.Config, configs configs) error
122 123 // configs is a slice of Config values. These values will be used by the
124 // AWSConfigResolvers to extract external configuration values to populate the
125 // AWS Config type.
126 //
127 // Use AppendFromLoaders to add additional external Config values that are
128 // loaded from external sources.
129 //
130 // Use ResolveAWSConfig after external Config values have been added or loaded
131 // to extract the loaded configuration values into the AWS Config.
132 type configs []Config
133 134 // AppendFromLoaders iterates over the slice of loaders passed in calling each
135 // loader function in order. The external config value returned by the loader
136 // will be added to the returned configs slice.
137 //
138 // If a loader returns an error this method will stop iterating and return
139 // that error.
140 func (cs configs) AppendFromLoaders(ctx context.Context, loaders []loader) (configs, error) {
141 for _, fn := range loaders {
142 cfg, err := fn(ctx, cs)
143 if err != nil {
144 return nil, err
145 }
146 147 cs = append(cs, cfg)
148 }
149 150 return cs, nil
151 }
152 153 // ResolveAWSConfig returns a AWS configuration populated with values by calling
154 // the resolvers slice passed in. Each resolver is called in order. Any resolver
155 // may overwrite the AWS Configuration value of a previous resolver.
156 //
157 // If an resolver returns an error this method will return that error, and stop
158 // iterating over the resolvers.
159 func (cs configs) ResolveAWSConfig(ctx context.Context, resolvers []awsConfigResolver) (aws.Config, error) {
160 var cfg aws.Config
161 162 for _, fn := range resolvers {
163 if err := fn(ctx, &cfg, cs); err != nil {
164 return aws.Config{}, err
165 }
166 }
167 168 return cfg, nil
169 }
170 171 // ResolveConfig calls the provide function passing slice of configuration sources.
172 // This implements the aws.ConfigResolver interface.
173 func (cs configs) ResolveConfig(f func(configs []interface{}) error) error {
174 var cfgs []interface{}
175 for i := range cs {
176 cfgs = append(cfgs, cs[i])
177 }
178 return f(cfgs)
179 }
180 181 // LoadDefaultConfig reads the SDK's default external configurations, and
182 // populates an AWS Config with the values from the external configurations.
183 //
184 // An optional variadic set of additional Config values can be provided as input
185 // that will be prepended to the configs slice. Use this to add custom configuration.
186 // The custom configurations must satisfy the respective providers for their data
187 // or the custom data will be ignored by the resolvers and config loaders.
188 //
189 // cfg, err := config.LoadDefaultConfig( context.TODO(),
190 // config.WithSharedConfigProfile("test-profile"),
191 // )
192 // if err != nil {
193 // panic(fmt.Sprintf("failed loading config, %v", err))
194 // }
195 //
196 // The default configuration sources are:
197 // * Environment Variables
198 // * Shared Configuration and Shared Credentials files.
199 func LoadDefaultConfig(ctx context.Context, optFns ...func(*LoadOptions) error) (cfg aws.Config, err error) {
200 var options LoadOptions
201 for _, optFn := range optFns {
202 if err := optFn(&options); err != nil {
203 return aws.Config{}, err
204 }
205 }
206 207 // assign Load Options to configs
208 var cfgCpy = configs{options}
209 210 cfgCpy, err = cfgCpy.AppendFromLoaders(ctx, resolveConfigLoaders(&options))
211 if err != nil {
212 return aws.Config{}, err
213 }
214 215 cfg, err = cfgCpy.ResolveAWSConfig(ctx, defaultAWSConfigResolvers)
216 if err != nil {
217 return aws.Config{}, err
218 }
219 220 return cfg, nil
221 }
222 223 func resolveConfigLoaders(options *LoadOptions) []loader {
224 loaders := make([]loader, 2)
225 loaders[0] = loadEnvConfig
226 227 // specification of a profile should cause a load failure if it doesn't exist
228 if os.Getenv(awsProfileEnv) != "" || options.SharedConfigProfile != "" {
229 loaders[1] = loadSharedConfig
230 } else {
231 loaders[1] = loadSharedConfigIgnoreNotExist
232 }
233 234 return loaders
235 }
236