1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"google.golang.org/api/compute/v1"
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
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
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
72
InstanceDisks(project, zone, instanceId string) ([]*compute.AttachedDisk, error)
75
// TODO(ericsnow) Add specific error types for common failures
76
// (e.g. BadRequest, RequestFailed, RequestError, ConnectionFailed)?
78
// Connection provides methods for interacting with the GCE API. The
79
// methods are limited to those needed by the juju GCE provider.
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
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)
99
return nil, errors.Trace(err)
104
region: connCfg.Region,
105
projectID: connCfg.ProjectID,
110
var newRawConnection = func(creds *Credentials) (*compute.Service, error) {
111
return newConnection(creds)
114
// TODO(ericsnow) Verify in each method that Connection.raw is set?
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)
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)
133
return nil, errors.Trace(err)
136
var zones []AvailabilityZone
137
for _, rawZone := range rawZones {
138
zones = append(zones, AvailabilityZone{rawZone})