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  }