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