Update Godeps.

This was a mechanical change:
```
make dep-restore
godep update -goversion
make dep-save
```

Signed-off-by: John Sirois <john.sirois@gmail.com>
This commit is contained in:
John Sirois
2016-02-24 13:13:01 -08:00
parent 8046e13683
commit 5896ecff40
445 changed files with 15585 additions and 50884 deletions

View File

@@ -1,106 +0,0 @@
package bootfromvolume
import (
"errors"
"strconv"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
// 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 = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 202},
})
return res
}

View File

@@ -1,10 +0,0 @@
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
}

View File

@@ -1,7 +0,0 @@
package bootfromvolume
import "github.com/rackspace/gophercloud"
func createURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("os-volumes_boot")
}

View File

@@ -1 +0,0 @@
package defsecrules

View File

@@ -1,108 +0,0 @@
package defsecrules
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
const rootPath = "/os-security-group-default-rules"
func mockListRulesResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_default_rules": [
{
"from_port": 80,
"id": "{ruleID}",
"ip_protocol": "TCP",
"ip_range": {
"cidr": "10.10.10.0/24"
},
"to_port": 80
}
]
}
`)
})
}
func mockCreateRuleResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group_default_rule": {
"ip_protocol": "TCP",
"from_port": 80,
"to_port": 80,
"cidr": "10.10.12.0/24"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_default_rule": {
"from_port": 80,
"id": "{ruleID}",
"ip_protocol": "TCP",
"ip_range": {
"cidr": "10.10.12.0/24"
},
"to_port": 80
}
}
`)
})
}
func mockGetRuleResponse(t *testing.T, ruleID string) {
url := rootPath + "/" + ruleID
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_default_rule": {
"id": "{ruleID}",
"from_port": 80,
"to_port": 80,
"ip_protocol": "TCP",
"ip_range": {
"cidr": "10.10.12.0/24"
}
}
}
`)
})
}
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
url := rootPath + "/" + ruleID
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusNoContent)
})
}

View File

@@ -1,95 +0,0 @@
package defsecrules
import (
"errors"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List will return a collection of default rules.
func List(client *gophercloud.ServiceClient) pagination.Pager {
createPage := func(r pagination.PageResult) pagination.Page {
return DefaultRulePage{pagination.SinglePageBase(r)}
}
return pagination.NewPager(client, rootURL(client), createPage)
}
// CreateOpts represents the configuration for adding a new default rule.
type CreateOpts struct {
// Required - the lower bound of the port range that will be opened.
FromPort int `json:"from_port"`
// Required - the upper bound of the port range that will be opened.
ToPort int `json:"to_port"`
// Required - the protocol type that will be allowed, e.g. TCP.
IPProtocol string `json:"ip_protocol"`
// ONLY required if FromGroupID is blank. This represents the IP range that
// will be the source of network traffic to your security group. Use
// 0.0.0.0/0 to allow all IP addresses.
CIDR string `json:"cidr,omitempty"`
}
// CreateOptsBuilder builds the create rule options into a serializable format.
type CreateOptsBuilder interface {
ToRuleCreateMap() (map[string]interface{}, error)
}
// ToRuleCreateMap builds the create rule options into a serializable format.
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
rule := make(map[string]interface{})
if opts.FromPort == 0 {
return rule, errors.New("A FromPort must be set")
}
if opts.ToPort == 0 {
return rule, errors.New("A ToPort must be set")
}
if opts.IPProtocol == "" {
return rule, errors.New("A IPProtocol must be set")
}
if opts.CIDR == "" {
return rule, errors.New("A CIDR must be set")
}
rule["from_port"] = opts.FromPort
rule["to_port"] = opts.ToPort
rule["ip_protocol"] = opts.IPProtocol
rule["cidr"] = opts.CIDR
return map[string]interface{}{"security_group_default_rule": rule}, nil
}
// Create is the operation responsible for creating a new default rule.
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var result CreateResult
reqBody, err := opts.ToRuleCreateMap()
if err != nil {
result.Err = err
return result
}
_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return result
}
// Get will return details for a particular default rule.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
return result
}
// Delete will permanently delete a default rule from the project.
func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = client.Delete(resourceURL(client, id), nil)
return result
}

View File

@@ -1,69 +0,0 @@
package defsecrules
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/pagination"
)
// DefaultRule represents a default rule - which is identical to a
// normal security rule.
type DefaultRule secgroups.Rule
// DefaultRulePage is a single page of a DefaultRule collection.
type DefaultRulePage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a page of default rules contains any results.
func (page DefaultRulePage) IsEmpty() (bool, error) {
users, err := ExtractDefaultRules(page)
if err != nil {
return false, err
}
return len(users) == 0, nil
}
// ExtractDefaultRules returns a slice of DefaultRules contained in a single
// page of results.
func ExtractDefaultRules(page pagination.Page) ([]DefaultRule, error) {
casted := page.(DefaultRulePage).Body
var response struct {
Rules []DefaultRule `mapstructure:"security_group_default_rules"`
}
err := mapstructure.WeakDecode(casted, &response)
return response.Rules, err
}
type commonResult struct {
gophercloud.Result
}
// CreateResult represents the result of a create operation.
type CreateResult struct {
commonResult
}
// GetResult represents the result of a get operation.
type GetResult struct {
commonResult
}
// Extract will extract a DefaultRule struct from most responses.
func (r commonResult) Extract() (*DefaultRule, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
Rule DefaultRule `mapstructure:"security_group_default_rule"`
}
err := mapstructure.WeakDecode(r.Body, &response)
return &response.Rule, err
}

View File

@@ -1,13 +0,0 @@
package defsecrules
import "github.com/rackspace/gophercloud"
const rulepath = "os-security-group-default-rules"
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rulepath, id)
}
func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rulepath)
}

View File

@@ -1,23 +0,0 @@
package extensions
import (
"github.com/rackspace/gophercloud"
common "github.com/rackspace/gophercloud/openstack/common/extensions"
"github.com/rackspace/gophercloud/pagination"
)
// ExtractExtensions interprets a Page as a slice of Extensions.
func ExtractExtensions(page pagination.Page) ([]common.Extension, error) {
return common.ExtractExtensions(page)
}
// Get retrieves information for a specific extension using its alias.
func Get(c *gophercloud.ServiceClient, alias string) common.GetResult {
return common.Get(c, alias)
}
// List returns a Pager which allows you to iterate over the full collection of extensions.
// It does not accept query parameters.
func List(c *gophercloud.ServiceClient) pagination.Pager {
return common.List(c)
}

View File

@@ -1,3 +0,0 @@
// Package diskconfig provides information and interaction with the Disk
// Config extension that works with the OpenStack Compute service.
package diskconfig

View File

@@ -1,114 +0,0 @@
package diskconfig
import (
"errors"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
// DiskConfig represents one of the two possible settings for the DiskConfig option when creating,
// rebuilding, or resizing servers: Auto or Manual.
type DiskConfig string
const (
// Auto builds a server with a single partition the size of the target flavor disk and
// automatically adjusts the filesystem to fit the entire partition. Auto may only be used with
// images and servers that use a single EXT3 partition.
Auto DiskConfig = "AUTO"
// Manual builds a server using whatever partition scheme and filesystem are present in the source
// image. If the target flavor disk is larger, the remaining space is left unpartitioned. This
// enables images to have non-EXT3 filesystems, multiple partitions, and so on, and enables you
// to manage the disk configuration. It also results in slightly shorter boot times.
Manual DiskConfig = "MANUAL"
)
// ErrInvalidDiskConfig is returned if an invalid string is specified for a DiskConfig option.
var ErrInvalidDiskConfig = errors.New("DiskConfig must be either diskconfig.Auto or diskconfig.Manual.")
// Validate ensures that a DiskConfig contains an appropriate value.
func (config DiskConfig) validate() error {
switch config {
case Auto, Manual:
return nil
default:
return ErrInvalidDiskConfig
}
}
// CreateOptsExt adds a DiskConfig option to the base CreateOpts.
type CreateOptsExt struct {
servers.CreateOptsBuilder
// DiskConfig [optional] controls how the created server's disk is partitioned.
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{}, 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, nil
}
// RebuildOptsExt adds a DiskConfig option to the base RebuildOpts.
type RebuildOptsExt struct {
servers.RebuildOptsBuilder
// DiskConfig [optional] controls how the rebuilt server's disk is partitioned.
DiskConfig DiskConfig
}
// ToServerRebuildMap adds the diskconfig option to the base server rebuild options.
func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) {
err := opts.DiskConfig.validate()
if err != nil {
return nil, err
}
base, err := opts.RebuildOptsBuilder.ToServerRebuildMap()
if err != nil {
return nil, err
}
serverMap := base["rebuild"].(map[string]interface{})
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig)
return base, nil
}
// ResizeOptsExt adds a DiskConfig option to the base server resize options.
type ResizeOptsExt struct {
servers.ResizeOptsBuilder
// DiskConfig [optional] controls how the resized server's disk is partitioned.
DiskConfig DiskConfig
}
// ToServerResizeMap adds the diskconfig option to the base server creation options.
func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) {
err := opts.DiskConfig.validate()
if err != nil {
return nil, err
}
base, err := opts.ResizeOptsBuilder.ToServerResizeMap()
if err != nil {
return nil, err
}
serverMap := base["resize"].(map[string]interface{})
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig)
return base, nil
}

View File

@@ -1,60 +0,0 @@
package diskconfig
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
)
func commonExtract(result gophercloud.Result) (*DiskConfig, error) {
var resp struct {
Server struct {
DiskConfig string `mapstructure:"OS-DCF:diskConfig"`
} `mapstructure:"server"`
}
err := mapstructure.Decode(result.Body, &resp)
if err != nil {
return nil, err
}
config := DiskConfig(resp.Server.DiskConfig)
return &config, nil
}
// ExtractGet returns the disk configuration from a servers.Get call.
func ExtractGet(result servers.GetResult) (*DiskConfig, error) {
return commonExtract(result.Result)
}
// ExtractUpdate returns the disk configuration from a servers.Update call.
func ExtractUpdate(result servers.UpdateResult) (*DiskConfig, error) {
return commonExtract(result.Result)
}
// ExtractRebuild returns the disk configuration from a servers.Rebuild call.
func ExtractRebuild(result servers.RebuildResult) (*DiskConfig, error) {
return commonExtract(result.Result)
}
// ExtractDiskConfig returns the DiskConfig setting for a specific server acquired from an
// servers.ExtractServers call, while iterating through a Pager.
func ExtractDiskConfig(page pagination.Page, index int) (*DiskConfig, error) {
casted := page.(servers.ServerPage).Body
type server struct {
DiskConfig string `mapstructure:"OS-DCF:diskConfig"`
}
var response struct {
Servers []server `mapstructure:"servers"`
}
err := mapstructure.Decode(casted, &response)
if err != nil {
return nil, err
}
config := DiskConfig(response.Servers[index].DiskConfig)
return &config, nil
}

View File

@@ -1,3 +0,0 @@
// Package extensions provides information and interaction with the
// different extensions available for the OpenStack Compute service.
package extensions

View File

@@ -1 +0,0 @@
package secgroups

View File

@@ -1,265 +0,0 @@
package secgroups
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
const rootPath = "/os-security-groups"
const listGroupsJSON = `
{
"security_groups": [
{
"description": "default",
"id": "{groupID}",
"name": "default",
"rules": [],
"tenant_id": "openstack"
}
]
}
`
func mockListGroupsResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, listGroupsJSON)
})
}
func mockListGroupsByServerResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("/servers/%s%s", serverID, rootPath)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, listGroupsJSON)
})
}
func mockCreateGroupResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group": {
"name": "test",
"description": "something"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"description": "something",
"id": "{groupID}",
"name": "test",
"rules": [],
"tenant_id": "openstack"
}
}
`)
})
}
func mockUpdateGroupResponse(t *testing.T, groupID string) {
url := fmt.Sprintf("%s/%s", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "PUT")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group": {
"name": "new_name",
"description": "new_desc"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"description": "something",
"id": "{groupID}",
"name": "new_name",
"rules": [],
"tenant_id": "openstack"
}
}
`)
})
}
func mockGetGroupsResponse(t *testing.T, groupID string) {
url := fmt.Sprintf("%s/%s", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"description": "default",
"id": "{groupID}",
"name": "default",
"rules": [
{
"from_port": 80,
"group": {
"tenant_id": "openstack",
"name": "default"
},
"ip_protocol": "TCP",
"to_port": 85,
"parent_group_id": "{groupID}",
"ip_range": {
"cidr": "0.0.0.0"
},
"id": "{ruleID}"
}
],
"tenant_id": "openstack"
}
}
`)
})
}
func mockGetNumericIDGroupResponse(t *testing.T, groupID int) {
url := fmt.Sprintf("%s/%d", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"id": 12345
}
}
`)
})
}
func mockDeleteGroupResponse(t *testing.T, groupID string) {
url := fmt.Sprintf("%s/%s", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
func mockAddRuleResponse(t *testing.T) {
th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group_rule": {
"from_port": 22,
"ip_protocol": "TCP",
"to_port": 22,
"parent_group_id": "{groupID}",
"cidr": "0.0.0.0/0"
}
} `)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_rule": {
"from_port": 22,
"group": {},
"ip_protocol": "TCP",
"to_port": 22,
"parent_group_id": "{groupID}",
"ip_range": {
"cidr": "0.0.0.0/0"
},
"id": "{ruleID}"
}
}`)
})
}
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
func mockAddServerToGroupResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("/servers/%s/action", serverID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"addSecurityGroup": {
"name": "test"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("/servers/%s/action", serverID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"removeSecurityGroup": {
"name": "test"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}

View File

@@ -1,257 +0,0 @@
package secgroups
import (
"errors"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
createPage := func(r pagination.PageResult) pagination.Page {
return SecurityGroupPage{pagination.SinglePageBase(r)}
}
return pagination.NewPager(client, url, createPage)
}
// List will return a collection of all the security groups for a particular
// tenant.
func List(client *gophercloud.ServiceClient) pagination.Pager {
return commonList(client, rootURL(client))
}
// ListByServer will return a collection of all the security groups which are
// associated with a particular server.
func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
return commonList(client, listByServerURL(client, serverID))
}
// GroupOpts is the underlying struct responsible for creating or updating
// security groups. It therefore represents the mutable attributes of a
// security group.
type GroupOpts struct {
// Required - the name of your security group.
Name string `json:"name"`
// Required - the description of your security group.
Description string `json:"description"`
}
// CreateOpts is the struct responsible for creating a security group.
type CreateOpts GroupOpts
// CreateOptsBuilder builds the create options into a serializable format.
type CreateOptsBuilder interface {
ToSecGroupCreateMap() (map[string]interface{}, error)
}
var (
errName = errors.New("Name is a required field")
errDesc = errors.New("Description is a required field")
)
// ToSecGroupCreateMap builds the create options into a serializable format.
func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
sg := make(map[string]interface{})
if opts.Name == "" {
return sg, errName
}
if opts.Description == "" {
return sg, errDesc
}
sg["name"] = opts.Name
sg["description"] = opts.Description
return map[string]interface{}{"security_group": sg}, nil
}
// Create will create a new security group.
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var result CreateResult
reqBody, err := opts.ToSecGroupCreateMap()
if err != nil {
result.Err = err
return result
}
_, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return result
}
// UpdateOpts is the struct responsible for updating an existing security group.
type UpdateOpts GroupOpts
// UpdateOptsBuilder builds the update options into a serializable format.
type UpdateOptsBuilder interface {
ToSecGroupUpdateMap() (map[string]interface{}, error)
}
// ToSecGroupUpdateMap builds the update options into a serializable format.
func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
sg := make(map[string]interface{})
if opts.Name == "" {
return sg, errName
}
if opts.Description == "" {
return sg, errDesc
}
sg["name"] = opts.Name
sg["description"] = opts.Description
return map[string]interface{}{"security_group": sg}, nil
}
// Update will modify the mutable properties of a security group, notably its
// name and description.
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
var result UpdateResult
reqBody, err := opts.ToSecGroupUpdateMap()
if err != nil {
result.Err = err
return result
}
_, result.Err = client.Put(resourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return result
}
// Get will return details for a particular security group.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = client.Get(resourceURL(client, id), &result.Body, nil)
return result
}
// Delete will permanently delete a security group from the project.
func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = client.Delete(resourceURL(client, id), nil)
return result
}
// CreateRuleOpts represents the configuration for adding a new rule to an
// existing security group.
type CreateRuleOpts struct {
// Required - the ID of the group that this rule will be added to.
ParentGroupID string `json:"parent_group_id"`
// Required - the lower bound of the port range that will be opened.
FromPort int `json:"from_port"`
// Required - the upper bound of the port range that will be opened.
ToPort int `json:"to_port"`
// Required - the protocol type that will be allowed, e.g. TCP.
IPProtocol string `json:"ip_protocol"`
// ONLY required if FromGroupID is blank. This represents the IP range that
// will be the source of network traffic to your security group. Use
// 0.0.0.0/0 to allow all IP addresses.
CIDR string `json:"cidr,omitempty"`
// ONLY required if CIDR is blank. This value represents the ID of a group
// that forwards traffic to the parent group. So, instead of accepting
// network traffic from an entire IP range, you can instead refine the
// inbound source by an existing security group.
FromGroupID string `json:"group_id,omitempty"`
}
// CreateRuleOptsBuilder builds the create rule options into a serializable format.
type CreateRuleOptsBuilder interface {
ToRuleCreateMap() (map[string]interface{}, error)
}
// ToRuleCreateMap builds the create rule options into a serializable format.
func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
rule := make(map[string]interface{})
if opts.ParentGroupID == "" {
return rule, errors.New("A ParentGroupID must be set")
}
if opts.FromPort == 0 {
return rule, errors.New("A FromPort must be set")
}
if opts.ToPort == 0 {
return rule, errors.New("A ToPort must be set")
}
if opts.IPProtocol == "" {
return rule, errors.New("A IPProtocol must be set")
}
if opts.CIDR == "" && opts.FromGroupID == "" {
return rule, errors.New("A CIDR or FromGroupID must be set")
}
rule["parent_group_id"] = opts.ParentGroupID
rule["from_port"] = opts.FromPort
rule["to_port"] = opts.ToPort
rule["ip_protocol"] = opts.IPProtocol
if opts.CIDR != "" {
rule["cidr"] = opts.CIDR
}
if opts.FromGroupID != "" {
rule["group_id"] = opts.FromGroupID
}
return map[string]interface{}{"security_group_rule": rule}, nil
}
// CreateRule will add a new rule to an existing security group (whose ID is
// specified in CreateRuleOpts). You have the option of controlling inbound
// traffic from either an IP range (CIDR) or from another security group.
func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) CreateRuleResult {
var result CreateRuleResult
reqBody, err := opts.ToRuleCreateMap()
if err != nil {
result.Err = err
return result
}
_, result.Err = client.Post(rootRuleURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return result
}
// DeleteRule will permanently delete a rule from a security group.
func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = client.Delete(resourceRuleURL(client, id), nil)
return result
}
func actionMap(prefix, groupName string) map[string]map[string]string {
return map[string]map[string]string{
prefix + "SecurityGroup": map[string]string{"name": groupName},
}
}
// AddServerToGroup will associate a server and a security group, enforcing the
// rules of the group on the server.
func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &result.Body, nil)
return result
}
// RemoveServerFromGroup will disassociate a server from a security group.
func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &result.Body, nil)
return result
}

View File

@@ -1,147 +0,0 @@
package secgroups
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// SecurityGroup represents a security group.
type SecurityGroup struct {
// The unique ID of the group. If Neutron is installed, this ID will be
// represented as a string UUID; if Neutron is not installed, it will be a
// numeric ID. For the sake of consistency, we always cast it to a string.
ID string
// The human-readable name of the group, which needs to be unique.
Name string
// The human-readable description of the group.
Description string
// The rules which determine how this security group operates.
Rules []Rule
// The ID of the tenant to which this security group belongs.
TenantID string `mapstructure:"tenant_id"`
}
// Rule represents a security group rule, a policy which determines how a
// security group operates and what inbound traffic it allows in.
type Rule struct {
// The unique ID. If Neutron is installed, this ID will be
// represented as a string UUID; if Neutron is not installed, it will be a
// numeric ID. For the sake of consistency, we always cast it to a string.
ID string
// The lower bound of the port range which this security group should open up
FromPort int `mapstructure:"from_port"`
// The upper bound of the port range which this security group should open up
ToPort int `mapstructure:"to_port"`
// The IP protocol (e.g. TCP) which the security group accepts
IPProtocol string `mapstructure:"ip_protocol"`
// The CIDR IP range whose traffic can be received
IPRange IPRange `mapstructure:"ip_range"`
// The security group ID to which this rule belongs
ParentGroupID string `mapstructure:"parent_group_id"`
// Not documented.
Group Group
}
// IPRange represents the IP range whose traffic will be accepted by the
// security group.
type IPRange struct {
CIDR string
}
// Group represents a group.
type Group struct {
TenantID string `mapstructure:"tenant_id"`
Name string
}
// SecurityGroupPage is a single page of a SecurityGroup collection.
type SecurityGroupPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a page of Security Groups contains any results.
func (page SecurityGroupPage) IsEmpty() (bool, error) {
users, err := ExtractSecurityGroups(page)
if err != nil {
return false, err
}
return len(users) == 0, nil
}
// ExtractSecurityGroups returns a slice of SecurityGroups contained in a single page of results.
func ExtractSecurityGroups(page pagination.Page) ([]SecurityGroup, error) {
casted := page.(SecurityGroupPage).Body
var response struct {
SecurityGroups []SecurityGroup `mapstructure:"security_groups"`
}
err := mapstructure.WeakDecode(casted, &response)
return response.SecurityGroups, err
}
type commonResult struct {
gophercloud.Result
}
// CreateResult represents the result of a create operation.
type CreateResult struct {
commonResult
}
// GetResult represents the result of a get operation.
type GetResult struct {
commonResult
}
// UpdateResult represents the result of an update operation.
type UpdateResult struct {
commonResult
}
// Extract will extract a SecurityGroup struct from most responses.
func (r commonResult) Extract() (*SecurityGroup, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
SecurityGroup SecurityGroup `mapstructure:"security_group"`
}
err := mapstructure.WeakDecode(r.Body, &response)
return &response.SecurityGroup, err
}
// CreateRuleResult represents the result when adding rules to a security group.
type CreateRuleResult struct {
gophercloud.Result
}
// Extract will extract a Rule struct from a CreateRuleResult.
func (r CreateRuleResult) Extract() (*Rule, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
Rule Rule `mapstructure:"security_group_rule"`
}
err := mapstructure.WeakDecode(r.Body, &response)
return &response.Rule, err
}

View File

@@ -1,32 +0,0 @@
package secgroups
import "github.com/rackspace/gophercloud"
const (
secgrouppath = "os-security-groups"
rulepath = "os-security-group-rules"
)
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(secgrouppath, id)
}
func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(secgrouppath)
}
func listByServerURL(c *gophercloud.ServiceClient, serverID string) string {
return c.ServiceURL("servers", serverID, secgrouppath)
}
func rootRuleURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rulepath)
}
func resourceRuleURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rulepath, id)
}
func serverActionURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL("servers", id, "action")
}

View File

@@ -1,2 +0,0 @@
// Package tenantnetworks provides the ability for tenants to see information about the networks they have access to
package tenantnetworks

View File

@@ -1,84 +0,0 @@
// +build fixtures
package tenantnetworks
import (
"fmt"
"net/http"
"testing"
"time"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
// ListOutput is a sample response to a List call.
const ListOutput = `
{
"networks": [
{
"cidr": "10.0.0.0/29",
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
"label": "mynet_0"
},
{
"cidr": "10.0.0.10/29",
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
"label": "mynet_1"
}
]
}
`
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"network": {
"cidr": "10.0.0.10/29",
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
"label": "mynet_1"
}
}
`
// FirstNetwork is the first result in ListOutput.
var nilTime time.Time
var FirstNetwork = Network{
CIDR: "10.0.0.0/29",
ID: "20c8acc0-f747-4d71-a389-46d078ebf047",
Name: "mynet_0",
}
// SecondNetwork is the second result in ListOutput.
var SecondNetwork = Network{
CIDR: "10.0.0.10/29",
ID: "20c8acc0-f747-4d71-a389-46d078ebf000",
Name: "mynet_1",
}
// ExpectedNetworkSlice is the slice of results that should be parsed
// from ListOutput, in the expected order.
var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork}
// HandleListSuccessfully configures the test server to respond to a List request.
func HandleListSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-tenant-networks", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, ListOutput)
})
}
// HandleGetSuccessfully configures the test server to respond to a Get request
// for an existing network.
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, GetOutput)
})
}

