Add support for Listing Servers.
Up to now, all you could do is authenticate with Gophercloud. Now, you
can list your servers too!
This is a pretty straight-ahead port of logic from Gorax to Gophercloud.
After you gain access to the ComputeApi, you simply invoke
ListServers(). This returns a slice of Server structures, one each for
the servers your account has access to. See the 02-list-servers.go
acceptance test.
Note that no unit-level tests exist for this functionality. Writing a
unit test for this code would only serve to test the compiler's ability
to emit bug-free machine code. (Observe ListServers() has no control
flow points, and all its dependencies already have their own unit
tests.)
Being that this is a straight-ahead port from Gorax, some things remain
to be done:
1) Provide an API for accessing Rackspace-specific fields. (#34)
2) Assuming (1) is successful, remove Rack-specific fields from Server
and other structures of interest. (#34)
3) Rename ComputeApi to something more descriptive -- like ServerApi().
(#33)
4) Implement token re-authentication logic. (#12)
diff --git a/authenticate.go b/authenticate.go
index d460673..8b87e44 100644
--- a/authenticate.go
+++ b/authenticate.go
@@ -134,3 +134,8 @@
urls := []string{ep.PublicURL, ep.InternalURL}
return urls[ac.UrlChoice]
}
+
+// See AccessProvider interface definition for details.
+func (a *Access) AuthToken() string {
+ return a.Token.Id
+}
diff --git a/common_types.go b/common_types.go
new file mode 100644
index 0000000..4e3d32c
--- /dev/null
+++ b/common_types.go
@@ -0,0 +1,9 @@
+package gophercloud
+
+// Link is used for JSON (un)marshalling.
+// It provides RESTful links to a resource.
+type Link struct {
+ Href string `json:"href"`
+ Rel string `json:"rel"`
+ Type string `json:"type"`
+}
diff --git a/context.go b/context.go
index 71ff757..1b5d390 100644
--- a/context.go
+++ b/context.go
@@ -87,6 +87,7 @@
gcp := &genericCloudProvider{
endpoint: url,
context: c,
+ access: acc,
}
return gcp, nil
diff --git a/flavors.go b/flavors.go
new file mode 100644
index 0000000..909a308
--- /dev/null
+++ b/flavors.go
@@ -0,0 +1,9 @@
+package gophercloud
+
+// FlavorLink provides a reference to a flavor by either ID or by direct URL.
+// Some services use just the ID, others use just the URL.
+// This structure provides a common means of expressing both in a single field.
+type FlavorLink struct {
+ Id string `json:"id"`
+ Links []Link `json:"links"`
+}
diff --git a/images.go b/images.go
new file mode 100644
index 0000000..95dad59
--- /dev/null
+++ b/images.go
@@ -0,0 +1,9 @@
+package gophercloud
+
+// ImageLink provides a reference to a image by either ID or by direct URL.
+// Some services use just the ID, others use just the URL.
+// This structure provides a common means of expressing both in a single field.
+type ImageLink struct {
+ Id string `json:"id"`
+ Links []Link `json:"links"`
+}
diff --git a/interfaces.go b/interfaces.go
index 671fa1a..63439c5 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -8,6 +8,11 @@
// endpoint, depending on both its existence and the setting of the ApiCriteria.UrlChoice
// field.
FirstEndpointUrlByCriteria(ApiCriteria) string
+
+ // TODO(sfalvo): get Token() to automatically renew the authentication token if it's near expiry.
+
+ // AuthToken provides a copy of the current authentication token for the user's credentials.
+ AuthToken() string
}
// ComputeProvider instances encapsulate a Cloud Servers API, should one exist in the service catalog
diff --git a/servers.go b/servers.go
index f6214c0..e3bc547 100644
--- a/servers.go
+++ b/servers.go
@@ -1,5 +1,12 @@
+// TODO(sfalvo): Remove Rackspace-specific Server structure fields and refactor them into a provider-specific access method.
+// Be sure to update godocs accordingly.
+
package gophercloud
+import (
+ "github.com/racker/perigee"
+)
+
// genericCloudProvider structures provide the implementation for generic OpenStack-compatible
// ComputeProvider interfaces.
type genericCloudProvider struct {
@@ -9,14 +16,148 @@
// Test context (if any) in which to issue requests.
context *Context
+
+ // access associates this API provider with a set of credentials,
+ // which may be automatically renewed if they near expiration.
+ access AccessProvider
}
// See the ComputeProvider interface for details.
func (gcp *genericCloudProvider) ListServers() ([]Server, error) {
- return nil, nil
+ var ss []Server
+
+ url := gcp.endpoint + "/servers"
+ err := perigee.Get(url, perigee.Options{
+ CustomClient: gcp.context.httpClient,
+ Results: &struct{ Servers *[]Server }{&ss},
+ MoreHeaders: map[string]string{
+ "X-Auth-Token": gcp.access.AuthToken(),
+ },
+ })
+ return ss, err
}
-// Server structures provide data about a server running in your provider's cloud.
+// RaxBandwidth provides measurement of server bandwidth consumed over a given audit interval.
+type RaxBandwidth struct {
+ AuditPeriodEnd string `json:"audit_period_end"`
+ AuditPeriodStart string `json:"audit_period_start"`
+ BandwidthInbound int64 `json:"bandwidth_inbound"`
+ BandwidthOutbound int64 `json:"bandwidth_outbound"`
+ Interface string `json:"interface"`
+}
+
+// A VersionedAddress denotes either an IPv4 or IPv6 (depending on version indicated)
+// address.
+type VersionedAddress struct {
+ Addr string `json:"addr"`
+ Version int `json:"version"`
+}
+
+// An AddressSet provides a set of public and private IP addresses for a resource.
+// Each address has a version to identify if IPv4 or IPv6.
+type AddressSet struct {
+ Public []VersionedAddress `json:"public"`
+ Private []VersionedAddress `json:"private"`
+}
+
+// Server records represent (virtual) hardware instances (not configurations) accessible by the user.
+//
+// The AccessIPv4 / AccessIPv6 fields provides IP addresses for the server in the IPv4 or IPv6 format, respectively.
+//
+// Addresses provides addresses for any attached isolated networks.
+// The version field indicates whether the IP address is version 4 or 6.
+//
+// Created tells when the server entity was created.
+//
+// The Flavor field includes the flavor ID and flavor links.
+//
+// The compute provisioning algorithm has an anti-affinity property that
+// attempts to spread customer VMs across hosts.
+// Under certain situations,
+// VMs from the same customer might be placed on the same host.
+// The HostId field represents the host your server runs on and
+// can be used to determine this scenario if it is relevant to your application.
+// Note that HostId is unique only per account; it is not globally unique.
+//
+// Id provides the server's unique identifier.
+// This field must be treated opaquely.
+//
+// Image indicates which image is installed on the server.
+//
+// Links provides one or more means of accessing the server.
+//
+// Metadata provides a small key-value store for application-specific information.
+//
+// Name provides a human-readable name for the server.
+//
+// Progress indicates how far along it is towards being provisioned.
+// 100 represents complete, while 0 represents just beginning.
+//
+// Status provides an indication of what the server's doing at the moment.
+// A server will be in ACTIVE state if it's ready for use.
+//
+// OsDcfDiskConfig indicates the server's boot volume configuration.
+// Valid values are:
+// AUTO
+// ----
+// The server is built with a single partition the size of the target flavor disk.
+// The file system is automatically adjusted to fit the entire partition.
+// This keeps things simple and automated.
+// AUTO is valid only for images and servers with a single partition that use the EXT3 file system.
+// This is the default setting for applicable Rackspace base images.
+//
+// MANUAL
+// ------
+// The server is built using whatever partition scheme and file system is in the source image.
+// If the target flavor disk is larger,
+// the remaining disk space is left unpartitioned.
+// This enables images to have non-EXT3 file systems, multiple partitions, and so on,
+// and enables you to manage the disk configuration.
+//
+// RaxBandwidth provides measures of the server's inbound and outbound bandwidth per interface.
+//
+// OsExtStsPowerState provides an indication of the server's power.
+// This field appears to be a set of flag bits:
+//
+// ... 4 3 2 1 0
+// +--//--+---+---+---+---+
+// | .... | 0 | S | 0 | I |
+// +--//--+---+---+---+---+
+// | |
+// | +--- 0=Instance is down.
+// | 1=Instance is up.
+// |
+// +----------- 0=Server is switched ON.
+// 1=Server is switched OFF.
+// (note reverse logic.)
+//
+// Unused bits should be ignored when read, and written as 0 for future compatibility.
+//
+// OsExtStsTaskState and OsExtStsVmState work together
+// to provide visibility in the provisioning process for the instance.
+// Consult Rackspace documentation at
+// http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ch_extensions.html#ext_status
+// for more details. It's too lengthy to include here.
type Server struct {
- Id string
+ AccessIPv4 string `json:"accessIPv4"`
+ AccessIPv6 string `json:"accessIPv6"`
+ Addresses AddressSet `json:"addresses"`
+ Created string `json:"created"`
+ Flavor FlavorLink `json:"flavor"`
+ HostId string `json:"hostId"`
+ Id string `json:"id"`
+ Image ImageLink `json:"image"`
+ Links []Link `json:"links"`
+ Metadata interface{} `json:"metadata"`
+ Name string `json:"name"`
+ Progress int `json:"progress"`
+ Status string `json:"status"`
+ TenantId string `json:"tenant_id"`
+ Updated string `json:"updated"`
+ UserId string `json:"user_id"`
+ OsDcfDiskConfig string `json:"OS-DCF:diskConfig"`
+ RaxBandwidth []RaxBandwidth `json:"rax-bandwidth:bandwidth"`
+ OsExtStsPowerState int `json:"OS-EXT-STS:power_state"`
+ OsExtStsTaskState string `json:"OS-EXT-STS:task_state"`
+ OsExtStsVmState string `json:"OS-EXT-STS:vm_state"`
}
diff --git a/servers_test.go b/servers_test.go
index 206290f..e296801 100644
--- a/servers_test.go
+++ b/servers_test.go
@@ -16,6 +16,10 @@
return urls[ac.UrlChoice]
}
+func (ta *testAccess) AuthToken() string {
+ return ""
+}
+
func TestGetServersApi(t *testing.T) {
c := TestContext().UseCustomClient(&http.Client{Transport: newTransport().WithResponse("Hello")})