Merge pull request #267 from smashwilson/unbreak-acceptance

Unbreak Acceptance Again
diff --git a/acceptance/openstack/compute/v2/bootfromvolume_test.go b/acceptance/openstack/compute/v2/bootfromvolume_test.go
new file mode 100644
index 0000000..d08abe6
--- /dev/null
+++ b/acceptance/openstack/compute/v2/bootfromvolume_test.go
@@ -0,0 +1,50 @@
+// +build acceptance
+
+package v2
+
+import (
+	"testing"
+
+	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
+	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/smashwilson/gophercloud/acceptance/tools"
+)
+
+func TestBootFromVolume(t *testing.T) {
+	client, err := newClient()
+	th.AssertNoErr(t, err)
+
+	if testing.Short() {
+		t.Skip("Skipping test that requires server creation in short mode.")
+	}
+
+	choices, err := ComputeChoicesFromEnv()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	name := tools.RandomString("Gophercloud-", 8)
+	t.Logf("Creating server [%s].", name)
+
+	bd := []bootfromvolume.BlockDevice{
+		bootfromvolume.BlockDevice{
+			UUID:       choices.ImageID,
+			SourceType: bootfromvolume.Image,
+			VolumeSize: 10,
+		},
+	}
+
+	serverCreateOpts := servers.CreateOpts{
+		Name:      name,
+		FlavorRef: "3",
+	}
+	server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
+		serverCreateOpts,
+		bd,
+	}).Extract()
+	th.AssertNoErr(t, err)
+	t.Logf("Created server: %+v\n", server)
+	//defer deleteServer(t, client, server)
+	t.Logf("Deleting server [%s]...", name)
+}
diff --git a/acceptance/openstack/identity/v3/pkg.go b/acceptance/openstack/identity/v3/pkg.go
index d3b5573..eac3ae9 100644
--- a/acceptance/openstack/identity/v3/pkg.go
+++ b/acceptance/openstack/identity/v3/pkg.go
@@ -1,2 +1 @@
-// Package v3 contains acceptance tests for identity v3 resources.
 package v3
diff --git a/acceptance/rackspace/compute/v2/bootfromvolume_test.go b/acceptance/rackspace/compute/v2/bootfromvolume_test.go
new file mode 100644
index 0000000..010bf42
--- /dev/null
+++ b/acceptance/rackspace/compute/v2/bootfromvolume_test.go
@@ -0,0 +1,46 @@
+// +build acceptance
+
+package v2
+
+import (
+	"testing"
+
+	osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
+	"github.com/rackspace/gophercloud/rackspace/compute/v2/bootfromvolume"
+	"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/smashwilson/gophercloud/acceptance/tools"
+)
+
+func TestBootFromVolume(t *testing.T) {
+	client, err := newClient()
+	th.AssertNoErr(t, err)
+
+	if testing.Short() {
+		t.Skip("Skipping test that requires server creation in short mode.")
+	}
+
+	options, err := optionsFromEnv()
+	th.AssertNoErr(t, err)
+
+	name := tools.RandomString("Gophercloud-", 8)
+	t.Logf("Creating server [%s].", name)
+
+	bd := []osBFV.BlockDevice{
+		osBFV.BlockDevice{
+			UUID:       options.imageID,
+			SourceType: osBFV.Image,
+			VolumeSize: 10,
+		},
+	}
+
+	server, err := bootfromvolume.Create(client, servers.CreateOpts{
+		Name:        name,
+		FlavorRef:   "performance1-1",
+		BlockDevice: bd,
+	}).Extract()
+	th.AssertNoErr(t, err)
+	t.Logf("Created server: %+v\n", server)
+	//defer deleteServer(t, client, server)
+	t.Logf("Deleting server [%s]...", name)
+}
diff --git a/auth_options.go b/auth_options.go
index f9e6ee5..bc0ef65 100644
--- a/auth_options.go
+++ b/auth_options.go
@@ -1,24 +1,26 @@
 package gophercloud
 
