github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/ec2/provider.go (about) 1 // Copyright 2011-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package ec2 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "gopkg.in/amz.v3/aws" 12 "gopkg.in/amz.v3/ec2" 13 "gopkg.in/amz.v3/s3" 14 15 "github.com/juju/juju/cloud" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/environs/simplestreams" 19 ) 20 21 var logger = loggo.GetLogger("juju.provider.ec2") 22 23 type environProvider struct { 24 environProviderCredentials 25 } 26 27 var providerInstance environProvider 28 29 // Open is specified in the EnvironProvider interface. 30 func (p environProvider) Open(args environs.OpenParams) (environs.Environ, error) { 31 logger.Infof("opening model %q", args.Config.Name()) 32 33 e := new(environ) 34 e.cloud = args.Cloud 35 e.name = args.Config.Name() 36 37 var err error 38 e.ec2, e.s3, err = awsClients(args.Cloud) 39 if err != nil { 40 return nil, errors.Trace(err) 41 } 42 43 if err := e.SetConfig(args.Config); err != nil { 44 return nil, errors.Trace(err) 45 } 46 return e, nil 47 } 48 49 func awsClients(cloud environs.CloudSpec) (*ec2.EC2, *s3.S3, error) { 50 if err := validateCloudSpec(cloud); err != nil { 51 return nil, nil, errors.Annotate(err, "validating cloud spec") 52 } 53 54 credentialAttrs := cloud.Credential.Attributes() 55 accessKey := credentialAttrs["access-key"] 56 secretKey := credentialAttrs["secret-key"] 57 auth := aws.Auth{ 58 AccessKey: accessKey, 59 SecretKey: secretKey, 60 } 61 62 // TODO(axw) define region in terms of EC2 and S3 endpoints. 63 region := aws.Regions[cloud.Region] 64 signer := aws.SignV4Factory(region.Name, "ec2") 65 return ec2.New(auth, region, signer), s3.New(auth, region), nil 66 } 67 68 // PrepareConfig is specified in the EnvironProvider interface. 69 func (p environProvider) PrepareConfig(args environs.PrepareConfigParams) (*config.Config, error) { 70 if err := validateCloudSpec(args.Cloud); err != nil { 71 return nil, errors.Annotate(err, "validating cloud spec") 72 } 73 // Set the default block-storage source. 74 attrs := make(map[string]interface{}) 75 if _, ok := args.Config.StorageDefaultBlockSource(); !ok { 76 attrs[config.StorageDefaultBlockSourceKey] = EBS_ProviderType 77 } 78 if len(attrs) == 0 { 79 return args.Config, nil 80 } 81 return args.Config.Apply(attrs) 82 } 83 84 func validateCloudSpec(c environs.CloudSpec) error { 85 if err := c.Validate(); err != nil { 86 return errors.Trace(err) 87 } 88 if _, ok := aws.Regions[c.Region]; !ok { 89 return errors.NotValidf("region name %q", c.Region) 90 } 91 if c.Credential == nil { 92 return errors.NotValidf("missing credential") 93 } 94 if authType := c.Credential.AuthType(); authType != cloud.AccessKeyAuthType { 95 return errors.NotSupportedf("%q auth-type", authType) 96 } 97 return nil 98 } 99 100 // Validate is specified in the EnvironProvider interface. 101 func (environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 102 newEcfg, err := validateConfig(cfg, old) 103 if err != nil { 104 return nil, fmt.Errorf("invalid EC2 provider config: %v", err) 105 } 106 return newEcfg.Apply(newEcfg.attrs) 107 } 108 109 // MetadataLookupParams returns parameters which are used to query image metadata to 110 // find matching image information. 111 func (p environProvider) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { 112 if region == "" { 113 return nil, fmt.Errorf("region must be specified") 114 } 115 ec2Region, ok := allRegions[region] 116 if !ok { 117 return nil, fmt.Errorf("unknown region %q", region) 118 } 119 return &simplestreams.MetadataLookupParams{ 120 Region: region, 121 Endpoint: ec2Region.EC2Endpoint, 122 }, nil 123 } 124 125 const badAccessKey = ` 126 Please ensure the Access Key ID you have specified is correct. 127 You can obtain the Access Key ID via the "Security Credentials" 128 page in the AWS console.` 129 130 const badSecretKey = ` 131 Please ensure the Secret Access Key you have specified is correct. 132 You can obtain the Secret Access Key via the "Security Credentials" 133 page in the AWS console.` 134 135 // verifyCredentials issues a cheap, non-modifying/idempotent request to EC2 to 136 // verify the configured credentials. If verification fails, a user-friendly 137 // error will be returned, and the original error will be logged at debug 138 // level. 139 var verifyCredentials = func(e *environ) error { 140 _, err := e.ec2.AccountAttributes() 141 if err != nil { 142 logger.Debugf("ec2 request failed: %v", err) 143 if err, ok := err.(*ec2.Error); ok { 144 switch err.Code { 145 case "AuthFailure": 146 return errors.New("authentication failed.\n" + badAccessKey) 147 case "SignatureDoesNotMatch": 148 return errors.New("authentication failed.\n" + badSecretKey) 149 default: 150 return err 151 } 152 } 153 return err 154 } 155 return nil 156 }