github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/common/access_config.go (about)

     1  package common
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/aws/credentials"
    12  	"github.com/aws/aws-sdk-go/aws/ec2metadata"
    13  	"github.com/aws/aws-sdk-go/aws/session"
    14  	"github.com/aws/aws-sdk-go/service/ec2"
    15  	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
    16  	"github.com/hashicorp/go-cleanhttp"
    17  	"github.com/hashicorp/packer/template/interpolate"
    18  )
    19  
    20  // AccessConfig is for common configuration related to AWS access
    21  type AccessConfig struct {
    22  	AccessKey            string `mapstructure:"access_key"`
    23  	CustomEndpointEc2    string `mapstructure:"custom_endpoint_ec2"`
    24  	DecodeAuthZMessages  bool   `mapstructure:"decode_authorization_messages"`
    25  	MFACode              string `mapstructure:"mfa_code"`
    26  	ProfileName          string `mapstructure:"profile"`
    27  	RawRegion            string `mapstructure:"region"`
    28  	SecretKey            string `mapstructure:"secret_key"`
    29  	SkipValidation       bool   `mapstructure:"skip_region_validation"`
    30  	SkipMetadataApiCheck bool   `mapstructure:"skip_metadata_api_check"`
    31  	Token                string `mapstructure:"token"`
    32  	session              *session.Session
    33  
    34  	getEC2Connection func() ec2iface.EC2API
    35  }
    36  
    37  // Config returns a valid aws.Config object for access to AWS services, or
    38  // an error if the authentication and region couldn't be resolved
    39  func (c *AccessConfig) Session() (*session.Session, error) {
    40  	if c.session != nil {
    41  		return c.session, nil
    42  	}
    43  
    44  	config := aws.NewConfig().WithCredentialsChainVerboseErrors(true)
    45  	staticCreds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
    46  	if _, err := staticCreds.Get(); err != credentials.ErrStaticCredentialsEmpty {
    47  		config.WithCredentials(staticCreds)
    48  	}
    49  
    50  	// default is 3, and when it was causing failures for users being throttled
    51  	config = config.WithMaxRetries(20)
    52  
    53  	if c.RawRegion != "" {
    54  		config = config.WithRegion(c.RawRegion)
    55  	} else if region := c.metadataRegion(); region != "" {
    56  		config = config.WithRegion(region)
    57  	}
    58  
    59  	if c.CustomEndpointEc2 != "" {
    60  		config = config.WithEndpoint(c.CustomEndpointEc2)
    61  	}
    62  
    63  	opts := session.Options{
    64  		SharedConfigState: session.SharedConfigEnable,
    65  		Config:            *config,
    66  	}
    67  
    68  	if c.ProfileName != "" {
    69  		opts.Profile = c.ProfileName
    70  	}
    71  
    72  	if c.MFACode != "" {
    73  		opts.AssumeRoleTokenProvider = func() (string, error) {
    74  			return c.MFACode, nil
    75  		}
    76  	}
    77  
    78  	if sess, err := session.NewSessionWithOptions(opts); err != nil {
    79  		return nil, err
    80  	} else if *sess.Config.Region == "" {
    81  		return nil, fmt.Errorf("Could not find AWS region, make sure it's set.")
    82  	} else {
    83  		log.Printf("Found region %s", *sess.Config.Region)
    84  		c.session = sess
    85  
    86  		cp, err := c.session.Config.Credentials.Get()
    87  		if err != nil {
    88  			if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
    89  				return nil, fmt.Errorf("No valid credential sources found for AWS Builder. " +
    90  					"Please see https://www.packer.io/docs/builders/amazon.html#specifying-amazon-credentials " +
    91  					"for more information on providing credentials for the AWS Builder.")
    92  			} else {
    93  				return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
    94  			}
    95  		}
    96  		log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
    97  	}
    98  
    99  	if c.DecodeAuthZMessages {
   100  		DecodeAuthZMessages(c.session)
   101  	}
   102  
   103  	return c.session, nil
   104  }
   105  
   106  func (c *AccessConfig) SessionRegion() string {
   107  	if c.session == nil {
   108  		panic("access config session should be set.")
   109  	}
   110  	return aws.StringValue(c.session.Config.Region)
   111  }
   112  
   113  func (c *AccessConfig) IsGovCloud() bool {
   114  	return strings.HasPrefix(c.SessionRegion(), "us-gov-")
   115  }
   116  
   117  func (c *AccessConfig) IsChinaCloud() bool {
   118  	return strings.HasPrefix(c.SessionRegion(), "cn-")
   119  }
   120  
   121  // metadataRegion returns the region from the metadata service
   122  func (c *AccessConfig) metadataRegion() string {
   123  
   124  	client := cleanhttp.DefaultClient()
   125  
   126  	// Keep the default timeout (100ms) low as we don't want to wait in non-EC2 environments
   127  	client.Timeout = 100 * time.Millisecond
   128  	ec2meta := ec2metadata.New(session.New(), &aws.Config{
   129  		HTTPClient: client,
   130  	})
   131  	region, err := ec2meta.Region()
   132  	if err != nil {
   133  		log.Println("Error getting region from metadata service, "+
   134  			"probably because we're not running on AWS.", err)
   135  		return ""
   136  	}
   137  	return region
   138  }
   139  
   140  func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
   141  	var errs []error
   142  
   143  	if c.SkipMetadataApiCheck {
   144  		log.Println("(WARN) skip_metadata_api_check ignored.")
   145  	}
   146  	// Either both access and secret key must be set or neither of them should
   147  	// be.
   148  	if (len(c.AccessKey) > 0) != (len(c.SecretKey) > 0) {
   149  		errs = append(errs,
   150  			fmt.Errorf("`access_key` and `secret_key` must both be either set or not set."))
   151  	}
   152  
   153  	if c.RawRegion != "" && !c.SkipValidation {
   154  		err := c.ValidateRegion(c.RawRegion)
   155  		if err != nil {
   156  			errs = append(errs, fmt.Errorf("error validating region: %s", err.Error()))
   157  		}
   158  	}
   159  
   160  	return errs
   161  }
   162  
   163  func (c *AccessConfig) NewEC2Connection() (ec2iface.EC2API, error) {
   164  	if c.getEC2Connection != nil {
   165  		return c.getEC2Connection(), nil
   166  	}
   167  	sess, err := c.Session()
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	return ec2.New(sess), nil
   172  }