View File

@@ -1,22 +0,0 @@
package tenantnetworks
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List returns a Pager that allows you to iterate over a collection of Network.
func List(client *gophercloud.ServiceClient) pagination.Pager {
url := listURL(client)
createPage := func(r pagination.PageResult) pagination.Page {
return NetworkPage{pagination.SinglePageBase(r)}
}
return pagination.NewPager(client, url, createPage)
}
// Get returns data about a previously created Network.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
return res
}

View File

@@ -1,68 +0,0 @@
package tenantnetworks
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// A Network represents a nova-network that an instance communicates on
type Network struct {
// CIDR is the IPv4 subnet.
CIDR string `mapstructure:"cidr"`
// ID is the UUID of the network.
ID string `mapstructure:"id"`
// Name is the common name that the network has.
Name string `mapstructure:"label"`
}
// NetworkPage stores a single, only page of Networks
// results from a List call.
type NetworkPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a NetworkPage is empty.
func (page NetworkPage) IsEmpty() (bool, error) {
va, err := ExtractNetworks(page)
return len(va) == 0, err
}
// ExtractNetworks interprets a page of results as a slice of Networks
func ExtractNetworks(page pagination.Page) ([]Network, error) {
networks := page.(NetworkPage).Body
var res struct {
Networks []Network `mapstructure:"networks"`
}
err := mapstructure.WeakDecode(networks, &res)
return res.Networks, err
}
type NetworkResult struct {
gophercloud.Result
}
// Extract is a method that attempts to interpret any Network resource
// response as a Network struct.
func (r NetworkResult) Extract() (*Network, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
Network *Network `json:"network" mapstructure:"network"`
}
err := mapstructure.Decode(r.Body, &res)
return res.Network, err
}
// GetResult is the response from a Get operation. Call its Extract method to interpret it
// as a Network.
type GetResult struct {
NetworkResult
}

