github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 "github.com/juju/utils/arch" 12 "gopkg.in/amz.v3/ec2" 13 14 "github.com/juju/juju/cloud" 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/environs/config" 17 "github.com/juju/juju/environs/simplestreams" 18 ) 19 20 var logger = loggo.GetLogger("juju.provider.ec2") 21 22 type environProvider struct { 23 environProviderCredentials 24 } 25 26 var providerInstance environProvider 27 28 // RestrictedConfigAttributes is specified in the EnvironProvider interface. 29 func (p environProvider) RestrictedConfigAttributes() []string { 30 return []string{"region", "vpc-id", "vpc-id-force"} 31 } 32 33 // PrepareForCreateEnvironment is specified in the EnvironProvider interface. 34 func (p environProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) { 35 return cfg, nil 36 } 37 38 // Open is specified in the EnvironProvider interface. 39 func (p environProvider) Open(cfg *config.Config) (environs.Environ, error) { 40 logger.Infof("opening model %q", cfg.Name()) 41 e := new(environ) 42 e.name = cfg.Name() 43 err := e.SetConfig(cfg) 44 if err != nil { 45 return nil, err 46 } 47 return e, nil 48 } 49 50 // BootstrapConfig is specified in the EnvironProvider interface. 51 func (p environProvider) BootstrapConfig(args environs.BootstrapConfigParams) (*config.Config, error) { 52 // Add credentials to the configuration. 53 attrs := map[string]interface{}{ 54 "region": args.CloudRegion, 55 // TODO(axw) stop relying on hard-coded 56 // region endpoint information 57 // in the provider, and use 58 // args.CloudEndpoint here. 59 } 60 switch authType := args.Credentials.AuthType(); authType { 61 case cloud.AccessKeyAuthType: 62 credentialAttrs := args.Credentials.Attributes() 63 attrs["access-key"] = credentialAttrs["access-key"] 64 attrs["secret-key"] = credentialAttrs["secret-key"] 65 default: 66 return nil, errors.NotSupportedf("%q auth-type", authType) 67 } 68 69 // Set the default block-storage source. 70 if _, ok := args.Config.StorageDefaultBlockSource(); !ok { 71 attrs[config.StorageDefaultBlockSourceKey] = EBS_ProviderType 72 } 73 74 cfg, err := args.Config.Apply(attrs) 75 if err != nil { 76 return nil, errors.Trace(err) 77 } 78 return p.PrepareForCreateEnvironment(cfg) 79 } 80 81 // PrepareForBootstrap is specified in the EnvironProvider interface. 82 func (p environProvider) PrepareForBootstrap( 83 ctx environs.BootstrapContext, 84 cfg *config.Config, 85 ) (environs.Environ, error) { 86 e, err := p.Open(cfg) 87 if err != nil { 88 return nil, err 89 } 90 91 env := e.(*environ) 92 if ctx.ShouldVerifyCredentials() { 93 if err := verifyCredentials(env); err != nil { 94 return nil, err 95 } 96 } 97 98 vpcID, forceVPCID := env.ecfg().vpcID(), env.ecfg().forceVPCID() 99 if vpcID != "" { 100 err := validateVPC(env.ec2(), vpcID) 101 switch { 102 case isVPCNotUsableError(err): 103 // VPC missing or has no subnets at all. 104 return nil, errors.Annotate(err, vpcNotUsableErrorPrefix) 105 case isVPCNotRecommendedError(err): 106 // VPC does not meet minumum validation criteria. 107 if !forceVPCID { 108 return nil, errors.Annotatef(err, vpcNotRecommendedErrorPrefix, vpcID) 109 } 110 ctx.Infof(vpcNotRecommendedButForcedWarning) 111 case err != nil: 112 // Anything else unexpected while validating the VPC. 113 return nil, errors.Annotate(err, cannotValidateVPCErrorPrefix) 114 } 115 116 ctx.Infof("Using VPC %q in region %q", vpcID, env.ecfg().region()) 117 } 118 119 return e, nil 120 } 121 122 // Validate is specified in the EnvironProvider interface. 123 func (environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) { 124 newEcfg, err := validateConfig(cfg, old) 125 if err != nil { 126 return nil, fmt.Errorf("invalid EC2 provider config: %v", err) 127 } 128 return newEcfg.Apply(newEcfg.attrs) 129 } 130 131 // MetadataLookupParams returns parameters which are used to query image metadata to 132 // find matching image information. 133 func (p environProvider) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) { 134 if region == "" { 135 return nil, fmt.Errorf("region must be specified") 136 } 137 ec2Region, ok := allRegions[region] 138 if !ok { 139 return nil, fmt.Errorf("unknown region %q", region) 140 } 141 return &simplestreams.MetadataLookupParams{ 142 Region: region, 143 Endpoint: ec2Region.EC2Endpoint, 144 Architectures: arch.AllSupportedArches, 145 }, nil 146 } 147 148 // SecretAttrs is specified in the EnvironProvider interface. 149 func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) { 150 m := make(map[string]string) 151 ecfg, err := providerInstance.newConfig(cfg) 152 if err != nil { 153 return nil, err 154 } 155 m["access-key"] = ecfg.accessKey() 156 m["secret-key"] = ecfg.secretKey() 157 return m, nil 158 } 159 160 const badAccessKey = ` 161 Please ensure the Access Key ID you have specified is correct. 162 You can obtain the Access Key ID via the "Security Credentials" 163 page in the AWS console.` 164 165 const badSecretKey = ` 166 Please ensure the Secret Access Key you have specified is correct. 167 You can obtain the Secret Access Key via the "Security Credentials" 168 page in the AWS console.` 169 170 // verifyCredentials issues a cheap, non-modifying/idempotent request to EC2 to 171 // verify the configured credentials. If verification fails, a user-friendly 172 // error will be returned, and the original error will be logged at debug 173 // level. 174 var verifyCredentials = func(e *environ) error { 175 _, err := e.ec2().AccountAttributes() 176 if err != nil { 177 logger.Debugf("ec2 request failed: %v", err) 178 if err, ok := err.(*ec2.Error); ok { 179 switch err.Code { 180 case "AuthFailure": 181 return errors.New("authentication failed.\n" + badAccessKey) 182 case "SignatureDoesNotMatch": 183 return errors.New("authentication failed.\n" + badSecretKey) 184 default: 185 return err 186 } 187 } 188 return err 189 } 190 return nil 191 }