github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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"}
    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  	if ctx.ShouldVerifyCredentials() {
    91  		if err := verifyCredentials(e.(*environ)); err != nil {
    92  			return nil, err
    93  		}
    94  	}
    95  	return e, nil
    96  }
    97  
    98  // Validate is specified in the EnvironProvider interface.
    99  func (environProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   100  	newEcfg, err := validateConfig(cfg, old)
   101  	if err != nil {
   102  		return nil, fmt.Errorf("invalid EC2 provider config: %v", err)
   103  	}
   104  	return newEcfg.Apply(newEcfg.attrs)
   105  }
   106  
   107  // MetadataLookupParams returns parameters which are used to query image metadata to
   108  // find matching image information.
   109  func (p environProvider) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) {
   110  	if region == "" {
   111  		return nil, fmt.Errorf("region must be specified")
   112  	}
   113  	ec2Region, ok := allRegions[region]
   114  	if !ok {
   115  		return nil, fmt.Errorf("unknown region %q", region)
   116  	}
   117  	return &simplestreams.MetadataLookupParams{
   118  		Region:        region,
   119  		Endpoint:      ec2Region.EC2Endpoint,
   120  		Architectures: arch.AllSupportedArches,
   121  	}, nil
   122  }
   123  
   124  // SecretAttrs is specified in the EnvironProvider interface.
   125  func (environProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   126  	m := make(map[string]string)
   127  	ecfg, err := providerInstance.newConfig(cfg)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	m["access-key"] = ecfg.accessKey()
   132  	m["secret-key"] = ecfg.secretKey()
   133  	return m, nil
   134  }
   135  
   136  const badAccessKey = `
   137  Please ensure the Access Key ID you have specified is correct.
   138  You can obtain the Access Key ID via the "Security Credentials"
   139  page in the AWS console.`
   140  
   141  const badSecretKey = `
   142  Please ensure the Secret Access Key you have specified is correct.
   143  You can obtain the Secret Access Key via the "Security Credentials"
   144  page in the AWS console.`
   145  
   146  // verifyCredentials issues a cheap, non-modifying/idempotent request to EC2 to
   147  // verify the configured credentials. If verification fails, a user-friendly
   148  // error will be returned, and the original error will be logged at debug
   149  // level.
   150  var verifyCredentials = func(e *environ) error {
   151  	_, err := e.ec2().AccountAttributes()
   152  	if err != nil {
   153  		logger.Debugf("ec2 request failed: %v", err)
   154  		if err, ok := err.(*ec2.Error); ok {
   155  			switch err.Code {
   156  			case "AuthFailure":
   157  				return errors.New("authentication failed.\n" + badAccessKey)
   158  			case "SignatureDoesNotMatch":
   159  				return errors.New("authentication failed.\n" + badSecretKey)
   160  			default:
   161  				return err
   162  			}
   163  		}
   164  		return err
   165  	}
   166  	return nil
   167  }