github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/ec2/environ.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  	"net"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/names"
    15  	"github.com/juju/retry"
    16  	"github.com/juju/utils"
    17  	"github.com/juju/utils/arch"
    18  	"github.com/juju/utils/clock"
    19  	"gopkg.in/amz.v3/aws"
    20  	"gopkg.in/amz.v3/ec2"
    21  	"gopkg.in/amz.v3/s3"
    22  
    23  	"github.com/juju/juju/cloudconfig/instancecfg"
    24  	"github.com/juju/juju/cloudconfig/providerinit"
    25  	"github.com/juju/juju/constraints"
    26  	"github.com/juju/juju/environs"
    27  	"github.com/juju/juju/environs/config"
    28  	"github.com/juju/juju/environs/imagemetadata"
    29  	"github.com/juju/juju/environs/instances"
    30  	"github.com/juju/juju/environs/simplestreams"
    31  	"github.com/juju/juju/environs/tags"
    32  	"github.com/juju/juju/instance"
    33  	"github.com/juju/juju/network"
    34  	"github.com/juju/juju/provider"
    35  	"github.com/juju/juju/provider/common"
    36  	"github.com/juju/juju/state"
    37  	"github.com/juju/juju/tools"
    38  )
    39  
    40  const (
    41  	none                        = "none"
    42  	invalidParameterValue       = "InvalidParameterValue"
    43  	privateAddressLimitExceeded = "PrivateIpAddressLimitExceeded"
    44  
    45  	// tagName is the AWS-specific tag key that populates resources'
    46  	// name columns in the console.
    47  	tagName = "Name"
    48  )
    49  
    50  // Use shortAttempt to poll for short-term events or for retrying API calls.
    51  var shortAttempt = utils.AttemptStrategy{
    52  	Total: 5 * time.Second,
    53  	Delay: 200 * time.Millisecond,
    54  }
    55  
    56  type environ struct {
    57  	common.SupportsUnitPlacementPolicy
    58  
    59  	name string
    60  
    61  	// archMutex gates access to supportedArchitectures
    62  	archMutex sync.Mutex
    63  	// supportedArchitectures caches the architectures
    64  	// for which images can be instantiated.
    65  	supportedArchitectures []string
    66  
    67  	// ecfgMutex protects the *Unlocked fields below.
    68  	ecfgMutex    sync.Mutex
    69  	ecfgUnlocked *environConfig
    70  	ec2Unlocked  *ec2.EC2
    71  	s3Unlocked   *s3.S3
    72  
    73  	availabilityZonesMutex sync.Mutex
    74  	availabilityZones      []common.AvailabilityZone
    75  
    76  	// cachedDefaultVpc caches the id of the ec2 default vpc
    77  	cachedDefaultVpc *defaultVpc
    78  }
    79  
    80  // Ensure EC2 provider supports environs.NetworkingEnviron.
    81  var _ environs.NetworkingEnviron = (*environ)(nil)
    82  var _ simplestreams.HasRegion = (*environ)(nil)
    83  var _ state.Prechecker = (*environ)(nil)
    84  var _ state.InstanceDistributor = (*environ)(nil)
    85  
    86  type defaultVpc struct {
    87  	hasDefaultVpc bool
    88  	id            network.Id
    89  }
    90  
    91  // AssignPrivateIPAddress is a wrapper around ec2Inst.AssignPrivateIPAddresses.
    92  var AssignPrivateIPAddress = assignPrivateIPAddress
    93  
    94  // assignPrivateIPAddress should not be called directly so tests can patch it (use
    95  // AssignPrivateIPAddress).
    96  func assignPrivateIPAddress(ec2Inst *ec2.EC2, netId string, addr network.Address) error {
    97  	_, err := ec2Inst.AssignPrivateIPAddresses(netId, []string{addr.Value}, 0, false)
    98  	return err
    99  }
   100  
   101  func (e *environ) Config() *config.Config {
   102  	return e.ecfg().Config
   103  }
   104  
   105  func awsClients(cfg *config.Config) (*ec2.EC2, *s3.S3, *environConfig, error) {
   106  	ecfg, err := providerInstance.newConfig(cfg)
   107  	if err != nil {
   108  		return nil, nil, nil, err
   109  	}
   110  
   111  	auth := aws.Auth{ecfg.accessKey(), ecfg.secretKey()}
   112  	region := aws.Regions[ecfg.region()]
   113  	signer := aws.SignV4Factory(region.Name, "ec2")
   114  	return ec2.New(auth, region, signer), s3.New(auth, region), ecfg, nil
   115  }
   116  
   117  func (e *environ) SetConfig(cfg *config.Config) error {
   118  	ec2Client, s3Client, ecfg, err := awsClients(cfg)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	e.ecfgMutex.Lock()
   123  	defer e.ecfgMutex.Unlock()
   124  	e.ecfgUnlocked = ecfg
   125  	e.ec2Unlocked = ec2Client
   126  	e.s3Unlocked = s3Client
   127  
   128  	return nil
   129  }
   130  
   131  func (e *environ) defaultVpc() (network.Id, bool, error) {
   132  	if e.cachedDefaultVpc != nil {
   133  		defaultVpc := e.cachedDefaultVpc
   134  		return defaultVpc.id, defaultVpc.hasDefaultVpc, nil
   135  	}
   136  	ec2 := e.ec2()
   137  	resp, err := ec2.AccountAttributes("default-vpc")
   138  	if err != nil {
   139  		return "", false, errors.Trace(err)
   140  	}
   141  
   142  	hasDefault := true
   143  	defaultVpcId := ""
   144  
   145  	if len(resp.Attributes) == 0 || len(resp.Attributes[0].Values) == 0 {
   146  		hasDefault = false
   147  		defaultVpcId = ""
   148  	} else {
   149  		defaultVpcId = resp.Attributes[0].Values[0]
   150  		if defaultVpcId == none {
   151  			hasDefault = false
   152  			defaultVpcId = ""
   153  		}
   154  	}
   155  	defaultVpc := &defaultVpc{
   156  		id:            network.Id(defaultVpcId),
   157  		hasDefaultVpc: hasDefault,
   158  	}
   159  	e.cachedDefaultVpc = defaultVpc
   160  	return defaultVpc.id, defaultVpc.hasDefaultVpc, nil
   161  }
   162  
   163  func (e *environ) ecfg() *environConfig {
   164  	e.ecfgMutex.Lock()
   165  	ecfg := e.ecfgUnlocked
   166  	e.ecfgMutex.Unlock()
   167  	return ecfg
   168  }
   169  
   170  func (e *environ) ec2() *ec2.EC2 {
   171  	e.ecfgMutex.Lock()
   172  	ec2 := e.ec2Unlocked
   173  	e.ecfgMutex.Unlock()
   174  	return ec2
   175  }
   176  
   177  func (e *environ) Name() string {
   178  	return e.name
   179  }
   180  
   181  func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (*environs.BootstrapResult, error) {
   182  	return common.Bootstrap(ctx, e, args)
   183  }
   184  
   185  func (e *environ) ControllerInstances() ([]instance.Id, error) {
   186  	filter := ec2.NewFilter()
   187  	filter.Add("instance-state-name", "pending", "running")
   188  	filter.Add(fmt.Sprintf("tag:%s", tags.JujuModel), e.Config().UUID())
   189  	filter.Add(fmt.Sprintf("tag:%s", tags.JujuIsController), "true")
   190  	err := e.addGroupFilter(filter)
   191  	if err != nil {
   192  		if ec2ErrCode(err) == "InvalidGroup.NotFound" {
   193  			return nil, environs.ErrNotBootstrapped
   194  		}
   195  		return nil, errors.Annotate(err, "adding a group filter for instances")
   196  	}
   197  	resp, err := e.ec2().Instances(nil, filter)
   198  	if err != nil {
   199  		return nil, errors.Annotate(err, "listing instances")
   200  	}
   201  	var insts []instance.Id
   202  	for _, r := range resp.Reservations {
   203  		for i := range r.Instances {
   204  			inst := &ec2Instance{
   205  				e:        e,
   206  				Instance: &r.Instances[i],
   207  			}
   208  			insts = append(insts, inst.Id())
   209  		}
   210  	}
   211  	return insts, nil
   212  
   213  }
   214  
   215  // SupportedArchitectures is specified on the EnvironCapability interface.
   216  func (e *environ) SupportedArchitectures() ([]string, error) {
   217  	e.archMutex.Lock()
   218  	defer e.archMutex.Unlock()
   219  	if e.supportedArchitectures != nil {
   220  		return e.supportedArchitectures, nil
   221  	}
   222  	// Create a filter to get all images from our region and for the correct stream.
   223  	cloudSpec, err := e.Region()
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{
   228  		CloudSpec: cloudSpec,
   229  		Stream:    e.Config().ImageStream(),
   230  	})
   231  	e.supportedArchitectures, err = common.SupportedArchitectures(e, imageConstraint)
   232  	return e.supportedArchitectures, err
   233  }
   234  
   235  // SupportsSpaces is specified on environs.Networking.
   236  func (e *environ) SupportsSpaces() (bool, error) {
   237  	return true, nil
   238  }
   239  
   240  // SupportsSpaceDiscovery is specified on environs.Networking.
   241  func (e *environ) SupportsSpaceDiscovery() (bool, error) {
   242  	return false, nil
   243  }
   244  
   245  // SupportsAddressAllocation is specified on environs.Networking.
   246  func (e *environ) SupportsAddressAllocation(_ network.Id) (bool, error) {
   247  	if !environs.AddressAllocationEnabled(provider.EC2) {
   248  		return false, errors.NotSupportedf("address allocation")
   249  	}
   250  	_, hasDefaultVpc, err := e.defaultVpc()
   251  	if err != nil {
   252  		return false, errors.Trace(err)
   253  	}
   254  	return hasDefaultVpc, nil
   255  }
   256  
   257  var unsupportedConstraints = []string{
   258  	constraints.Tags,
   259  	// TODO(anastasiamac 2016-03-16) LP#1557874
   260  	// use virt-type in StartInstances
   261  	constraints.VirtType,
   262  }
   263  
   264  // ConstraintsValidator is defined on the Environs interface.
   265  func (e *environ) ConstraintsValidator() (constraints.Validator, error) {
   266  	validator := constraints.NewValidator()
   267  	validator.RegisterConflicts(
   268  		[]string{constraints.InstanceType},
   269  		[]string{constraints.Mem, constraints.CpuCores, constraints.CpuPower})
   270  	validator.RegisterUnsupported(unsupportedConstraints)
   271  	supportedArches, err := e.SupportedArchitectures()
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	validator.RegisterVocabulary(constraints.Arch, supportedArches)
   276  	instTypeNames := make([]string, len(allInstanceTypes))
   277  	for i, itype := range allInstanceTypes {
   278  		instTypeNames[i] = itype.Name
   279  	}
   280  	validator.RegisterVocabulary(constraints.InstanceType, instTypeNames)
   281  	return validator, nil
   282  }
   283  
   284  func archMatches(arches []string, arch *string) bool {
   285  	if arch == nil {
   286  		return true
   287  	}
   288  	for _, a := range arches {
   289  		if a == *arch {
   290  			return true
   291  		}
   292  	}
   293  	return false
   294  }
   295  
   296  var ec2AvailabilityZones = (*ec2.EC2).AvailabilityZones
   297  
   298  type ec2AvailabilityZone struct {
   299  	ec2.AvailabilityZoneInfo
   300  }
   301  
   302  func (z *ec2AvailabilityZone) Name() string {
   303  	return z.AvailabilityZoneInfo.Name
   304  }
   305  
   306  func (z *ec2AvailabilityZone) Available() bool {
   307  	return z.AvailabilityZoneInfo.State == "available"
   308  }
   309  
   310  // AvailabilityZones returns a slice of availability zones
   311  // for the configured region.
   312  func (e *environ) AvailabilityZones() ([]common.AvailabilityZone, error) {
   313  	e.availabilityZonesMutex.Lock()
   314  	defer e.availabilityZonesMutex.Unlock()
   315  	if e.availabilityZones == nil {
   316  		filter := ec2.NewFilter()
   317  		filter.Add("region-name", e.ecfg().region())
   318  		resp, err := ec2AvailabilityZones(e.ec2(), filter)
   319  		if err != nil {
   320  			return nil, err
   321  		}
   322  		logger.Debugf("availability zones: %+v", resp)
   323  		e.availabilityZones = make([]common.AvailabilityZone, len(resp.Zones))
   324  		for i, z := range resp.Zones {
   325  			e.availabilityZones[i] = &ec2AvailabilityZone{z}
   326  		}
   327  	}
   328  	return e.availabilityZones, nil
   329  }
   330  
   331  // InstanceAvailabilityZoneNames returns the availability zone names for each
   332  // of the specified instances.
   333  func (e *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) {
   334  	instances, err := e.Instances(ids)
   335  	if err != nil && err != environs.ErrPartialInstances {
   336  		return nil, err
   337  	}
   338  	zones := make([]string, len(instances))
   339  	for i, inst := range instances {
   340  		if inst == nil {
   341  			continue
   342  		}
   343  		zones[i] = inst.(*ec2Instance).AvailZone
   344  	}
   345  	return zones, err
   346  }
   347  
   348  type ec2Placement struct {
   349  	availabilityZone ec2.AvailabilityZoneInfo
   350  }
   351  
   352  func (e *environ) parsePlacement(placement string) (*ec2Placement, error) {
   353  	pos := strings.IndexRune(placement, '=')
   354  	if pos == -1 {
   355  		return nil, fmt.Errorf("unknown placement directive: %v", placement)
   356  	}
   357  	switch key, value := placement[:pos], placement[pos+1:]; key {
   358  	case "zone":
   359  		availabilityZone := value
   360  		zones, err := e.AvailabilityZones()
   361  		if err != nil {
   362  			return nil, err
   363  		}
   364  		for _, z := range zones {
   365  			if z.Name() == availabilityZone {
   366  				return &ec2Placement{
   367  					z.(*ec2AvailabilityZone).AvailabilityZoneInfo,
   368  				}, nil
   369  			}
   370  		}
   371  		return nil, fmt.Errorf("invalid availability zone %q", availabilityZone)
   372  	}
   373  	return nil, fmt.Errorf("unknown placement directive: %v", placement)
   374  }
   375  
   376  // PrecheckInstance is defined on the state.Prechecker interface.
   377  func (e *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
   378  	if placement != "" {
   379  		if _, err := e.parsePlacement(placement); err != nil {
   380  			return err
   381  		}
   382  	}
   383  	if !cons.HasInstanceType() {
   384  		return nil
   385  	}
   386  	// Constraint has an instance-type constraint so let's see if it is valid.
   387  	for _, itype := range allInstanceTypes {
   388  		if itype.Name != *cons.InstanceType {
   389  			continue
   390  		}
   391  		if archMatches(itype.Arches, cons.Arch) {
   392  			return nil
   393  		}
   394  	}
   395  	if cons.Arch == nil {
   396  		return fmt.Errorf("invalid AWS instance type %q specified", *cons.InstanceType)
   397  	}
   398  	return fmt.Errorf("invalid AWS instance type %q and arch %q specified", *cons.InstanceType, *cons.Arch)
   399  }
   400  
   401  // MetadataLookupParams returns parameters which are used to query simplestreams metadata.
   402  func (e *environ) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) {
   403  	if region == "" {
   404  		region = e.ecfg().region()
   405  	}
   406  	cloudSpec, err := e.cloudSpec(region)
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  	return &simplestreams.MetadataLookupParams{
   411  		Series:        config.PreferredSeries(e.ecfg()),
   412  		Region:        cloudSpec.Region,
   413  		Endpoint:      cloudSpec.Endpoint,
   414  		Architectures: arch.AllSupportedArches,
   415  	}, nil
   416  }
   417  
   418  // Region is specified in the HasRegion interface.
   419  func (e *environ) Region() (simplestreams.CloudSpec, error) {
   420  	return e.cloudSpec(e.ecfg().region())
   421  }
   422  
   423  func (e *environ) cloudSpec(region string) (simplestreams.CloudSpec, error) {
   424  	ec2Region, ok := allRegions[region]
   425  	if !ok {
   426  		return simplestreams.CloudSpec{}, fmt.Errorf("unknown region %q", region)
   427  	}
   428  	return simplestreams.CloudSpec{
   429  		Region:   region,
   430  		Endpoint: ec2Region.EC2Endpoint,
   431  	}, nil
   432  }
   433  
   434  const (
   435  	ebsStorage = "ebs"
   436  	ssdStorage = "ssd"
   437  )
   438  
   439  // DistributeInstances implements the state.InstanceDistributor policy.
   440  func (e *environ) DistributeInstances(candidates, distributionGroup []instance.Id) ([]instance.Id, error) {
   441  	return common.DistributeInstances(e, candidates, distributionGroup)
   442  }
   443  
   444  var availabilityZoneAllocations = common.AvailabilityZoneAllocations
   445  
   446  // MaintainInstance is specified in the InstanceBroker interface.
   447  func (*environ) MaintainInstance(args environs.StartInstanceParams) error {
   448  	return nil
   449  }
   450  
   451  // resourceName returns the string to use for a resource's Name tag,
   452  // to help users identify Juju-managed resources in the AWS console.
   453  func resourceName(tag names.Tag, envName string) string {
   454  	return fmt.Sprintf("juju-%s-%s", envName, tag)
   455  }
   456  
   457  // StartInstance is specified in the InstanceBroker interface.
   458  func (e *environ) StartInstance(args environs.StartInstanceParams) (_ *environs.StartInstanceResult, resultErr error) {
   459  	var inst *ec2Instance
   460  	defer func() {
   461  		if resultErr == nil || inst == nil {
   462  			return
   463  		}
   464  		if err := e.StopInstances(inst.Id()); err != nil {
   465  			logger.Errorf("error stopping failed instance: %v", err)
   466  		}
   467  	}()
   468  
   469  	var availabilityZones []string
   470  	if args.Placement != "" {
   471  		placement, err := e.parsePlacement(args.Placement)
   472  		if err != nil {
   473  			return nil, err
   474  		}
   475  		if placement.availabilityZone.State != "available" {
   476  			return nil, errors.Errorf("availability zone %q is %s", placement.availabilityZone.Name, placement.availabilityZone.State)
   477  		}
   478  		availabilityZones = append(availabilityZones, placement.availabilityZone.Name)
   479  	}
   480  
   481  	// If no availability zone is specified, then automatically spread across
   482  	// the known zones for optimal spread across the instance distribution
   483  	// group.
   484  	var zoneInstances []common.AvailabilityZoneInstances
   485  	if len(availabilityZones) == 0 {
   486  		var err error
   487  		var group []instance.Id
   488  		if args.DistributionGroup != nil {
   489  			group, err = args.DistributionGroup()
   490  			if err != nil {
   491  				return nil, err
   492  			}
   493  		}
   494  		zoneInstances, err = availabilityZoneAllocations(e, group)
   495  		if err != nil {
   496  			return nil, err
   497  		}
   498  		for _, z := range zoneInstances {
   499  			availabilityZones = append(availabilityZones, z.ZoneName)
   500  		}
   501  		if len(availabilityZones) == 0 {
   502  			return nil, errors.New("failed to determine availability zones")
   503  		}
   504  	}
   505  
   506  	arches := args.Tools.Arches()
   507  
   508  	spec, err := findInstanceSpec(args.ImageMetadata, &instances.InstanceConstraint{
   509  		Region:      e.ecfg().region(),
   510  		Series:      args.InstanceConfig.Series,
   511  		Arches:      arches,
   512  		Constraints: args.Constraints,
   513  		Storage:     []string{ssdStorage, ebsStorage},
   514  	})
   515  	if err != nil {
   516  		return nil, err
   517  	}
   518  	tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch})
   519  	if err != nil {
   520  		return nil, errors.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches)
   521  	}
   522  
   523  	if spec.InstanceType.Deprecated {
   524  		logger.Warningf("deprecated instance type specified: %s", spec.InstanceType.Name)
   525  	}
   526  
   527  	if err := args.InstanceConfig.SetTools(tools); err != nil {
   528  		return nil, errors.Trace(err)
   529  	}
   530  	if err := instancecfg.FinishInstanceConfig(args.InstanceConfig, e.Config()); err != nil {
   531  		return nil, err
   532  	}
   533  
   534  	userData, err := providerinit.ComposeUserData(args.InstanceConfig, nil, AmazonRenderer{})
   535  	if err != nil {
   536  		return nil, errors.Annotate(err, "cannot make user data")
   537  	}
   538  	logger.Debugf("ec2 user data; %d bytes", len(userData))
   539  	cfg := e.Config()
   540  	groups, err := e.setUpGroups(args.InstanceConfig.MachineId, cfg.APIPort())
   541  	if err != nil {
   542  		return nil, errors.Annotate(err, "cannot set up groups")
   543  	}
   544  
   545  	blockDeviceMappings := getBlockDeviceMappings(args.Constraints, args.InstanceConfig.Series)
   546  	rootDiskSize := uint64(blockDeviceMappings[0].VolumeSize) * 1024
   547  
   548  	// If --constraints spaces=foo was passed, the provisioner will populate
   549  	// args.SubnetsToZones map. In AWS a subnet can span only one zone, so here
   550  	// we build the reverse map zonesToSubnets, which we will use to below in
   551  	// the RunInstance loop to provide an explicit subnet ID, rather than just
   552  	// AZ. This ensures instances in the same group (units of a service or all
   553  	// instances when adding a machine manually) will still be evenly
   554  	// distributed across AZs, but only within subnets of the space constraint.
   555  	//
   556  	// TODO(dimitern): This should be done in a provider-independant way.
   557  	zonesToSubnets := make(map[string]string, len(args.SubnetsToZones))
   558  	var spaceSubnetIDs []string
   559  	for subnetID, zones := range args.SubnetsToZones {
   560  		// EC2-specific: subnets can only be in a single zone, hence the
   561  		// zones slice will always contain exactly one element when
   562  		// SubnetsToZones is populated.
   563  		zone := zones[0]
   564  		sid := string(subnetID)
   565  		zonesToSubnets[zone] = sid
   566  		spaceSubnetIDs = append(spaceSubnetIDs, sid)
   567  	}
   568  
   569  	// TODO(dimitern): For the network model MVP we only respect the
   570  	// first positive (a.k.a. "included") space specified in the
   571  	// constraints. Once we support any given list of positive or
   572  	// negative (prefixed with "^") spaces, fix this approach.
   573  	var spaceName string
   574  	if spaces := args.Constraints.IncludeSpaces(); len(spaces) > 0 {
   575  		spaceName = spaces[0]
   576  	}
   577  
   578  	var instResp *ec2.RunInstancesResp
   579  	commonRunArgs := &ec2.RunInstances{
   580  		MinCount:            1,
   581  		MaxCount:            1,
   582  		UserData:            userData,
   583  		InstanceType:        spec.InstanceType.Name,
   584  		SecurityGroups:      groups,
   585  		BlockDeviceMappings: blockDeviceMappings,
   586  		ImageId:             spec.Image.Id,
   587  	}
   588  
   589  	for _, zone := range availabilityZones {
   590  		runArgs := commonRunArgs
   591  
   592  		if subnetID, found := zonesToSubnets[zone]; found {
   593  			// Use SubnetId explicitly here so the instance ends up in the
   594  			// right space.
   595  			runArgs.SubnetId = subnetID
   596  		} else if spaceName != "" {
   597  			// Ignore AZs not matching any subnet of the space in constraints.
   598  			logger.Infof(
   599  				"skipping zone %q: not associated with any of space %q's subnets %q",
   600  				zone, spaceName, strings.Join(spaceSubnetIDs, ", "),
   601  			)
   602  			continue
   603  		} else {
   604  			// No space constraint specified, just use the usual zone
   605  			// distribution without an explicit SubnetId.
   606  			runArgs.AvailZone = zone
   607  		}
   608  
   609  		instResp, err = runInstances(e.ec2(), runArgs)
   610  		if err == nil {
   611  			break
   612  		}
   613  		if runArgs.SubnetId != "" && isSubnetConstrainedError(err) {
   614  			subID := runArgs.SubnetId
   615  			logger.Infof("%q (in zone %q) is constrained, try another subnet", subID, zone)
   616  			continue
   617  		} else if !isZoneConstrainedError(err) {
   618  			// Something else went wrong - bail out.
   619  			break
   620  		}
   621  		logger.Infof("%q is constrained, trying another availability zone", zone)
   622  	}
   623  
   624  	if err != nil {
   625  		return nil, errors.Annotate(err, "cannot run instances")
   626  	}
   627  	if len(instResp.Instances) != 1 {
   628  		return nil, errors.Errorf("expected 1 started instance, got %d", len(instResp.Instances))
   629  	}
   630  
   631  	inst = &ec2Instance{
   632  		e:        e,
   633  		Instance: &instResp.Instances[0],
   634  	}
   635  	instAZ, instSubnet := inst.Instance.AvailZone, inst.Instance.SubnetId
   636  	logger.Infof("started instance %q in AZ %q, subnet %q", inst.Id(), instAZ, instSubnet)
   637  
   638  	// Tag instance, for accounting and identification.
   639  	instanceName := resourceName(
   640  		names.NewMachineTag(args.InstanceConfig.MachineId), e.Config().Name(),
   641  	)
   642  	args.InstanceConfig.Tags[tagName] = instanceName
   643  	if err := tagResources(e.ec2(), args.InstanceConfig.Tags, string(inst.Id())); err != nil {
   644  		return nil, errors.Annotate(err, "tagging instance")
   645  	}
   646  
   647  	// Tag the machine's root EBS volume, if it has one.
   648  	if inst.Instance.RootDeviceType == "ebs" {
   649  		tags := tags.ResourceTags(
   650  			names.NewModelTag(cfg.UUID()),
   651  			names.NewModelTag(cfg.ControllerUUID()),
   652  			cfg,
   653  		)
   654  		tags[tagName] = instanceName + "-root"
   655  		if err := tagRootDisk(e.ec2(), tags, inst.Instance); err != nil {
   656  			return nil, errors.Annotate(err, "tagging root disk")
   657  		}
   658  	}
   659  
   660  	hc := instance.HardwareCharacteristics{
   661  		Arch:     &spec.Image.Arch,
   662  		Mem:      &spec.InstanceType.Mem,
   663  		CpuCores: &spec.InstanceType.CpuCores,
   664  		CpuPower: spec.InstanceType.CpuPower,
   665  		RootDisk: &rootDiskSize,
   666  		// Tags currently not supported by EC2
   667  		AvailabilityZone: &inst.Instance.AvailZone,
   668  	}
   669  	return &environs.StartInstanceResult{
   670  		Instance: inst,
   671  		Hardware: &hc,
   672  	}, nil
   673  }
   674  
   675  // tagResources calls ec2.CreateTags, tagging each of the specified resources
   676  // with the given tags. tagResources will retry for a short period of time
   677  // if it receives a *.NotFound error response from EC2.
   678  func tagResources(e *ec2.EC2, tags map[string]string, resourceIds ...string) error {
   679  	if len(tags) == 0 {
   680  		return nil
   681  	}
   682  	ec2Tags := make([]ec2.Tag, 0, len(tags))
   683  	for k, v := range tags {
   684  		ec2Tags = append(ec2Tags, ec2.Tag{k, v})
   685  	}
   686  	var err error
   687  	for a := shortAttempt.Start(); a.Next(); {
   688  		_, err = e.CreateTags(resourceIds, ec2Tags)
   689  		if err == nil || !strings.HasSuffix(ec2ErrCode(err), ".NotFound") {
   690  			return err
   691  		}
   692  	}
   693  	return err
   694  }
   695  
   696  func tagRootDisk(e *ec2.EC2, tags map[string]string, inst *ec2.Instance) error {
   697  	if len(tags) == 0 {
   698  		return nil
   699  	}
   700  	findVolumeId := func(inst *ec2.Instance) string {
   701  		for _, m := range inst.BlockDeviceMappings {
   702  			if m.DeviceName != inst.RootDeviceName {
   703  				continue
   704  			}
   705  			return m.VolumeId
   706  		}
   707  		return ""
   708  	}
   709  	// Wait until the instance has an associated EBS volume in the
   710  	// block-device-mapping.
   711  	volumeId := findVolumeId(inst)
   712  	waitRootDiskAttempt := utils.AttemptStrategy{
   713  		Total: 5 * time.Minute,
   714  		Delay: 5 * time.Second,
   715  	}
   716  	for a := waitRootDiskAttempt.Start(); volumeId == "" && a.Next(); {
   717  		resp, err := e.Instances([]string{inst.InstanceId}, nil)
   718  		if err != nil {
   719  			return err
   720  		}
   721  		if len(resp.Reservations) > 0 && len(resp.Reservations[0].Instances) > 0 {
   722  			inst = &resp.Reservations[0].Instances[0]
   723  			volumeId = findVolumeId(inst)
   724  		}
   725  	}
   726  	if volumeId == "" {
   727  		return errors.New("timed out waiting for EBS volume to be associated")
   728  	}
   729  	return tagResources(e, tags, volumeId)
   730  }
   731  
   732  var runInstances = _runInstances
   733  
   734  // runInstances calls ec2.RunInstances for a fixed number of attempts until
   735  // RunInstances returns an error code that does not indicate an error that
   736  // may be caused by eventual consistency.
   737  func _runInstances(e *ec2.EC2, ri *ec2.RunInstances) (resp *ec2.RunInstancesResp, err error) {
   738  	for a := shortAttempt.Start(); a.Next(); {
   739  		resp, err = e.RunInstances(ri)
   740  		if err == nil || ec2ErrCode(err) != "InvalidGroup.NotFound" {
   741  			break
   742  		}
   743  	}
   744  	return resp, err
   745  }
   746  
   747  func (e *environ) StopInstances(ids ...instance.Id) error {
   748  	return errors.Trace(e.terminateInstances(ids))
   749  }
   750  
   751  // groupInfoByName returns information on the security group
   752  // with the given name including rules and other details.
   753  func (e *environ) groupInfoByName(groupName string) (ec2.SecurityGroupInfo, error) {
   754  	// Non-default VPC does not support name-based group lookups, can
   755  	// use a filter by group name instead when support is needed.
   756  	limitToGroups := []ec2.SecurityGroup{{Name: groupName}}
   757  	resp, err := e.ec2().SecurityGroups(limitToGroups, nil)
   758  	if err != nil {
   759  		return ec2.SecurityGroupInfo{}, err
   760  	}
   761  	if len(resp.Groups) != 1 {
   762  		return ec2.SecurityGroupInfo{}, fmt.Errorf("expected one security group named %q, got %v", groupName, resp.Groups)
   763  	}
   764  	return resp.Groups[0], nil
   765  }
   766  
   767  // groupByName returns the security group with the given name.
   768  func (e *environ) groupByName(groupName string) (ec2.SecurityGroup, error) {
   769  	groupInfo, err := e.groupInfoByName(groupName)
   770  	return groupInfo.SecurityGroup, err
   771  }
   772  
   773  // addGroupFilter sets a limit an instance filter so only those machines
   774  // with the juju environment wide security group associated will be listed.
   775  //
   776  // An EC2 API call is required to resolve the group name to an id, as VPC
   777  // enabled accounts do not support name based filtering.
   778  // TODO: Detect classic accounts and just filter by name for those.
   779  //
   780  // Callers must handle InvalidGroup.NotFound errors to mean the same as no
   781  // matching instances.
   782  func (e *environ) addGroupFilter(filter *ec2.Filter) error {
   783  	groupName := e.jujuGroupName()
   784  	group, err := e.groupByName(groupName)
   785  	if err != nil {
   786  		return err
   787  	}
   788  	// EC2 should support filtering with and without the 'instance.'
   789  	// prefix, but only the form with seems to work with default VPC.
   790  	filter.Add("instance.group-id", group.Id)
   791  	return nil
   792  }
   793  
   794  // gatherInstances tries to get information on each instance
   795  // id whose corresponding insts slot is nil.
   796  // It returns environs.ErrPartialInstances if the insts
   797  // slice has not been completely filled.
   798  func (e *environ) gatherInstances(ids []instance.Id, insts []instance.Instance) error {
   799  	var need []string
   800  	for i, inst := range insts {
   801  		if inst == nil {
   802  			need = append(need, string(ids[i]))
   803  		}
   804  	}
   805  	if len(need) == 0 {
   806  		return nil
   807  	}
   808  	filter := ec2.NewFilter()
   809  	filter.Add("instance-state-name", "pending", "running")
   810  	err := e.addGroupFilter(filter)
   811  	if err != nil {
   812  		if ec2ErrCode(err) == "InvalidGroup.NotFound" {
   813  			return environs.ErrPartialInstances
   814  		}
   815  		return err
   816  	}
   817  	filter.Add("instance-id", need...)
   818  	resp, err := e.ec2().Instances(nil, filter)
   819  	if err != nil {
   820  		return err
   821  	}
   822  	n := 0
   823  	// For each requested id, add it to the returned instances
   824  	// if we find it in the response.
   825  	for i, id := range ids {
   826  		if insts[i] != nil {
   827  			continue
   828  		}
   829  		for j := range resp.Reservations {
   830  			r := &resp.Reservations[j]
   831  			for k := range r.Instances {
   832  				if r.Instances[k].InstanceId == string(id) {
   833  					inst := r.Instances[k]
   834  					// TODO(wallyworld): lookup the details to fill in the instance type data
   835  					insts[i] = &ec2Instance{e: e, Instance: &inst}
   836  					n++
   837  				}
   838  			}
   839  		}
   840  	}
   841  	if n < len(ids) {
   842  		return environs.ErrPartialInstances
   843  	}
   844  	return nil
   845  }
   846  
   847  func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
   848  	if len(ids) == 0 {
   849  		return nil, nil
   850  	}
   851  	insts := make([]instance.Instance, len(ids))
   852  	// Make a series of requests to cope with eventual consistency.
   853  	// Each request will attempt to add more instances to the requested
   854  	// set.
   855  	var err error
   856  	for a := shortAttempt.Start(); a.Next(); {
   857  		err = e.gatherInstances(ids, insts)
   858  		if err == nil || err != environs.ErrPartialInstances {
   859  			break
   860  		}
   861  	}
   862  	if err == environs.ErrPartialInstances {
   863  		for _, inst := range insts {
   864  			if inst != nil {
   865  				return insts, environs.ErrPartialInstances
   866  			}
   867  		}
   868  		return nil, environs.ErrNoInstances
   869  	}
   870  	if err != nil {
   871  		return nil, err
   872  	}
   873  	return insts, nil
   874  }
   875  
   876  func (e *environ) fetchNetworkInterfaceId(ec2Inst *ec2.EC2, instId instance.Id) (string, error) {
   877  	var err error
   878  	var instancesResp *ec2.InstancesResp
   879  	for a := shortAttempt.Start(); a.Next(); {
   880  		instancesResp, err = ec2Inst.Instances([]string{string(instId)}, nil)
   881  		if err == nil {
   882  			break
   883  		}
   884  		logger.Tracef("Instances(%q) returned: %v", instId, err)
   885  	}
   886  	if err != nil {
   887  		// either the instance doesn't exist or we couldn't get through to
   888  		// the ec2 api
   889  		return "", err
   890  	}
   891  
   892  	if len(instancesResp.Reservations) == 0 {
   893  		return "", errors.New("unexpected AWS response: reservation not found")
   894  	}
   895  	if len(instancesResp.Reservations[0].Instances) == 0 {
   896  		return "", errors.New("unexpected AWS response: instance not found")
   897  	}
   898  	if len(instancesResp.Reservations[0].Instances[0].NetworkInterfaces) == 0 {
   899  		return "", errors.New("unexpected AWS response: network interface not found")
   900  	}
   901  	networkInterfaceId := instancesResp.Reservations[0].Instances[0].NetworkInterfaces[0].Id
   902  	return networkInterfaceId, nil
   903  }
   904  
   905  // AllocateAddress requests an address to be allocated for the given
   906  // instance on the given subnet. Implements NetworkingEnviron.AllocateAddress.
   907  func (e *environ) AllocateAddress(instId instance.Id, _ network.Id, addr *network.Address, _, _ string) (err error) {
   908  	if !environs.AddressAllocationEnabled(provider.EC2) {
   909  		return errors.NotSupportedf("address allocation")
   910  	}
   911  	if addr == nil || addr.Value == "" {
   912  		return errors.NewNotValid(nil, "invalid address: nil or empty")
   913  	}
   914  
   915  	defer errors.DeferredAnnotatef(&err, "failed to allocate address %q for instance %q", addr, instId)
   916  
   917  	var nicId string
   918  	ec2Inst := e.ec2()
   919  	nicId, err = e.fetchNetworkInterfaceId(ec2Inst, instId)
   920  	if err != nil {
   921  		return errors.Trace(err)
   922  	}
   923  	for a := shortAttempt.Start(); a.Next(); {
   924  		err = AssignPrivateIPAddress(ec2Inst, nicId, *addr)
   925  		logger.Tracef("AssignPrivateIPAddresses(%v, %v) returned: %v", nicId, *addr, err)
   926  		if err == nil {
   927  			logger.Tracef("allocated address %v for instance %v, NIC %v", *addr, instId, nicId)
   928  			break
   929  		}
   930  		if ec2Err, ok := err.(*ec2.Error); ok {
   931  			if ec2Err.Code == invalidParameterValue {
   932  				// Note: this Code is also used if we specify
   933  				// an IP address outside the subnet. Take care!
   934  				logger.Tracef("address %q not available for allocation", *addr)
   935  				return environs.ErrIPAddressUnavailable
   936  			} else if ec2Err.Code == privateAddressLimitExceeded {
   937  				logger.Tracef("no more addresses available on the subnet")
   938  				return environs.ErrIPAddressesExhausted
   939  			}
   940  		}
   941  
   942  	}
   943  	return err
   944  }
   945  
   946  // ReleaseAddress releases a specific address previously allocated with
   947  // AllocateAddress. Implements NetworkingEnviron.ReleaseAddress.
   948  func (e *environ) ReleaseAddress(instId instance.Id, _ network.Id, addr network.Address, _, _ string) (err error) {
   949  	if !environs.AddressAllocationEnabled(provider.EC2) {
   950  		return errors.NotSupportedf("address allocation")
   951  	}
   952  
   953  	defer errors.DeferredAnnotatef(&err, "failed to release address %q from instance %q", addr, instId)
   954  
   955  	// If the instance ID is unknown the address has already been released
   956  	// and we can ignore this request.
   957  	if instId == instance.UnknownId {
   958  		logger.Debugf("release address %q with an unknown instance ID is a no-op (ignoring)", addr.Value)
   959  		return nil
   960  	}
   961  
   962  	var nicId string
   963  	ec2Inst := e.ec2()
   964  	nicId, err = e.fetchNetworkInterfaceId(ec2Inst, instId)
   965  	if err != nil {
   966  		return errors.Trace(err)
   967  	}
   968  	for a := shortAttempt.Start(); a.Next(); {
   969  		_, err = ec2Inst.UnassignPrivateIPAddresses(nicId, []string{addr.Value})
   970  		logger.Tracef("UnassignPrivateIPAddresses(%q, %q) returned: %v", nicId, addr, err)
   971  		if err == nil {
   972  			logger.Tracef("released address %q from instance %q, NIC %q", addr, instId, nicId)
   973  			break
   974  		}
   975  	}
   976  	return err
   977  }
   978  
   979  // NetworkInterfaces implements NetworkingEnviron.NetworkInterfaces.
   980  func (e *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) {
   981  	ec2Client := e.ec2()
   982  	var err error
   983  	var networkInterfacesResp *ec2.NetworkInterfacesResp
   984  	for a := shortAttempt.Start(); a.Next(); {
   985  		logger.Tracef("retrieving NICs for instance %q", instId)
   986  		filter := ec2.NewFilter()
   987  		filter.Add("attachment.instance-id", string(instId))
   988  		networkInterfacesResp, err = ec2Client.NetworkInterfaces(nil, filter)
   989  		logger.Tracef("instance %q NICs: %#v (err: %v)", instId, networkInterfacesResp, err)
   990  		if err != nil {
   991  			logger.Warningf("failed to get instance %q interfaces: %v (retrying)", instId, err)
   992  			continue
   993  		}
   994  		if len(networkInterfacesResp.Interfaces) == 0 {
   995  			logger.Tracef("instance %q has no NIC attachment yet, retrying...", instId)
   996  			continue
   997  		}
   998  		logger.Tracef("found instance %q NICS: %#v", instId, networkInterfacesResp.Interfaces)
   999  		break
  1000  	}
  1001  	if err != nil {
  1002  		// either the instance doesn't exist or we couldn't get through to
  1003  		// the ec2 api
  1004  		return nil, errors.Annotatef(err, "cannot get instance %q network interfaces", instId)
  1005  	}
  1006  	ec2Interfaces := networkInterfacesResp.Interfaces
  1007  	result := make([]network.InterfaceInfo, len(ec2Interfaces))
  1008  	for i, iface := range ec2Interfaces {
  1009  		resp, err := ec2Client.Subnets([]string{iface.SubnetId}, nil)
  1010  		if err != nil {
  1011  			return nil, errors.Annotatef(err, "failed to retrieve subnet %q info", iface.SubnetId)
  1012  		}
  1013  		if len(resp.Subnets) != 1 {
  1014  			return nil, errors.Errorf("expected 1 subnet, got %d", len(resp.Subnets))
  1015  		}
  1016  		subnet := resp.Subnets[0]
  1017  		cidr := subnet.CIDRBlock
  1018  
  1019  		result[i] = network.InterfaceInfo{
  1020  			DeviceIndex:       iface.Attachment.DeviceIndex,
  1021  			MACAddress:        iface.MACAddress,
  1022  			CIDR:              cidr,
  1023  			ProviderId:        network.Id(iface.Id),
  1024  			ProviderSubnetId:  network.Id(iface.SubnetId),
  1025  			AvailabilityZones: []string{subnet.AvailZone},
  1026  			VLANTag:           0, // Not supported on EC2.
  1027  			// Getting the interface name is not supported on EC2, so fake it.
  1028  			InterfaceName: fmt.Sprintf("unsupported%d", iface.Attachment.DeviceIndex),
  1029  			Disabled:      false,
  1030  			NoAutoStart:   false,
  1031  			ConfigType:    network.ConfigDHCP,
  1032  			InterfaceType: network.EthernetInterface,
  1033  			Address:       network.NewScopedAddress(iface.PrivateIPAddress, network.ScopeCloudLocal),
  1034  		}
  1035  	}
  1036  	return result, nil
  1037  }
  1038  
  1039  func makeSubnetInfo(cidr string, subnetId network.Id, availZones []string) (network.SubnetInfo, error) {
  1040  	ip, ipnet, err := net.ParseCIDR(cidr)
  1041  	if err != nil {
  1042  		logger.Warningf("skipping subnet %q, invalid CIDR: %v", cidr, err)
  1043  		return network.SubnetInfo{}, err
  1044  	}
  1045  	// ec2 only uses IPv4 addresses for subnets
  1046  	start, err := network.IPv4ToDecimal(ip)
  1047  	if err != nil {
  1048  		logger.Warningf("skipping subnet %q, invalid IP: %v", cidr, err)
  1049  		return network.SubnetInfo{}, err
  1050  	}
  1051  	// First four addresses in a subnet are reserved, see
  1052  	// http://goo.gl/rrWTIo
  1053  	allocatableLow := network.DecimalToIPv4(start + 4)
  1054  
  1055  	ones, bits := ipnet.Mask.Size()
  1056  	zeros := bits - ones
  1057  	numIPs := uint32(1) << uint32(zeros)
  1058  	highIP := start + numIPs - 1
  1059  	// The last address in a subnet is also reserved (see same ref).
  1060  	allocatableHigh := network.DecimalToIPv4(highIP - 1)
  1061  
  1062  	info := network.SubnetInfo{
  1063  		CIDR:              cidr,
  1064  		ProviderId:        subnetId,
  1065  		VLANTag:           0, // Not supported on EC2
  1066  		AllocatableIPLow:  allocatableLow,
  1067  		AllocatableIPHigh: allocatableHigh,
  1068  		AvailabilityZones: availZones,
  1069  	}
  1070  	logger.Tracef("found subnet with info %#v", info)
  1071  	return info, nil
  1072  
  1073  }
  1074  
  1075  // Spaces is not implemented by the ec2 provider as we don't currently have
  1076  // provider level spaces.
  1077  func (e *environ) Spaces() ([]network.SpaceInfo, error) {
  1078  	return nil, errors.NotSupportedf("Spaces")
  1079  }
  1080  
  1081  // Subnets returns basic information about the specified subnets known
  1082  // by the provider for the specified instance or list of ids. subnetIds can be
  1083  // empty, in which case all known are returned. Implements
  1084  // NetworkingEnviron.Subnets.
  1085  func (e *environ) Subnets(instId instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) {
  1086  	var results []network.SubnetInfo
  1087  	subIdSet := make(map[string]bool)
  1088  	for _, subId := range subnetIds {
  1089  		subIdSet[string(subId)] = false
  1090  	}
  1091  
  1092  	if instId != instance.UnknownId {
  1093  		interfaces, err := e.NetworkInterfaces(instId)
  1094  		if err != nil {
  1095  			return results, errors.Trace(err)
  1096  		}
  1097  		if len(subnetIds) == 0 {
  1098  			for _, iface := range interfaces {
  1099  				subIdSet[string(iface.ProviderSubnetId)] = false
  1100  			}
  1101  		}
  1102  		for _, iface := range interfaces {
  1103  			_, ok := subIdSet[string(iface.ProviderSubnetId)]
  1104  			if !ok {
  1105  				logger.Tracef("subnet %q not in %v, skipping", iface.ProviderSubnetId, subnetIds)
  1106  				continue
  1107  			}
  1108  			subIdSet[string(iface.ProviderSubnetId)] = true
  1109  			info, err := makeSubnetInfo(iface.CIDR, iface.ProviderSubnetId, iface.AvailabilityZones)
  1110  			if err != nil {
  1111  				// Error will already have been logged.
  1112  				continue
  1113  			}
  1114  			results = append(results, info)
  1115  		}
  1116  	} else {
  1117  		ec2Inst := e.ec2()
  1118  		resp, err := ec2Inst.Subnets(nil, nil)
  1119  		if err != nil {
  1120  			return nil, errors.Annotatef(err, "failed to retrieve subnets")
  1121  		}
  1122  		if len(subnetIds) == 0 {
  1123  			for _, subnet := range resp.Subnets {
  1124  				subIdSet[subnet.Id] = false
  1125  			}
  1126  		}
  1127  
  1128  		for _, subnet := range resp.Subnets {
  1129  			_, ok := subIdSet[subnet.Id]
  1130  			if !ok {
  1131  				logger.Tracef("subnet %q not in %v, skipping", subnet.Id, subnetIds)
  1132  				continue
  1133  			}
  1134  			subIdSet[subnet.Id] = true
  1135  			cidr := subnet.CIDRBlock
  1136  			info, err := makeSubnetInfo(cidr, network.Id(subnet.Id), []string{subnet.AvailZone})
  1137  			if err != nil {
  1138  				// Error will already have been logged.
  1139  				continue
  1140  			}
  1141  			results = append(results, info)
  1142  
  1143  		}
  1144  	}
  1145  
  1146  	notFound := []string{}
  1147  	for subId, found := range subIdSet {
  1148  		if !found {
  1149  			notFound = append(notFound, subId)
  1150  		}
  1151  	}
  1152  	if len(notFound) != 0 {
  1153  		return nil, errors.Errorf("failed to find the following subnet ids: %v", notFound)
  1154  	}
  1155  
  1156  	return results, nil
  1157  }
  1158  
  1159  func getTagByKey(key string, ec2Tags []ec2.Tag) (string, bool) {
  1160  	for _, tag := range ec2Tags {
  1161  		if tag.Key == key {
  1162  			return tag.Value, true
  1163  		}
  1164  	}
  1165  	return "", false
  1166  }
  1167  
  1168  func (e *environ) AllInstancesByState(states ...string) ([]instance.Instance, error) {
  1169  	filter := ec2.NewFilter()
  1170  	filter.Add("instance-state-name", states...)
  1171  	err := e.addGroupFilter(filter)
  1172  	if err != nil {
  1173  		if ec2ErrCode(err) == "InvalidGroup.NotFound" {
  1174  			return nil, nil
  1175  		}
  1176  		return nil, err
  1177  	}
  1178  	resp, err := e.ec2().Instances(nil, filter)
  1179  	if err != nil {
  1180  		return nil, err
  1181  	}
  1182  	eUUID := e.Config().UUID()
  1183  	var insts []instance.Instance
  1184  	for _, r := range resp.Reservations {
  1185  		for i := range r.Instances {
  1186  			inst := r.Instances[i]
  1187  			tagUUID, ok := getTagByKey(tags.JujuModel, inst.Tags)
  1188  			// tagless instances will always be included to avoid
  1189  			// breakage of old environments, if one of these exists it might
  1190  			// hinder the ability to deploy a second environment of the same
  1191  			// name.
  1192  			if ok && tagUUID != eUUID {
  1193  				continue
  1194  			}
  1195  
  1196  			// TODO(wallyworld): lookup the details to fill in the instance type data
  1197  			insts = append(insts, &ec2Instance{e: e, Instance: &inst})
  1198  		}
  1199  	}
  1200  	return insts, nil
  1201  }
  1202  
  1203  func (e *environ) AllInstances() ([]instance.Instance, error) {
  1204  	return e.AllInstancesByState("pending", "running")
  1205  }
  1206  
  1207  func (e *environ) Destroy() error {
  1208  	if err := common.Destroy(e); err != nil {
  1209  		return errors.Trace(err)
  1210  	}
  1211  
  1212  	if err := e.cleanEnvironmentSecurityGroup(); err != nil {
  1213  		logger.Warningf("cannot delete default security group: %v", err)
  1214  	}
  1215  
  1216  	return nil
  1217  }
  1218  
  1219  func portsToIPPerms(ports []network.PortRange) []ec2.IPPerm {
  1220  	ipPerms := make([]ec2.IPPerm, len(ports))
  1221  	for i, p := range ports {
  1222  		ipPerms[i] = ec2.IPPerm{
  1223  			Protocol:  p.Protocol,
  1224  			FromPort:  p.FromPort,
  1225  			ToPort:    p.ToPort,
  1226  			SourceIPs: []string{"0.0.0.0/0"},
  1227  		}
  1228  	}
  1229  	return ipPerms
  1230  }
  1231  
  1232  func (e *environ) openPortsInGroup(name, legacyName string, ports []network.PortRange) error {
  1233  	if len(ports) == 0 {
  1234  		return nil
  1235  	}
  1236  	// Give permissions for anyone to access the given ports.
  1237  	g, err := e.groupByName(name)
  1238  	if ec2ErrCode(err) != "InvalidGroup.NotFound" {
  1239  		// We might be trying to destroy a legacy system
  1240  		g, err = e.groupByName(legacyName)
  1241  	}
  1242  
  1243  	if err != nil {
  1244  		return err
  1245  	}
  1246  	ipPerms := portsToIPPerms(ports)
  1247  	_, err = e.ec2().AuthorizeSecurityGroup(g, ipPerms)
  1248  	if err != nil && ec2ErrCode(err) == "InvalidPermission.Duplicate" {
  1249  		if len(ports) == 1 {
  1250  			return nil
  1251  		}
  1252  		// If there's more than one port and we get a duplicate error,
  1253  		// then we go through authorizing each port individually,
  1254  		// otherwise the ports that were *not* duplicates will have
  1255  		// been ignored
  1256  		for i := range ipPerms {
  1257  			_, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms[i:i+1])
  1258  			if err != nil && ec2ErrCode(err) != "InvalidPermission.Duplicate" {
  1259  				return fmt.Errorf("cannot open port %v: %v", ipPerms[i], err)
  1260  			}
  1261  		}
  1262  		return nil
  1263  	}
  1264  	if err != nil {
  1265  		return fmt.Errorf("cannot open ports: %v", err)
  1266  	}
  1267  	return nil
  1268  }
  1269  
  1270  func (e *environ) closePortsInGroup(name, legacyName string, ports []network.PortRange) error {
  1271  	if len(ports) == 0 {
  1272  		return nil
  1273  	}
  1274  	// Revoke permissions for anyone to access the given ports.
  1275  	// Note that ec2 allows the revocation of permissions that aren't
  1276  	// granted, so this is naturally idempotent.
  1277  	g, err := e.groupByName(name)
  1278  	if ec2ErrCode(err) != "InvalidGroup.NotFound" {
  1279  		// We might be trying to destroy a legacy system
  1280  		g, err = e.groupByName(legacyName)
  1281  	}
  1282  	if err != nil {
  1283  		return err
  1284  	}
  1285  	_, err = e.ec2().RevokeSecurityGroup(g, portsToIPPerms(ports))
  1286  	if err != nil {
  1287  		return fmt.Errorf("cannot close ports: %v", err)
  1288  	}
  1289  	return nil
  1290  }
  1291  
  1292  func (e *environ) portsInGroup(name string) (ports []network.PortRange, err error) {
  1293  	group, err := e.groupInfoByName(name)
  1294  	if err != nil {
  1295  		return nil, err
  1296  	}
  1297  	for _, p := range group.IPPerms {
  1298  		if len(p.SourceIPs) != 1 {
  1299  			logger.Warningf("unexpected IP permission found: %v", p)
  1300  			continue
  1301  		}
  1302  		ports = append(ports, network.PortRange{
  1303  			Protocol: p.Protocol,
  1304  			FromPort: p.FromPort,
  1305  			ToPort:   p.ToPort,
  1306  		})
  1307  	}
  1308  	network.SortPortRanges(ports)
  1309  	return ports, nil
  1310  }
  1311  
  1312  func (e *environ) OpenPorts(ports []network.PortRange) error {
  1313  	if e.Config().FirewallMode() != config.FwGlobal {
  1314  		return fmt.Errorf("invalid firewall mode %q for opening ports on model",
  1315  			e.Config().FirewallMode())
  1316  	}
  1317  	if err := e.openPortsInGroup(e.globalGroupName(), e.legacyGlobalGroupName(), ports); err != nil {
  1318  		return err
  1319  	}
  1320  	logger.Infof("opened ports in global group: %v", ports)
  1321  	return nil
  1322  }
  1323  
  1324  func (e *environ) ClosePorts(ports []network.PortRange) error {
  1325  	if e.Config().FirewallMode() != config.FwGlobal {
  1326  		return fmt.Errorf("invalid firewall mode %q for closing ports on model",
  1327  			e.Config().FirewallMode())
  1328  	}
  1329  	if err := e.closePortsInGroup(e.globalGroupName(), e.legacyGlobalGroupName(), ports); err != nil {
  1330  		return err
  1331  	}
  1332  	logger.Infof("closed ports in global group: %v", ports)
  1333  	return nil
  1334  }
  1335  
  1336  func (e *environ) Ports() ([]network.PortRange, error) {
  1337  	if e.Config().FirewallMode() != config.FwGlobal {
  1338  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from model",
  1339  			e.Config().FirewallMode())
  1340  	}
  1341  	return e.portsInGroup(e.globalGroupName())
  1342  }
  1343  
  1344  func (*environ) Provider() environs.EnvironProvider {
  1345  	return &providerInstance
  1346  }
  1347  
  1348  func (e *environ) instanceSecurityGroups(instIDs []instance.Id, states ...string) ([]ec2.SecurityGroup, error) {
  1349  	ec2inst := e.ec2()
  1350  	strInstID := make([]string, len(instIDs))
  1351  	for i := range instIDs {
  1352  		strInstID[i] = string(instIDs[i])
  1353  	}
  1354  
  1355  	filter := ec2.NewFilter()
  1356  	if len(states) > 0 {
  1357  		filter.Add("instance-state-name", states...)
  1358  	}
  1359  
  1360  	resp, err := ec2inst.Instances(strInstID, filter)
  1361  	if err != nil {
  1362  		return nil, errors.Annotatef(err, "cannot retrieve instance information from aws to delete security groups")
  1363  	}
  1364  
  1365  	securityGroups := []ec2.SecurityGroup{}
  1366  	for _, res := range resp.Reservations {
  1367  		for _, inst := range res.Instances {
  1368  			securityGroups = append(securityGroups, inst.SecurityGroups...)
  1369  		}
  1370  	}
  1371  	return securityGroups, nil
  1372  }
  1373  
  1374  func (e *environ) cleanEnvironmentSecurityGroup() error {
  1375  	ec2inst := e.ec2()
  1376  	var err error
  1377  	jujuGroup := e.jujuGroupName()
  1378  	g, err := e.groupByName(jujuGroup)
  1379  	if ec2ErrCode(err) != "InvalidGroup.NotFound" {
  1380  		// We might be trying to destroy a legacy system
  1381  		g, err = e.groupByName(e.legacyJujuGroupName())
  1382  	}
  1383  	if err != nil {
  1384  		return errors.Annotatef(err, "cannot retrieve default security group: %q", jujuGroup)
  1385  	}
  1386  
  1387  	if err := deleteSecurityGroupInsistently(ec2inst, g); err != nil {
  1388  		return errors.Annotate(err, "cannot delete default security group")
  1389  	}
  1390  	return nil
  1391  }
  1392  
  1393  func (e *environ) terminateInstances(ids []instance.Id) error {
  1394  	if len(ids) == 0 {
  1395  		return nil
  1396  	}
  1397  	ec2inst := e.ec2()
  1398  
  1399  	// TODO (anastasiamac 2016-04-11) Err if instances still have resources hanging around.
  1400  	// LP#1568654
  1401  	defer func() {
  1402  		e.deleteSecurityGroupsForInstances(ids)
  1403  	}()
  1404  
  1405  	// TODO (anastasiamac 2016-04-7) instance termination would benefit
  1406  	// from retry with exponential delay just like security groups
  1407  	// in defer. Bug#1567179.
  1408  	var err error
  1409  	for a := shortAttempt.Start(); a.Next(); {
  1410  		_, err = terminateInstancesById(ec2inst, ids...)
  1411  		if err == nil || ec2ErrCode(err) != "InvalidInstanceID.NotFound" {
  1412  			// This will return either success at terminating all instances (1st condition) or
  1413  			// encountered error as long as it's not NotFound (2nd condition).
  1414  			return err
  1415  		}
  1416  	}
  1417  
  1418  	// We will get here only if we got a NotFound error.
  1419  	// 1. If we attempted to terminate only one instance was, return now.
  1420  	if len(ids) == 1 {
  1421  		ids = nil
  1422  		return nil
  1423  	}
  1424  	// 2. If we attempted to terminate several instances and got a NotFound error,
  1425  	// it means that no instances were terminated.
  1426  	// So try each instance individually, ignoring a NotFound error this time.
  1427  	deletedIDs := []instance.Id{}
  1428  	for _, id := range ids {
  1429  		_, err = terminateInstancesById(ec2inst, id)
  1430  		if err == nil {
  1431  			deletedIDs = append(deletedIDs, id)
  1432  		}
  1433  		if err != nil && ec2ErrCode(err) != "InvalidInstanceID.NotFound" {
  1434  			ids = deletedIDs
  1435  			return err
  1436  		}
  1437  	}
  1438  	// We will get here if all of the instances are deleted successfully,
  1439  	// or are not found, which implies they were previously deleted.
  1440  	ids = deletedIDs
  1441  	return nil
  1442  }
  1443  
  1444  var terminateInstancesById = func(ec2inst *ec2.EC2, ids ...instance.Id) (*ec2.TerminateInstancesResp, error) {
  1445  	strs := make([]string, len(ids))
  1446  	for i, id := range ids {
  1447  		strs[i] = string(id)
  1448  	}
  1449  	return ec2inst.TerminateInstances(strs)
  1450  }
  1451  
  1452  func (e *environ) deleteSecurityGroupsForInstances(ids []instance.Id) {
  1453  	if len(ids) == 0 {
  1454  		logger.Debugf("no need to delete security groups: no intances were terminated successfully")
  1455  		return
  1456  	}
  1457  	// We only want to attempt deleting security groups for the
  1458  	// instances that have been successfully terminated.
  1459  	securityGroups, err := e.instanceSecurityGroups(ids, "shutting-down", "terminated")
  1460  	if err != nil {
  1461  		logger.Warningf("cannot determine security groups to delete: %v", err)
  1462  	}
  1463  	// TODO(perrito666) we need to tag global security groups to be able
  1464  	// to tell them apart from future groups that are neither machine
  1465  	// nor environment group.
  1466  	// https://bugs.launchpad.net/juju-core/+bug/1534289
  1467  	jujuGroup := e.jujuGroupName()
  1468  	legacyJujuGroup := e.legacyJujuGroupName()
  1469  
  1470  	ec2inst := e.ec2()
  1471  	for _, deletable := range securityGroups {
  1472  		if deletable.Name != jujuGroup && deletable.Name != legacyJujuGroup {
  1473  			if err := deleteSecurityGroupInsistently(ec2inst, deletable); err != nil {
  1474  				// In ideal world, we would err out here.
  1475  				// However:
  1476  				// 1. We do not know if all instances have been terminated.
  1477  				// If some instances erred out, they may still be using this security group.
  1478  				// In this case, our failure to delete security group is reasonable: it's still in use.
  1479  				// 2. Some security groups may be shared by multiple instances,
  1480  				// for example, global firewalling. We should not delete these.
  1481  				logger.Warningf("provider failure: %v", err)
  1482  			}
  1483  		}
  1484  	}
  1485  }
  1486  
  1487  // SecurityGroupCleaner defines provider instance methods needed to delete
  1488  // a security group.
  1489  type SecurityGroupCleaner interface {
  1490  
  1491  	// DeleteSecurityGroup deletes security group on the provider.
  1492  	DeleteSecurityGroup(group ec2.SecurityGroup) (resp *ec2.SimpleResp, err error)
  1493  }
  1494  
  1495  var deleteSecurityGroupInsistently = func(inst SecurityGroupCleaner, group ec2.SecurityGroup) error {
  1496  	var lastErr error
  1497  	err := retry.Call(retry.CallArgs{
  1498  		Attempts:    30,
  1499  		Delay:       time.Second,
  1500  		MaxDelay:    time.Minute, // because 2**29 seconds is beyond reasonable
  1501  		BackoffFunc: retry.DoubleDelay,
  1502  		Clock:       clock.WallClock,
  1503  		Func: func() error {
  1504  			_, deleteErr := inst.DeleteSecurityGroup(group)
  1505  			if ec2ErrCode(deleteErr) != "InvalidGroup.NotFound" {
  1506  				return errors.Trace(deleteErr)
  1507  			}
  1508  			return nil
  1509  		},
  1510  		NotifyFunc: func(err error, attempt int) {
  1511  			lastErr = err
  1512  			logger.Infof(fmt.Sprintf("deleting security group %q, attempt %d", group.Name, attempt))
  1513  		},
  1514  	})
  1515  	if err != nil {
  1516  		logger.Warningf("cannot delete security group %q: consider deleting it manually", group.Name)
  1517  		return lastErr
  1518  	}
  1519  	return nil
  1520  }
  1521  
  1522  func (e *environ) uuid() string {
  1523  	return e.Config().UUID()
  1524  }
  1525  
  1526  func (e *environ) globalGroupName() string {
  1527  	return fmt.Sprintf("%s-global", e.jujuGroupName())
  1528  }
  1529  
  1530  func (e *environ) machineGroupName(machineId string) string {
  1531  	return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId)
  1532  }
  1533  
  1534  func (e *environ) jujuGroupName() string {
  1535  	return "juju-" + e.uuid()
  1536  }
  1537  
  1538  // Legacy naming for groups, before multi environments with the same
  1539  // name where supported.
  1540  
  1541  func (e *environ) legacyGlobalGroupName() string {
  1542  	return fmt.Sprintf("%s-global", e.legacyJujuGroupName())
  1543  }
  1544  
  1545  func (e *environ) legacyMachineGroupName(machineId string) string {
  1546  	return fmt.Sprintf("%s-%s", e.legacyJujuGroupName(), machineId)
  1547  }
  1548  
  1549  func (e *environ) legacyJujuGroupName() string {
  1550  	return "juju-" + e.uuid()
  1551  }
  1552  
  1553  // setUpGroups creates the security groups for the new machine, and
  1554  // returns them.
  1555  //
  1556  // Instances are tagged with a group so they can be distinguished from
  1557  // other instances that might be running on the same EC2 account.  In
  1558  // addition, a specific machine security group is created for each
  1559  // machine, so that its firewall rules can be configured per machine.
  1560  func (e *environ) setUpGroups(machineId string, apiPort int) ([]ec2.SecurityGroup, error) {
  1561  	jujuGroup, err := e.ensureGroup(e.jujuGroupName(),
  1562  		[]ec2.IPPerm{
  1563  			{
  1564  				Protocol:  "tcp",
  1565  				FromPort:  22,
  1566  				ToPort:    22,
  1567  				SourceIPs: []string{"0.0.0.0/0"},
  1568  			},
  1569  			{
  1570  				Protocol:  "tcp",
  1571  				FromPort:  apiPort,
  1572  				ToPort:    apiPort,
  1573  				SourceIPs: []string{"0.0.0.0/0"},
  1574  			},
  1575  			{
  1576  				Protocol: "tcp",
  1577  				FromPort: 0,
  1578  				ToPort:   65535,
  1579  			},
  1580  			{
  1581  				Protocol: "udp",
  1582  				FromPort: 0,
  1583  				ToPort:   65535,
  1584  			},
  1585  			{
  1586  				Protocol: "icmp",
  1587  				FromPort: -1,
  1588  				ToPort:   -1,
  1589  			},
  1590  		})
  1591  	if err != nil {
  1592  		return nil, err
  1593  	}
  1594  	var machineGroup ec2.SecurityGroup
  1595  	switch e.Config().FirewallMode() {
  1596  	case config.FwInstance:
  1597  		machineGroup, err = e.ensureGroup(e.machineGroupName(machineId), nil)
  1598  	case config.FwGlobal:
  1599  		machineGroup, err = e.ensureGroup(e.globalGroupName(), nil)
  1600  	}
  1601  	if err != nil {
  1602  		return nil, err
  1603  	}
  1604  	return []ec2.SecurityGroup{jujuGroup, machineGroup}, nil
  1605  }
  1606  
  1607  // zeroGroup holds the zero security group.
  1608  var zeroGroup ec2.SecurityGroup
  1609  
  1610  // ensureGroup returns the security group with name and perms.
  1611  // If a group with name does not exist, one will be created.
  1612  // If it exists, its permissions are set to perms.
  1613  // Any entries in perms without SourceIPs will be granted for
  1614  // the named group only.
  1615  func (e *environ) ensureGroup(name string, perms []ec2.IPPerm) (g ec2.SecurityGroup, err error) {
  1616  	ec2inst := e.ec2()
  1617  	resp, err := ec2inst.CreateSecurityGroup("", name, "juju group")
  1618  	if err != nil && ec2ErrCode(err) != "InvalidGroup.Duplicate" {
  1619  		return zeroGroup, err
  1620  	}
  1621  
  1622  	var have permSet
  1623  	if err == nil {
  1624  		g = resp.SecurityGroup
  1625  	} else {
  1626  		resp, err := ec2inst.SecurityGroups(ec2.SecurityGroupNames(name), nil)
  1627  		if err != nil {
  1628  			return zeroGroup, err
  1629  		}
  1630  		info := resp.Groups[0]
  1631  		// It's possible that the old group has the wrong
  1632  		// description here, but if it does it's probably due
  1633  		// to something deliberately playing games with juju,
  1634  		// so we ignore it.
  1635  		g = info.SecurityGroup
  1636  		have = newPermSetForGroup(info.IPPerms, g)
  1637  	}
  1638  	want := newPermSetForGroup(perms, g)
  1639  	revoke := make(permSet)
  1640  	for p := range have {
  1641  		if !want[p] {
  1642  			revoke[p] = true
  1643  		}
  1644  	}
  1645  	if len(revoke) > 0 {
  1646  		_, err := ec2inst.RevokeSecurityGroup(g, revoke.ipPerms())
  1647  		if err != nil {
  1648  			return zeroGroup, fmt.Errorf("cannot revoke security group: %v", err)
  1649  		}
  1650  	}
  1651  
  1652  	add := make(permSet)
  1653  	for p := range want {
  1654  		if !have[p] {
  1655  			add[p] = true
  1656  		}
  1657  	}
  1658  	if len(add) > 0 {
  1659  		_, err := ec2inst.AuthorizeSecurityGroup(g, add.ipPerms())
  1660  		if err != nil {
  1661  			return zeroGroup, fmt.Errorf("cannot authorize securityGroup: %v", err)
  1662  		}
  1663  	}
  1664  	return g, nil
  1665  }
  1666  
  1667  // permKey represents a permission for a group or an ip address range
  1668  // to access the given range of ports. Only one of groupName or ipAddr
  1669  // should be non-empty.
  1670  type permKey struct {
  1671  	protocol string
  1672  	fromPort int
  1673  	toPort   int
  1674  	groupId  string
  1675  	ipAddr   string
  1676  }
  1677  
  1678  type permSet map[permKey]bool
  1679  
  1680  // newPermSetForGroup returns a set of all the permissions in the
  1681  // given slice of IPPerms. It ignores the name and owner
  1682  // id in source groups, and any entry with no source ips will
  1683  // be granted for the given group only.
  1684  func newPermSetForGroup(ps []ec2.IPPerm, group ec2.SecurityGroup) permSet {
  1685  	m := make(permSet)
  1686  	for _, p := range ps {
  1687  		k := permKey{
  1688  			protocol: p.Protocol,
  1689  			fromPort: p.FromPort,
  1690  			toPort:   p.ToPort,
  1691  		}
  1692  		if len(p.SourceIPs) > 0 {
  1693  			for _, ip := range p.SourceIPs {
  1694  				k.ipAddr = ip
  1695  				m[k] = true
  1696  			}
  1697  		} else {
  1698  			k.groupId = group.Id
  1699  			m[k] = true
  1700  		}
  1701  	}
  1702  	return m
  1703  }
  1704  
  1705  // ipPerms returns m as a slice of permissions usable
  1706  // with the ec2 package.
  1707  func (m permSet) ipPerms() (ps []ec2.IPPerm) {
  1708  	// We could compact the permissions, but it
  1709  	// hardly seems worth it.
  1710  	for p := range m {
  1711  		ipp := ec2.IPPerm{
  1712  			Protocol: p.protocol,
  1713  			FromPort: p.fromPort,
  1714  			ToPort:   p.toPort,
  1715  		}
  1716  		if p.ipAddr != "" {
  1717  			ipp.SourceIPs = []string{p.ipAddr}
  1718  		} else {
  1719  			ipp.SourceGroups = []ec2.UserSecurityGroup{{Id: p.groupId}}
  1720  		}
  1721  		ps = append(ps, ipp)
  1722  	}
  1723  	return
  1724  }
  1725  
  1726  // isZoneConstrainedError reports whether or not the error indicates
  1727  // RunInstances failed due to the specified availability zone being
  1728  // constrained for the instance type being provisioned, or is
  1729  // otherwise unusable for the specific request made.
  1730  func isZoneConstrainedError(err error) bool {
  1731  	switch err := err.(type) {
  1732  	case *ec2.Error:
  1733  		switch err.Code {
  1734  		case "Unsupported", "InsufficientInstanceCapacity":
  1735  			// A big hammer, but we've now seen several different error messages
  1736  			// for constrained zones, and who knows how many more there might
  1737  			// be. If the message contains "Availability Zone", it's a fair
  1738  			// bet that it's constrained or otherwise unusable.
  1739  			return strings.Contains(err.Message, "Availability Zone")
  1740  		case "InvalidInput":
  1741  			// If the region has a default VPC, then we will receive an error
  1742  			// if the AZ does not have a default subnet. Until we have proper
  1743  			// support for networks, we'll skip over these.
  1744  			return strings.HasPrefix(err.Message, "No default subnet for availability zone")
  1745  		case "VolumeTypeNotAvailableInZone":
  1746  			return true
  1747  		}
  1748  	}
  1749  	return false
  1750  }
  1751  
  1752  // isSubnetConstrainedError reports whether or not the error indicates
  1753  // RunInstances failed due to the specified VPC subnet ID being constrained for
  1754  // the instance type being provisioned, or is otherwise unusable for the
  1755  // specific request made.
  1756  func isSubnetConstrainedError(err error) bool {
  1757  	switch err := err.(type) {
  1758  	case *ec2.Error:
  1759  		switch err.Code {
  1760  		case "InsufficientFreeAddressesInSubnet", "InsufficientInstanceCapacity":
  1761  			// Subnet and/or VPC general limits reached.
  1762  			return true
  1763  		case "InvalidSubnetID.NotFound":
  1764  			// This shouldn't happen, as we validate the subnet IDs, but it can
  1765  			// happen if the user manually deleted the subnet outside of Juju.
  1766  			return true
  1767  		}
  1768  	}
  1769  	return false
  1770  }
  1771  
  1772  // If the err is of type *ec2.Error, ec2ErrCode returns
  1773  // its code, otherwise it returns the empty string.
  1774  func ec2ErrCode(err error) string {
  1775  	ec2err, _ := errors.Cause(err).(*ec2.Error)
  1776  	if ec2err == nil {
  1777  		return ""
  1778  	}
  1779  	return ec2err.Code
  1780  }
  1781  
  1782  func (e *environ) AllocateContainerAddresses(hostInstanceID instance.Id, preparedInfo []network.InterfaceInfo) ([]network.InterfaceInfo, error) {
  1783  	return nil, errors.NotSupportedf("container address allocation")
  1784  }