github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  }