github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/environ_instance.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  	"strings"
     8  
     9  	"github.com/juju/errors"
    10  
    11  	"github.com/juju/juju/constraints"
    12  	"github.com/juju/juju/environs"
    13  	"github.com/juju/juju/instance"
    14  	"github.com/juju/juju/provider/common"
    15  	"github.com/juju/juju/provider/gce/google"
    16  )
    17  
    18  // instStatus is the list of statuses to accept when filtering
    19  // for "alive" instances.
    20  var instStatuses = []string{
    21  	google.StatusPending,
    22  	google.StatusStaging,
    23  	google.StatusRunning,
    24  }
    25  
    26  // Instances returns the available instances in the environment that
    27  // match the provided instance IDs. For IDs that did not match any
    28  // instances, the result at the corresponding index will be nil. In that
    29  // case the error will be environs.ErrPartialInstances (or
    30  // ErrNoInstances if none of the IDs match an instance).
    31  func (env *environ) Instances(ids []instance.Id) ([]instance.Instance, error) {
    32  	if len(ids) == 0 {
    33  		return nil, environs.ErrNoInstances
    34  	}
    35  
    36  	instances, err := getInstances(env)
    37  	if err != nil {
    38  		// We don't return the error since we need to pack one instance
    39  		// for each ID into the result. If there is a problem then we
    40  		// will return either ErrPartialInstances or ErrNoInstances.
    41  		// TODO(ericsnow) Skip returning here only for certain errors?
    42  		logger.Errorf("failed to get instances from GCE: %v", err)
    43  		err = errors.Trace(err)
    44  	}
    45  
    46  	// Build the result, matching the provided instance IDs.
    47  	numFound := 0 // This will never be greater than len(ids).
    48  	results := make([]instance.Instance, len(ids))
    49  	for i, id := range ids {
    50  		inst := findInst(id, instances)
    51  		if inst != nil {
    52  			numFound++
    53  		}
    54  		results[i] = inst
    55  	}
    56  
    57  	if numFound == 0 {
    58  		if err == nil {
    59  			err = environs.ErrNoInstances
    60  		}
    61  	} else if numFound != len(ids) {
    62  		err = environs.ErrPartialInstances
    63  	}
    64  	return results, err
    65  }
    66  
    67  var getInstances = func(env *environ) ([]instance.Instance, error) {
    68  	return env.instances()
    69  }
    70  
    71  // instances returns a list of all "alive" instances in the environment.
    72  // This means only instances where the IDs match
    73  // "juju-<env name>-machine-*". This is important because otherwise juju
    74  // will see they are not tracked in state, assume they're stale/rogue,
    75  // and shut them down.
    76  func (env *environ) instances() ([]instance.Instance, error) {
    77  	prefix := common.MachineFullName(env.Config().UUID(), "")
    78  	instances, err := env.gce.Instances(prefix, instStatuses...)
    79  	err = errors.Trace(err)
    80  
    81  	// Turn google.Instance values into *environInstance values,
    82  	// whether or not we got an error.
    83  	var results []instance.Instance
    84  	for _, base := range instances {
    85  		// If we don't make a copy then the same pointer is used for the
    86  		// base of all resulting instances.
    87  		copied := base
    88  		inst := newInstance(&copied, env)
    89  		results = append(results, inst)
    90  	}
    91  
    92  	return results, err
    93  }
    94  
    95  // ControllerInstances returns the IDs of the instances corresponding
    96  // to juju controllers.
    97  func (env *environ) ControllerInstances() ([]instance.Id, error) {
    98  	prefix := common.MachineFullName(env.Config().ControllerUUID(), "")
    99  	instances, err := env.gce.Instances(prefix, instStatuses...)
   100  	if err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  
   104  	var results []instance.Id
   105  	for _, inst := range instances {
   106  		metadata := inst.Metadata()
   107  		isState, ok := metadata[metadataKeyIsState]
   108  		if ok && isState == metadataValueTrue {
   109  			results = append(results, instance.Id(inst.ID))
   110  		}
   111  	}
   112  	if len(results) == 0 {
   113  		return nil, environs.ErrNotBootstrapped
   114  	}
   115  	return results, nil
   116  }
   117  
   118  // TODO(ericsnow) Turn into an interface.
   119  type instPlacement struct {
   120  	Zone *google.AvailabilityZone
   121  }
   122  
   123  // parsePlacement extracts the availability zone from the placement
   124  // string and returns it. If no zone is found there then an error is
   125  // returned.
   126  func (env *environ) parsePlacement(placement string) (*instPlacement, error) {
   127  	if placement == "" {
   128  		return nil, nil
   129  	}
   130  
   131  	pos := strings.IndexRune(placement, '=')
   132  	if pos == -1 {
   133  		return nil, errors.Errorf("unknown placement directive: %v", placement)
   134  	}
   135  
   136  	switch key, value := placement[:pos], placement[pos+1:]; key {
   137  	case "zone":
   138  		zone, err := env.availZoneUp(value)
   139  		if err != nil {
   140  			return nil, errors.Trace(err)
   141  		}
   142  		return &instPlacement{Zone: zone}, nil
   143  	}
   144  	return nil, errors.Errorf("unknown placement directive: %v", placement)
   145  }
   146  
   147  // checkInstanceType is used to ensure the the provided constraints
   148  // specify a recognized instance type.
   149  func checkInstanceType(cons constraints.Value) bool {
   150  	// Constraint has an instance-type constraint so let's see if it is valid.
   151  	for _, itype := range allInstanceTypes {
   152  		if itype.Name == *cons.InstanceType {
   153  			return true
   154  		}
   155  	}
   156  	return false
   157  }