-// AuthOptions lets anyone calling Authenticate() supply the required access credentials.
-// Its fields are the union of those recognized by each identity implementation and provider.
+// AuthOptions allows anyone calling Authenticate to supply the required access
+// credentials. Its fields are the union of those recognized by each identity
+// implementation and provider.
 type AuthOptions struct {
-
-	// IdentityEndpoint specifies the HTTP endpoint offering the Identity API of the appropriate version.
-	// Required by the identity services, but often populated by a provider Client.
+	// IdentityEndpoint specifies the HTTP endpoint that is required to work with
+	// the Identity API of the appropriate version. Required by the identity
+	// services, but often populated by a provider Client.
 	IdentityEndpoint string
 
-	// Username is required if using Identity V2 API.
-	// Consult with your provider's control panel to discover your account's username.
-	// In Identity V3, either UserID or a combination of Username and DomainID or DomainName.
+	// Username is required if using Identity V2 API. Consult with your provider's
+	// control panel to discover your account's username. In Identity V3, either
+	// UserID or a combination of Username and DomainID or DomainName.
 	Username, UserID string
 
-	// Exactly one of Password or ApiKey is required for the Identity V2 and V3 APIs.
-	// Consult with your provider's control panel to discover your account's preferred method of authentication.
+	// Exactly one of Password or ApiKey is required for the Identity V2 and V3
+	// APIs. Consult with your provider's control panel to discover your account's
+	// preferred method of authentication.
 	Password, APIKey string
 
-	// At most one of DomainID and DomainName must be provided if using Username with Identity V3.
-	// Otherwise, either are optional.
+	// At most one of DomainID and DomainName must be provided if using Username
+	// with Identity V3. Otherwise, either are optional.
 	DomainID, DomainName string
 
 	// The TenantID and TenantName fields are optional for the Identity V2 API.
diff --git a/auth_results.go b/auth_results.go
index 07e0fc7..1a1faa5 100644
--- a/auth_results.go
+++ b/auth_results.go
@@ -5,10 +5,9 @@
 // AuthResults encapsulates the raw results from an authentication request. As OpenStack allows
 // extensions to influence the structure returned in ways that Gophercloud cannot predict at
 // compile-time, you should use type-safe accessors to work with the data represented by this type,
-// such as ServiceCatalog() and TokenID().
+// such as ServiceCatalog and TokenID.
 type AuthResults interface {
-
-	// Retrieve the authentication token's value from the authentication response.
+	// TokenID returns the token's ID value from the authentication response.
 	TokenID() (string, error)
 
 	// ExpiresAt retrieves the token's expiration time.
diff --git a/endpoint_search.go b/endpoint_search.go
index 828bcfd..b6f6b48 100644
--- a/endpoint_search.go
+++ b/endpoint_search.go
@@ -10,9 +10,9 @@
 	ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.")
 )
 
-// Availability describes the accessibility of a specific service endpoint.
-// Identity v2 lists these as different kinds of URLs ("adminURL", "internalURL", and "publicURL"), while
-// v3 lists them as "Interfaces".
+// Availability indicates whether a specific service endpoint is accessible.
+// Identity v2 lists these as different kinds of URLs ("adminURL",
+// "internalURL", and "publicURL"), while v3 lists them as "Interfaces".
 type Availability string
 
 const (
@@ -28,31 +28,33 @@
 
 // EndpointOpts contains options for finding an endpoint for an Openstack client.
 type EndpointOpts struct {
-
 	// Type is the service type for the client (e.g., "compute", "object-store").
-	// Type is a required field.
+	// Required.
 	Type string
 
-	// Name is the service name for the client (e.g., "nova").
-	// Name is not a required field, but it is used if present.
-	// Services can have the same Type but a different Name, which is one example of when both Type and Name are needed.
+	// Name is the service name for the client (e.g., "nova") as it appears in
+	// the service catalog. Services can have the same Type but a different Name,
+	// which is why both Type and Name are sometimes needed. Optional.
 	Name string
 
-	// Region is the region in which the service resides.
-	// Region must be specified for services that span multiple regions.
+	// Region is the geographic region in which the service resides. Required only
+	// for services that span multiple regions.
 	Region string
 
-	// Availability is the visibility of the endpoint to be returned: AvailabilityPublic, AvailabilityInternal, or AvailabilityAdmin.
+	// Availability is the visibility of the endpoint to be returned. Valid types
+	// are: AvailabilityPublic, AvailabilityInternal, or AvailabilityAdmin.
 	// Availability is not required, and defaults to AvailabilityPublic.
 	// Not all providers or services offer all Availability options.
 	Availability Availability
 }
 
-// EndpointLocator is a function that describes how to locate a single endpoint from a service catalog for a specific ProviderClient.
-// It should be set during ProviderClient authentication and used to discover related ServiceClients.
+// EndpointLocator is a function that describes how to locate a single endpoint
+// from a service catalog for a specific ProviderClient. It should be set
+// during ProviderClient authentication and used to discover related ServiceClients.
 type EndpointLocator func(EndpointOpts) (string, error)
 
-// ApplyDefaults sets EndpointOpts fields if not already set. Currently, EndpointOpts.Availability defaults to the public endpoint.
+// ApplyDefaults sets EndpointOpts fields if not already set. Currently,
+// EndpointOpts.Availability defaults to the public endpoint.
 func (eo *EndpointOpts) ApplyDefaults(t string) {
 	if eo.Type == "" {
 		eo.Type = t
diff --git a/openstack/blockstorage/v1/apiversions/doc.go b/openstack/blockstorage/v1/apiversions/doc.go
index c3c486f..e3af39f 100644
--- a/openstack/blockstorage/v1/apiversions/doc.go
+++ b/openstack/blockstorage/v1/apiversions/doc.go
@@ -1,3 +1,3 @@
 // Package apiversions provides information and interaction with the different
-// API versions for the OpenStack Cinder service.
+// API versions for the OpenStack Block Storage service, code-named Cinder.
 package apiversions
diff --git a/openstack/blockstorage/v1/snapshots/doc.go b/openstack/blockstorage/v1/snapshots/doc.go
new file mode 100644
index 0000000..198f830
--- /dev/null
+++ b/openstack/blockstorage/v1/snapshots/doc.go
@@ -0,0 +1,5 @@
+// Package snapshots provides information and interaction with snapshots in the
+// OpenStack Block Storage service. A snapshot is a point in time copy of the
+// data contained in an external storage volume, and can be controlled
+// programmatically.
+package snapshots
diff --git a/openstack/blockstorage/v1/volumes/doc.go b/openstack/blockstorage/v1/volumes/doc.go
new file mode 100644
index 0000000..307b8b1
--- /dev/null
+++ b/openstack/blockstorage/v1/volumes/doc.go
@@ -0,0 +1,5 @@
+// Package volumes provides information and interaction with volumes in the
+// OpenStack Block Storage service. A volume is a detachable block storage
+// device, akin to a USB hard drive. It can only be attached to one instance at
+// a time.
+package volumes
diff --git a/openstack/blockstorage/v1/volumetypes/doc.go b/openstack/blockstorage/v1/volumetypes/doc.go
new file mode 100644
index 0000000..793084f
--- /dev/null
+++ b/openstack/blockstorage/v1/volumetypes/doc.go
@@ -0,0 +1,9 @@
+// Package volumetypes provides information and interaction with volume types
+// in the OpenStack Block Storage service. A volume type indicates the type of
+// a block storage volume, such as SATA, SCSCI, SSD, etc. These can be
+// customized or defined by the OpenStack admin.
+//
+// You can also define extra_specs associated with your volume types. For
+// instance, you could have a VolumeType=SATA, with extra_specs (RPM=10000,
+// RAID-Level=5) . Extra_specs are defined and customized by the admin.
+package volumetypes
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests.go b/openstack/compute/v2/extensions/bootfromvolume/requests.go
new file mode 100644
index 0000000..5a976d1
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests.go
@@ -0,0 +1,111 @@
+package bootfromvolume
+
+import (
+	"errors"
+	"strconv"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+
+	"github.com/racker/perigee"
+)
+
+// SourceType represents the type of medium being used to create the volume.
+type SourceType string
+
+const (
+	Volume   SourceType = "volume"
+	Snapshot SourceType = "snapshot"
+	Image    SourceType = "image"
+)
+
+// BlockDevice is a structure with options for booting a server instance
+// from a volume. The volume may be created from an image, snapshot, or another
+// volume.
+type BlockDevice struct {
+	// BootIndex [optional] is the boot index. It defaults to 0.
+	BootIndex int `json:"boot_index"`
+
+	// DeleteOnTermination [optional] specifies whether or not to delete the attached volume
+	// when the server is deleted. Defaults to `false`.
+	DeleteOnTermination bool `json:"delete_on_termination"`
+
+	// DestinationType [optional] is the type that gets created. Possible values are "volume"
+	// and "local".
+	DestinationType string `json:"destination_type"`
+
+	// SourceType [required] must be one of: "volume", "snapshot", "image".
+	SourceType SourceType `json:"source_type"`
+
+	// UUID [required] is the unique identifier for the volume, snapshot, or image (see above)
+	UUID string `json:"uuid"`
+
+	// VolumeSize [optional] is the size of the volume to create (in gigabytes).
+	VolumeSize int `json:"volume_size"`
+}
+
+// CreateOptsExt is a structure that extends the server `CreateOpts` structure
+// by allowing for a block device mapping.
+type CreateOptsExt struct {
+	servers.CreateOptsBuilder
+	BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"`
+}
+
+// ToServerCreateMap adds the block device mapping option to the base server
+// creation options.
+func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
+	base, err := opts.CreateOptsBuilder.ToServerCreateMap()
+	if err != nil {
+		return nil, err
+	}
+
+	if len(opts.BlockDevice) == 0 {
+		return nil, errors.New("Required fields UUID and SourceType not set.")
+	}
+
+	serverMap := base["server"].(map[string]interface{})
+
+	blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
+
+	for i, bd := range opts.BlockDevice {
+		if string(bd.SourceType) == "" {
+			return nil, errors.New("SourceType must be one of: volume, image, snapshot.")
+		}
+
+		blockDevice[i] = make(map[string]interface{})
+
+		blockDevice[i]["source_type"] = bd.SourceType
+		blockDevice[i]["boot_index"] = strconv.Itoa(bd.BootIndex)
+		blockDevice[i]["delete_on_termination"] = strconv.FormatBool(bd.DeleteOnTermination)
+		blockDevice[i]["volume_size"] = strconv.Itoa(bd.VolumeSize)
+		if bd.UUID != "" {
+			blockDevice[i]["uuid"] = bd.UUID
+		}
+		if bd.DestinationType != "" {
+			blockDevice[i]["destination_type"] = bd.DestinationType
+		}
+
+	}
+	serverMap["block_device_mapping_v2"] = blockDevice
+
+	return base, nil
+}
+
+// Create requests the creation of a server from the given block device mapping.
+func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) servers.CreateResult {
+	var res servers.CreateResult
+
+	reqBody, err := opts.ToServerCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
+		MoreHeaders: client.AuthenticatedHeaders(),
+		ReqBody:     reqBody,
+		Results:     &res.Body,
+		OkCodes:     []int{200, 202},
+	})
+	return res
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/requests_test.go b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
new file mode 100644
index 0000000..5bf9137
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/requests_test.go
@@ -0,0 +1,51 @@
+package bootfromvolume
+
+import (
+	"testing"
+
+	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestCreateOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	ext := CreateOptsExt{
+		CreateOptsBuilder: base,
+		BlockDevice: []BlockDevice{
+			BlockDevice{
+				UUID:            "123456",
+				SourceType:      Image,
+				DestinationType: "volume",
+				VolumeSize:      10,
+			},
+		},
+	}
+
+	expected := `
+    {
+      "server": {
+        "name": "createdserver",
+        "imageRef": "asdfasdfasdf",
+        "flavorRef": "performance1-1",
+        "block_device_mapping_v2":[
+          {
+            "uuid":"123456",
+            "source_type":"image",
+            "destination_type":"volume",
+            "boot_index": "0",
+            "delete_on_termination": "false",
+            "volume_size": "10"
+          }
+        ]
+      }
+    }
+  `
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/results.go b/openstack/compute/v2/extensions/bootfromvolume/results.go
new file mode 100644
index 0000000..f60329f
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/results.go
@@ -0,0 +1,10 @@
+package bootfromvolume
+
+import (
+	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+)
+
+// CreateResult temporarily contains the response from a Create call.
+type CreateResult struct {
+	os.CreateResult
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls.go b/openstack/compute/v2/extensions/bootfromvolume/urls.go
new file mode 100644
index 0000000..0cffe25
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/urls.go
@@ -0,0 +1,7 @@
+package bootfromvolume
+
+import "github.com/rackspace/gophercloud"
+
+func createURL(c *gophercloud.ServiceClient) string {
+	return c.ServiceURL("os-volumes_boot")
+}
diff --git a/openstack/compute/v2/extensions/bootfromvolume/urls_test.go b/openstack/compute/v2/extensions/bootfromvolume/urls_test.go
new file mode 100644
index 0000000..6ee6477
--- /dev/null
+++ b/openstack/compute/v2/extensions/bootfromvolume/urls_test.go
@@ -0,0 +1,16 @@
+package bootfromvolume
+
+import (
+	"testing"
+
+	th "github.com/rackspace/gophercloud/testhelper"
+	"github.com/rackspace/gophercloud/testhelper/client"
+)
+
+func TestCreateURL(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	c := client.ServiceClient()
+
+	th.CheckEquals(t, c.Endpoint+"os-volumes_boot", createURL(c))
+}
diff --git a/openstack/compute/v2/extensions/diskconfig/doc.go b/openstack/compute/v2/extensions/diskconfig/doc.go
new file mode 100644
index 0000000..a971b35
--- /dev/null
+++ b/openstack/compute/v2/extensions/diskconfig/doc.go
@@ -0,0 +1,3 @@
+// Package diskconfig provides information and interaction with the the Disk
+// Config extension that works with the OpenStack Compute service.
+package diskconfig
diff --git a/openstack/compute/v2/extensions/diskconfig/requests.go b/openstack/compute/v2/extensions/diskconfig/requests.go
index 06a922a..7407e0d 100644
--- a/openstack/compute/v2/extensions/diskconfig/requests.go
+++ b/openstack/compute/v2/extensions/diskconfig/requests.go
@@ -41,17 +41,24 @@
 	servers.CreateOptsBuilder
 
 	// DiskConfig [optional] controls how the created server's disk is partitioned.
-	DiskConfig DiskConfig
+	DiskConfig DiskConfig `json:"OS-DCF:diskConfig,omitempty"`
 }
 
 // ToServerCreateMap adds the diskconfig option to the base server creation options.
-func (opts CreateOptsExt) ToServerCreateMap() map[string]interface{} {
-	base := opts.CreateOptsBuilder.ToServerCreateMap()
+func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
+	base, err := opts.CreateOptsBuilder.ToServerCreateMap()
+	if err != nil {
+		return nil, err
+	}
+
+	if string(opts.DiskConfig) == "" {
+		return base, nil
+	}
 
 	serverMap := base["server"].(map[string]interface{})
 	serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig)
 
-	return base
+	return base, nil
 }
 
 // RebuildOptsExt adds a DiskConfig option to the base RebuildOpts.
diff --git a/openstack/compute/v2/extensions/diskconfig/requests_test.go b/openstack/compute/v2/extensions/diskconfig/requests_test.go
index 1f4f626..e3c26d4 100644
--- a/openstack/compute/v2/extensions/diskconfig/requests_test.go
+++ b/openstack/compute/v2/extensions/diskconfig/requests_test.go
@@ -29,7 +29,9 @@
 			}
 		}
 	`
-	th.CheckJSONEquals(t, expected, ext.ToServerCreateMap())
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
 }
 
 func TestRebuildOpts(t *testing.T) {
diff --git a/openstack/compute/v2/extensions/doc.go b/openstack/compute/v2/extensions/doc.go
new file mode 100644
index 0000000..2b447da
--- /dev/null
+++ b/openstack/compute/v2/extensions/doc.go
@@ -0,0 +1,3 @@
+// Package extensions provides information and interaction with the
+// different extensions available for the OpenStack Compute service.
+package extensions
diff --git a/openstack/compute/v2/extensions/keypairs/doc.go b/openstack/compute/v2/extensions/keypairs/doc.go
new file mode 100644
index 0000000..856f41b
--- /dev/null
+++ b/openstack/compute/v2/extensions/keypairs/doc.go
@@ -0,0 +1,3 @@
+// Package keypairs provides information and interaction with the Keypairs
+// extension for the OpenStack Compute service.
+package keypairs
diff --git a/openstack/compute/v2/flavors/doc.go b/openstack/compute/v2/flavors/doc.go
new file mode 100644
index 0000000..5822e1b
--- /dev/null
+++ b/openstack/compute/v2/flavors/doc.go
@@ -0,0 +1,7 @@
+// Package flavors provides information and interaction with the flavor API
+// resource in the OpenStack Compute service.
+//
+// A flavor is an available hardware configuration for a server. Each flavor
+// has a unique combination of disk space, memory capacity and priority for CPU
+// time.
+package flavors
diff --git a/openstack/compute/v2/images/doc.go b/openstack/compute/v2/images/doc.go
new file mode 100644
index 0000000..0edaa3f
--- /dev/null
+++ b/openstack/compute/v2/images/doc.go
@@ -0,0 +1,7 @@
+// Package images provides information and interaction with the image API
+// resource in the OpenStack Compute service.
+//
+// An image is a collection of files used to create or rebuild a server.
+// Operators provide a number of pre-built OS images by default. You may also
+// create custom images from cloud servers you have launched.
+package images
diff --git a/openstack/compute/v2/servers/doc.go b/openstack/compute/v2/servers/doc.go
index 0a1791d..fe45671 100644
--- a/openstack/compute/v2/servers/doc.go
+++ b/openstack/compute/v2/servers/doc.go
@@ -1,3 +1,6 @@
-// Package servers provides convenient access to standard, OpenStack-defined
-// compute services.
+// Package servers provides information and interaction with the server API
+// resource in the OpenStack Compute service.
+//
+// A server is a virtual machine instance in the compute system. In order for
+// one to be provisioned, a valid flavor and image are required.
 package servers
diff --git a/openstack/compute/v2/servers/requests.go b/openstack/compute/v2/servers/requests.go
index c6eca11..544f816 100644
--- a/openstack/compute/v2/servers/requests.go
+++ b/openstack/compute/v2/servers/requests.go
@@ -79,7 +79,7 @@
 // CreateOptsBuilder describes struct types that can be accepted by the Create call.
 // The CreateOpts struct in this package does.
 type CreateOptsBuilder interface {
-	ToServerCreateMap() map[string]interface{}
+	ToServerCreateMap() (map[string]interface{}, error)
 }
 
 // Network is used within CreateOpts to control a new server's network attachments.
@@ -134,7 +134,7 @@
 }
 
 // ToServerCreateMap assembles a request body based on the contents of a CreateOpts.
-func (opts CreateOpts) ToServerCreateMap() map[string]interface{} {
+func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
 	server := make(map[string]interface{})
 
 	server["name"] = opts.Name
@@ -183,19 +183,26 @@
 		server["networks"] = networks
 	}
 
-	return map[string]interface{}{"server": server}
+	return map[string]interface{}{"server": server}, nil
 }
 
 // Create requests a server to be provisioned to the user in the current tenant.
 func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
-	var result CreateResult
-	_, result.Err = perigee.Request("POST", listURL(client), perigee.Options{
-		Results:     &result.Body,
-		ReqBody:     opts.ToServerCreateMap(),
+	var res CreateResult
+
+	reqBody, err := opts.ToServerCreateMap()
+	if err != nil {
+		res.Err = err
+		return res
+	}
+
+	_, res.Err = perigee.Request("POST", listURL(client), perigee.Options{
+		Results:     &res.Body,
+		ReqBody:     reqBody,
 		MoreHeaders: client.AuthenticatedHeaders(),
 		OkCodes:     []int{202},
 	})
-	return result
+	return res
 }
 
 // Delete requests that a server previously provisioned be removed from your account.
diff --git a/openstack/identity/v2/extensions/doc.go b/openstack/identity/v2/extensions/doc.go
new file mode 100644
index 0000000..791e4e3
--- /dev/null
+++ b/openstack/identity/v2/extensions/doc.go
@@ -0,0 +1,3 @@
+// Package extensions provides information and interaction with the
+// different extensions available for the OpenStack Identity service.
+package extensions
diff --git a/openstack/identity/v2/tenants/doc.go b/openstack/identity/v2/tenants/doc.go
index 473a302..0c2d49d 100644
--- a/openstack/identity/v2/tenants/doc.go
+++ b/openstack/identity/v2/tenants/doc.go
@@ -1,7 +1,7 @@
-/*
-Package tenants contains API calls that query for information about tenants on an OpenStack deployment.
-
-See: http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
-And: http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants
-*/
+// Package tenants provides information and interaction with the
+// tenants API resource for the OpenStack Identity service.
+//
+// See http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
+// and http://developer.openstack.org/api-ref-identity-v2.html#admin-tenants
+// for more information.
 package tenants
diff --git a/openstack/identity/v2/tokens/doc.go b/openstack/identity/v2/tokens/doc.go
index d26f642..31cacc5 100644
--- a/openstack/identity/v2/tokens/doc.go
+++ b/openstack/identity/v2/tokens/doc.go
@@ -1,6 +1,5 @@
-/*
-Package tokens contains functions that issue and manipulate identity tokens.
-
-Reference: http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
-*/
+// Package tokens provides information and interaction with the token API
+// resource for the OpenStack Identity service.
+// For more information, see:
+// http://developer.openstack.org/api-ref-identity-v2.html#identity-auth-v2
 package tokens
diff --git a/openstack/identity/v3/endpoints/doc.go b/openstack/identity/v3/endpoints/doc.go
index 7d38ee3..8516394 100644
--- a/openstack/identity/v3/endpoints/doc.go
+++ b/openstack/identity/v3/endpoints/doc.go
@@ -1,3 +1,6 @@
-// Package endpoints queries and manages service endpoints.
-// Reference: http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3
+// Package endpoints provides information and interaction with the service
+// endpoints API resource in the OpenStack Identity service.
+//
+// For more information, see:
+// http://developer.openstack.org/api-ref-identity-v3.html#endpoints-v3
 package endpoints
diff --git a/openstack/identity/v3/services/doc.go b/openstack/identity/v3/services/doc.go
index c4772c0..fa56411 100644
--- a/openstack/identity/v3/services/doc.go
+++ b/openstack/identity/v3/services/doc.go
@@ -1,4 +1,3 @@
-/*
-Package services queries and manages the service catalog.
-*/
+// Package services provides information and interaction with the services API
+// resource for the OpenStack Identity service.
 package services
diff --git a/openstack/identity/v3/tokens/doc.go b/openstack/identity/v3/tokens/doc.go
index 02fce0d..76ff5f4 100644
--- a/openstack/identity/v3/tokens/doc.go
+++ b/openstack/identity/v3/tokens/doc.go
@@ -1,6 +1,6 @@
-/*
-Package tokens defines operations performed on the token resource.
-
-Documentation: http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3
-*/
+// Package tokens provides information and interaction with the token API
+// resource for the OpenStack Identity service.
+//
+// For more information, see:
+// http://developer.openstack.org/api-ref-identity-v3.html#tokens-v3
 package tokens
diff --git a/openstack/networking/v2/extensions/external/doc.go b/openstack/networking/v2/extensions/external/doc.go
index d244f26..dad3a84 100755
--- a/openstack/networking/v2/extensions/external/doc.go
+++ b/openstack/networking/v2/extensions/external/doc.go
@@ -1 +1,3 @@
+// Package external provides information and interaction with the external
+// extension for the OpenStack Networking service.
 package external
diff --git a/openstack/networking/v2/extensions/lbaas/doc.go b/openstack/networking/v2/extensions/lbaas/doc.go
new file mode 100644
index 0000000..bc1fc28
--- /dev/null
+++ b/openstack/networking/v2/extensions/lbaas/doc.go
@@ -0,0 +1,3 @@
+// Package lbaas provides information and interaction with the Load Balancer
+// as a Service extension for the OpenStack Networking service.
+package lbaas
diff --git a/package.go b/package.go
index fb6f24a..e8c2e82 100644
--- a/package.go
+++ b/package.go
@@ -1,4 +1,38 @@
-// Package gophercloud provides a multi-vendor interface to OpenStack-compatible
-// clouds. The package attempts to follow established community standards and
-// golang idioms. Contributions are welcome!
+/*
+Package gophercloud provides a multi-vendor interface to OpenStack-compatible
+clouds. The library has a three-level hierarchy: providers, services, and
+resources.
+
+Provider structs represent the service providers that offer and manage a
+collection of services. Examples of providers include: OpenStack, Rackspace,
+HP. These are defined like so:
+
+  opts := gophercloud.AuthOptions{
+    IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
+    Username: "{username}",
+    Password: "{password}",
+    TenantID: "{tenant_id}",
+  }
+
+  provider, err := openstack.AuthenticatedClient(opts)
+
+Service structs are specific to a provider and handle all of the logic and
+operations for a particular OpenStack service. Examples of services include:
+Compute, Object Storage, Block Storage. In order to define one, you need to
+pass in the parent provider, like so:
+
+  opts := gophercloud.EndpointOpts{Region: "RegionOne"}
+
+  client := openstack.NewComputeV2(provider, opts)
+
+Resource structs are the domain models that services make use of in order
+to work with and represent the state of API resources:
+
+  server, err := servers.Get(client, "{serverId}").Extract()
+
+Another convention is to return Result structs for API operations, which allow
+you to access the HTTP headers, response body, and associated errors with the
+network transaction. To get a resource struct, you then call the Extract
+method which is chained to the response.
+*/
 package gophercloud
diff --git a/params.go b/params.go
index 26c48c0..5fe3c2c 100644
--- a/params.go
+++ b/params.go
@@ -60,19 +60,21 @@
 	return v.Interface() == z.Interface()
 }
 
-// BuildQueryString accepts a generic structure and parses it URL struct. It
-// converts field names into query names based on tags. So for example, this
-// type:
-//
-// struct {
-//    Bar string `q:"x_bar"`
-//    Baz int    `q:"lorem_ipsum"`
-// }{
-//    Bar: "XXX",
-//    Baz: "YYY",
-// }
-//
-// will be converted into ?x_bar=XXX&lorem_ipsum=YYYY
+/*
+BuildQueryString accepts a generic structure and parses it URL struct. It
+converts field names into query names based on "q" tags. So for example, this
+type:
+
+	struct {
+	   Bar string `q:"x_bar"`
+	   Baz int    `q:"lorem_ipsum"`
+	}{
+	   Bar: "XXX",
+	   Baz: "YYY",
+	}
+
+will be converted into ?x_bar=XXX&lorem_ipsum=YYYY
+*/
 func BuildQueryString(opts interface{}) (*url.URL, error) {
 	optsValue := reflect.ValueOf(opts)
 	if optsValue.Kind() == reflect.Ptr {
@@ -130,7 +132,7 @@
 	return nil, fmt.Errorf("Options type is not a struct.")
 }
 
-// BuildHeaders accepts a generic structure and parses it string map. It
+// BuildHeaders accepts a generic structure and parses it into a string map. It
 // converts field names into header names based on "h" tags, and field values
 // into header values by a simple one-to-one mapping.
 func BuildHeaders(opts interface{}) (map[string]string, error) {
diff --git a/provider_client.go b/provider_client.go
index 2be665e..7754c20 100644
--- a/provider_client.go
+++ b/provider_client.go
@@ -1,29 +1,33 @@
 package gophercloud
 
-// ProviderClient stores details that are required to interact with any services within a specific provider's API.
+// ProviderClient stores details that are required to interact with any
+// services within a specific provider's API.
 //
-// Generally, you acquire a ProviderClient by calling the `NewClient()` method in the appropriate provider's child package,
-// providing whatever authentication credentials are required.
+// Generally, you acquire a ProviderClient by calling the NewClient method in
+// the appropriate provider's child package, providing whatever authentication
+// credentials are required.
 type ProviderClient struct {
-
-	// IdentityBase is the front door to an openstack provider.
-	// Generally this will be populated when you authenticate.
-	// It should be the *root* resource of the identity service, not of a specific identity version.
+	// IdentityBase is the base URL used for a particular provider's identity
+	// service - it will be used when issuing authenticatation requests. It
+	// should point to the root resource of the identity service, not a specific
+	// identity version.
 	IdentityBase string
 
-	// IdentityEndpoint is the originally requested identity endpoint.
-	// This may be a specific version of the identity service, in which case that endpoint is used rather than querying the
-	// version-negotiation endpoint.
+	// IdentityEndpoint is the identity endpoint. This may be a specific version
+	// of the identity service. If this is the case, this endpoint is used rather
+	// than querying versions first.
 	IdentityEndpoint string
 
-	// TokenID is the most recently valid token issued.
+	// TokenID is the ID of the most recently issued valid token.
 	TokenID string
 
-	// EndpointLocator describes how this provider discovers the endpoints for its constituent services.
+	// EndpointLocator describes how this provider discovers the endpoints for
+	// its constituent services.
 	EndpointLocator EndpointLocator
 }
 
-// AuthenticatedHeaders returns a map of HTTP headers that are common for all authenticated service requests.
+// AuthenticatedHeaders returns a map of HTTP headers that are common for all
+// authenticated service requests.
 func (client *ProviderClient) AuthenticatedHeaders() map[string]string {
 	return map[string]string{"X-Auth-Token": client.TokenID}
 }
diff --git a/rackspace/blockstorage/v1/snapshots/doc.go b/rackspace/blockstorage/v1/snapshots/doc.go
new file mode 100644
index 0000000..ad6064f
--- /dev/null
+++ b/rackspace/blockstorage/v1/snapshots/doc.go
@@ -0,0 +1,3 @@
+// Package snapshots provides information and interaction with the snapshot
+// API resource for the Rackspace Block Storage service.
+package snapshots
diff --git a/rackspace/blockstorage/v1/volumes/doc.go b/rackspace/blockstorage/v1/volumes/doc.go
new file mode 100644
index 0000000..b2be25c
--- /dev/null
+++ b/rackspace/blockstorage/v1/volumes/doc.go
@@ -0,0 +1,3 @@
+// Package volumes provides information and interaction with the volume
+// API resource for the Rackspace Block Storage service.
+package volumes
diff --git a/rackspace/blockstorage/v1/volumetypes/doc.go b/rackspace/blockstorage/v1/volumetypes/doc.go
new file mode 100644
index 0000000..70122b7
--- /dev/null
+++ b/rackspace/blockstorage/v1/volumetypes/doc.go
@@ -0,0 +1,3 @@
+// Package volumetypes provides information and interaction with the volume type
+// API resource for the Rackspace Block Storage service.
+package volumetypes
diff --git a/rackspace/compute/v2/bootfromvolume/delegate.go b/rackspace/compute/v2/bootfromvolume/delegate.go
new file mode 100644
index 0000000..2580459
--- /dev/null
+++ b/rackspace/compute/v2/bootfromvolume/delegate.go
@@ -0,0 +1,12 @@
+package bootfromvolume
+
+import (
+	"github.com/rackspace/gophercloud"
+	osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
+	osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+)
+
+// Create requests the creation of a server from the given block device mapping.
+func Create(client *gophercloud.ServiceClient, opts osServers.CreateOptsBuilder) osServers.CreateResult {
+	return osBFV.Create(client, opts)
+}
diff --git a/rackspace/compute/v2/bootfromvolume/delegate_test.go b/rackspace/compute/v2/bootfromvolume/delegate_test.go
new file mode 100644
index 0000000..0b53527
--- /dev/null
+++ b/rackspace/compute/v2/bootfromvolume/delegate_test.go
@@ -0,0 +1,52 @@
+package bootfromvolume
+
+import (
+	"testing"
+
+	osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
+	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
+	th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestCreateOpts(t *testing.T) {
+	base := servers.CreateOpts{
+		Name:      "createdserver",
+		ImageRef:  "asdfasdfasdf",
+		FlavorRef: "performance1-1",
+	}
+
+	ext := osBFV.CreateOptsExt{
+		CreateOptsBuilder: base,
+		BlockDevice: []osBFV.BlockDevice{
+			osBFV.BlockDevice{
+				UUID:            "123456",
+				SourceType:      osBFV.Image,
+				DestinationType: "volume",
+				VolumeSize:      10,
+			},
+		},
+	}
+
+	expected := `
+    {
+      "server": {
+        "name": "createdserver",
+        "imageRef": "asdfasdfasdf",
+        "flavorRef": "performance1-1",
+        "block_device_mapping_v2":[
+          {
+            "uuid":"123456",
+            "source_type":"image",
+            "destination_type":"volume",
+            "boot_index": "0",
+            "delete_on_termination": "false",
+            "volume_size": "10"
+          }
+        ]
+      }
+    }
+  `
+	actual, err := ext.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
+}
diff --git a/rackspace/compute/v2/flavors/doc.go b/rackspace/compute/v2/flavors/doc.go
new file mode 100644
index 0000000..278229a
--- /dev/null
+++ b/rackspace/compute/v2/flavors/doc.go
@@ -0,0 +1,3 @@
+// Package flavors provides information and interaction with the flavor
+// API resource for the Rackspace Cloud Servers service.
+package flavors
diff --git a/rackspace/compute/v2/images/doc.go b/rackspace/compute/v2/images/doc.go
new file mode 100644
index 0000000..cfae806
--- /dev/null
+++ b/rackspace/compute/v2/images/doc.go
@@ -0,0 +1,3 @@
+// Package images provides information and interaction with the image
+// API resource for the Rackspace Cloud Servers service.
+package images
diff --git a/rackspace/compute/v2/keypairs/doc.go b/rackspace/compute/v2/keypairs/doc.go
new file mode 100644
index 0000000..3171375
--- /dev/null
+++ b/rackspace/compute/v2/keypairs/doc.go
@@ -0,0 +1,3 @@
+// Package keypairs provides information and interaction with the keypair
+// API resource for the Rackspace Cloud Servers service.
+package keypairs
diff --git a/rackspace/compute/v2/networks/doc.go b/rackspace/compute/v2/networks/doc.go
new file mode 100644
index 0000000..8e5c773
--- /dev/null
+++ b/rackspace/compute/v2/networks/doc.go
@@ -0,0 +1,3 @@
+// Package networks provides information and interaction with the network
+// API resource for the Rackspace Cloud Servers service.
+package networks
diff --git a/rackspace/compute/v2/servers/doc.go b/rackspace/compute/v2/servers/doc.go
new file mode 100644
index 0000000..c9f77f6
--- /dev/null
+++ b/rackspace/compute/v2/servers/doc.go
@@ -0,0 +1,3 @@
+// Package servers provides information and interaction with the server
+// API resource for the Rackspace Cloud Servers service.
+package servers
diff --git a/rackspace/compute/v2/servers/requests.go b/rackspace/compute/v2/servers/requests.go
index b83a893..884b9cb 100644
--- a/rackspace/compute/v2/servers/requests.go
+++ b/rackspace/compute/v2/servers/requests.go
@@ -1,6 +1,7 @@
 package servers
 
 import (
+	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
 	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
 	os "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
 )
@@ -51,11 +52,15 @@
 	// DiskConfig [optional] controls how the created server's disk is partitioned. See the "diskconfig"
 	// extension in OpenStack compute v2.
 	DiskConfig diskconfig.DiskConfig
+
+	// BlockDevice [optional] will create the server from a volume, which is created from an image,
+	// a snapshot, or an another volume.
+	BlockDevice []bootfromvolume.BlockDevice
 }
 
 // ToServerCreateMap constructs a request body using all of the OpenStack extensions that are
 // active on Rackspace.
-func (opts CreateOpts) ToServerCreateMap() map[string]interface{} {
+func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
 	base := os.CreateOpts{
 		Name:             opts.Name,
 		ImageRef:         opts.ImageRef,
@@ -74,14 +79,29 @@
 		DiskConfig:        opts.DiskConfig,
 	}
 
-	result := drive.ToServerCreateMap()
+	res, err := drive.ToServerCreateMap()
+	if err != nil {
+		return nil, err
+	}
+
+	if len(opts.BlockDevice) != 0 {
+		bfv := bootfromvolume.CreateOptsExt{
+			CreateOptsBuilder: drive,
+			BlockDevice:       opts.BlockDevice,
+		}
+
+		res, err = bfv.ToServerCreateMap()
+		if err != nil {
+			return nil, err
+		}
+	}
 
 	// key_name doesn't actually come from the extension (or at least isn't documented there) so
 	// we need to add it manually.
-	serverMap := result["server"].(map[string]interface{})
+	serverMap := res["server"].(map[string]interface{})
 	serverMap["key_name"] = opts.KeyPair
 
-	return result
+	return res, nil
 }
 
 // RebuildOpts represents all of the configuration options used in a server rebuild operation that
diff --git a/rackspace/compute/v2/servers/requests_test.go b/rackspace/compute/v2/servers/requests_test.go
index ac7058f..3c0f806 100644
--- a/rackspace/compute/v2/servers/requests_test.go
+++ b/rackspace/compute/v2/servers/requests_test.go
@@ -27,7 +27,9 @@
 		}
 	}
 	`
