github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/google/conn_instance.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package google 5 6 import ( 7 "path" 8 9 "github.com/juju/errors" 10 "google.golang.org/api/compute/v1" 11 ) 12 13 // addInstance sends a request to GCE to add a new instance to the 14 // connection's project, with the provided instance data and machine 15 // type. Each of the provided zones is attempted and the first available 16 // zone is where the instance is provisioned. If no zones are available 17 // then an error is returned. The instance that was passed in is updated 18 // with the new instance's data upon success. The call blocks until the 19 // instance is created or the request fails. 20 // TODO(ericsnow) Return a new inst. 21 func (gce *Connection) addInstance(requestedInst *compute.Instance, machineType string, zones []string) error { 22 for _, zoneName := range zones { 23 var waitErr error 24 inst := *requestedInst 25 inst.MachineType = formatMachineType(zoneName, machineType) 26 err := gce.raw.AddInstance(gce.projectID, zoneName, &inst) 27 if isWaitError(err) { 28 waitErr = err 29 } else if err != nil { 30 // We are guaranteed the insert failed at the point. 31 return errors.Annotate(err, "sending new instance request") 32 } 33 34 // Check if the instance was created. 35 realized, err := gce.raw.GetInstance(gce.projectID, zoneName, inst.Name) 36 if err != nil { 37 if waitErr == nil { 38 return errors.Trace(err) 39 } 40 // Try the next zone. 41 logger.Errorf("failed to get new instance in zone %q: %v", zoneName, waitErr) 42 continue 43 } 44 45 // Success! 46 *requestedInst = *realized 47 return nil 48 } 49 return errors.Errorf("not able to provision in any zone") 50 } 51 52 // AddInstance creates a new instance based on the spec's data and 53 // returns it. The instance will be created using the provided 54 // connection and in one of the provided zones. 55 func (gce *Connection) AddInstance(spec InstanceSpec, zones ...string) (*Instance, error) { 56 raw := spec.raw() 57 if err := gce.addInstance(raw, spec.Type, zones); err != nil { 58 return nil, errors.Trace(err) 59 } 60 61 return newInstance(raw, &spec), nil 62 } 63 64 // Instance gets the up-to-date info about the given instance 65 // and returns it. 66 func (gce *Connection) Instance(id, zone string) (Instance, error) { 67 var result Instance 68 raw, err := gce.raw.GetInstance(gce.projectID, zone, id) 69 if err != nil { 70 return result, errors.Trace(err) 71 } 72 result = *newInstance(raw, nil) 73 return result, nil 74 } 75 76 // Instances sends a request to the GCE API for a list of all instances 77 // (in the Connection's project) for which the name starts with the 78 // provided prefix. The result is also limited to those instances with 79 // one of the specified statuses (if any). 80 func (gce *Connection) Instances(prefix string, statuses ...string) ([]Instance, error) { 81 rawInsts, err := gce.raw.ListInstances(gce.projectID, prefix, statuses...) 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 86 var insts []Instance 87 for _, rawInst := range rawInsts { 88 inst := newInstance(rawInst, nil) 89 insts = append(insts, *inst) 90 } 91 return insts, nil 92 } 93 94 // removeInstance sends a request to the GCE API to remove the instance 95 // with the provided ID (in the specified zone). The call blocks until 96 // the instance is removed (or the request fails). 97 func (gce *Connection) removeInstance(id, zone string) error { 98 err := gce.raw.RemoveInstance(gce.projectID, zone, id) 99 if err != nil { 100 // TODO(ericsnow) Try removing the firewall anyway? 101 return errors.Trace(err) 102 } 103 104 fwname := id 105 err = gce.raw.RemoveFirewall(gce.projectID, fwname) 106 if errors.IsNotFound(err) { 107 return nil 108 } 109 if err != nil { 110 return errors.Trace(err) 111 } 112 return nil 113 } 114 115 // RemoveInstances sends a request to the GCE API to terminate all 116 // instances (in the Connection's project) that match one of the 117 // provided IDs. If a prefix is provided, only IDs that start with the 118 // prefix will be considered. The call blocks until all the instances 119 // are removed or the request fails. 120 func (gce *Connection) RemoveInstances(prefix string, ids ...string) error { 121 if len(ids) == 0 { 122 return nil 123 } 124 125 instances, err := gce.Instances(prefix) 126 if err != nil { 127 return errors.Annotatef(err, "while removing instances %v", ids) 128 } 129 130 // TODO(ericsnow) Remove instances in parallel? 131 var failed []string 132 for _, instID := range ids { 133 for _, inst := range instances { 134 if inst.ID == instID { 135 zoneName := path.Base(inst.InstanceSummary.ZoneName) 136 if err := gce.removeInstance(instID, zoneName); err != nil { 137 failed = append(failed, instID) 138 logger.Errorf("while removing instance %q: %v", instID, err) 139 } 140 break 141 } 142 } 143 } 144 if len(failed) != 0 { 145 return errors.Errorf("some instance removals failed: %v", failed) 146 } 147 return nil 148 }