github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/environ_availzones.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package gce
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/environs"
    10  	"github.com/juju/juju/instance"
    11  	"github.com/juju/juju/provider/common"
    12  	"github.com/juju/juju/provider/gce/google"
    13  )
    14  
    15  // AvailabilityZones returns all availability zones in the environment.
    16  func (env *environ) AvailabilityZones() ([]common.AvailabilityZone, error) {
    17  	zones, err := env.gce.AvailabilityZones(env.ecfg.region())
    18  	if err != nil {
    19  		return nil, errors.Trace(err)
    20  	}
    21  
    22  	var result []common.AvailabilityZone
    23  	for _, zone := range zones {
    24  		if zone.Deprecated() {
    25  			continue
    26  		}
    27  		// We make a copy since the loop var keeps the same pointer.
    28  		zoneCopy := zone
    29  		result = append(result, &zoneCopy)
    30  	}
    31  	return result, nil
    32  }
    33  
    34  // InstanceAvailabilityZoneNames returns the names of the availability
    35  // zones for the specified instances. The error returned follows the same
    36  // rules as Environ.Instances.
    37  func (env *environ) InstanceAvailabilityZoneNames(ids []instance.Id) ([]string, error) {
    38  	instances, err := env.Instances(ids)
    39  	if err != nil && err != environs.ErrPartialInstances && err != environs.ErrNoInstances {
    40  		return nil, errors.Trace(err)
    41  	}
    42  	// We let the two environs errors pass on through. However, we do
    43  	// not use errors.Trace in that case since callers may not call
    44  	// errors.Cause.
    45  
    46  	results := make([]string, len(ids))
    47  	for i, inst := range instances {
    48  		if eInst := inst.(*environInstance); eInst != nil {
    49  			results[i] = eInst.base.ZoneName
    50  		}
    51  	}
    52  
    53  	return results, err
    54  }
    55  
    56  func (env *environ) availZone(name string) (*google.AvailabilityZone, error) {
    57  	zones, err := env.gce.AvailabilityZones(env.ecfg.region())
    58  	if err != nil {
    59  		return nil, errors.Trace(err)
    60  	}
    61  	for _, z := range zones {
    62  		if z.Name() == name {
    63  			return &z, nil
    64  		}
    65  	}
    66  	return nil, errors.NotFoundf("invalid availability zone %q", name)
    67  }
    68  
    69  func (env *environ) availZoneUp(name string) (*google.AvailabilityZone, error) {
    70  	zone, err := env.availZone(name)
    71  	if err != nil {
    72  		return nil, errors.Trace(err)
    73  	}
    74  	if !zone.Available() {
    75  		return nil, errors.Errorf("availability zone %q is %s", zone.Name(), zone.Status())
    76  	}
    77  	return zone, nil
    78  }
    79  
    80  var availabilityZoneAllocations = common.AvailabilityZoneAllocations
    81  
    82  // parseAvailabilityZones returns the availability zones that should be
    83  // tried for the given instance spec. If a placement argument was
    84  // provided then only that one is returned. Otherwise the environment is
    85  // queried for available zones. In that case, the resulting list is
    86  // roughly ordered such that the environment's instances are spread
    87  // evenly across the region.
    88  func (env *environ) parseAvailabilityZones(args environs.StartInstanceParams) ([]string, error) {
    89  	if args.Placement != "" {
    90  		// args.Placement will always be a zone name or empty.
    91  		placement, err := env.parsePlacement(args.Placement)
    92  		if err != nil {
    93  			return nil, errors.Trace(err)
    94  		}
    95  		// TODO(ericsnow) Fail if placement.Zone is not in the env's configured region?
    96  		return []string{placement.Zone.Name()}, nil
    97  	}
    98  
    99  	// If no availability zone is specified, then automatically spread across
   100  	// the known zones for optimal spread across the instance distribution
   101  	// group.
   102  	var group []instance.Id
   103  	var err error
   104  	if args.DistributionGroup != nil {
   105  		group, err = args.DistributionGroup()
   106  		if err != nil {
   107  			return nil, errors.Trace(err)
   108  		}
   109  	}
   110  	zoneInstances, err := availabilityZoneAllocations(env, group)
   111  	if err != nil {
   112  		return nil, errors.Trace(err)
   113  	}
   114  	logger.Infof("found %d zones: %v", len(zoneInstances), zoneInstances)
   115  
   116  	var zoneNames []string
   117  	for _, z := range zoneInstances {
   118  		zoneNames = append(zoneNames, z.ZoneName)
   119  	}
   120  
   121  	if len(zoneNames) == 0 {
   122  		return nil, errors.NotFoundf("failed to determine availability zones")
   123  	}
   124  
   125  	return zoneNames, nil
   126  }