github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/utils"
    16  	"gopkg.in/amz.v3/aws"
    17  	"gopkg.in/amz.v3/ec2"
    18  	"gopkg.in/amz.v3/s3"
    19  
    20  	"github.com/juju/juju/cloudconfig/instancecfg"
    21  	"github.com/juju/juju/cloudconfig/providerinit"
    22  	"github.com/juju/juju/constraints"
    23  	"github.com/juju/juju/environs"
    24  	"github.com/juju/juju/environs/config"
    25  	"github.com/juju/juju/environs/imagemetadata"
    26  	"github.com/juju/juju/environs/instances"
    27  	"github.com/juju/juju/environs/simplestreams"
    28  	envstorage "github.com/juju/juju/environs/storage"
    29  	"github.com/juju/juju/instance"
    30  	"github.com/juju/juju/juju/arch"
    31  	"github.com/juju/juju/network"
    32  	"github.com/juju/juju/provider/common"
    33  	"github.com/juju/juju/state"
    34  	"github.com/juju/juju/state/multiwatcher"
    35  	"github.com/juju/juju/tools"
    36  )
    37  
    38  const (
    39  	none                        = "none"
    40  	invalidParameterValue       = "InvalidParameterValue"
    41  	privateAddressLimitExceeded = "PrivateIpAddressLimitExceeded"
    42  
    43  	// tagName is the AWS-specific tag key that populates resources'
    44  	// name columns in the console.
    45  	tagName = "Name"
    46  )
    47  
    48  // Use shortAttempt to poll for short-term events or for retrying API calls.
    49  var shortAttempt = utils.AttemptStrategy{
    50  	Total: 5 * time.Second,
    51  	Delay: 200 * time.Millisecond,
    52  }
    53  
    54  var AssignPrivateIPAddress = assignPrivateIPAddress
    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  	storageUnlocked envstorage.Storage
    73  
    74  	availabilityZonesMutex sync.Mutex
    75  	availabilityZones      []common.AvailabilityZone
    76  
    77  	// cachedDefaultVpc caches the id of the ec2 default vpc
    78  	cachedDefaultVpc *defaultVpc
    79  }
    80  
    81  // Ensure EC2 provider supports environs.NetworkingEnviron.
    82  var _ environs.NetworkingEnviron = (*environ)(nil)
    83  var _ simplestreams.HasRegion = (*environ)(nil)
    84  var _ state.Prechecker = (*environ)(nil)
    85  var _ state.InstanceDistributor = (*environ)(nil)
    86  
    87  type defaultVpc struct {
    88  	hasDefaultVpc bool
    89  	id            network.Id
    90  }
    91  
    92  func assignPrivateIPAddress(ec2Inst *ec2.EC2, netId string, addr network.Address) error {
    93  	_, err := ec2Inst.AssignPrivateIPAddresses(netId, []string{addr.Value}, 0, false)
    94  	return err
    95  }
    96  
    97  func (e *environ) Config() *config.Config {
    98  	return e.ecfg().Config
    99  }
   100  
   101  func awsClients(cfg *config.Config) (*ec2.EC2, *s3.S3, *environConfig, error) {
   102  	ecfg, err := providerInstance.newConfig(cfg)
   103  	if err != nil {
   104  		return nil, nil, nil, err
   105  	}
   106  
   107  	auth := aws.Auth{ecfg.accessKey(), ecfg.secretKey()}
   108  	region := aws.Regions[ecfg.region()]
   109  	signer := aws.SignV4Factory(region.Name, "ec2")
   110  	return ec2.New(auth, region, signer), s3.New(auth, region), ecfg, nil
   111  }
   112  
   113  func (e *environ) SetConfig(cfg *config.Config) error {
   114  	ec2Client, s3Client, ecfg, err := awsClients(cfg)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	e.ecfgMutex.Lock()
   119  	defer e.ecfgMutex.Unlock()
   120  	e.ecfgUnlocked = ecfg
   121  	e.ec2Unlocked = ec2Client
   122  	e.s3Unlocked = s3Client
   123  
   124  	bucket, err := e.s3Unlocked.Bucket(ecfg.controlBucket())
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	// create new storage instances, existing instances continue
   130  	// to reference their existing configuration.
   131  	e.storageUnlocked = &ec2storage{bucket: bucket}
   132  	return nil
   133  }
   134  
   135  func (e *environ) defaultVpc() (network.Id, bool, error) {
   136  	if e.cachedDefaultVpc != nil {
   137  		defaultVpc := e.cachedDefaultVpc
   138  		return defaultVpc.id, defaultVpc.hasDefaultVpc, nil
   139  	}
   140  	ec2 := e.ec2()
   141  	resp, err := ec2.AccountAttributes("default-vpc")
   142  	if err != nil {
   143  		return "", false, errors.Trace(err)
   144  	}
   145  
   146  	hasDefault := true
   147  	defaultVpcId := ""
   148  
   149  	if len(resp.Attributes) == 0 || len(resp.Attributes[0].Values) == 0 {
   150  		hasDefault = false
   151  		defaultVpcId = ""
   152  	} else {
   153  		defaultVpcId = resp.Attributes[0].Values[0]
   154  		if defaultVpcId == none {
   155  			hasDefault = false
   156  			defaultVpcId = ""
   157  		}
   158  	}
   159  	defaultVpc := &defaultVpc{
   160  		id:            network.Id(defaultVpcId),
   161  		hasDefaultVpc: hasDefault,
   162  	}
   163  	e.cachedDefaultVpc = defaultVpc
   164  	return defaultVpc.id, defaultVpc.hasDefaultVpc, nil
   165  }
   166  
   167  func (e *environ) ecfg() *environConfig {
   168  	e.ecfgMutex.Lock()
   169  	ecfg := e.ecfgUnlocked
   170  	e.ecfgMutex.Unlock()
   171  	return ecfg
   172  }
   173  
   174  func (e *environ) ec2() *ec2.EC2 {
   175  	e.ecfgMutex.Lock()
   176  	ec2 := e.ec2Unlocked
   177  	e.ecfgMutex.Unlock()
   178  	return ec2
   179  }
   180  
   181  func (e *environ) s3() *s3.S3 {
   182  	e.ecfgMutex.Lock()
   183  	s3 := e.s3Unlocked
   184  	e.ecfgMutex.Unlock()
   185  	return s3
   186  }
   187  
   188  func (e *environ) Name() string {
   189  	return e.name
   190  }
   191  
   192  func (e *environ) Storage() envstorage.Storage {
   193  	e.ecfgMutex.Lock()
   194  	stor := e.storageUnlocked
   195  	e.ecfgMutex.Unlock()
   196  	return stor
   197  }
   198  
   199  func (e *environ) Bootstrap(ctx environs.BootstrapContext, args environs.BootstrapParams) (arch, series string, _ environs.BootstrapFinalizer, _ error) {
   200  	return common.Bootstrap(ctx, e, args)
   201  }
   202  
   203  func (e *environ) StateServerInstances() ([]instance.Id, error) {
   204  	return common.ProviderStateInstances(e, e.Storage())
   205  }
   206  
   207  // SupportedArchitectures is specified on the EnvironCapability interface.
   208  func (e *environ) SupportedArchitectures() ([]string, error) {
   209  	e.archMutex.Lock()
   210  	defer e.archMutex.Unlock()
   211  	if e.supportedArchitectures != nil {
   212  		return e.supportedArchitectures, nil
   213  	}
   214  	// Create a filter to get all images from our region and for the correct stream.
   215  	cloudSpec, err := e.Region()
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  	imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{
   220  		CloudSpec: cloudSpec,
   221  		Stream:    e.Config().ImageStream(),
   222  	})
   223  	e.supportedArchitectures, err = common.SupportedArchitectures(e, imageConstraint)
   224  	return e.supportedArchitectures, err
   225  }
   226  
   227  // SupportsAddressAllocation is specified on environs.Networking.
   228  func (e *environ) SupportsAddressAllocation(_ network.Id) (bool, error) {
   229  	if !environs.AddressAllocationEnabled() {
   230  		return false, errors.NotSupportedf("address allocation")
   231  	}
   232  	_, hasDefaultVpc, err := e.defaultVpc()
   233  	if err != nil {
   234  		return false, errors.Trace(err)
   235  	}
   236  	return hasDefaultVpc, nil
   237  }
   238  
   239  var unsupportedConstraints = []string{
   240  	constraints.Tags,
   241  }
   242  
   243  // ConstraintsValidator is defined on the Environs interface.
   244  func (e *environ) ConstraintsValidator() (constraints.Validator, error) {
   245  	validator := constraints.NewValidator()
   246  	validator.RegisterConflicts(
   247  		[]string{constraints.InstanceType},
   248  		[]string{constraints.Mem, constraints.CpuCores, constraints.CpuPower})
   249  	validator.RegisterUnsupported(unsupportedConstraints)
   250  	supportedArches, err := e.SupportedArchitectures()
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	validator.RegisterVocabulary(constraints.Arch, supportedArches)
   255  	instTypeNames := make([]string, len(allInstanceTypes))
   256  	for i, itype := range allInstanceTypes {
   257  		instTypeNames[i] = itype.Name
   258  	}
   259  	validator.RegisterVocabulary(constraints.InstanceType, instTypeNames)
   260  	return validator, nil
   261  }
   262  
   263  func archMatches(arches []string, arch *string) bool {
   264  	if arch == nil {
   265  		return true
   266  	}
   267  	for _, a := range arches {
   268  		if a == *arch {
   269  			return true
   270  		}
   271  	}
   272  	return false
   273  }
   274  
   275  var ec2AvailabilityZones = (*ec2.EC2).AvailabilityZones
   276  
   277  type ec2AvailabilityZone struct {
   278  	ec2.AvailabilityZoneInfo
   279  }
   280  
   281  func (z *ec2AvailabilityZone) Name() string {
   282  	return z.AvailabilityZoneInfo.Name
   283  }
   284  
   285  func (z *ec2AvailabilityZone) Available() bool {
   286  	return z.AvailabilityZoneInfo.State == "available"
   287  }
   288  
   289  // AvailabilityZones returns a slice of availability zones
   290  // for the configured region.
   291  func (e *environ) AvailabilityZones() ([]common.AvailabilityZone, error) {
   292  	e.availabilityZonesMutex.Lock()
   293  	defer e.availabilityZonesMutex.Unlock()
   294  	if e.availabilityZones == nil {
   295  		filter := ec2.NewFilter()
   296  		filter.Add("region-name", e.ecfg().region())
   297  		resp, err := ec2AvailabilityZones(e.ec2(), filter)
   298  		if err != nil {
   299  			return nil, err
   300  		}
   301  		logger.Debugf("availability zones: %+v", resp)
   302  		e.availabilityZones = make([]common.AvailabilityZone, len(resp.Zones))
   303  		for i, z := range resp.Zones {
   304  			e.availabilityZones[i] = &ec2AvailabilityZone{z}
   305  		}
   306  	}
   307  	return e.availabilityZones, nil
   308  }
   309  
   310  // InstanceAvailabilityZoneNames returns the availability zone names for each
   311  // of the specified instances.
   312  func (e *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) {
   313  	instances, err := e.Instances(ids)
   314  	if err != nil && err != environs.ErrPartialInstances {
   315  		return nil, err
   316  	}
   317  	zones := make([]string, len(instances))
   318  	for i, inst := range instances {
   319  		if inst == nil {
   320  			continue
   321  		}
   322  		zones[i] = inst.(*ec2Instance).AvailZone
   323  	}
   324  	return zones, err
   325  }
   326  
   327  type ec2Placement struct {
   328  	availabilityZone ec2.AvailabilityZoneInfo
   329  }
   330  
   331  func (e *environ) parsePlacement(placement string) (*ec2Placement, error) {
   332  	pos := strings.IndexRune(placement, '=')
   333  	if pos == -1 {
   334  		return nil, fmt.Errorf("unknown placement directive: %v", placement)
   335  	}
   336  	switch key, value := placement[:pos], placement[pos+1:]; key {
   337  	case "zone":
   338  		availabilityZone := value
   339  		zones, err := e.AvailabilityZones()
   340  		if err != nil {
   341  			return nil, err
   342  		}
   343  		for _, z := range zones {
   344  			if z.Name() == availabilityZone {
   345  				return &ec2Placement{
   346  					z.(*ec2AvailabilityZone).AvailabilityZoneInfo,
   347  				}, nil
   348  			}
   349  		}
   350  		return nil, fmt.Errorf("invalid availability zone %q", availabilityZone)
   351  	}
   352  	return nil, fmt.Errorf("unknown placement directive: %v", placement)
   353  }
   354  
   355  // PrecheckInstance is defined on the state.Prechecker interface.
   356  func (e *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
   357  	if placement != "" {
   358  		if _, err := e.parsePlacement(placement); err != nil {
   359  			return err
   360  		}
   361  	}
   362  	if !cons.HasInstanceType() {
   363  		return nil
   364  	}
   365  	// Constraint has an instance-type constraint so let's see if it is valid.
   366  	for _, itype := range allInstanceTypes {
   367  		if itype.Name != *cons.InstanceType {
   368  			continue
   369  		}
   370  		if archMatches(itype.Arches, cons.Arch) {
   371  			return nil
   372  		}
   373  	}
   374  	if cons.Arch == nil {
   375  		return fmt.Errorf("invalid AWS instance type %q specified", *cons.InstanceType)
   376  	}
   377  	return fmt.Errorf("invalid AWS instance type %q and arch %q specified", *cons.InstanceType, *cons.Arch)
   378  }
   379  
   380  // MetadataLookupParams returns parameters which are used to query simplestreams metadata.
   381  func (e *environ) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) {
   382  	if region == "" {
   383  		region = e.ecfg().region()
   384  	}
   385  	cloudSpec, err := e.cloudSpec(region)
   386  	if err != nil {
   387  		return nil, err
   388  	}
   389  	return &simplestreams.MetadataLookupParams{
   390  		Series:        config.PreferredSeries(e.ecfg()),
   391  		Region:        cloudSpec.Region,
   392  		Endpoint:      cloudSpec.Endpoint,
   393  		Architectures: arch.AllSupportedArches,
   394  	}, nil
   395  }
   396  
   397  // Region is specified in the HasRegion interface.
   398  func (e *environ) Region() (simplestreams.CloudSpec, error) {
   399  	return e.cloudSpec(e.ecfg().region())
   400  }
   401  
   402  func (e *environ) cloudSpec(region string) (simplestreams.CloudSpec, error) {
   403  	ec2Region, ok := allRegions[region]
   404  	if !ok {
   405  		return simplestreams.CloudSpec{}, fmt.Errorf("unknown region %q", region)
   406  	}
   407  	return simplestreams.CloudSpec{
   408  		Region:   region,
   409  		Endpoint: ec2Region.EC2Endpoint,
   410  	}, nil
   411  }
   412  
   413  const (
   414  	ebsStorage = "ebs"
   415  	ssdStorage = "ssd"
   416  )
   417  
   418  // DistributeInstances implements the state.InstanceDistributor policy.
   419  func (e *environ) DistributeInstances(candidates, distributionGroup []instance.Id) ([]instance.Id, error) {
   420  	return common.DistributeInstances(e, candidates, distributionGroup)
   421  }
   422  
   423  var availabilityZoneAllocations = common.AvailabilityZoneAllocations
   424  
   425  // MaintainInstance is specified in the InstanceBroker interface.
   426  func (*environ) MaintainInstance(args environs.StartInstanceParams) error {
   427  	return nil
   428  }
   429  
   430  // resourceName returns the string to use for a resource's Name tag,
   431  // to help users identify Juju-managed resources in the AWS console.
   432  func resourceName(tag names.Tag, envName string) string {
   433  	return fmt.Sprintf("juju-%s-%s", envName, tag)
   434  }
   435  
   436  // StartInstance is specified in the InstanceBroker interface.
   437  func (e *environ) StartInstance(args environs.StartInstanceParams) (_ *environs.StartInstanceResult, resultErr error) {
   438  	var inst *ec2Instance
   439  	defer func() {
   440  		if resultErr == nil || inst == nil {
   441  			return
   442  		}
   443  		if err := e.StopInstances(inst.Id()); err != nil {
   444  			logger.Errorf("error stopping failed instance: %v", err)
   445  		}
   446  	}()
   447  
   448  	var availabilityZones []string
   449  	if args.Placement != "" {
   450  		placement, err := e.parsePlacement(args.Placement)
   451  		if err != nil {
   452  			return nil, err
   453  		}
   454  		if placement.availabilityZone.State != "available" {
   455  			return nil, errors.Errorf("availability zone %q is %s", placement.availabilityZone.Name, placement.availabilityZone.State)
   456  		}
   457  		availabilityZones = append(availabilityZones, placement.availabilityZone.Name)
   458  	}
   459  
   460  	// If no availability zone is specified, then automatically spread across
   461  	// the known zones for optimal spread across the instance distribution
   462  	// group.
   463  	if len(availabilityZones) == 0 {
   464  		var group []instance.Id
   465  		var err error
   466  		if args.DistributionGroup != nil {
   467  			group, err = args.DistributionGroup()
   468  			if err != nil {
   469  				return nil, err
   470  			}
   471  		}
   472  		zoneInstances, err := availabilityZoneAllocations(e, group)
   473  		if err != nil {
   474  			return nil, err
   475  		}
   476  		for _, z := range zoneInstances {
   477  			availabilityZones = append(availabilityZones, z.ZoneName)
   478  		}
   479  		if len(availabilityZones) == 0 {
   480  			return nil, errors.New("failed to determine availability zones")
   481  		}
   482  	}
   483  
   484  	if args.InstanceConfig.HasNetworks() {
   485  		return nil, errors.New("starting instances with networks is not supported yet")
   486  	}
   487  	arches := args.Tools.Arches()
   488  	sources, err := environs.ImageMetadataSources(e)
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  
   493  	series := args.Tools.OneSeries()
   494  	spec, err := findInstanceSpec(sources, e.Config().ImageStream(), &instances.InstanceConstraint{
   495  		Region:      e.ecfg().region(),
   496  		Series:      series,
   497  		Arches:      arches,
   498  		Constraints: args.Constraints,
   499  		Storage:     []string{ssdStorage, ebsStorage},
   500  	})
   501  	if err != nil {
   502  		return nil, err
   503  	}
   504  	tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch})
   505  	if err != nil {
   506  		return nil, errors.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches)
   507  	}
   508  
   509  	args.InstanceConfig.Tools = tools[0]
   510  	if err := instancecfg.FinishInstanceConfig(args.InstanceConfig, e.Config()); err != nil {
   511  		return nil, err
   512  	}
   513  
   514  	userData, err := providerinit.ComposeUserData(args.InstanceConfig, nil)
   515  	if err != nil {
   516  		return nil, errors.Annotate(err, "cannot make user data")
   517  	}
   518  	logger.Debugf("ec2 user data; %d bytes", len(userData))
   519  	cfg := e.Config()
   520  	groups, err := e.setUpGroups(args.InstanceConfig.MachineId, cfg.APIPort())
   521  	if err != nil {
   522  		return nil, errors.Annotate(err, "cannot set up groups")
   523  	}
   524  	var instResp *ec2.RunInstancesResp
   525  
   526  	blockDeviceMappings, err := getBlockDeviceMappings(args.Constraints)
   527  	if err != nil {
   528  		return nil, errors.Annotate(err, "cannot create block device mappings")
   529  	}
   530  	rootDiskSize := uint64(blockDeviceMappings[0].VolumeSize) * 1024
   531  
   532  	for _, availZone := range availabilityZones {
   533  		instResp, err = runInstances(e.ec2(), &ec2.RunInstances{
   534  			AvailZone:           availZone,
   535  			ImageId:             spec.Image.Id,
   536  			MinCount:            1,
   537  			MaxCount:            1,
   538  			UserData:            userData,
   539  			InstanceType:        spec.InstanceType.Name,
   540  			SecurityGroups:      groups,
   541  			BlockDeviceMappings: blockDeviceMappings,
   542  		})
   543  		if isZoneConstrainedError(err) {
   544  			logger.Infof("%q is constrained, trying another availability zone", availZone)
   545  		} else {
   546  			break
   547  		}
   548  	}
   549  	if err != nil {
   550  		return nil, errors.Annotate(err, "cannot run instances")
   551  	}
   552  	if len(instResp.Instances) != 1 {
   553  		return nil, errors.Errorf("expected 1 started instance, got %d", len(instResp.Instances))
   554  	}
   555  
   556  	inst = &ec2Instance{
   557  		e:        e,
   558  		Instance: &instResp.Instances[0],
   559  	}
   560  	logger.Infof("started instance %q in %q", inst.Id(), inst.Instance.AvailZone)
   561  
   562  	// Tag instance, for accounting and identification.
   563  	args.InstanceConfig.Tags[tagName] = resourceName(
   564  		names.NewMachineTag(args.InstanceConfig.MachineId), e.Config().Name(),
   565  	)
   566  	if err := tagResources(e.ec2(), args.InstanceConfig.Tags, string(inst.Id())); err != nil {
   567  		return nil, errors.Annotate(err, "tagging instance")
   568  	}
   569  
   570  	if multiwatcher.AnyJobNeedsState(args.InstanceConfig.Jobs...) {
   571  		if err := common.AddStateInstance(e.Storage(), inst.Id()); err != nil {
   572  			return nil, errors.Annotate(err, "recording instance in provider-state")
   573  		}
   574  	}
   575  
   576  	hc := instance.HardwareCharacteristics{
   577  		Arch:     &spec.Image.Arch,
   578  		Mem:      &spec.InstanceType.Mem,
   579  		CpuCores: &spec.InstanceType.CpuCores,
   580  		CpuPower: spec.InstanceType.CpuPower,
   581  		RootDisk: &rootDiskSize,
   582  		// Tags currently not supported by EC2
   583  		AvailabilityZone: &inst.Instance.AvailZone,
   584  	}
   585  	return &environs.StartInstanceResult{
   586  		Instance: inst,
   587  		Hardware: &hc,
   588  	}, nil
   589  }
   590  
   591  // tagResources calls ec2.CreateTags, tagging each of the specified resources
   592  // with the given tags.
   593  func tagResources(e *ec2.EC2, tags map[string]string, resourceIds ...string) error {
   594  	if len(tags) == 0 {
   595  		return nil
   596  	}
   597  	ec2Tags := make([]ec2.Tag, 0, len(tags))
   598  	for k, v := range tags {
   599  		ec2Tags = append(ec2Tags, ec2.Tag{k, v})
   600  	}
   601  	_, err := e.CreateTags(resourceIds, ec2Tags)
   602  	return err
   603  }
   604  
   605  var runInstances = _runInstances
   606  
   607  // runInstances calls ec2.RunInstances for a fixed number of attempts until
   608  // RunInstances returns an error code that does not indicate an error that
   609  // may be caused by eventual consistency.
   610  func _runInstances(e *ec2.EC2, ri *ec2.RunInstances) (resp *ec2.RunInstancesResp, err error) {
   611  	for a := shortAttempt.Start(); a.Next(); {
   612  		resp, err = e.RunInstances(ri)
   613  		if err == nil || ec2ErrCode(err) != "InvalidGroup.NotFound" {
   614  			break
   615  		}
   616  	}
   617  	return resp, err
   618  }
   619  
   620  func (e *environ) StopInstances(ids ...instance.Id) error {
   621  	if err := e.terminateInstances(ids); err != nil {
   622  		return errors.Trace(err)
   623  	}
   624  	return common.RemoveStateInstances(e.Storage(), ids...)
   625  }
   626  
   627  // groupInfoByName returns information on the security group
   628  // with the given name including rules and other details.
   629  func (e *environ) groupInfoByName(groupName string) (ec2.SecurityGroupInfo, error) {
   630  	// Non-default VPC does not support name-based group lookups, can
   631  	// use a filter by group name instead when support is needed.
   632  	limitToGroups := []ec2.SecurityGroup{{Name: groupName}}
   633  	resp, err := e.ec2().SecurityGroups(limitToGroups, nil)
   634  	if err != nil {
   635  		return ec2.SecurityGroupInfo{}, err
   636  	}
   637  	if len(resp.Groups) != 1 {
   638  		return ec2.SecurityGroupInfo{}, fmt.Errorf("expected one security group named %q, got %v", groupName, resp.Groups)
   639  	}
   640  	return resp.Groups[0], nil
   641  }
   642  
   643  // groupByName returns the security group with the given name.
   644  func (e *environ) groupByName(groupName string) (ec2.SecurityGroup, error) {
   645  	groupInfo, err := e.groupInfoByName(groupName)
   646  	return groupInfo.SecurityGroup, err
   647  }
   648  
   649  // addGroupFilter sets a limit an instance filter so only those machines
   650  // with the juju environment wide security group associated will be listed.
   651  //
   652  // An EC2 API call is required to resolve the group name to an id, as VPC
   653  // enabled accounts do not support name based filtering.
   654  // TODO: Detect classic accounts and just filter by name for those.
   655  //
   656  // Callers must handle InvalidGroup.NotFound errors to mean the same as no
   657  // matching instances.
   658  func (e *environ) addGroupFilter(filter *ec2.Filter) error {
   659  	groupName := e.jujuGroupName()
   660  	group, err := e.groupByName(groupName)
   661  	if err != nil {
   662  		return err
   663  	}
   664  	// EC2 should support filtering with and without the 'instance.'
   665  	// prefix, but only the form with seems to work with default VPC.
   666  	filter.Add("instance.group-id", group.Id)
   667  	return nil
   668  }
   669  
   670  // gatherInstances tries to get information on each instance
   671  // id whose corresponding insts slot is nil.
   672  // It returns environs.ErrPartialInstances if the insts
   673  // slice has not been completely filled.
   674  func (e *environ) gatherInstances(ids []instance.Id, insts []instance.Instance) error {
   675  	var need []string
   676  	for i, inst := range insts {
   677  		if inst == nil {
   678  			need = append(need, string(ids[i]))
   679  		}
   680  	}
   681  	if len(need) == 0 {
   682  		return nil
   683  	}
   684  	filter := ec2.NewFilter()
   685  	filter.Add("instance-state-name", "pending", "running")
   686  	err := e.addGroupFilter(filter)
   687  	if err != nil {
   688  		if ec2ErrCode(err) == "InvalidGroup.NotFound" {
   689  			return environs.ErrPartialInstances
   690  		}
   691  		return err
   692  	}
   693  	filter.Add("instance-id", need...)
   694  	resp, err := e.ec2().Instances(nil, filter)
   695  	if err != nil {
   696  		return err
   697  	}
   698  	n := 0
   699  	// For each requested id, add it to the returned instances
   700  	// if we find it in the response.
   701  	for i, id := range ids {
   702  		if insts[i] != nil {
   703  			continue
   704  		}
   705  		for j := range resp.Reservations {
   706  			r := &resp.Reservations[j]
   707  			for k := range r.Instances {
   708  				if r.Instances[k].InstanceId == string(id) {
   709  					inst := r.Instances[k]
   710  					// TODO(wallyworld): lookup the details to fill in the instance type data
   711  					insts[i] = &ec2Instance{e: e, Instance: &inst}
   712  					n++
   713  				}
   714  			}
   715  		}
   716  	}
   717  	if n < len(ids) {
   718  		return environs.ErrPartialInstances
   719  	}
   720  	return nil
   721  }
   722  
   723  func (e *environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
   724  	if len(ids) == 0 {
   725  		return nil, nil
   726  	}
   727  	insts := make([]instance.Instance, len(ids))
   728  	// Make a series of requests to cope with eventual consistency.
   729  	// Each request will attempt to add more instances to the requested
   730  	// set.
   731  	var err error
   732  	for a := shortAttempt.Start(); a.Next(); {
   733  		err = e.gatherInstances(ids, insts)
   734  		if err == nil || err != environs.ErrPartialInstances {
   735  			break
   736  		}
   737  	}
   738  	if err == environs.ErrPartialInstances {
   739  		for _, inst := range insts {
   740  			if inst != nil {
   741  				return insts, environs.ErrPartialInstances
   742  			}
   743  		}
   744  		return nil, environs.ErrNoInstances
   745  	}
   746  	if err != nil {
   747  		return nil, err
   748  	}
   749  	return insts, nil
   750  }
   751  
   752  func (e *environ) fetchNetworkInterfaceId(ec2Inst *ec2.EC2, instId instance.Id) (string, error) {
   753  	var err error
   754  	var instancesResp *ec2.InstancesResp
   755  	for a := shortAttempt.Start(); a.Next(); {
   756  		instancesResp, err = ec2Inst.Instances([]string{string(instId)}, nil)
   757  		if err == nil {
   758  			break
   759  		}
   760  		logger.Tracef("Instances(%q) returned: %v", instId, err)
   761  	}
   762  	if err != nil {
   763  		// either the instance doesn't exist or we couldn't get through to
   764  		// the ec2 api
   765  		return "", err
   766  	}
   767  
   768  	if len(instancesResp.Reservations) == 0 {
   769  		return "", errors.New("unexpected AWS response: reservation not found")
   770  	}
   771  	if len(instancesResp.Reservations[0].Instances) == 0 {
   772  		return "", errors.New("unexpected AWS response: instance not found")
   773  	}
   774  	if len(instancesResp.Reservations[0].Instances[0].NetworkInterfaces) == 0 {
   775  		return "", errors.New("unexpected AWS response: network interface not found")
   776  	}
   777  	networkInterfaceId := instancesResp.Reservations[0].Instances[0].NetworkInterfaces[0].Id
   778  	return networkInterfaceId, nil
   779  }
   780  
   781  // AllocateAddress requests an address to be allocated for the given
   782  // instance on the given subnet. Implements NetworkingEnviron.AllocateAddress.
   783  func (e *environ) AllocateAddress(instId instance.Id, _ network.Id, addr network.Address) (err error) {
   784  	if !environs.AddressAllocationEnabled() {
   785  		return errors.NotSupportedf("address allocation")
   786  	}
   787  
   788  	defer errors.DeferredAnnotatef(&err, "failed to allocate address %q for instance %q", addr, instId)
   789  
   790  	var nicId string
   791  	ec2Inst := e.ec2()
   792  	nicId, err = e.fetchNetworkInterfaceId(ec2Inst, instId)
   793  	if err != nil {
   794  		return errors.Trace(err)
   795  	}
   796  	for a := shortAttempt.Start(); a.Next(); {
   797  		err = AssignPrivateIPAddress(ec2Inst, nicId, addr)
   798  		logger.Tracef("AssignPrivateIPAddresses(%v, %v) returned: %v", nicId, addr, err)
   799  		if err == nil {
   800  			logger.Tracef("allocated address %v for instance %v, NIC %v", addr, instId, nicId)
   801  			break
   802  		}
   803  		if ec2Err, ok := err.(*ec2.Error); ok {
   804  			if ec2Err.Code == invalidParameterValue {
   805  				// Note: this Code is also used if we specify
   806  				// an IP address outside the subnet. Take care!
   807  				logger.Tracef("address %q not available for allocation", addr)
   808  				return environs.ErrIPAddressUnavailable
   809  			} else if ec2Err.Code == privateAddressLimitExceeded {
   810  				logger.Tracef("no more addresses available on the subnet")
   811  				return environs.ErrIPAddressesExhausted
   812  			}
   813  		}
   814  
   815  	}
   816  	return err
   817  }
   818  
   819  // ReleaseAddress releases a specific address previously allocated with
   820  // AllocateAddress. Implements NetworkingEnviron.ReleaseAddress.
   821  func (e *environ) ReleaseAddress(instId instance.Id, _ network.Id, addr network.Address) (err error) {
   822  	if !environs.AddressAllocationEnabled() {
   823  		return errors.NotSupportedf("address allocation")
   824  	}
   825  
   826  	defer errors.DeferredAnnotatef(&err, "failed to release address %q from instance %q", addr, instId)
   827  
   828  	// If the instance ID is unknown the address has already been released
   829  	// and we can ignore this request.
   830  	if instId == instance.UnknownId {
   831  		logger.Debugf("release address %q with an unknown instance ID is a no-op (ignoring)", addr.Value)
   832  		return nil
   833  	}
   834  
   835  	var nicId string
   836  	ec2Inst := e.ec2()
   837  	nicId, err = e.fetchNetworkInterfaceId(ec2Inst, instId)
   838  	if err != nil {
   839  		return errors.Trace(err)
   840  	}
   841  	for a := shortAttempt.Start(); a.Next(); {
   842  		_, err = ec2Inst.UnassignPrivateIPAddresses(nicId, []string{addr.Value})
   843  		logger.Tracef("UnassignPrivateIPAddresses(%q, %q) returned: %v", nicId, addr, err)
   844  		if err == nil {
   845  			logger.Tracef("released address %q from instance %q, NIC %q", addr, instId, nicId)
   846  			break
   847  		}
   848  	}
   849  	return err
   850  }
   851  
   852  // NetworkInterfaces implements NetworkingEnviron.NetworkInterfaces.
   853  func (e *environ) NetworkInterfaces(instId instance.Id) ([]network.InterfaceInfo, error) {
   854  	ec2Client := e.ec2()
   855  	var err error
   856  	var networkInterfacesResp *ec2.NetworkInterfacesResp
   857  	for a := shortAttempt.Start(); a.Next(); {
   858  		logger.Tracef("retrieving NICs for instance %q", instId)
   859  		filter := ec2.NewFilter()
   860  		filter.Add("attachment.instance-id", string(instId))
   861  		networkInterfacesResp, err = ec2Client.NetworkInterfaces(nil, filter)
   862  		logger.Tracef("instance %q NICs: %#v (err: %v)", instId, networkInterfacesResp, err)
   863  		if err != nil {
   864  			logger.Warningf("failed to get instance %q interfaces: %v (retrying)", instId, err)
   865  			continue
   866  		}
   867  		if len(networkInterfacesResp.Interfaces) == 0 {
   868  			logger.Tracef("instance %q has no NIC attachment yet, retrying...", instId)
   869  			continue
   870  		}
   871  		logger.Tracef("found instance %q NICS: %#v", instId, networkInterfacesResp.Interfaces)
   872  		break
   873  	}
   874  	if err != nil {
   875  		// either the instance doesn't exist or we couldn't get through to
   876  		// the ec2 api
   877  		return nil, errors.Annotatef(err, "cannot get instance %q network interfaces", instId)
   878  	}
   879  	ec2Interfaces := networkInterfacesResp.Interfaces
   880  	result := make([]network.InterfaceInfo, len(ec2Interfaces))
   881  	for i, iface := range ec2Interfaces {
   882  		resp, err := ec2Client.Subnets([]string{iface.SubnetId}, nil)
   883  		if err != nil {
   884  			return nil, errors.Annotatef(err, "failed to retrieve subnet %q info", iface.SubnetId)
   885  		}
   886  		if len(resp.Subnets) != 1 {
   887  			return nil, errors.Errorf("expected 1 subnet, got %d", len(resp.Subnets))
   888  		}
   889  		subnet := resp.Subnets[0]
   890  		cidr := subnet.CIDRBlock
   891  
   892  		result[i] = network.InterfaceInfo{
   893  			DeviceIndex:      iface.Attachment.DeviceIndex,
   894  			MACAddress:       iface.MACAddress,
   895  			CIDR:             cidr,
   896  			NetworkName:      "", // Not needed for now.
   897  			ProviderId:       network.Id(iface.Id),
   898  			ProviderSubnetId: network.Id(iface.SubnetId),
   899  			VLANTag:          0, // Not supported on EC2.
   900  			// Getting the interface name is not supported on EC2, so fake it.
   901  			InterfaceName: fmt.Sprintf("unsupported%d", iface.Attachment.DeviceIndex),
   902  			Disabled:      false,
   903  			NoAutoStart:   false,
   904  			ConfigType:    network.ConfigDHCP,
   905  			Address:       network.NewScopedAddress(iface.PrivateIPAddress, network.ScopeCloudLocal),
   906  		}
   907  	}
   908  	return result, nil
   909  }
   910  
   911  // Subnets returns basic information about the specified subnets known
   912  // by the provider for the specified instance. subnetIds must not be
   913  // empty. Implements NetworkingEnviron.Subnets.
   914  func (e *environ) Subnets(_ instance.Id, subnetIds []network.Id) ([]network.SubnetInfo, error) {
   915  	// At some point in the future an empty netIds may mean "fetch all subnets"
   916  	// but until that functionality is needed it's an error.
   917  	if len(subnetIds) == 0 {
   918  		return nil, errors.Errorf("subnetIds must not be empty")
   919  	}
   920  	ec2Inst := e.ec2()
   921  	// We can't filter by instance id here, unfortunately.
   922  	resp, err := ec2Inst.Subnets(nil, nil)
   923  	if err != nil {
   924  		return nil, errors.Annotatef(err, "failed to retrieve subnets")
   925  	}
   926  
   927  	subIdSet := make(map[string]bool)
   928  	for _, subId := range subnetIds {
   929  		subIdSet[string(subId)] = false
   930  	}
   931  
   932  	var results []network.SubnetInfo
   933  	for _, subnet := range resp.Subnets {
   934  		_, ok := subIdSet[subnet.Id]
   935  		if !ok {
   936  			logger.Tracef("subnet %q not in %v, skipping", subnet.Id, subnetIds)
   937  			continue
   938  		}
   939  		subIdSet[subnet.Id] = true
   940  
   941  		cidr := subnet.CIDRBlock
   942  		ip, ipnet, err := net.ParseCIDR(cidr)
   943  		if err != nil {
   944  			logger.Warningf("skipping subnet %q, invalid CIDR: %v", cidr, err)
   945  			continue
   946  		}
   947  		// ec2 only uses IPv4 addresses for subnets
   948  		start, err := network.IPv4ToDecimal(ip)
   949  		if err != nil {
   950  			logger.Warningf("skipping subnet %q, invalid IP: %v", cidr, err)
   951  			continue
   952  		}
   953  		// First four addresses in a subnet are reserved, see
   954  		// http://goo.gl/rrWTIo
   955  		allocatableLow := network.DecimalToIPv4(start + 4)
   956  
   957  		ones, bits := ipnet.Mask.Size()
   958  		zeros := bits - ones
   959  		numIPs := uint32(1) << uint32(zeros)
   960  		highIP := start + numIPs - 1
   961  		// The last address in a subnet is also reserved (see same ref).
   962  		allocatableHigh := network.DecimalToIPv4(highIP - 1)
   963  
   964  		info := network.SubnetInfo{
   965  			CIDR:              cidr,
   966  			ProviderId:        network.Id(subnet.Id),
   967  			VLANTag:           0, // Not supported on EC2
   968  			AllocatableIPLow:  allocatableLow,
   969  			AllocatableIPHigh: allocatableHigh,
   970  		}
   971  		logger.Tracef("found subnet with info %#v", info)
   972  		results = append(results, info)
   973  	}
   974  
   975  	notFound := []string{}
   976  	for subId, found := range subIdSet {
   977  		if !found {
   978  			notFound = append(notFound, subId)
   979  		}
   980  	}
   981  	if len(notFound) != 0 {
   982  		return nil, errors.Errorf("failed to find the following subnet ids: %v", notFound)
   983  	}
   984  
   985  	return results, nil
   986  }
   987  
   988  func (e *environ) AllInstances() ([]instance.Instance, error) {
   989  	filter := ec2.NewFilter()
   990  	filter.Add("instance-state-name", "pending", "running")
   991  	err := e.addGroupFilter(filter)
   992  	if err != nil {
   993  		if ec2ErrCode(err) == "InvalidGroup.NotFound" {
   994  			return nil, nil
   995  		}
   996  		return nil, err
   997  	}
   998  	resp, err := e.ec2().Instances(nil, filter)
   999  	if err != nil {
  1000  		return nil, err
  1001  	}
  1002  	var insts []instance.Instance
  1003  	for _, r := range resp.Reservations {
  1004  		for i := range r.Instances {
  1005  			inst := r.Instances[i]
  1006  			// TODO(wallyworld): lookup the details to fill in the instance type data
  1007  			insts = append(insts, &ec2Instance{e: e, Instance: &inst})
  1008  		}
  1009  	}
  1010  	return insts, nil
  1011  }
  1012  
  1013  func (e *environ) Destroy() error {
  1014  	if err := common.Destroy(e); err != nil {
  1015  		return errors.Trace(err)
  1016  	}
  1017  	return e.Storage().RemoveAll()
  1018  }
  1019  
  1020  func portsToIPPerms(ports []network.PortRange) []ec2.IPPerm {
  1021  	ipPerms := make([]ec2.IPPerm, len(ports))
  1022  	for i, p := range ports {
  1023  		ipPerms[i] = ec2.IPPerm{
  1024  			Protocol:  p.Protocol,
  1025  			FromPort:  p.FromPort,
  1026  			ToPort:    p.ToPort,
  1027  			SourceIPs: []string{"0.0.0.0/0"},
  1028  		}
  1029  	}
  1030  	return ipPerms
  1031  }
  1032  
  1033  func (e *environ) openPortsInGroup(name string, ports []network.PortRange) error {
  1034  	if len(ports) == 0 {
  1035  		return nil
  1036  	}
  1037  	// Give permissions for anyone to access the given ports.
  1038  	g, err := e.groupByName(name)
  1039  	if err != nil {
  1040  		return err
  1041  	}
  1042  	ipPerms := portsToIPPerms(ports)
  1043  	_, err = e.ec2().AuthorizeSecurityGroup(g, ipPerms)
  1044  	if err != nil && ec2ErrCode(err) == "InvalidPermission.Duplicate" {
  1045  		if len(ports) == 1 {
  1046  			return nil
  1047  		}
  1048  		// If there's more than one port and we get a duplicate error,
  1049  		// then we go through authorizing each port individually,
  1050  		// otherwise the ports that were *not* duplicates will have
  1051  		// been ignored
  1052  		for i := range ipPerms {
  1053  			_, err := e.ec2().AuthorizeSecurityGroup(g, ipPerms[i:i+1])
  1054  			if err != nil && ec2ErrCode(err) != "InvalidPermission.Duplicate" {
  1055  				return fmt.Errorf("cannot open port %v: %v", ipPerms[i], err)
  1056  			}
  1057  		}
  1058  		return nil
  1059  	}
  1060  	if err != nil {
  1061  		return fmt.Errorf("cannot open ports: %v", err)
  1062  	}
  1063  	return nil
  1064  }
  1065  
  1066  func (e *environ) closePortsInGroup(name string, ports []network.PortRange) error {
  1067  	if len(ports) == 0 {
  1068  		return nil
  1069  	}
  1070  	// Revoke permissions for anyone to access the given ports.
  1071  	// Note that ec2 allows the revocation of permissions that aren't
  1072  	// granted, so this is naturally idempotent.
  1073  	g, err := e.groupByName(name)
  1074  	if err != nil {
  1075  		return err
  1076  	}
  1077  	_, err = e.ec2().RevokeSecurityGroup(g, portsToIPPerms(ports))
  1078  	if err != nil {
  1079  		return fmt.Errorf("cannot close ports: %v", err)
  1080  	}
  1081  	return nil
  1082  }
  1083  
  1084  func (e *environ) portsInGroup(name string) (ports []network.PortRange, err error) {
  1085  	group, err := e.groupInfoByName(name)
  1086  	if err != nil {
  1087  		return nil, err
  1088  	}
  1089  	for _, p := range group.IPPerms {
  1090  		if len(p.SourceIPs) != 1 {
  1091  			logger.Warningf("unexpected IP permission found: %v", p)
  1092  			continue
  1093  		}
  1094  		ports = append(ports, network.PortRange{
  1095  			Protocol: p.Protocol,
  1096  			FromPort: p.FromPort,
  1097  			ToPort:   p.ToPort,
  1098  		})
  1099  	}
  1100  	network.SortPortRanges(ports)
  1101  	return ports, nil
  1102  }
  1103  
  1104  func (e *environ) OpenPorts(ports []network.PortRange) error {
  1105  	if e.Config().FirewallMode() != config.FwGlobal {
  1106  		return fmt.Errorf("invalid firewall mode %q for opening ports on environment",
  1107  			e.Config().FirewallMode())
  1108  	}
  1109  	if err := e.openPortsInGroup(e.globalGroupName(), ports); err != nil {
  1110  		return err
  1111  	}
  1112  	logger.Infof("opened ports in global group: %v", ports)
  1113  	return nil
  1114  }
  1115  
  1116  func (e *environ) ClosePorts(ports []network.PortRange) error {
  1117  	if e.Config().FirewallMode() != config.FwGlobal {
  1118  		return fmt.Errorf("invalid firewall mode %q for closing ports on environment",
  1119  			e.Config().FirewallMode())
  1120  	}
  1121  	if err := e.closePortsInGroup(e.globalGroupName(), ports); err != nil {
  1122  		return err
  1123  	}
  1124  	logger.Infof("closed ports in global group: %v", ports)
  1125  	return nil
  1126  }
  1127  
  1128  func (e *environ) Ports() ([]network.PortRange, error) {
  1129  	if e.Config().FirewallMode() != config.FwGlobal {
  1130  		return nil, fmt.Errorf("invalid firewall mode %q for retrieving ports from environment",
  1131  			e.Config().FirewallMode())
  1132  	}
  1133  	return e.portsInGroup(e.globalGroupName())
  1134  }
  1135  
  1136  func (*environ) Provider() environs.EnvironProvider {
  1137  	return &providerInstance
  1138  }
  1139  
  1140  func (e *environ) terminateInstances(ids []instance.Id) error {
  1141  	if len(ids) == 0 {
  1142  		return nil
  1143  	}
  1144  	var err error
  1145  	ec2inst := e.ec2()
  1146  	strs := make([]string, len(ids))
  1147  	for i, id := range ids {
  1148  		strs[i] = string(id)
  1149  	}
  1150  	for a := shortAttempt.Start(); a.Next(); {
  1151  		_, err = ec2inst.TerminateInstances(strs)
  1152  		if err == nil || ec2ErrCode(err) != "InvalidInstanceID.NotFound" {
  1153  			return err
  1154  		}
  1155  	}
  1156  	if len(ids) == 1 {
  1157  		return err
  1158  	}
  1159  	// If we get a NotFound error, it means that no instances have been
  1160  	// terminated even if some exist, so try them one by one, ignoring
  1161  	// NotFound errors.
  1162  	var firstErr error
  1163  	for _, id := range ids {
  1164  		_, err = ec2inst.TerminateInstances([]string{string(id)})
  1165  		if ec2ErrCode(err) == "InvalidInstanceID.NotFound" {
  1166  			err = nil
  1167  		}
  1168  		if err != nil && firstErr == nil {
  1169  			firstErr = err
  1170  		}
  1171  	}
  1172  	return firstErr
  1173  }
  1174  
  1175  func (e *environ) globalGroupName() string {
  1176  	return fmt.Sprintf("%s-global", e.jujuGroupName())
  1177  }
  1178  
  1179  func (e *environ) machineGroupName(machineId string) string {
  1180  	return fmt.Sprintf("%s-%s", e.jujuGroupName(), machineId)
  1181  }
  1182  
  1183  func (e *environ) jujuGroupName() string {
  1184  	return "juju-" + e.name
  1185  }
  1186  
  1187  // setUpGroups creates the security groups for the new machine, and
  1188  // returns them.
  1189  //
  1190  // Instances are tagged with a group so they can be distinguished from
  1191  // other instances that might be running on the same EC2 account.  In
  1192  // addition, a specific machine security group is created for each
  1193  // machine, so that its firewall rules can be configured per machine.
  1194  func (e *environ) setUpGroups(machineId string, apiPort int) ([]ec2.SecurityGroup, error) {
  1195  	jujuGroup, err := e.ensureGroup(e.jujuGroupName(),
  1196  		[]ec2.IPPerm{
  1197  			{
  1198  				Protocol:  "tcp",
  1199  				FromPort:  22,
  1200  				ToPort:    22,
  1201  				SourceIPs: []string{"0.0.0.0/0"},
  1202  			},
  1203  			{
  1204  				Protocol:  "tcp",
  1205  				FromPort:  apiPort,
  1206  				ToPort:    apiPort,
  1207  				SourceIPs: []string{"0.0.0.0/0"},
  1208  			},
  1209  			{
  1210  				Protocol: "tcp",
  1211  				FromPort: 0,
  1212  				ToPort:   65535,
  1213  			},
  1214  			{
  1215  				Protocol: "udp",
  1216  				FromPort: 0,
  1217  				ToPort:   65535,
  1218  			},
  1219  			{
  1220  				Protocol: "icmp",
  1221  				FromPort: -1,
  1222  				ToPort:   -1,
  1223  			},
  1224  		})
  1225  	if err != nil {
  1226  		return nil, err
  1227  	}
  1228  	var machineGroup ec2.SecurityGroup
  1229  	switch e.Config().FirewallMode() {
  1230  	case config.FwInstance:
  1231  		machineGroup, err = e.ensureGroup(e.machineGroupName(machineId), nil)
  1232  	case config.FwGlobal:
  1233  		machineGroup, err = e.ensureGroup(e.globalGroupName(), nil)
  1234  	}
  1235  	if err != nil {
  1236  		return nil, err
  1237  	}
  1238  	return []ec2.SecurityGroup{jujuGroup, machineGroup}, nil
  1239  }
  1240  
  1241  // zeroGroup holds the zero security group.
  1242  var zeroGroup ec2.SecurityGroup
  1243  
  1244  // ensureGroup returns the security group with name and perms.
  1245  // If a group with name does not exist, one will be created.
  1246  // If it exists, its permissions are set to perms.
  1247  // Any entries in perms without SourceIPs will be granted for
  1248  // the named group only.
  1249  func (e *environ) ensureGroup(name string, perms []ec2.IPPerm) (g ec2.SecurityGroup, err error) {
  1250  	ec2inst := e.ec2()
  1251  	resp, err := ec2inst.CreateSecurityGroup("", name, "juju group")
  1252  	if err != nil && ec2ErrCode(err) != "InvalidGroup.Duplicate" {
  1253  		return zeroGroup, err
  1254  	}
  1255  
  1256  	var have permSet
  1257  	if err == nil {
  1258  		g = resp.SecurityGroup
  1259  	} else {
  1260  		resp, err := ec2inst.SecurityGroups(ec2.SecurityGroupNames(name), nil)
  1261  		if err != nil {
  1262  			return zeroGroup, err
  1263  		}
  1264  		info := resp.Groups[0]
  1265  		// It's possible that the old group has the wrong
  1266  		// description here, but if it does it's probably due
  1267  		// to something deliberately playing games with juju,
  1268  		// so we ignore it.
  1269  		g = info.SecurityGroup
  1270  		have = newPermSetForGroup(info.IPPerms, g)
  1271  	}
  1272  	want := newPermSetForGroup(perms, g)
  1273  	revoke := make(permSet)
  1274  	for p := range have {
  1275  		if !want[p] {
  1276  			revoke[p] = true
  1277  		}
  1278  	}
  1279  	if len(revoke) > 0 {
  1280  		_, err := ec2inst.RevokeSecurityGroup(g, revoke.ipPerms())
  1281  		if err != nil {
  1282  			return zeroGroup, fmt.Errorf("cannot revoke security group: %v", err)
  1283  		}
  1284  	}
  1285  
  1286  	add := make(permSet)
  1287  	for p := range want {
  1288  		if !have[p] {
  1289  			add[p] = true
  1290  		}
  1291  	}
  1292  	if len(add) > 0 {
  1293  		_, err := ec2inst.AuthorizeSecurityGroup(g, add.ipPerms())
  1294  		if err != nil {
  1295  			return zeroGroup, fmt.Errorf("cannot authorize securityGroup: %v", err)
  1296  		}
  1297  	}
  1298  	return g, nil
  1299  }
  1300  
  1301  // permKey represents a permission for a group or an ip address range
  1302  // to access the given range of ports. Only one of groupName or ipAddr
  1303  // should be non-empty.
  1304  type permKey struct {
  1305  	protocol string
  1306  	fromPort int
  1307  	toPort   int
  1308  	groupId  string
  1309  	ipAddr   string
  1310  }
  1311  
  1312  type permSet map[permKey]bool
  1313  
  1314  // newPermSetForGroup returns a set of all the permissions in the
  1315  // given slice of IPPerms. It ignores the name and owner
  1316  // id in source groups, and any entry with no source ips will
  1317  // be granted for the given group only.
  1318  func newPermSetForGroup(ps []ec2.IPPerm, group ec2.SecurityGroup) permSet {
  1319  	m := make(permSet)
  1320  	for _, p := range ps {
  1321  		k := permKey{
  1322  			protocol: p.Protocol,
  1323  			fromPort: p.FromPort,
  1324  			toPort:   p.ToPort,
  1325  		}
  1326  		if len(p.SourceIPs) > 0 {
  1327  			for _, ip := range p.SourceIPs {
  1328  				k.ipAddr = ip
  1329  				m[k] = true
  1330  			}
  1331  		} else {
  1332  			k.groupId = group.Id
  1333  			m[k] = true
  1334  		}
  1335  	}
  1336  	return m
  1337  }
  1338  
  1339  // ipPerms returns m as a slice of permissions usable
  1340  // with the ec2 package.
  1341  func (m permSet) ipPerms() (ps []ec2.IPPerm) {
  1342  	// We could compact the permissions, but it
  1343  	// hardly seems worth it.
  1344  	for p := range m {
  1345  		ipp := ec2.IPPerm{
  1346  			Protocol: p.protocol,
  1347  			FromPort: p.fromPort,
  1348  			ToPort:   p.toPort,
  1349  		}
  1350  		if p.ipAddr != "" {
  1351  			ipp.SourceIPs = []string{p.ipAddr}
  1352  		} else {
  1353  			ipp.SourceGroups = []ec2.UserSecurityGroup{{Id: p.groupId}}
  1354  		}
  1355  		ps = append(ps, ipp)
  1356  	}
  1357  	return
  1358  }
  1359  
  1360  // isZoneConstrainedError reports whether or not the error indicates
  1361  // RunInstances failed due to the specified availability zone being
  1362  // constrained for the instance type being provisioned, or is
  1363  // otherwise unusable for the specific request made.
  1364  func isZoneConstrainedError(err error) bool {
  1365  	switch err := err.(type) {
  1366  	case *ec2.Error:
  1367  		switch err.Code {
  1368  		case "Unsupported", "InsufficientInstanceCapacity":
  1369  			// A big hammer, but we've now seen several different error messages
  1370  			// for constrained zones, and who knows how many more there might
  1371  			// be. If the message contains "Availability Zone", it's a fair
  1372  			// bet that it's constrained or otherwise unusable.
  1373  			return strings.Contains(err.Message, "Availability Zone")
  1374  		case "InvalidInput":
  1375  			// If the region has a default VPC, then we will receive an error
  1376  			// if the AZ does not have a default subnet. Until we have proper
  1377  			// support for networks, we'll skip over these.
  1378  			return strings.HasPrefix(err.Message, "No default subnet for availability zone")
  1379  		case "VolumeTypeNotAvailableInZone":
  1380  			return true
  1381  		}
  1382  	}
  1383  	return false
  1384  }
  1385  
  1386  // If the err is of type *ec2.Error, ec2ErrCode returns
  1387  // its code, otherwise it returns the empty string.
  1388  func ec2ErrCode(err error) string {
  1389  	ec2err, _ := err.(*ec2.Error)
  1390  	if ec2err == nil {
  1391  		return ""
  1392  	}
  1393  	return ec2err.Code
  1394  }