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 }