github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 env = env.getSnapshot() 78 79 prefix := common.MachineFullName(env, "") 80 instances, err := env.gce.Instances(prefix, instStatuses...) 81 err = errors.Trace(err) 82 83 // Turn google.Instance values into *environInstance values, 84 // whether or not we got an error. 85 var results []instance.Instance 86 for _, base := range instances { 87 // If we don't make a copy then the same pointer is used for the 88 // base of all resulting instances. 89 copied := base 90 inst := newInstance(&copied, env) 91 results = append(results, inst) 92 } 93 94 return results, err 95 } 96 97 // StateServerInstances returns the IDs of the instances corresponding 98 // to juju state servers. 99 func (env *environ) StateServerInstances() ([]instance.Id, error) { 100 env = env.getSnapshot() 101 102 prefix := common.MachineFullName(env, "") 103 instances, err := env.gce.Instances(prefix, instStatuses...) 104 if err != nil { 105 return nil, errors.Trace(err) 106 } 107 108 var results []instance.Id 109 for _, inst := range instances { 110 metadata := inst.Metadata() 111 isState, ok := metadata[metadataKeyIsState] 112 if ok && isState == metadataValueTrue { 113 results = append(results, instance.Id(inst.ID)) 114 } 115 } 116 if len(results) == 0 { 117 return nil, environs.ErrNotBootstrapped 118 } 119 return results, nil 120 } 121 122 // TODO(ericsnow) Turn into an interface. 123 type instPlacement struct { 124 Zone *google.AvailabilityZone 125 } 126 127 // parsePlacement extracts the availability zone from the placement 128 // string and returns it. If no zone is found there then an error is 129 // returned. 130 func (env *environ) parsePlacement(placement string) (*instPlacement, error) { 131 if placement == "" { 132 return nil, nil 133 } 134 135 pos := strings.IndexRune(placement, '=') 136 if pos == -1 { 137 return nil, errors.Errorf("unknown placement directive: %v", placement) 138 } 139 140 switch key, value := placement[:pos], placement[pos+1:]; key { 141 case "zone": 142 zone, err := env.availZoneUp(value) 143 if err != nil { 144 return nil, errors.Trace(err) 145 } 146 return &instPlacement{Zone: zone}, nil 147 } 148 return nil, errors.Errorf("unknown placement directive: %v", placement) 149 } 150 151 // checkInstanceType is used to ensure the the provided constraints 152 // specify a recognized instance type. 153 func checkInstanceType(cons constraints.Value) bool { 154 // Constraint has an instance-type constraint so let's see if it is valid. 155 for _, itype := range allInstanceTypes { 156 if itype.Name == *cons.InstanceType { 157 return true 158 } 159 } 160 return false 161 }