github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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/environs/tags"
    14  	"github.com/juju/juju/instance"
    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 := env.namespace.Prefix()
    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(controllerUUID string) ([]instance.Id, error) {
    98  	prefix := env.namespace.Prefix()
    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  		if uuid, ok := metadata[tags.JujuController]; !ok || uuid != controllerUUID {
   108  			continue
   109  		}
   110  		isController, ok := metadata[tags.JujuIsController]
   111  		if ok && isController == "true" {
   112  			results = append(results, instance.Id(inst.ID))
   113  		}
   114  	}
   115  	if len(results) == 0 {
   116  		return nil, environs.ErrNotBootstrapped
   117  	}
   118  	return results, nil
   119  }
   120  
   121  // TODO(ericsnow) Turn into an interface.
   122  type instPlacement struct {
   123  	Zone *google.AvailabilityZone
   124  }
   125  
   126  // parsePlacement extracts the availability zone from the placement
   127  // string and returns it. If no zone is found there then an error is
   128  // returned.
   129  func (env *environ) parsePlacement(placement string) (*instPlacement, error) {
   130  	if placement == "" {
   131  		return nil, nil
   132  	}
   133  
   134  	pos := strings.IndexRune(placement, '=')
   135  	if pos == -1 {
   136  		return nil, errors.Errorf("unknown placement directive: %v", placement)
   137  	}
   138  
   139  	switch key, value := placement[:pos], placement[pos+1:]; key {
   140  	case "zone":
   141  		zone, err := env.availZoneUp(value)
   142  		if err != nil {
   143  			return nil, errors.Trace(err)
   144  		}
   145  		return &instPlacement{Zone: zone}, nil
   146  	}
   147  	return nil, errors.Errorf("unknown placement directive: %v", placement)
   148  }
   149  
   150  // checkInstanceType is used to ensure the the provided constraints
   151  // specify a recognized instance type.
   152  func checkInstanceType(cons constraints.Value) bool {
   153  	// Constraint has an instance-type constraint so let's see if it is valid.
   154  	for _, itype := range allInstanceTypes {
   155  		if itype.Name == *cons.InstanceType {
   156  			return true
   157  		}
   158  	}
   159  	return false
   160  }