-	th.CheckJSONEquals(t, expected, opts.ToServerCreateMap())
+	actual, err := opts.ToServerCreateMap()
+	th.AssertNoErr(t, err)
+	th.CheckJSONEquals(t, expected, actual)
 }
 
 func TestRebuildOpts(t *testing.T) {
diff --git a/rackspace/identity/v2/extensions/doc.go b/rackspace/identity/v2/extensions/doc.go
new file mode 100644
index 0000000..b02a95b
--- /dev/null
+++ b/rackspace/identity/v2/extensions/doc.go
@@ -0,0 +1,3 @@
+// Package extensions provides information and interaction with the all the
+// extensions available for the Rackspace Identity service.
+package extensions
diff --git a/rackspace/identity/v2/tenants/doc.go b/rackspace/identity/v2/tenants/doc.go
new file mode 100644
index 0000000..c1825c2
--- /dev/null
+++ b/rackspace/identity/v2/tenants/doc.go
@@ -0,0 +1,3 @@
+// Package tenants provides information and interaction with the tenant
+// API resource for the Rackspace Identity service.
+package tenants
diff --git a/rackspace/identity/v2/tokens/doc.go b/rackspace/identity/v2/tokens/doc.go
new file mode 100644
index 0000000..44043e5
--- /dev/null
+++ b/rackspace/identity/v2/tokens/doc.go
@@ -0,0 +1,3 @@
+// Package tokens provides information and interaction with the token
+// API resource for the Rackspace Identity service.
+package tokens
diff --git a/rackspace/objectstorage/v1/accounts/doc.go b/rackspace/objectstorage/v1/accounts/doc.go
new file mode 100644
index 0000000..293a930
--- /dev/null
+++ b/rackspace/objectstorage/v1/accounts/doc.go
@@ -0,0 +1,3 @@
+// Package accounts provides information and interaction with the account
+// API resource for the Rackspace Cloud Files service.
+package accounts
diff --git a/rackspace/objectstorage/v1/bulk/doc.go b/rackspace/objectstorage/v1/bulk/doc.go
new file mode 100644
index 0000000..9c89e22
--- /dev/null
+++ b/rackspace/objectstorage/v1/bulk/doc.go
@@ -0,0 +1,3 @@
+// Package bulk provides functionality for working with bulk operations in the
+// Rackspace Cloud Files service.
+package bulk
diff --git a/rackspace/objectstorage/v1/cdncontainers/doc.go b/rackspace/objectstorage/v1/cdncontainers/doc.go
new file mode 100644
index 0000000..7b0930e
--- /dev/null
+++ b/rackspace/objectstorage/v1/cdncontainers/doc.go
@@ -0,0 +1,3 @@
+// Package cdncontainers provides information and interaction with the CDN
+// Container API resource for the Rackspace Cloud Files service.
+package cdncontainers
diff --git a/rackspace/objectstorage/v1/cdnobjects/doc.go b/rackspace/objectstorage/v1/cdnobjects/doc.go
new file mode 100644
index 0000000..90cd5c9
--- /dev/null
+++ b/rackspace/objectstorage/v1/cdnobjects/doc.go
@@ -0,0 +1,3 @@
+// Package cdnobjects provides information and interaction with the CDN
+// Object API resource for the Rackspace Cloud Files service.
+package cdnobjects
diff --git a/rackspace/objectstorage/v1/containers/doc.go b/rackspace/objectstorage/v1/containers/doc.go
new file mode 100644
index 0000000..d132a07
--- /dev/null
+++ b/rackspace/objectstorage/v1/containers/doc.go
@@ -0,0 +1,3 @@
+// Package containers provides information and interaction with the Container
+// API resource for the Rackspace Cloud Files service.
+package containers
diff --git a/rackspace/objectstorage/v1/objects/doc.go b/rackspace/objectstorage/v1/objects/doc.go
new file mode 100644
index 0000000..781984b
--- /dev/null
+++ b/rackspace/objectstorage/v1/objects/doc.go
@@ -0,0 +1,3 @@
+// Package objects provides information and interaction with the Object
+// API resource for the Rackspace Cloud Files service.
+package objects
diff --git a/results.go b/results.go
index 19557fb..a8e3705 100644
--- a/results.go
+++ b/results.go
@@ -39,7 +39,8 @@
 }
 
 // ExtractNextURL attempts to extract the next URL from a JSON structure. It
