1package session 2 3import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "os" 11 "time" 12 13 "github.com/aws/aws-sdk-go/aws" 14 "github.com/aws/aws-sdk-go/aws/awserr" 15 "github.com/aws/aws-sdk-go/aws/client" 16 "github.com/aws/aws-sdk-go/aws/corehandlers" 17 "github.com/aws/aws-sdk-go/aws/credentials" 18 "github.com/aws/aws-sdk-go/aws/csm" 19 "github.com/aws/aws-sdk-go/aws/defaults" 20 "github.com/aws/aws-sdk-go/aws/endpoints" 21 "github.com/aws/aws-sdk-go/aws/request" 22) 23 24const ( 25 // ErrCodeSharedConfig represents an error that occurs in the shared 26 // configuration logic 27 ErrCodeSharedConfig = "SharedConfigErr" 28) 29 30// ErrSharedConfigSourceCollision will be returned if a section contains both 31// source_profile and credential_source 32var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only source profile or credential source can be specified, not both", nil) 33 34// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment 35// variables are empty and Environment was set as the credential source 36var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil) 37 38// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided 39var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil) 40 41// A Session provides a central location to create service clients from and 42// store configurations and request handlers for those services. 43// 44// Sessions are safe to create service clients concurrently, but it is not safe 45// to mutate the Session concurrently. 46// 47// The Session satisfies the service client's client.ConfigProvider. 48type Session struct { 49 Config *aws.Config 50 Handlers request.Handlers 51 52 options Options 53} 54 55// New creates a new instance of the handlers merging in the provided configs 56// on top of the SDK's default configurations. Once the Session is created it 57// can be mutated to modify the Config or Handlers. The Session is safe to be 58// read concurrently, but it should not be written to concurrently. 59// 60// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New 61// method could now encounter an error when loading the configuration. When 62// The environment variable is set, and an error occurs, New will return a 63// session that will fail all requests reporting the error that occurred while 64// loading the session. Use NewSession to get the error when creating the 65// session. 66// 67// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value 68// the shared config file (~/.aws/config) will also be loaded, in addition to 69// the shared credentials file (~/.aws/credentials). Values set in both the 70// shared config, and shared credentials will be taken from the shared 71// credentials file. 72// 73// Deprecated: Use NewSession functions to create sessions instead. NewSession 74// has the same functionality as New except an error can be returned when the 75// func is called instead of waiting to receive an error until a request is made. 76func New(cfgs ...*aws.Config) *Session { 77 // load initial config from environment 78 envCfg, envErr := loadEnvConfig() 79 80 if envCfg.EnableSharedConfig { 81 var cfg aws.Config 82 cfg.MergeIn(cfgs...) 83 s, err := NewSessionWithOptions(Options{ 84 Config: cfg, 85 SharedConfigState: SharedConfigEnable, 86 }) 87 if err != nil { 88 // Old session.New expected all errors to be discovered when 89 // a request is made, and would report the errors then. This 90 // needs to be replicated if an error occurs while creating 91 // the session. 92 msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " + 93 "Use session.NewSession to handle errors occurring during session creation." 94 95 // Session creation failed, need to report the error and prevent 96 // any requests from succeeding. 97 s = &Session{Config: defaults.Config()} 98 s.logDeprecatedNewSessionError(msg, err, cfgs) 99 } 100 101 return s 102 } 103 104 s := deprecatedNewSession(envCfg, cfgs...) 105 if envErr != nil { 106 msg := "failed to load env config" 107 s.logDeprecatedNewSessionError(msg, envErr, cfgs) 108 } 109 110 if csmCfg, err := loadCSMConfig(envCfg, []string{}); err != nil { 111 if l := s.Config.Logger; l != nil { 112 l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err)) 113 } 114 } else if csmCfg.Enabled { 115 err := enableCSM(&s.Handlers, csmCfg, s.Config.Logger) 116 if err != nil { 117 msg := "failed to enable CSM" 118 s.logDeprecatedNewSessionError(msg, err, cfgs) 119 } 120 } 121 122 return s 123} 124 125// NewSession returns a new Session created from SDK defaults, config files, 126// environment, and user provided config files. Once the Session is created 127// it can be mutated to modify the Config or Handlers. The Session is safe to 128// be read concurrently, but it should not be written to concurrently. 129// 130// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value 131// the shared config file (~/.aws/config) will also be loaded in addition to 132// the shared credentials file (~/.aws/credentials). Values set in both the 133// shared config, and shared credentials will be taken from the shared 134// credentials file. Enabling the Shared Config will also allow the Session 135// to be built with retrieving credentials with AssumeRole set in the config. 136// 137// See the NewSessionWithOptions func for information on how to override or 138// control through code how the Session will be created, such as specifying the 139// config profile, and controlling if shared config is enabled or not. 140func NewSession(cfgs ...*aws.Config) (*Session, error) { 141 opts := Options{} 142 opts.Config.MergeIn(cfgs...) 143 144 return NewSessionWithOptions(opts) 145} 146 147// SharedConfigState provides the ability to optionally override the state 148// of the session's creation based on the shared config being enabled or 149// disabled. 150type SharedConfigState int 151 152const ( 153 // SharedConfigStateFromEnv does not override any state of the 154 // AWS_SDK_LOAD_CONFIG env var. It is the default value of the 155 // SharedConfigState type. 156 SharedConfigStateFromEnv SharedConfigState = iota 157 158 // SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value 159 // and disables the shared config functionality. 160 SharedConfigDisable 161 162 // SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value 163 // and enables the shared config functionality. 164 SharedConfigEnable 165) 166 167// Options provides the means to control how a Session is created and what 168// configuration values will be loaded. 169// 170type Options struct { 171 // Provides config values for the SDK to use when creating service clients 172 // and making API requests to services. Any value set in with this field 173 // will override the associated value provided by the SDK defaults, 174 // environment or config files where relevant. 175 // 176 // If not set, configuration values from from SDK defaults, environment, 177 // config will be used. 178 Config aws.Config 179 180 // Overrides the config profile the Session should be created from. If not 181 // set the value of the environment variable will be loaded (AWS_PROFILE, 182 // or AWS_DEFAULT_PROFILE if the Shared Config is enabled). 183 // 184 // If not set and environment variables are not set the "default" 185 // (DefaultSharedConfigProfile) will be used as the profile to load the 186 // session config from. 187 Profile string 188 189 // Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG 190 // environment variable. By default a Session will be created using the 191 // value provided by the AWS_SDK_LOAD_CONFIG environment variable. 192 // 193 // Setting this value to SharedConfigEnable or SharedConfigDisable 194 // will allow you to override the AWS_SDK_LOAD_CONFIG environment variable 195 // and enable or disable the shared config functionality. 196 SharedConfigState SharedConfigState 197 198 // Ordered list of files the session will load configuration from. 199 // It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE. 200 SharedConfigFiles []string 201 202 // When the SDK's shared config is configured to assume a role with MFA 203 // this option is required in order to provide the mechanism that will 204 // retrieve the MFA token. There is no default value for this field. If 205 // it is not set an error will be returned when creating the session. 206 // 207 // This token provider will be called when ever the assumed role's 208 // credentials need to be refreshed. Within the context of service clients 209 // all sharing the same session the SDK will ensure calls to the token 210 // provider are atomic. When sharing a token provider across multiple 211 // sessions additional synchronization logic is needed to ensure the 212 // token providers do not introduce race conditions. It is recommend to 213 // share the session where possible. 214 // 215 // stscreds.StdinTokenProvider is a basic implementation that will prompt 216 // from stdin for the MFA token code. 217 // 218 // This field is only used if the shared configuration is enabled, and 219 // the config enables assume role wit MFA via the mfa_serial field. 220 AssumeRoleTokenProvider func() (string, error) 221 222 // When the SDK's shared config is configured to assume a role this option 223 // may be provided to set the expiry duration of the STS credentials. 224 // Defaults to 15 minutes if not set as documented in the 225 // stscreds.AssumeRoleProvider. 226 AssumeRoleDuration time.Duration 227 228 // Reader for a custom Credentials Authority (CA) bundle in PEM format that 229 // the SDK will use instead of the default system's root CA bundle. Use this 230 // only if you want to replace the CA bundle the SDK uses for TLS requests. 231 // 232 // Enabling this option will attempt to merge the Transport into the SDK's HTTP 233 // client. If the client's Transport is not a http.Transport an error will be 234 // returned. If the Transport's TLS config is set this option will cause the SDK 235 // to overwrite the Transport's TLS config's RootCAs value. If the CA 236 // bundle reader contains multiple certificates all of them will be loaded. 237 // 238 // The Session option CustomCABundle is also available when creating sessions 239 // to also enable this feature. CustomCABundle session option field has priority 240 // over the AWS_CA_BUNDLE environment variable, and will be used if both are set. 241 CustomCABundle io.Reader 242 243 // The handlers that the session and all API clients will be created with. 244 // This must be a complete set of handlers. Use the defaults.Handlers() 245 // function to initialize this value before changing the handlers to be 246 // used by the SDK. 247 Handlers request.Handlers 248 249 // Allows specifying a custom endpoint to be used by the EC2 IMDS client 250 // when making requests to the EC2 IMDS API. The must endpoint value must 251 // include protocol prefix. 252 // 253 // If unset, will the EC2 IMDS client will use its default endpoint. 254 // 255 // Can also be specified via the environment variable, 256 // AWS_EC2_METADATA_SERVICE_ENDPOINT. 257 // 258 // AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254 259 // 260 // If using an URL with an IPv6 address literal, the IPv6 address 261 // component must be enclosed in square brackets. 262 // 263 // AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1] 264 EC2IMDSEndpoint string 265} 266 267// NewSessionWithOptions returns a new Session created from SDK defaults, config files, 268// environment, and user provided config files. This func uses the Options 269// values to configure how the Session is created. 270// 271// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value 272// the shared config file (~/.aws/config) will also be loaded in addition to 273// the shared credentials file (~/.aws/credentials). Values set in both the 274// shared config, and shared credentials will be taken from the shared 275// credentials file. Enabling the Shared Config will also allow the Session 276// to be built with retrieving credentials with AssumeRole set in the config. 277// 278// // Equivalent to session.New 279// sess := session.Must(session.NewSessionWithOptions(session.Options{})) 280// 281// // Specify profile to load for the session's config 282// sess := session.Must(session.NewSessionWithOptions(session.Options{ 283// Profile: "profile_name", 284// })) 285// 286// // Specify profile for config and region for requests 287// sess := session.Must(session.NewSessionWithOptions(session.Options{ 288// Config: aws.Config{Region: aws.String("us-east-1")}, 289// Profile: "profile_name", 290// })) 291// 292// // Force enable Shared Config support 293// sess := session.Must(session.NewSessionWithOptions(session.Options{ 294// SharedConfigState: session.SharedConfigEnable, 295// })) 296func NewSessionWithOptions(opts Options) (*Session, error) { 297 var envCfg envConfig 298 var err error 299 if opts.SharedConfigState == SharedConfigEnable { 300 envCfg, err = loadSharedEnvConfig() 301 if err != nil { 302 return nil, fmt.Errorf("failed to load shared config, %v", err) 303 } 304 } else { 305 envCfg, err = loadEnvConfig() 306 if err != nil { 307 return nil, fmt.Errorf("failed to load environment config, %v", err) 308 } 309 } 310 311 if len(opts.Profile) != 0 { 312 envCfg.Profile = opts.Profile 313 } 314 315 switch opts.SharedConfigState { 316 case SharedConfigDisable: 317 envCfg.EnableSharedConfig = false 318 case SharedConfigEnable: 319 envCfg.EnableSharedConfig = true 320 } 321 322 // Only use AWS_CA_BUNDLE if session option is not provided. 323 if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil { 324 f, err := os.Open(envCfg.CustomCABundle) 325 if err != nil { 326 return nil, awserr.New("LoadCustomCABundleError", 327 "failed to open custom CA bundle PEM file", err) 328 } 329 defer f.Close() 330 opts.CustomCABundle = f 331 } 332 333 return newSession(opts, envCfg, &opts.Config) 334} 335 336// Must is a helper function to ensure the Session is valid and there was no 337// error when calling a NewSession function. 338// 339// This helper is intended to be used in variable initialization to load the 340// Session and configuration at startup. Such as: 341// 342// var sess = session.Must(session.NewSession()) 343func Must(sess *Session, err error) *Session { 344 if err != nil { 345 panic(err) 346 } 347 348 return sess 349} 350 351// Wraps the endpoint resolver with a resolver that will return a custom 352// endpoint for EC2 IMDS. 353func wrapEC2IMDSEndpoint(resolver endpoints.Resolver, endpoint string) endpoints.Resolver { 354 return endpoints.ResolverFunc( 355 func(service, region string, opts ...func(*endpoints.Options)) ( 356 endpoints.ResolvedEndpoint, error, 357 ) { 358 if service == ec2MetadataServiceID { 359 return endpoints.ResolvedEndpoint{ 360 URL: endpoint, 361 SigningName: ec2MetadataServiceID, 362 SigningRegion: region, 363 }, nil 364 } 365 return resolver.EndpointFor(service, region) 366 }) 367} 368 369func deprecatedNewSession(envCfg envConfig, cfgs ...*aws.Config) *Session { 370 cfg := defaults.Config() 371 handlers := defaults.Handlers() 372 373 // Apply the passed in configs so the configuration can be applied to the 374 // default credential chain 375 cfg.MergeIn(cfgs...) 376 if cfg.EndpointResolver == nil { 377 // An endpoint resolver is required for a session to be able to provide 378 // endpoints for service client configurations. 379 cfg.EndpointResolver = endpoints.DefaultResolver() 380 } 381 382 if len(envCfg.EC2IMDSEndpoint) != 0 { 383 cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, envCfg.EC2IMDSEndpoint) 384 } 385 386 cfg.Credentials = defaults.CredChain(cfg, handlers) 387 388 // Reapply any passed in configs to override credentials if set 389 cfg.MergeIn(cfgs...) 390 391 s := &Session{ 392 Config: cfg, 393 Handlers: handlers, 394 options: Options{ 395 EC2IMDSEndpoint: envCfg.EC2IMDSEndpoint, 396 }, 397 } 398 399 initHandlers(s) 400 return s 401} 402 403func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error { 404 if logger != nil { 405 logger.Log("Enabling CSM") 406 } 407 408 r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port)) 409 if err != nil { 410 return err 411 } 412 r.InjectHandlers(handlers) 413 414 return nil 415} 416 417func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) { 418 cfg := defaults.Config() 419 420 handlers := opts.Handlers 421 if handlers.IsEmpty() { 422 handlers = defaults.Handlers() 423 } 424 425 // Get a merged version of the user provided config to determine if 426 // credentials were. 427 userCfg := &aws.Config{} 428 userCfg.MergeIn(cfgs...) 429 cfg.MergeIn(userCfg) 430 431 // Ordered config files will be loaded in with later files overwriting 432 // previous config file values. 433 var cfgFiles []string 434 if opts.SharedConfigFiles != nil { 435 cfgFiles = opts.SharedConfigFiles 436 } else { 437 cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile} 438 if !envCfg.EnableSharedConfig { 439 // The shared config file (~/.aws/config) is only loaded if instructed 440 // to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG). 441 cfgFiles = cfgFiles[1:] 442 } 443 } 444 445 // Load additional config from file(s) 446 sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig) 447 if err != nil { 448 if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) { 449 // Special case where the user has not explicitly specified an AWS_PROFILE, 450 // or session.Options.profile, shared config is not enabled, and the 451 // environment has credentials, allow the shared config file to fail to 452 // load since the user has already provided credentials, and nothing else 453 // is required to be read file. Github(aws/aws-sdk-go#2455) 454 } else if _, ok := err.(SharedConfigProfileNotExistsError); !ok { 455 return nil, err 456 } 457 } 458 459 if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil { 460 return nil, err 461 } 462 463 s := &Session{ 464 Config: cfg, 465 Handlers: handlers, 466 options: opts, 467 } 468 469 initHandlers(s) 470 471 if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil { 472 if l := s.Config.Logger; l != nil { 473 l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err)) 474 } 475 } else if csmCfg.Enabled { 476 err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger) 477 if err != nil { 478 return nil, err 479 } 480 } 481 482 // Setup HTTP client with custom cert bundle if enabled 483 if opts.CustomCABundle != nil { 484 if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil { 485 return nil, err 486 } 487 } 488 489 return s, nil 490} 491 492type csmConfig struct { 493 Enabled bool 494 Host string 495 Port string 496 ClientID string 497} 498 499var csmProfileName = "aws_csm" 500 501func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) { 502 if envCfg.CSMEnabled != nil { 503 if *envCfg.CSMEnabled { 504 return csmConfig{ 505 Enabled: true, 506 ClientID: envCfg.CSMClientID, 507 Host: envCfg.CSMHost, 508 Port: envCfg.CSMPort, 509 }, nil 510 } 511 return csmConfig{}, nil 512 } 513 514 sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false) 515 if err != nil { 516 if _, ok := err.(SharedConfigProfileNotExistsError); !ok { 517 return csmConfig{}, err 518 } 519 } 520 if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true { 521 return csmConfig{ 522 Enabled: true, 523 ClientID: sharedCfg.CSMClientID, 524 Host: sharedCfg.CSMHost, 525 Port: sharedCfg.CSMPort, 526 }, nil 527 } 528 529 return csmConfig{}, nil 530} 531 532func loadCustomCABundle(s *Session, bundle io.Reader) error { 533 var t *http.Transport 534 switch v := s.Config.HTTPClient.Transport.(type) { 535 case *http.Transport: 536 t = v 537 default: 538 if s.Config.HTTPClient.Transport != nil { 539 return awserr.New("LoadCustomCABundleError", 540 "unable to load custom CA bundle, HTTPClient's transport unsupported type", nil) 541 } 542 } 543 if t == nil { 544 // Nil transport implies `http.DefaultTransport` should be used. Since 545 // the SDK cannot modify, nor copy the `DefaultTransport` specifying 546 // the values the next closest behavior. 547 t = getCABundleTransport() 548 } 549 550 p, err := loadCertPool(bundle) 551 if err != nil { 552 return err 553 } 554 if t.TLSClientConfig == nil { 555 t.TLSClientConfig = &tls.Config{} 556 } 557 t.TLSClientConfig.RootCAs = p 558 559 s.Config.HTTPClient.Transport = t 560 561 return nil 562} 563 564func loadCertPool(r io.Reader) (*x509.CertPool, error) { 565 b, err := ioutil.ReadAll(r) 566 if err != nil { 567 return nil, awserr.New("LoadCustomCABundleError", 568 "failed to read custom CA bundle PEM file", err) 569 } 570 571 p := x509.NewCertPool() 572 if !p.AppendCertsFromPEM(b) { 573 return nil, awserr.New("LoadCustomCABundleError", 574 "failed to load custom CA bundle PEM file", err) 575 } 576 577 return p, nil 578} 579 580func mergeConfigSrcs(cfg, userCfg *aws.Config, 581 envCfg envConfig, sharedCfg sharedConfig, 582 handlers request.Handlers, 583 sessOpts Options, 584) error { 585 586 // Region if not already set by user 587 if len(aws.StringValue(cfg.Region)) == 0 { 588 if len(envCfg.Region) > 0 { 589 cfg.WithRegion(envCfg.Region) 590 } else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 { 591 cfg.WithRegion(sharedCfg.Region) 592 } 593 } 594 595 if cfg.EnableEndpointDiscovery == nil { 596 if envCfg.EnableEndpointDiscovery != nil { 597 cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery) 598 } else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil { 599 cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery) 600 } 601 } 602 603 // Regional Endpoint flag for STS endpoint resolving 604 mergeSTSRegionalEndpointConfig(cfg, []endpoints.STSRegionalEndpoint{ 605 userCfg.STSRegionalEndpoint, 606 envCfg.STSRegionalEndpoint, 607 sharedCfg.STSRegionalEndpoint, 608 endpoints.LegacySTSEndpoint, 609 }) 610 611 // Regional Endpoint flag for S3 endpoint resolving 612 mergeS3UsEast1RegionalEndpointConfig(cfg, []endpoints.S3UsEast1RegionalEndpoint{ 613 userCfg.S3UsEast1RegionalEndpoint, 614 envCfg.S3UsEast1RegionalEndpoint, 615 sharedCfg.S3UsEast1RegionalEndpoint, 616 endpoints.LegacyS3UsEast1Endpoint, 617 }) 618 619 ec2IMDSEndpoint := sessOpts.EC2IMDSEndpoint 620 if len(ec2IMDSEndpoint) == 0 { 621 ec2IMDSEndpoint = envCfg.EC2IMDSEndpoint 622 } 623 if len(ec2IMDSEndpoint) != 0 { 624 cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, ec2IMDSEndpoint) 625 } 626 627 // Configure credentials if not already set by the user when creating the 628 // Session. 629 if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil { 630 creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts) 631 if err != nil { 632 return err 633 } 634 cfg.Credentials = creds 635 } 636 637 cfg.S3UseARNRegion = userCfg.S3UseARNRegion 638 if cfg.S3UseARNRegion == nil { 639 cfg.S3UseARNRegion = &envCfg.S3UseARNRegion 640 } 641 if cfg.S3UseARNRegion == nil { 642 cfg.S3UseARNRegion = &sharedCfg.S3UseARNRegion 643 } 644 645 return nil 646} 647 648func mergeSTSRegionalEndpointConfig(cfg *aws.Config, values []endpoints.STSRegionalEndpoint) { 649 for _, v := range values { 650 if v != endpoints.UnsetSTSEndpoint { 651 cfg.STSRegionalEndpoint = v 652 break 653 } 654 } 655} 656 657func mergeS3UsEast1RegionalEndpointConfig(cfg *aws.Config, values []endpoints.S3UsEast1RegionalEndpoint) { 658 for _, v := range values { 659 if v != endpoints.UnsetS3UsEast1Endpoint { 660 cfg.S3UsEast1RegionalEndpoint = v 661 break 662 } 663 } 664} 665 666func initHandlers(s *Session) { 667 // Add the Validate parameter handler if it is not disabled. 668 s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler) 669 if !aws.BoolValue(s.Config.DisableParamValidation) { 670 s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler) 671 } 672} 673 674// Copy creates and returns a copy of the current Session, copying the config 675// and handlers. If any additional configs are provided they will be merged 676// on top of the Session's copied config. 677// 678// // Create a copy of the current Session, configured for the us-west-2 region. 679// sess.Copy(&aws.Config{Region: aws.String("us-west-2")}) 680func (s *Session) Copy(cfgs ...*aws.Config) *Session { 681 newSession := &Session{ 682 Config: s.Config.Copy(cfgs...), 683 Handlers: s.Handlers.Copy(), 684 options: s.options, 685 } 686 687 initHandlers(newSession) 688 689 return newSession 690} 691 692// ClientConfig satisfies the client.ConfigProvider interface and is used to 693// configure the service client instances. Passing the Session to the service 694// client's constructor (New) will use this method to configure the client. 695func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Config { 696 s = s.Copy(cfgs...) 697 698 region := aws.StringValue(s.Config.Region) 699 resolved, err := s.resolveEndpoint(service, region, s.Config) 700 if err != nil { 701 s.Handlers.Validate.PushBack(func(r *request.Request) { 702 if len(r.ClientInfo.Endpoint) != 0 { 703 // Error occurred while resolving endpoint, but the request 704 // being invoked has had an endpoint specified after the client 705 // was created. 706 return 707 } 708 r.Error = err 709 }) 710 } 711 712 return client.Config{ 713 Config: s.Config, 714 Handlers: s.Handlers, 715 PartitionID: resolved.PartitionID, 716 Endpoint: resolved.URL, 717 SigningRegion: resolved.SigningRegion, 718 SigningNameDerived: resolved.SigningNameDerived, 719 SigningName: resolved.SigningName, 720 } 721} 722 723const ec2MetadataServiceID = "ec2metadata" 724 725func (s *Session) resolveEndpoint(service, region string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) { 726 727 if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 { 728 return endpoints.ResolvedEndpoint{ 729 URL: endpoints.AddScheme(ep, aws.BoolValue(cfg.DisableSSL)), 730 SigningRegion: region, 731 }, nil 732 } 733 734 resolved, err := cfg.EndpointResolver.EndpointFor(service, region, 735 func(opt *endpoints.Options) { 736 opt.DisableSSL = aws.BoolValue(cfg.DisableSSL) 737 opt.UseDualStack = aws.BoolValue(cfg.UseDualStack) 738 // Support for STSRegionalEndpoint where the STSRegionalEndpoint is 739 // provided in envConfig or sharedConfig with envConfig getting 740 // precedence. 741 opt.STSRegionalEndpoint = cfg.STSRegionalEndpoint 742 743 // Support for S3UsEast1RegionalEndpoint where the S3UsEast1RegionalEndpoint is 744 // provided in envConfig or sharedConfig with envConfig getting 745 // precedence. 746 opt.S3UsEast1RegionalEndpoint = cfg.S3UsEast1RegionalEndpoint 747 748 // Support the condition where the service is modeled but its 749 // endpoint metadata is not available. 750 opt.ResolveUnknownService = true 751 }, 752 ) 753 if err != nil { 754 return endpoints.ResolvedEndpoint{}, err 755 } 756 757 return resolved, nil 758} 759 760// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception 761// that the EndpointResolver will not be used to resolve the endpoint. The only 762// endpoint set must come from the aws.Config.Endpoint field. 763func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config { 764 s = s.Copy(cfgs...) 765 766 var resolved endpoints.ResolvedEndpoint 767 if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 { 768 resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL)) 769 resolved.SigningRegion = aws.StringValue(s.Config.Region) 770 } 771 772 return client.Config{ 773 Config: s.Config, 774 Handlers: s.Handlers, 775 Endpoint: resolved.URL, 776 SigningRegion: resolved.SigningRegion, 777 SigningNameDerived: resolved.SigningNameDerived, 778 SigningName: resolved.SigningName, 779 } 780} 781 782// logDeprecatedNewSessionError function enables error handling for session 783func (s *Session) logDeprecatedNewSessionError(msg string, err error, cfgs []*aws.Config) { 784 // Session creation failed, need to report the error and prevent 785 // any requests from succeeding. 786 s.Config.MergeIn(cfgs...) 787 s.Config.Logger.Log("ERROR:", msg, "Error:", err) 788 s.Handlers.Validate.PushBack(func(r *request.Request) { 789 r.Error = err 790 }) 791} 792