View File

@@ -1,17 +0,0 @@
package tenantnetworks
import "github.com/rackspace/gophercloud"
const resourcePath = "os-tenant-networks"
func resourceURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(resourcePath)
}
func listURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func getURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(resourcePath, id)
}

View File

@@ -1,3 +0,0 @@
// Package volumeattach provides the ability to attach and detach volumes
// to instances
package volumeattach

View File

@@ -1,138 +0,0 @@
// +build fixtures
package volumeattach
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
// ListOutput is a sample response to a List call.
const ListOutput = `
{
"volumeAttachments": [
{
"device": "/dev/vdd",
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f803",
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f803"
},
{
"device": "/dev/vdc",
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804"
}
]
}
`
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"volumeAttachment": {
"device": "/dev/vdc",
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804"
}
}
`
// CreateOutput is a sample response to a Create call.
const CreateOutput = `
{
"volumeAttachment": {
"device": "/dev/vdc",
"id": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
"serverId": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804"
}
}
`
// FirstVolumeAttachment is the first result in ListOutput.
var FirstVolumeAttachment = VolumeAttachment{
Device: "/dev/vdd",
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
}
// SecondVolumeAttachment is the first result in ListOutput.
var SecondVolumeAttachment = VolumeAttachment{
Device: "/dev/vdc",
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
}
// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
// from ListOutput, in the expected order.
var ExpectedVolumeAttachmentSlice = []VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
// CreatedVolumeAttachment is the parsed result from CreatedOutput.
var CreatedVolumeAttachment = VolumeAttachment{
Device: "/dev/vdc",
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
}
// HandleListSuccessfully configures the test server to respond to a List request.
func HandleListSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, ListOutput)
})
}
// HandleGetSuccessfully configures the test server to respond to a Get request
// for an existing attachment
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments/a26887c6-c47b-4654-abb5-dfadf7d3f804", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, GetOutput)
})
}
// HandleCreateSuccessfully configures the test server to respond to a Create request
// for a new attachment
func HandleCreateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
th.TestJSONRequest(t, r, `
{
"volumeAttachment": {
"volumeId": "a26887c6-c47b-4654-abb5-dfadf7d3f804",
"device": "/dev/vdc"
}
}
`)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, CreateOutput)
})
}
// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
// an existing attachment
func HandleDeleteSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments/a26887c6-c47b-4654-abb5-dfadf7d3f804", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
w.WriteHeader(http.StatusAccepted)
})
}

View File

@@ -1,75 +0,0 @@
package volumeattach
import (
"errors"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List returns a Pager that allows you to iterate over a collection of VolumeAttachments.
func List(client *gophercloud.ServiceClient, serverId string) pagination.Pager {
return pagination.NewPager(client, listURL(client, serverId), func(r pagination.PageResult) pagination.Page {
return VolumeAttachmentsPage{pagination.SinglePageBase(r)}
})
}
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
// CreateOpts struct in this package does.
type CreateOptsBuilder interface {
ToVolumeAttachmentCreateMap() (map[string]interface{}, error)
}
// CreateOpts specifies volume attachment creation or import parameters.
type CreateOpts struct {
// Device is the device that the volume will attach to the instance as. Omit for "auto"
Device string
// VolumeID is the ID of the volume to attach to the instance
VolumeID string
}
// ToVolumeAttachmentCreateMap constructs a request body from CreateOpts.
func (opts CreateOpts) ToVolumeAttachmentCreateMap() (map[string]interface{}, error) {
if opts.VolumeID == "" {
return nil, errors.New("Missing field required for volume attachment creation: VolumeID")
}
volumeAttachment := make(map[string]interface{})
volumeAttachment["volumeId"] = opts.VolumeID
if opts.Device != "" {
volumeAttachment["device"] = opts.Device
}
return map[string]interface{}{"volumeAttachment": volumeAttachment}, nil
}
// Create requests the creation of a new volume attachment on the server
func Create(client *gophercloud.ServiceClient, serverId string, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToVolumeAttachmentCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = client.Post(createURL(client, serverId), reqBody, &res.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return res
}
// Get returns public data about a previously created VolumeAttachment.
func Get(client *gophercloud.ServiceClient, serverId, aId string) GetResult {
var res GetResult
_, res.Err = client.Get(getURL(client, serverId, aId), &res.Body, nil)
return res
}
// Delete requests the deletion of a previous stored VolumeAttachment from the server.
func Delete(client *gophercloud.ServiceClient, serverId, aId string) DeleteResult {
var res DeleteResult
_, res.Err = client.Delete(deleteURL(client, serverId, aId), nil)
return res
}

View File

@@ -1,84 +0,0 @@
package volumeattach
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// VolumeAttach controls the attachment of a volume to an instance.
type VolumeAttachment struct {
// ID is a unique id of the attachment
ID string `mapstructure:"id"`
// Device is what device the volume is attached as
Device string `mapstructure:"device"`
// VolumeID is the ID of the attached volume
VolumeID string `mapstructure:"volumeId"`
// ServerID is the ID of the instance that has the volume attached
ServerID string `mapstructure:"serverId"`
}
// VolumeAttachmentsPage stores a single, only page of VolumeAttachments
// results from a List call.
type VolumeAttachmentsPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a VolumeAttachmentsPage is empty.
func (page VolumeAttachmentsPage) IsEmpty() (bool, error) {
va, err := ExtractVolumeAttachments(page)
return len(va) == 0, err
}
// ExtractVolumeAttachments interprets a page of results as a slice of
// VolumeAttachments.
func ExtractVolumeAttachments(page pagination.Page) ([]VolumeAttachment, error) {
casted := page.(VolumeAttachmentsPage).Body
var response struct {
VolumeAttachments []VolumeAttachment `mapstructure:"volumeAttachments"`
}
err := mapstructure.WeakDecode(casted, &response)
return response.VolumeAttachments, err
}
type VolumeAttachmentResult struct {
gophercloud.Result
}
// Extract is a method that attempts to interpret any VolumeAttachment resource
// response as a VolumeAttachment struct.
func (r VolumeAttachmentResult) Extract() (*VolumeAttachment, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
VolumeAttachment *VolumeAttachment `json:"volumeAttachment" mapstructure:"volumeAttachment"`
}
err := mapstructure.Decode(r.Body, &res)
return res.VolumeAttachment, err
}
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
// as a VolumeAttachment.
type CreateResult struct {
VolumeAttachmentResult
}
// GetResult is the response from a Get operation. Call its Extract method to interpret it
// as a VolumeAttachment.
type GetResult struct {
VolumeAttachmentResult
}
// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
// the call succeeded or failed.
type DeleteResult struct {
gophercloud.ErrResult
}

View File

@@ -1,25 +0,0 @@
package volumeattach
import "github.com/rackspace/gophercloud"
const resourcePath = "os-volume_attachments"
func resourceURL(c *gophercloud.ServiceClient, serverId string) string {
return c.ServiceURL("servers", serverId, resourcePath)
}
func listURL(c *gophercloud.ServiceClient, serverId string) string {
return resourceURL(c, serverId)
}
func createURL(c *gophercloud.ServiceClient, serverId string) string {
return resourceURL(c, serverId)
}
func getURL(c *gophercloud.ServiceClient, serverId, aId string) string {
return c.ServiceURL("servers", serverId, resourcePath, aId)
}
func deleteURL(c *gophercloud.ServiceClient, serverId, aId string) string {
return getURL(c, serverId, aId)
}