]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/aws/aws-sdk-go/aws/session/session.go
deps: use go modules for dep mgmt
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / aws / aws-sdk-go / aws / session / session.go
CommitLineData
bae9f6d2
JC
1package session
2
3import (
4 "crypto/tls"
5 "crypto/x509"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "net/http"
10 "os"
11
12 "github.com/aws/aws-sdk-go/aws"
13 "github.com/aws/aws-sdk-go/aws/awserr"
14 "github.com/aws/aws-sdk-go/aws/client"
15 "github.com/aws/aws-sdk-go/aws/corehandlers"
16 "github.com/aws/aws-sdk-go/aws/credentials"
17 "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
18 "github.com/aws/aws-sdk-go/aws/defaults"
19 "github.com/aws/aws-sdk-go/aws/endpoints"
20 "github.com/aws/aws-sdk-go/aws/request"
21)
22
23// A Session provides a central location to create service clients from and
24// store configurations and request handlers for those services.
25//
26// Sessions are safe to create service clients concurrently, but it is not safe
27// to mutate the Session concurrently.
28//
29// The Session satisfies the service client's client.ClientConfigProvider.
30type Session struct {
31 Config *aws.Config
32 Handlers request.Handlers
33}
34
35// New creates a new instance of the handlers merging in the provided configs
36// on top of the SDK's default configurations. Once the Session is created it
37// can be mutated to modify the Config or Handlers. The Session is safe to be
38// read concurrently, but it should not be written to concurrently.
39//
40// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
41// method could now encounter an error when loading the configuration. When
42// The environment variable is set, and an error occurs, New will return a
43// session that will fail all requests reporting the error that occurred while
44// loading the session. Use NewSession to get the error when creating the
45// session.
46//
47// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
48// the shared config file (~/.aws/config) will also be loaded, in addition to
49// the shared credentials file (~/.aws/credentials). Values set in both the
50// shared config, and shared credentials will be taken from the shared
51// credentials file.
52//
53// Deprecated: Use NewSession functions to create sessions instead. NewSession
54// has the same functionality as New except an error can be returned when the
55// func is called instead of waiting to receive an error until a request is made.
56func New(cfgs ...*aws.Config) *Session {
57 // load initial config from environment
58 envCfg := loadEnvConfig()
59
60 if envCfg.EnableSharedConfig {
61 s, err := newSession(Options{}, envCfg, cfgs...)
62 if err != nil {
63 // Old session.New expected all errors to be discovered when
64 // a request is made, and would report the errors then. This
65 // needs to be replicated if an error occurs while creating
66 // the session.
67 msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
68 "Use session.NewSession to handle errors occurring during session creation."
69
70 // Session creation failed, need to report the error and prevent
71 // any requests from succeeding.
72 s = &Session{Config: defaults.Config()}
73 s.Config.MergeIn(cfgs...)
74 s.Config.Logger.Log("ERROR:", msg, "Error:", err)
75 s.Handlers.Validate.PushBack(func(r *request.Request) {
76 r.Error = err
77 })
78 }
79 return s
80 }
81
82 return deprecatedNewSession(cfgs...)
83}
84
85// NewSession returns a new Session created from SDK defaults, config files,
86// environment, and user provided config files. Once the Session is created
87// it can be mutated to modify the Config or Handlers. The Session is safe to
88// be read concurrently, but it should not be written to concurrently.
89//
90// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
91// the shared config file (~/.aws/config) will also be loaded in addition to
92// the shared credentials file (~/.aws/credentials). Values set in both the
93// shared config, and shared credentials will be taken from the shared
94// credentials file. Enabling the Shared Config will also allow the Session
95// to be built with retrieving credentials with AssumeRole set in the config.
96//
97// See the NewSessionWithOptions func for information on how to override or
98// control through code how the Session will be created. Such as specifying the
99// config profile, and controlling if shared config is enabled or not.
100func NewSession(cfgs ...*aws.Config) (*Session, error) {
101 opts := Options{}
102 opts.Config.MergeIn(cfgs...)
103
104 return NewSessionWithOptions(opts)
105}
106
107// SharedConfigState provides the ability to optionally override the state
108// of the session's creation based on the shared config being enabled or
109// disabled.
110type SharedConfigState int
111
112const (
113 // SharedConfigStateFromEnv does not override any state of the
114 // AWS_SDK_LOAD_CONFIG env var. It is the default value of the
115 // SharedConfigState type.
116 SharedConfigStateFromEnv SharedConfigState = iota
117
118 // SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
119 // and disables the shared config functionality.
120 SharedConfigDisable
121
122 // SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
123 // and enables the shared config functionality.
124 SharedConfigEnable
125)
126
127// Options provides the means to control how a Session is created and what
128// configuration values will be loaded.
129//
130type Options struct {
131 // Provides config values for the SDK to use when creating service clients
132 // and making API requests to services. Any value set in with this field
133 // will override the associated value provided by the SDK defaults,
134 // environment or config files where relevant.
135 //
136 // If not set, configuration values from from SDK defaults, environment,
137 // config will be used.
138 Config aws.Config
139
140 // Overrides the config profile the Session should be created from. If not
141 // set the value of the environment variable will be loaded (AWS_PROFILE,
142 // or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
143 //
144 // If not set and environment variables are not set the "default"
145 // (DefaultSharedConfigProfile) will be used as the profile to load the
146 // session config from.
147 Profile string
148
149 // Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
150 // environment variable. By default a Session will be created using the
151 // value provided by the AWS_SDK_LOAD_CONFIG environment variable.
152 //
153 // Setting this value to SharedConfigEnable or SharedConfigDisable
154 // will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
155 // and enable or disable the shared config functionality.
156 SharedConfigState SharedConfigState
157
9b12e4fe
JC
158 // Ordered list of files the session will load configuration from.
159 // It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE.
160 SharedConfigFiles []string
161
bae9f6d2
JC
162 // When the SDK's shared config is configured to assume a role with MFA
163 // this option is required in order to provide the mechanism that will
164 // retrieve the MFA token. There is no default value for this field. If
165 // it is not set an error will be returned when creating the session.
166 //
167 // This token provider will be called when ever the assumed role's
168 // credentials need to be refreshed. Within the context of service clients
169 // all sharing the same session the SDK will ensure calls to the token
170 // provider are atomic. When sharing a token provider across multiple
171 // sessions additional synchronization logic is needed to ensure the
172 // token providers do not introduce race conditions. It is recommend to
173 // share the session where possible.
174 //
175 // stscreds.StdinTokenProvider is a basic implementation that will prompt
176 // from stdin for the MFA token code.
177 //
178 // This field is only used if the shared configuration is enabled, and
179 // the config enables assume role wit MFA via the mfa_serial field.
180 AssumeRoleTokenProvider func() (string, error)
181
182 // Reader for a custom Credentials Authority (CA) bundle in PEM format that
183 // the SDK will use instead of the default system's root CA bundle. Use this
184 // only if you want to replace the CA bundle the SDK uses for TLS requests.
185 //
186 // Enabling this option will attempt to merge the Transport into the SDK's HTTP
187 // client. If the client's Transport is not a http.Transport an error will be
188 // returned. If the Transport's TLS config is set this option will cause the SDK
189 // to overwrite the Transport's TLS config's RootCAs value. If the CA
190 // bundle reader contains multiple certificates all of them will be loaded.
191 //
192 // The Session option CustomCABundle is also available when creating sessions
193 // to also enable this feature. CustomCABundle session option field has priority
194 // over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
195 CustomCABundle io.Reader
196}
197
198// NewSessionWithOptions returns a new Session created from SDK defaults, config files,
199// environment, and user provided config files. This func uses the Options
200// values to configure how the Session is created.
201//
202// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
203// the shared config file (~/.aws/config) will also be loaded in addition to
204// the shared credentials file (~/.aws/credentials). Values set in both the
205// shared config, and shared credentials will be taken from the shared
206// credentials file. Enabling the Shared Config will also allow the Session
207// to be built with retrieving credentials with AssumeRole set in the config.
208//
209// // Equivalent to session.New
210// sess := session.Must(session.NewSessionWithOptions(session.Options{}))
211//
212// // Specify profile to load for the session's config
213// sess := session.Must(session.NewSessionWithOptions(session.Options{
214// Profile: "profile_name",
215// }))
216//
217// // Specify profile for config and region for requests
218// sess := session.Must(session.NewSessionWithOptions(session.Options{
219// Config: aws.Config{Region: aws.String("us-east-1")},
220// Profile: "profile_name",
221// }))
222//
223// // Force enable Shared Config support
224// sess := session.Must(session.NewSessionWithOptions(session.Options{
225// SharedConfigState: session.SharedConfigEnable,
226// }))
227func NewSessionWithOptions(opts Options) (*Session, error) {
228 var envCfg envConfig
229 if opts.SharedConfigState == SharedConfigEnable {
230 envCfg = loadSharedEnvConfig()
231 } else {
232 envCfg = loadEnvConfig()
233 }
234
235 if len(opts.Profile) > 0 {
236 envCfg.Profile = opts.Profile
237 }
238
239 switch opts.SharedConfigState {
240 case SharedConfigDisable:
241 envCfg.EnableSharedConfig = false
242 case SharedConfigEnable:
243 envCfg.EnableSharedConfig = true
244 }
245
9b12e4fe
JC
246 if len(envCfg.SharedCredentialsFile) == 0 {
247 envCfg.SharedCredentialsFile = defaults.SharedCredentialsFilename()
248 }
249 if len(envCfg.SharedConfigFile) == 0 {
250 envCfg.SharedConfigFile = defaults.SharedConfigFilename()
251 }
252
bae9f6d2
JC
253 // Only use AWS_CA_BUNDLE if session option is not provided.
254 if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
255 f, err := os.Open(envCfg.CustomCABundle)
256 if err != nil {
257 return nil, awserr.New("LoadCustomCABundleError",
258 "failed to open custom CA bundle PEM file", err)
259 }
260 defer f.Close()
261 opts.CustomCABundle = f
262 }
263
264 return newSession(opts, envCfg, &opts.Config)
265}
266
267// Must is a helper function to ensure the Session is valid and there was no
268// error when calling a NewSession function.
269//
270// This helper is intended to be used in variable initialization to load the
271// Session and configuration at startup. Such as:
272//
273// var sess = session.Must(session.NewSession())
274func Must(sess *Session, err error) *Session {
275 if err != nil {
276 panic(err)
277 }
278
279 return sess
280}
281
282func deprecatedNewSession(cfgs ...*aws.Config) *Session {
283 cfg := defaults.Config()
284 handlers := defaults.Handlers()
285
286 // Apply the passed in configs so the configuration can be applied to the
287 // default credential chain
288 cfg.MergeIn(cfgs...)
289 if cfg.EndpointResolver == nil {
290 // An endpoint resolver is required for a session to be able to provide
291 // endpoints for service client configurations.
292 cfg.EndpointResolver = endpoints.DefaultResolver()
293 }
294 cfg.Credentials = defaults.CredChain(cfg, handlers)
295
296 // Reapply any passed in configs to override credentials if set
297 cfg.MergeIn(cfgs...)
298
299 s := &Session{
300 Config: cfg,
301 Handlers: handlers,
302 }
303
304 initHandlers(s)
305
306 return s
307}
308
309func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
310 cfg := defaults.Config()
311 handlers := defaults.Handlers()
312
313 // Get a merged version of the user provided config to determine if
314 // credentials were.
315 userCfg := &aws.Config{}
316 userCfg.MergeIn(cfgs...)
317
9b12e4fe 318 // Ordered config files will be loaded in with later files overwriting
bae9f6d2 319 // previous config file values.
9b12e4fe
JC
320 var cfgFiles []string
321 if opts.SharedConfigFiles != nil {
322 cfgFiles = opts.SharedConfigFiles
323 } else {
324 cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
325 if !envCfg.EnableSharedConfig {
326 // The shared config file (~/.aws/config) is only loaded if instructed
327 // to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
328 cfgFiles = cfgFiles[1:]
329 }
bae9f6d2
JC
330 }
331
332 // Load additional config from file(s)
333 sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles)
334 if err != nil {
335 return nil, err
336 }
337
338 if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
339 return nil, err
340 }
341
342 s := &Session{
343 Config: cfg,
344 Handlers: handlers,
345 }
346
347 initHandlers(s)
348
349 // Setup HTTP client with custom cert bundle if enabled
350 if opts.CustomCABundle != nil {
351 if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil {
352 return nil, err
353 }
354 }
355
356 return s, nil
357}
358
359func loadCustomCABundle(s *Session, bundle io.Reader) error {
360 var t *http.Transport
361 switch v := s.Config.HTTPClient.Transport.(type) {
362 case *http.Transport:
363 t = v
364 default:
365 if s.Config.HTTPClient.Transport != nil {
366 return awserr.New("LoadCustomCABundleError",
367 "unable to load custom CA bundle, HTTPClient's transport unsupported type", nil)
368 }
369 }
370 if t == nil {
371 t = &http.Transport{}
372 }
373
374 p, err := loadCertPool(bundle)
375 if err != nil {
376 return err
377 }
378 if t.TLSClientConfig == nil {
379 t.TLSClientConfig = &tls.Config{}
380 }
381 t.TLSClientConfig.RootCAs = p
382
383 s.Config.HTTPClient.Transport = t
384
385 return nil
386}
387
388func loadCertPool(r io.Reader) (*x509.CertPool, error) {
389 b, err := ioutil.ReadAll(r)
390 if err != nil {
391 return nil, awserr.New("LoadCustomCABundleError",
392 "failed to read custom CA bundle PEM file", err)
393 }
394
395 p := x509.NewCertPool()
396 if !p.AppendCertsFromPEM(b) {
397 return nil, awserr.New("LoadCustomCABundleError",
398 "failed to load custom CA bundle PEM file", err)
399 }
400
401 return p, nil
402}
403
404func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig, handlers request.Handlers, sessOpts Options) error {
405 // Merge in user provided configuration
406 cfg.MergeIn(userCfg)
407
408 // Region if not already set by user
409 if len(aws.StringValue(cfg.Region)) == 0 {
410 if len(envCfg.Region) > 0 {
411 cfg.WithRegion(envCfg.Region)
412 } else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
413 cfg.WithRegion(sharedCfg.Region)
414 }
415 }
416
417 // Configure credentials if not already set
418 if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
419 if len(envCfg.Creds.AccessKeyID) > 0 {
420 cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
421 envCfg.Creds,
422 )
423 } else if envCfg.EnableSharedConfig && len(sharedCfg.AssumeRole.RoleARN) > 0 && sharedCfg.AssumeRoleSource != nil {
424 cfgCp := *cfg
425 cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds(
426 sharedCfg.AssumeRoleSource.Creds,
427 )
428 if len(sharedCfg.AssumeRole.MFASerial) > 0 && sessOpts.AssumeRoleTokenProvider == nil {
429 // AssumeRole Token provider is required if doing Assume Role
430 // with MFA.
431 return AssumeRoleTokenProviderNotSetError{}
432 }
433 cfg.Credentials = stscreds.NewCredentials(
434 &Session{
435 Config: &cfgCp,
436 Handlers: handlers.Copy(),
437 },
438 sharedCfg.AssumeRole.RoleARN,
439 func(opt *stscreds.AssumeRoleProvider) {
440 opt.RoleSessionName = sharedCfg.AssumeRole.RoleSessionName
441
442 // Assume role with external ID
443 if len(sharedCfg.AssumeRole.ExternalID) > 0 {
444 opt.ExternalID = aws.String(sharedCfg.AssumeRole.ExternalID)
445 }
446
447 // Assume role with MFA
448 if len(sharedCfg.AssumeRole.MFASerial) > 0 {
449 opt.SerialNumber = aws.String(sharedCfg.AssumeRole.MFASerial)
450 opt.TokenProvider = sessOpts.AssumeRoleTokenProvider
451 }
452 },
453 )
454 } else if len(sharedCfg.Creds.AccessKeyID) > 0 {
455 cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
456 sharedCfg.Creds,
457 )
458 } else {
459 // Fallback to default credentials provider, include mock errors
460 // for the credential chain so user can identify why credentials
461 // failed to be retrieved.
462 cfg.Credentials = credentials.NewCredentials(&credentials.ChainProvider{
463 VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
464 Providers: []credentials.Provider{
465 &credProviderError{Err: awserr.New("EnvAccessKeyNotFound", "failed to find credentials in the environment.", nil)},
466 &credProviderError{Err: awserr.New("SharedCredsLoad", fmt.Sprintf("failed to load profile, %s.", envCfg.Profile), nil)},
467 defaults.RemoteCredProvider(*cfg, handlers),
468 },
469 })
470 }
471 }
472
473 return nil
474}
475
476// AssumeRoleTokenProviderNotSetError is an error returned when creating a session when the
477// MFAToken option is not set when shared config is configured load assume a
478// role with an MFA token.
479type AssumeRoleTokenProviderNotSetError struct{}
480
481// Code is the short id of the error.
482func (e AssumeRoleTokenProviderNotSetError) Code() string {
483 return "AssumeRoleTokenProviderNotSetError"
484}
485
486// Message is the description of the error
487func (e AssumeRoleTokenProviderNotSetError) Message() string {
488 return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.")
489}
490
491// OrigErr is the underlying error that caused the failure.
492func (e AssumeRoleTokenProviderNotSetError) OrigErr() error {
493 return nil
494}
495
496// Error satisfies the error interface.
497func (e AssumeRoleTokenProviderNotSetError) Error() string {
498 return awserr.SprintError(e.Code(), e.Message(), "", nil)
499}
500
501type credProviderError struct {
502 Err error
503}
504
505var emptyCreds = credentials.Value{}
506
507func (c credProviderError) Retrieve() (credentials.Value, error) {
508 return credentials.Value{}, c.Err
509}
510func (c credProviderError) IsExpired() bool {
511 return true
512}
513
514func initHandlers(s *Session) {
515 // Add the Validate parameter handler if it is not disabled.
516 s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
517 if !aws.BoolValue(s.Config.DisableParamValidation) {
518 s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
519 }
520}
521
522// Copy creates and returns a copy of the current Session, coping the config
523// and handlers. If any additional configs are provided they will be merged
524// on top of the Session's copied config.
525//
526// // Create a copy of the current Session, configured for the us-west-2 region.
527// sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
528func (s *Session) Copy(cfgs ...*aws.Config) *Session {
529 newSession := &Session{
530 Config: s.Config.Copy(cfgs...),
531 Handlers: s.Handlers.Copy(),
532 }
533
534 initHandlers(newSession)
535
536 return newSession
537}
538
539// ClientConfig satisfies the client.ConfigProvider interface and is used to
540// configure the service client instances. Passing the Session to the service
541// client's constructor (New) will use this method to configure the client.
542func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
543 // Backwards compatibility, the error will be eaten if user calls ClientConfig
544 // directly. All SDK services will use ClientconfigWithError.
545 cfg, _ := s.clientConfigWithErr(serviceName, cfgs...)
546
547 return cfg
548}
549
550func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (client.Config, error) {
551 s = s.Copy(cfgs...)
552
553 var resolved endpoints.ResolvedEndpoint
554 var err error
555
556 region := aws.StringValue(s.Config.Region)
557
558 if endpoint := aws.StringValue(s.Config.Endpoint); len(endpoint) != 0 {
559 resolved.URL = endpoints.AddScheme(endpoint, aws.BoolValue(s.Config.DisableSSL))
560 resolved.SigningRegion = region
561 } else {
562 resolved, err = s.Config.EndpointResolver.EndpointFor(
563 serviceName, region,
564 func(opt *endpoints.Options) {
565 opt.DisableSSL = aws.BoolValue(s.Config.DisableSSL)
566 opt.UseDualStack = aws.BoolValue(s.Config.UseDualStack)
567
568 // Support the condition where the service is modeled but its
569 // endpoint metadata is not available.
570 opt.ResolveUnknownService = true
571 },
572 )
573 }
574
575 return client.Config{
576 Config: s.Config,
577 Handlers: s.Handlers,
578 Endpoint: resolved.URL,
579 SigningRegion: resolved.SigningRegion,
580 SigningName: resolved.SigningName,
581 }, err
582}
583
584// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
585// that the EndpointResolver will not be used to resolve the endpoint. The only
586// endpoint set must come from the aws.Config.Endpoint field.
587func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
588 s = s.Copy(cfgs...)
589
590 var resolved endpoints.ResolvedEndpoint
591
592 region := aws.StringValue(s.Config.Region)
593
594 if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
595 resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
596 resolved.SigningRegion = region
597 }
598
599 return client.Config{
600 Config: s.Config,
601 Handlers: s.Handlers,
602 Endpoint: resolved.URL,
603 SigningRegion: resolved.SigningRegion,
604 SigningName: resolved.SigningName,
605 }
606}