-// follows the common structure of nesting back and next links.
+// follows the common convention of nesting back and next URLs in a "links"
+// JSON array.
 func ExtractNextURL(links []Link) (string, error) {
 	var url string
 
diff --git a/testhelper/convenience.go b/testhelper/convenience.go
index adb77e5..cf33e1a 100644
--- a/testhelper/convenience.go
+++ b/testhelper/convenience.go
@@ -259,14 +259,19 @@
 // isJSONEquals is a utility function that implements JSON comparison for AssertJSONEquals and
 // CheckJSONEquals.
 func isJSONEquals(t *testing.T, expectedJSON string, actual interface{}) bool {
-	var parsedExpected interface{}
+	var parsedExpected, parsedActual interface{}
 	err := json.Unmarshal([]byte(expectedJSON), &parsedExpected)
 	if err != nil {
 		t.Errorf("Unable to parse expected value as JSON: %v", err)
 		return false
 	}
 
-	if !reflect.DeepEqual(parsedExpected, actual) {
+	jsonActual, err := json.Marshal(actual)
+	AssertNoErr(t, err)
+	err = json.Unmarshal(jsonActual, &parsedActual)
+	AssertNoErr(t, err)
+
+	if !reflect.DeepEqual(parsedExpected, parsedActual) {
 		prettyExpected, err := json.MarshalIndent(parsedExpected, "", "  ")
 		if err != nil {
 			t.Logf("Unable to pretty-print expected JSON: %v\n%s", err, expectedJSON)
diff --git a/util.go b/util.go
index 6f62944..7f5ead7 100644
--- a/util.go
+++ b/util.go
@@ -6,8 +6,8 @@
 	"time"
 )
 
-// WaitFor polls a predicate function once per second up to secs times to wait
-// for a certain state to arrive.
+// WaitFor polls a predicate function, once per second, up to a timeout limit.
+// It usually does this to wait for the resource to transition to a certain state.
 func WaitFor(timeout int, predicate func() (bool, error)) error {
 	start := time.Now().Second()
 	for {