github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/google/conn.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  	"github.com/juju/errors"
     8  	"google.golang.org/api/compute/v1"
     9  )
    10  
    11  // rawConnectionWrapper facilitates mocking out the GCE API during tests.
    12  type rawConnectionWrapper interface {
    13  	// GetProject sends a request to the GCE API for info about the
    14  	// specified project. If the project does not exist then an error
    15  	// will be returned.
    16  	GetProject(projectID string) (*compute.Project, error)
    17  	// GetInstance sends a request to the GCE API for info about the
    18  	// specified instance. If the instance does not exist then an error
    19  	// will be returned.
    20  	GetInstance(projectID, id, zone string) (*compute.Instance, error)
    21  	// ListInstances sends a request to the GCE API for a list of all
    22  	// instances in project for which the name starts with the provided
    23  	// prefix. The result is also limited to those instances with one of
    24  	// the specified statuses (if any).
    25  	ListInstances(projectID, prefix string, status ...string) ([]*compute.Instance, error)
    26  	// AddInstance sends a request to GCE to add a new instance to the
    27  	// given project, with the provided instance data. The call blocks
    28  	// until the instance is created or the request fails.
    29  	AddInstance(projectID, zone string, spec *compute.Instance) error
    30  	// RemoveInstance sends a request to the GCE API to remove the instance
    31  	// with the provided ID (in the specified zone). The call blocks until
    32  	// the instance is removed (or the request fails).
    33  	RemoveInstance(projectID, id, zone string) error
    34  	// GetFirewall sends an API request to GCE for the information about
    35  	// the named firewall and returns it. If the firewall is not found,
    36  	// errors.NotFound is returned.
    37  	GetFirewall(projectID, name string) (*compute.Firewall, error)
    38  	// AddFirewall requests GCE to add a firewall with the provided info.
    39  	// If the firewall already exists then an error will be returned.
    40  	// The call blocks until the firewall is added or the request fails.
    41  	AddFirewall(projectID string, firewall *compute.Firewall) error
    42  	// UpdateFirewall requests GCE to update the named firewall with the
    43  	// provided info, overwriting the existing data. If the firewall does
    44  	// not exist then an error will be returned. The call blocks until the
    45  	// firewall is updated or the request fails.
    46  	UpdateFirewall(projectID, name string, firewall *compute.Firewall) error
    47  	// RemoveFirewall removed the named firewall from the project. If it
    48  	// does not exist then this is a noop. The call blocks until the
    49  	// firewall is added or the request fails.
    50  	RemoveFirewall(projectID, name string) error
    51  	// ListAvailabilityZones returns the list of availability zones for a given
    52  	// GCE region. If none are found the the list is empty. Any failure in
    53  	// the low-level request is returned as an error.
    54  	ListAvailabilityZones(projectID, region string) ([]*compute.Zone, error)
    55  	// CreateDisk will create a gce Persistent Block device that matches
    56  	// the specified in spec.
    57  	CreateDisk(project, zone string, spec *compute.Disk) error
    58  	// ListDisks returns a list of disks available for a given project.
    59  	ListDisks(project, zone string) ([]*compute.Disk, error)
    60  	// RemoveDisk will delete the disk identified by id.
    61  	RemoveDisk(project, zone, id string) error
    62  	// GetDisk will return the disk correspondent to the passed id.
    63  	GetDisk(project, zone, id string) (*compute.Disk, error)
    64  	// AttachDisk will attach the disk described in attachedDisks (if it exists) into
    65  	// the instance with id instanceId.
    66  	AttachDisk(project, zone, instanceId string, attachedDisk *compute.AttachedDisk) error
    67  	// Detach disk detaches device diskDeviceName (if it exists and its attached)
    68  	// form the machine with id instanceId.
    69  	DetachDisk(project, zone, instanceId, diskDeviceName string) error
    70  	// InstanceDisks returns the disks attached to the instance identified
    71  	// by instanceId
    72  	InstanceDisks(project, zone, instanceId string) ([]*compute.AttachedDisk, error)
    73  }
    74  
    75  // TODO(ericsnow) Add specific error types for common failures
    76  // (e.g. BadRequest, RequestFailed, RequestError, ConnectionFailed)?
    77  
    78  // Connection provides methods for interacting with the GCE API. The
    79  // methods are limited to those needed by the juju GCE provider.
    80  //
    81  // Before calling any of the methods, the Connect method should be
    82  // called to authenticate and open the raw connection to the GCE API.
    83  // Otherwise a panic will result.
    84  type Connection struct {
    85  	// TODO(ericsnow) name this something else?
    86  	raw       rawConnectionWrapper
    87  	region    string
    88  	projectID string
    89  }
    90  
    91  // Connect authenticates using the provided credentials and opens a
    92  // low-level connection to the GCE API for the Connection. Calling
    93  // Connect after a successful connection has already been made will
    94  // result in an error. All errors that happen while authenticating and
    95  // connecting are returned by Connect.
    96  func Connect(connCfg ConnectionConfig, creds *Credentials) (*Connection, error) {
    97  	raw, err := newRawConnection(creds)
    98  	if err != nil {
    99  		return nil, errors.Trace(err)
   100  	}
   101  
   102  	conn := &Connection{
   103  		raw:       &rawConn{raw},
   104  		region:    connCfg.Region,
   105  		projectID: connCfg.ProjectID,
   106  	}
   107  	return conn, nil
   108  }
   109  
   110  var newRawConnection = func(creds *Credentials) (*compute.Service, error) {
   111  	return newConnection(creds)
   112  }
   113  
   114  // TODO(ericsnow) Verify in each method that Connection.raw is set?
   115  
   116  // VerifyCredentials ensures that the authentication credentials used
   117  // to connect are valid for use in the project and region defined for
   118  // the Connection. If they are not then an error is returned.
   119  func (gc Connection) VerifyCredentials() error {
   120  	if _, err := gc.raw.GetProject(gc.projectID); err != nil {
   121  		// TODO(ericsnow) Wrap err with something about bad credentials?
   122  		return errors.Trace(err)
   123  	}
   124  	return nil
   125  }
   126  
   127  // AvailabilityZones returns the list of availability zones for a given
   128  // GCE region. If none are found the the list is empty. Any failure in
   129  // the low-level request is returned as an error.
   130  func (gc *Connection) AvailabilityZones(region string) ([]AvailabilityZone, error) {
   131  	rawZones, err := gc.raw.ListAvailabilityZones(gc.projectID, region)
   132  	if err != nil {
   133  		return nil, errors.Trace(err)
   134  	}
   135  
   136  	var zones []AvailabilityZone
   137  	for _, rawZone := range rawZones {
   138  		zones = append(zones, AvailabilityZone{rawZone})
   139  	}
   140  	return zones, nil
   141  }