Update vendor dir and Godeps.json with new Godep

This commit is contained in:
saadali
2016-05-11 16:59:55 -07:00
parent b83af3d481
commit c708e2cc82
2053 changed files with 955 additions and 140589 deletions

View File

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

View File

@@ -1,143 +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 mockCreateRuleResponseICMPZero(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": "ICMP",
"from_port": 0,
"to_port": 0,
"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": 0,
"id": "{ruleID}",
"ip_protocol": "ICMP",
"ip_range": {
"cidr": "10.10.12.0/24"
},
"to_port": 0
}
}
`)
})
}
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,96 +0,0 @@
package defsecrules
import (
"errors"
"strings"
"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 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
return rule, errors.New("A FromPort must be set")
}
if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
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 extensions provides information and interaction with the
// different extensions available for the OpenStack Compute service.
package extensions

View File

@@ -1,3 +0,0 @@
// Package floatingip provides the ability to manage floating ips through
// nova-network
package floatingip

View File

@@ -1,193 +0,0 @@
// +build fixtures
package floatingip
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 = `
{
"floating_ips": [
{
"fixed_ip": null,
"id": 1,
"instance_id": null,
"ip": "10.10.10.1",
"pool": "nova"
},
{
"fixed_ip": "166.78.185.201",
"id": 2,
"instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"ip": "10.10.10.2",
"pool": "nova"
}
]
}
`
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"floating_ip": {
"fixed_ip": "166.78.185.201",
"id": 2,
"instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"ip": "10.10.10.2",
"pool": "nova"
}
}
`
// CreateOutput is a sample response to a Post call
const CreateOutput = `
{
"floating_ip": {
"fixed_ip": null,
"id": 1,
"instance_id": null,
"ip": "10.10.10.1",
"pool": "nova"
}
}
`
// FirstFloatingIP is the first result in ListOutput.
var FirstFloatingIP = FloatingIP{
ID: "1",
IP: "10.10.10.1",
Pool: "nova",
}
// SecondFloatingIP is the first result in ListOutput.
var SecondFloatingIP = FloatingIP{
FixedIP: "166.78.185.201",
ID: "2",
InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
IP: "10.10.10.2",
Pool: "nova",
}
// ExpectedFloatingIPsSlice is the slice of results that should be parsed
// from ListOutput, in the expected order.
var ExpectedFloatingIPsSlice = []FloatingIP{FirstFloatingIP, SecondFloatingIP}
// CreatedFloatingIP is the parsed result from CreateOutput.
var CreatedFloatingIP = FloatingIP{
ID: "1",
IP: "10.10.10.1",
Pool: "nova",
}
// HandleListSuccessfully configures the test server to respond to a List request.
func HandleListSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-floating-ips", 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 floating ip
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-floating-ips/2", 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 floating ip
func HandleCreateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-floating-ips", 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, `
{
"pool": "nova"
}
`)
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 floating ip
func HandleDeleteSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-floating-ips/1", 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)
})
}
// HandleAssociateSuccessfully configures the test server to respond to a Post request
// to associate an allocated floating IP
func HandleAssociateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", 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, `
{
"addFloatingIp": {
"address": "10.10.10.2"
}
}
`)
w.WriteHeader(http.StatusAccepted)
})
}
// HandleFixedAssociateSucessfully configures the test server to respond to a Post request
// to associate an allocated floating IP with a specific fixed IP address
func HandleAssociateFixedSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", 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, `
{
"addFloatingIp": {
"address": "10.10.10.2",
"fixed_address": "166.78.185.201"
}
}
`)
w.WriteHeader(http.StatusAccepted)
})
}
// HandleDisassociateSuccessfully configures the test server to respond to a Post request
// to disassociate an allocated floating IP
func HandleDisassociateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", 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, `
{
"removeFloatingIp": {
"address": "10.10.10.2"
}
}
`)
w.WriteHeader(http.StatusAccepted)
})
}

View File

@@ -1,171 +0,0 @@
package floatingip
import (
"errors"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List returns a Pager that allows you to iterate over a collection of FloatingIPs.
func List(client *gophercloud.ServiceClient) pagination.Pager {
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
return FloatingIPsPage{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 {
ToFloatingIPCreateMap() (map[string]interface{}, error)
}
// CreateOpts specifies a Floating IP allocation request
type CreateOpts struct {
// Pool is the pool of floating IPs to allocate one from
Pool string
}
// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance
type AssociateOpts struct {
// ServerID is the UUID of the server
ServerID string
// FixedIP is an optional fixed IP address of the server
FixedIP string
// FloatingIP is the floating IP to associate with an instance
FloatingIP string
}
// ToFloatingIPCreateMap constructs a request body from CreateOpts.
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
if opts.Pool == "" {
return nil, errors.New("Missing field required for floating IP creation: Pool")
}
return map[string]interface{}{"pool": opts.Pool}, nil
}
// ToAssociateMap constructs a request body from AssociateOpts.
func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
if opts.ServerID == "" {
return nil, errors.New("Required field missing for floating IP association: ServerID")
}
if opts.FloatingIP == "" {
return nil, errors.New("Required field missing for floating IP association: FloatingIP")
}
associateInfo := map[string]interface{}{
"serverId": opts.ServerID,
"floatingIp": opts.FloatingIP,
"fixedIp": opts.FixedIP,
}
return associateInfo, nil
}
// Create requests the creation of a new floating IP
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToFloatingIPCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return res
}
// Get returns data about a previously created FloatingIP.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
return res
}
// Delete requests the deletion of a previous allocated FloatingIP.
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = client.Delete(deleteURL(client, id), nil)
return res
}
// association / disassociation
// Associate pairs an allocated floating IP with an instance
// Deprecated. Use AssociateInstance.
func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
var res AssociateResult
addFloatingIp := make(map[string]interface{})
addFloatingIp["address"] = fip
reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
return res
}
// AssociateInstance pairs an allocated floating IP with an instance.
func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult {
var res AssociateResult
associateInfo, err := opts.ToAssociateMap()
if err != nil {
res.Err = err
return res
}
addFloatingIp := make(map[string]interface{})
addFloatingIp["address"] = associateInfo["floatingIp"].(string)
// fixedIp is not required
if associateInfo["fixedIp"] != "" {
addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string)
}
serverId := associateInfo["serverId"].(string)
reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
return res
}
// Disassociate decouples an allocated floating IP from an instance
// Deprecated. Use DisassociateInstance.
func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
var res DisassociateResult
removeFloatingIp := make(map[string]interface{})
removeFloatingIp["address"] = fip
reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
return res
}
// DisassociateInstance decouples an allocated floating IP from an instance
func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult {
var res DisassociateResult
associateInfo, err := opts.ToAssociateMap()
if err != nil {
res.Err = err
return res
}
removeFloatingIp := make(map[string]interface{})
removeFloatingIp["address"] = associateInfo["floatingIp"].(string)
reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
serverId := associateInfo["serverId"].(string)
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
return res
}

View File

@@ -1,99 +0,0 @@
package floatingip
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// A FloatingIP is an IP that can be associated with an instance
type FloatingIP struct {
// ID is a unique ID of the Floating IP
ID string `mapstructure:"id"`
// FixedIP is the IP of the instance related to the Floating IP
FixedIP string `mapstructure:"fixed_ip,omitempty"`
// InstanceID is the ID of the instance that is using the Floating IP
InstanceID string `mapstructure:"instance_id"`
// IP is the actual Floating IP
IP string `mapstructure:"ip"`
// Pool is the pool of floating IPs that this floating IP belongs to
Pool string `mapstructure:"pool"`
}
// FloatingIPsPage stores a single, only page of FloatingIPs
// results from a List call.
type FloatingIPsPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a FloatingIPsPage is empty.
func (page FloatingIPsPage) IsEmpty() (bool, error) {
va, err := ExtractFloatingIPs(page)
return len(va) == 0, err
}
// ExtractFloatingIPs interprets a page of results as a slice of
// FloatingIPs.
func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) {
casted := page.(FloatingIPsPage).Body
var response struct {
FloatingIPs []FloatingIP `mapstructure:"floating_ips"`
}
err := mapstructure.WeakDecode(casted, &response)
return response.FloatingIPs, err
}
type FloatingIPResult struct {
gophercloud.Result
}
// Extract is a method that attempts to interpret any FloatingIP resource
// response as a FloatingIP struct.
func (r FloatingIPResult) Extract() (*FloatingIP, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
FloatingIP *FloatingIP `json:"floating_ip" mapstructure:"floating_ip"`
}
err := mapstructure.WeakDecode(r.Body, &res)
return res.FloatingIP, err
}
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
// as a FloatingIP.
type CreateResult struct {
FloatingIPResult
}
// GetResult is the response from a Get operation. Call its Extract method to interpret it
// as a FloatingIP.
type GetResult struct {
FloatingIPResult
}
// 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
}
// AssociateResult is the response from a Delete operation. Call its Extract method to determine if
// the call succeeded or failed.
type AssociateResult struct {
gophercloud.ErrResult
}
// DisassociateResult is the response from a Delete operation. Call its Extract method to determine if
// the call succeeded or failed.
type DisassociateResult struct {
gophercloud.ErrResult
}

View File

@@ -1,37 +0,0 @@
package floatingip
import "github.com/rackspace/gophercloud"
const resourcePath = "os-floating-ips"
func resourceURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(resourcePath)
}
func listURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func createURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func getURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(resourcePath, id)
}
func deleteURL(c *gophercloud.ServiceClient, id string) string {
return getURL(c, id)
}
func serverURL(c *gophercloud.ServiceClient, serverId string) string {
return c.ServiceURL("servers/" + serverId + "/action")
}
func associateURL(c *gophercloud.ServiceClient, serverId string) string {
return serverURL(c, serverId)
}
func disassociateURL(c *gophercloud.ServiceClient, serverId string) string {
return serverURL(c, serverId)
}

View File

@@ -1,3 +0,0 @@
// Package keypairs provides information and interaction with the Keypairs
// extension for the OpenStack Compute service.
package keypairs

View File

@@ -1,171 +0,0 @@
// +build fixtures
package keypairs
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 = `
{
"keypairs": [
{
"keypair": {
"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
"name": "firstkey",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n"
}
},
{
"keypair": {
"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
"name": "secondkey",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n"
}
}
]
}
`
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"keypair": {
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
"name": "firstkey",
"fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a"
}
}
`
// CreateOutput is a sample response to a Create call.
const CreateOutput = `
{
"keypair": {
"fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
"name": "createdkey",
"private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
"user_id": "fake"
}
}
`
// ImportOutput is a sample response to a Create call that provides its own public key.
const ImportOutput = `
{
"keypair": {
"fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
"name": "importedkey",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
"user_id": "fake"
}
}
`
// FirstKeyPair is the first result in ListOutput.
var FirstKeyPair = KeyPair{
Name: "firstkey",
Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a",
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n",
}
// SecondKeyPair is the second result in ListOutput.
var SecondKeyPair = KeyPair{
Name: "secondkey",
Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
}
// ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected
// order.
var ExpectedKeyPairSlice = []KeyPair{FirstKeyPair, SecondKeyPair}
// CreatedKeyPair is the parsed result from CreatedOutput.
var CreatedKeyPair = KeyPair{
Name: "createdkey",
Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8",
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n",
PrivateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n",
UserID: "fake",
}
// ImportedKeyPair is the parsed result from ImportOutput.
var ImportedKeyPair = KeyPair{
Name: "importedkey",
Fingerprint: "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c",
PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova",
UserID: "fake",
}
// HandleListSuccessfully configures the test server to respond to a List request.
func HandleListSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-keypairs", 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 "firstkey".
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-keypairs/firstkey", 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
// keypair called "createdkey".
func HandleCreateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-keypairs", 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, `{ "keypair": { "name": "createdkey" } }`)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, CreateOutput)
})
}
// HandleImportSuccessfully configures the test server to respond to an Import request for an
// existing keypair called "importedkey".
func HandleImportSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-keypairs", 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, `
{
"keypair": {
"name": "importedkey",
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova"
}
}
`)
w.Header().Add("Content-Type", "application/json")
fmt.Fprintf(w, ImportOutput)
})
}
// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a
// keypair called "deletedkey".
func HandleDeleteSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-keypairs/deletedkey", 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,102 +0,0 @@
package keypairs
import (
"errors"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
)
// CreateOptsExt adds a KeyPair option to the base CreateOpts.
type CreateOptsExt struct {
servers.CreateOptsBuilder
KeyName string `json:"key_name,omitempty"`
}
// ToServerCreateMap adds the key_name and, optionally, key_data options 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 opts.KeyName == "" {
return base, nil
}
serverMap := base["server"].(map[string]interface{})
serverMap["key_name"] = opts.KeyName
return base, nil
}
// List returns a Pager that allows you to iterate over a collection of KeyPairs.
func List(client *gophercloud.ServiceClient) pagination.Pager {
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
return KeyPairPage{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 {
ToKeyPairCreateMap() (map[string]interface{}, error)
}
// CreateOpts specifies keypair creation or import parameters.
type CreateOpts struct {
// Name [required] is a friendly name to refer to this KeyPair in other services.
Name string
// PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key
// will be imported and no new key will be created.
PublicKey string
}
// ToKeyPairCreateMap constructs a request body from CreateOpts.
func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
if opts.Name == "" {
return nil, errors.New("Missing field required for keypair creation: Name")
}
keypair := make(map[string]interface{})
keypair["name"] = opts.Name
if opts.PublicKey != "" {
keypair["public_key"] = opts.PublicKey
}
return map[string]interface{}{"keypair": keypair}, nil
}
// Create requests the creation of a new keypair on the server, or to import a pre-existing
// keypair.
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToKeyPairCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return res
}
// Get returns public data about a previously uploaded KeyPair.
func Get(client *gophercloud.ServiceClient, name string) GetResult {
var res GetResult
_, res.Err = client.Get(getURL(client, name), &res.Body, nil)
return res
}
// Delete requests the deletion of a previous stored KeyPair from the server.
func Delete(client *gophercloud.ServiceClient, name string) DeleteResult {
var res DeleteResult
_, res.Err = client.Delete(deleteURL(client, name), nil)
return res
}

View File

@@ -1,94 +0,0 @@
package keypairs
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// KeyPair is an SSH key known to the OpenStack cluster that is available to be injected into
// servers.
type KeyPair struct {
// Name is used to refer to this keypair from other services within this region.
Name string `mapstructure:"name"`
// Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer
// public key.
Fingerprint string `mapstructure:"fingerprint"`
// PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..."
PublicKey string `mapstructure:"public_key"`
// PrivateKey is the private key from this pair, in PEM format.
// "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just
// returned from a Create call
PrivateKey string `mapstructure:"private_key"`
// UserID is the user who owns this keypair.
UserID string `mapstructure:"user_id"`
}
// KeyPairPage stores a single, only page of KeyPair results from a List call.
type KeyPairPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a KeyPairPage is empty.
func (page KeyPairPage) IsEmpty() (bool, error) {
ks, err := ExtractKeyPairs(page)
return len(ks) == 0, err
}
// ExtractKeyPairs interprets a page of results as a slice of KeyPairs.
func ExtractKeyPairs(page pagination.Page) ([]KeyPair, error) {
type pair struct {
KeyPair KeyPair `mapstructure:"keypair"`
}
var resp struct {
KeyPairs []pair `mapstructure:"keypairs"`
}
err := mapstructure.Decode(page.(KeyPairPage).Body, &resp)
results := make([]KeyPair, len(resp.KeyPairs))
for i, pair := range resp.KeyPairs {
results[i] = pair.KeyPair
}
return results, err
}
type keyPairResult struct {
gophercloud.Result
}
// Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct.
func (r keyPairResult) Extract() (*KeyPair, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
KeyPair *KeyPair `json:"keypair" mapstructure:"keypair"`
}
err := mapstructure.Decode(r.Body, &res)
return res.KeyPair, err
}
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
// as a KeyPair.
type CreateResult struct {
keyPairResult
}
// GetResult is the response from a Get operation. Call its Extract method to interpret it
// as a KeyPair.
type GetResult struct {
keyPairResult
}
// 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 keypairs
import "github.com/rackspace/gophercloud"
const resourcePath = "os-keypairs"
func resourceURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(resourcePath)
}
func listURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func createURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func getURL(c *gophercloud.ServiceClient, name string) string {
return c.ServiceURL(resourcePath, name)
}
func deleteURL(c *gophercloud.ServiceClient, name string) string {
return getURL(c, name)
}

View File

@@ -1,2 +0,0 @@
// Package network provides the ability to manage nova-networks
package networks

View File

@@ -1,209 +0,0 @@
// +build fixtures
package networks
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": [
{
"bridge": "br100",
"bridge_interface": "eth0",
"broadcast": "10.0.0.7",
"cidr": "10.0.0.0/29",
"cidr_v6": null,
"created_at": "2011-08-15 06:19:19.387525",
"deleted": false,
"deleted_at": null,
"dhcp_start": "10.0.0.3",
"dns1": null,
"dns2": null,
"gateway": "10.0.0.1",
"gateway_v6": null,
"host": "nsokolov-desktop",
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
"injected": false,
"label": "mynet_0",
"multi_host": false,
"netmask": "255.255.255.248",
"netmask_v6": null,
"priority": null,
"project_id": "1234",
"rxtx_base": null,
"updated_at": "2011-08-16 09:26:13.048257",
"vlan": 100,
"vpn_private_address": "10.0.0.2",
"vpn_public_address": "127.0.0.1",
"vpn_public_port": 1000
},
{
"bridge": "br101",
"bridge_interface": "eth0",
"broadcast": "10.0.0.15",
"cidr": "10.0.0.10/29",
"cidr_v6": null,
"created_at": "2011-08-15 06:19:19.885495",
"deleted": false,
"deleted_at": null,
"dhcp_start": "10.0.0.11",
"dns1": null,
"dns2": null,
"gateway": "10.0.0.9",
"gateway_v6": null,
"host": null,
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
"injected": false,
"label": "mynet_1",
"multi_host": false,
"netmask": "255.255.255.248",
"netmask_v6": null,
"priority": null,
"project_id": null,
"rxtx_base": null,
"updated_at": null,
"vlan": 101,
"vpn_private_address": "10.0.0.10",
"vpn_public_address": null,
"vpn_public_port": 1001
}
]
}
`
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"network": {
"bridge": "br101",
"bridge_interface": "eth0",
"broadcast": "10.0.0.15",
"cidr": "10.0.0.10/29",
"cidr_v6": null,
"created_at": "2011-08-15 06:19:19.885495",
"deleted": false,
"deleted_at": null,
"dhcp_start": "10.0.0.11",
"dns1": null,
"dns2": null,
"gateway": "10.0.0.9",
"gateway_v6": null,
"host": null,
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
"injected": false,
"label": "mynet_1",
"multi_host": false,
"netmask": "255.255.255.248",
"netmask_v6": null,
"priority": null,
"project_id": null,
"rxtx_base": null,
"updated_at": null,
"vlan": 101,
"vpn_private_address": "10.0.0.10",
"vpn_public_address": null,
"vpn_public_port": 1001
}
}
`
// FirstNetwork is the first result in ListOutput.
var nilTime time.Time
var FirstNetwork = Network{
Bridge: "br100",
BridgeInterface: "eth0",
Broadcast: "10.0.0.7",
CIDR: "10.0.0.0/29",
CIDRv6: "",
CreatedAt: time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC),
Deleted: false,
DeletedAt: nilTime,
DHCPStart: "10.0.0.3",
DNS1: "",
DNS2: "",
Gateway: "10.0.0.1",
Gatewayv6: "",
Host: "nsokolov-desktop",
ID: "20c8acc0-f747-4d71-a389-46d078ebf047",
Injected: false,
Label: "mynet_0",
MultiHost: false,
Netmask: "255.255.255.248",
Netmaskv6: "",
Priority: 0,
ProjectID: "1234",
RXTXBase: 0,
UpdatedAt: time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC),
VLAN: 100,
VPNPrivateAddress: "10.0.0.2",
VPNPublicAddress: "127.0.0.1",
VPNPublicPort: 1000,
}
// SecondNetwork is the second result in ListOutput.
var SecondNetwork = Network{
Bridge: "br101",
BridgeInterface: "eth0",
Broadcast: "10.0.0.15",
CIDR: "10.0.0.10/29",
CIDRv6: "",
CreatedAt: time.Date(2011, 8, 15, 6, 19, 19, 885495000, time.UTC),
Deleted: false,
DeletedAt: nilTime,
DHCPStart: "10.0.0.11",
DNS1: "",
DNS2: "",
Gateway: "10.0.0.9",
Gatewayv6: "",
Host: "",
ID: "20c8acc0-f747-4d71-a389-46d078ebf000",
Injected: false,
Label: "mynet_1",
MultiHost: false,
Netmask: "255.255.255.248",
Netmaskv6: "",
Priority: 0,
ProjectID: "",
RXTXBase: 0,
UpdatedAt: nilTime,
VLAN: 101,
VPNPrivateAddress: "10.0.0.10",
VPNPublicAddress: "",
VPNPublicPort: 1001,
}
// 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-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-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 networks
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,222 +0,0 @@
package networks
import (
"fmt"
"time"
"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 {
// The Bridge that VIFs on this network are connected to
Bridge string `mapstructure:"bridge"`
// BridgeInterface is what interface is connected to the Bridge
BridgeInterface string `mapstructure:"bridge_interface"`
// The Broadcast address of the network.
Broadcast string `mapstructure:"broadcast"`
// CIDR is the IPv4 subnet.
CIDR string `mapstructure:"cidr"`
// CIDRv6 is the IPv6 subnet.
CIDRv6 string `mapstructure:"cidr_v6"`
// CreatedAt is when the network was created..
CreatedAt time.Time `mapstructure:"-"`
// Deleted shows if the network has been deleted.
Deleted bool `mapstructure:"deleted"`
// DeletedAt is the time when the network was deleted.
DeletedAt time.Time `mapstructure:"-"`
// DHCPStart is the start of the DHCP address range.
DHCPStart string `mapstructure:"dhcp_start"`
// DNS1 is the first DNS server to use through DHCP.
DNS1 string `mapstructure:"dns_1"`
// DNS2 is the first DNS server to use through DHCP.
DNS2 string `mapstructure:"dns_2"`
// Gateway is the network gateway.
Gateway string `mapstructure:"gateway"`
// Gatewayv6 is the IPv6 network gateway.
Gatewayv6 string `mapstructure:"gateway_v6"`
// Host is the host that the network service is running on.
Host string `mapstructure:"host"`
// ID is the UUID of the network.
ID string `mapstructure:"id"`
// Injected determines if network information is injected into the host.
Injected bool `mapstructure:"injected"`
// Label is the common name that the network has..
Label string `mapstructure:"label"`
// MultiHost is if multi-host networking is enablec..
MultiHost bool `mapstructure:"multi_host"`
// Netmask is the network netmask.
Netmask string `mapstructure:"netmask"`
// Netmaskv6 is the IPv6 netmask.
Netmaskv6 string `mapstructure:"netmask_v6"`
// Priority is the network interface priority.
Priority int `mapstructure:"priority"`
// ProjectID is the project associated with this network.
ProjectID string `mapstructure:"project_id"`
// RXTXBase configures bandwidth entitlement.
RXTXBase int `mapstructure:"rxtx_base"`
// UpdatedAt is the time when the network was last updated.
UpdatedAt time.Time `mapstructure:"-"`
// VLAN is the vlan this network runs on.
VLAN int `mapstructure:"vlan"`
// VPNPrivateAddress is the private address of the CloudPipe VPN.
VPNPrivateAddress string `mapstructure:"vpn_private_address"`
// VPNPublicAddress is the public address of the CloudPipe VPN.
VPNPublicAddress string `mapstructure:"vpn_public_address"`
// VPNPublicPort is the port of the CloudPipe VPN.
VPNPublicPort int `mapstructure:"vpn_public_port"`
}
// 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) {
var res struct {
Networks []Network `mapstructure:"networks"`
}
err := mapstructure.Decode(page.(NetworkPage).Body, &res)
var rawNetworks []interface{}
body := page.(NetworkPage).Body
switch body.(type) {
case map[string]interface{}:
rawNetworks = body.(map[string]interface{})["networks"].([]interface{})
case map[string][]interface{}:
rawNetworks = body.(map[string][]interface{})["networks"]
default:
return res.Networks, fmt.Errorf("Unknown type")
}
for i := range rawNetworks {
thisNetwork := rawNetworks[i].(map[string]interface{})
if t, ok := thisNetwork["created_at"].(string); ok && t != "" {
createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
if err != nil {
return res.Networks, err
}
res.Networks[i].CreatedAt = createdAt
}
if t, ok := thisNetwork["updated_at"].(string); ok && t != "" {
updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
if err != nil {
return res.Networks, err
}
res.Networks[i].UpdatedAt = updatedAt
}
if t, ok := thisNetwork["deleted_at"].(string); ok && t != "" {
deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
if err != nil {
return res.Networks, err
}
res.Networks[i].DeletedAt = deletedAt
}
}
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"`
}
config := &mapstructure.DecoderConfig{
Result: &res,
WeaklyTypedInput: true,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return nil, err
}
if err := decoder.Decode(r.Body); err != nil {
return nil, err
}
b := r.Body.(map[string]interface{})["network"].(map[string]interface{})
if t, ok := b["created_at"].(string); ok && t != "" {
createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
if err != nil {
return res.Network, err
}
res.Network.CreatedAt = createdAt
}
if t, ok := b["updated_at"].(string); ok && t != "" {
updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
if err != nil {
return res.Network, err
}
res.Network.UpdatedAt = updatedAt
}
if t, ok := b["deleted_at"].(string); ok && t != "" {
deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
if err != nil {
return res.Network, err
}
res.Network.DeletedAt = deletedAt
}
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 networks
import "github.com/rackspace/gophercloud"
const resourcePath = "os-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 quotasets provides information and interaction with QuotaSet
// extension for the OpenStack Compute service.
package quotasets

View File

@@ -1,59 +0,0 @@
// +build fixtures
package quotasets
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"quota_set" : {
"instances" : 25,
"security_groups" : 10,
"security_group_rules" : 20,
"cores" : 200,
"injected_file_content_bytes" : 10240,
"injected_files" : 5,
"metadata_items" : 128,
"ram" : 200000,
"keypairs" : 10,
"injected_file_path_bytes" : 255
}
}
`
const FirstTenantID = "555544443333222211110000ffffeeee"
// FirstQuotaSet is the first result in ListOutput.
var FirstQuotaSet = QuotaSet{
FixedIps: 0,
FloatingIps: 0,
InjectedFileContentBytes: 10240,
InjectedFilePathBytes: 255,
InjectedFiles: 5,
KeyPairs: 10,
MetadataItems: 128,
Ram: 200000,
SecurityGroupRules: 20,
SecurityGroups: 10,
Cores: 200,
Instances: 25,
}
// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, 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,12 +0,0 @@
package quotasets
import (
"github.com/rackspace/gophercloud"
)
// Get returns public data about a previously created QuotaSet.
func Get(client *gophercloud.ServiceClient, tenantID string) GetResult {
var res GetResult
_, res.Err = client.Get(getURL(client, tenantID), &res.Body, nil)
return res
}

View File

@@ -1,86 +0,0 @@
package quotasets
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// QuotaSet is a set of operational limits that allow for control of compute usage.
type QuotaSet struct {
//ID is tenant associated with this quota_set
ID string `mapstructure:"id"`
//FixedIps is number of fixed ips alloted this quota_set
FixedIps int `mapstructure:"fixed_ips"`
// FloatingIps is number of floating ips alloted this quota_set
FloatingIps int `mapstructure:"floating_ips"`
// InjectedFileContentBytes is content bytes allowed for each injected file
InjectedFileContentBytes int `mapstructure:"injected_file_content_bytes"`
// InjectedFilePathBytes is allowed bytes for each injected file path
InjectedFilePathBytes int `mapstructure:"injected_file_path_bytes"`
// InjectedFiles is injected files allowed for each project
InjectedFiles int `mapstructure:"injected_files"`
// KeyPairs is number of ssh keypairs
KeyPairs int `mapstructure:"keypairs"`
// MetadataItems is number of metadata items allowed for each instance
MetadataItems int `mapstructure:"metadata_items"`
// Ram is megabytes allowed for each instance
Ram int `mapstructure:"ram"`
// SecurityGroupRules is rules allowed for each security group
SecurityGroupRules int `mapstructure:"security_group_rules"`
// SecurityGroups security groups allowed for each project
SecurityGroups int `mapstructure:"security_groups"`
// Cores is number of instance cores allowed for each project
Cores int `mapstructure:"cores"`
// Instances is number of instances allowed for each project
Instances int `mapstructure:"instances"`
}
// QuotaSetPage stores a single, only page of QuotaSet results from a List call.
type QuotaSetPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a QuotaSetsetPage is empty.
func (page QuotaSetPage) IsEmpty() (bool, error) {
ks, err := ExtractQuotaSets(page)
return len(ks) == 0, err
}
// ExtractQuotaSets interprets a page of results as a slice of QuotaSets.
func ExtractQuotaSets(page pagination.Page) ([]QuotaSet, error) {
var resp struct {
QuotaSets []QuotaSet `mapstructure:"quotas"`
}
err := mapstructure.Decode(page.(QuotaSetPage).Body, &resp)
results := make([]QuotaSet, len(resp.QuotaSets))
for i, q := range resp.QuotaSets {
results[i] = q
}
return results, err
}
type quotaResult struct {
gophercloud.Result
}
// Extract is a method that attempts to interpret any QuotaSet resource response as a QuotaSet struct.
func (r quotaResult) Extract() (*QuotaSet, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
QuotaSet *QuotaSet `json:"quota_set" mapstructure:"quota_set"`
}
err := mapstructure.Decode(r.Body, &res)
return res.QuotaSet, err
}
// GetResult is the response from a Get operation. Call its Extract method to interpret it
// as a QuotaSet.
type GetResult struct {
quotaResult
}

View File

@@ -1,13 +0,0 @@
package quotasets
import "github.com/rackspace/gophercloud"
const resourcePath = "os-quota-sets"
func resourceURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(resourcePath)
}
func getURL(c *gophercloud.ServiceClient, tenantID string) string {
return c.ServiceURL(resourcePath, tenantID)
}

View File

@@ -1,3 +0,0 @@
// Package schedulerhints enables instances to provide the OpenStack scheduler
// hints about where they should be placed in the cloud.
package schedulerhints

View File

@@ -1,134 +0,0 @@
package schedulerhints
import (
"fmt"
"net"
"regexp"
"strings"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
)
// SchedulerHints represents a set of scheduling hints that are passed to the
// OpenStack scheduler
type SchedulerHints struct {
// Group specifies a Server Group to place the instance in.
Group string
// DifferentHost will place the instance on a compute node that does not
// host the given instances.
DifferentHost []string
// SameHost will place the instance on a compute node that hosts the given
// instances.
SameHost []string
// Query is a conditional statement that results in compute nodes able to
// host the instance.
Query []interface{}
// TargetCell specifies a cell name where the instance will be placed.
TargetCell string
// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
BuildNearHostIP string
}
// SchedulerHintsBuilder builds the scheduler hints into a serializable format.
type SchedulerHintsBuilder interface {
ToServerSchedulerHintsMap() (map[string]interface{}, error)
}
// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
func (opts SchedulerHints) ToServerSchedulerHintsMap() (map[string]interface{}, error) {
sh := make(map[string]interface{})
uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
if opts.Group != "" {
if !uuidRegex.MatchString(opts.Group) {
return nil, fmt.Errorf("Group must be a UUID")
}
sh["group"] = opts.Group
}
if len(opts.DifferentHost) > 0 {
for _, diffHost := range opts.DifferentHost {
if !uuidRegex.MatchString(diffHost) {
return nil, fmt.Errorf("The hosts in DifferentHost must be in UUID format.")
}
}
sh["different_host"] = opts.DifferentHost
}
if len(opts.SameHost) > 0 {
for _, sameHost := range opts.SameHost {
if !uuidRegex.MatchString(sameHost) {
return nil, fmt.Errorf("The hosts in SameHost must be in UUID format.")
}
}
sh["same_host"] = opts.SameHost
}
/* Query can be something simple like:
[">=", "$free_ram_mb", 1024]
Or more complex like:
['and',
['>=', '$free_ram_mb', 1024],
['>=', '$free_disk_mb', 200 * 1024]
]
Because of the possible complexity, just make sure the length is a minimum of 3.
*/
if len(opts.Query) > 0 {
if len(opts.Query) < 3 {
return nil, fmt.Errorf("Query must be a conditional statement in the format of [op,variable,value]")
}
sh["query"] = opts.Query
}
if opts.TargetCell != "" {
sh["target_cell"] = opts.TargetCell
}
if opts.BuildNearHostIP != "" {
if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
return nil, fmt.Errorf("BuildNearHostIP must be a valid subnet in the form 192.168.1.1/24")
}
ipParts := strings.Split(opts.BuildNearHostIP, "/")
sh["build_near_host_ip"] = ipParts[0]
sh["cidr"] = "/" + ipParts[1]
}
return sh, nil
}
// CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
type CreateOptsExt struct {
servers.CreateOptsBuilder
// SchedulerHints provides a set of hints to the scheduler.
SchedulerHints SchedulerHintsBuilder
}
// ToServerCreateMap adds the SchedulerHints 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
}
schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsMap()
if err != nil {
return nil, err
}
if len(schedulerHints) == 0 {
return base, nil
}
base["os:scheduler_hints"] = schedulerHints
return base, nil
}

View File

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

View File

@@ -1,303 +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 mockAddRuleResponseICMPZero(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": 0,
"ip_protocol": "ICMP",
"to_port": 0,
"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": 0,
"group": {},
"ip_protocol": "ICMP",
"to_port": 0,
"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)
fmt.Fprintf(w, `{}`)
})
}
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)
fmt.Fprintf(w, `{}`)
})
}

View File

@@ -1,258 +0,0 @@
package secgroups
import (
"errors"
"strings"
"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 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
return rule, errors.New("A FromPort must be set")
}
if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
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 servergroups provides the ability to manage server groups
package servergroups

View File

@@ -1,161 +0,0 @@
// +build fixtures
package servergroups
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 = `
{
"server_groups": [
{
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
"name": "test",
"policies": [
"anti-affinity"
],
"members": [],
"metadata": {}
},
{
"id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
"name": "test2",
"policies": [
"affinity"
],
"members": [],
"metadata": {}
}
]
}
`
// GetOutput is a sample response to a Get call.
const GetOutput = `
{
"server_group": {
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
"name": "test",
"policies": [
"anti-affinity"
],
"members": [],
"metadata": {}
}
}
`
// CreateOutput is a sample response to a Post call
const CreateOutput = `
{
"server_group": {
"id": "616fb98f-46ca-475e-917e-2563e5a8cd19",
"name": "test",
"policies": [
"anti-affinity"
],
"members": [],
"metadata": {}
}
}
`
// FirstServerGroup is the first result in ListOutput.
var FirstServerGroup = ServerGroup{
ID: "616fb98f-46ca-475e-917e-2563e5a8cd19",
Name: "test",
Policies: []string{
"anti-affinity",
},
Members: []string{},
Metadata: map[string]interface{}{},
}
// SecondServerGroup is the second result in ListOutput.
var SecondServerGroup = ServerGroup{
ID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
Name: "test2",
Policies: []string{
"affinity",
},
Members: []string{},
Metadata: map[string]interface{}{},
}
// ExpectedServerGroupSlice is the slice of results that should be parsed
// from ListOutput, in the expected order.
var ExpectedServerGroupSlice = []ServerGroup{FirstServerGroup, SecondServerGroup}
// CreatedServerGroup is the parsed result from CreateOutput.
var CreatedServerGroup = ServerGroup{
ID: "616fb98f-46ca-475e-917e-2563e5a8cd19",
Name: "test",
Policies: []string{
"anti-affinity",
},
Members: []string{},
Metadata: map[string]interface{}{},
}
// HandleListSuccessfully configures the test server to respond to a List request.
func HandleListSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-server-groups", 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 server group
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-server-groups/4d8c3732-a248-40ed-bebc-539a6ffd25c0", 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 server group
func HandleCreateSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-server-groups", 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, `
{
"server_group": {
"name": "test",
"policies": [
"anti-affinity"
]
}
}
`)
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 server group
func HandleDeleteSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/os-server-groups/616fb98f-46ca-475e-917e-2563e5a8cd19", 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,77 +0,0 @@
package servergroups
import (
"errors"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List returns a Pager that allows you to iterate over a collection of ServerGroups.
func List(client *gophercloud.ServiceClient) pagination.Pager {
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
return ServerGroupsPage{pagination.SinglePageBase(r)}
})
}
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notably, the
// CreateOpts struct in this package does.
type CreateOptsBuilder interface {
ToServerGroupCreateMap() (map[string]interface{}, error)
}
// CreateOpts specifies a Server Group allocation request
type CreateOpts struct {
// Name is the name of the server group
Name string
// Policies are the server group policies
Policies []string
}
// ToServerGroupCreateMap constructs a request body from CreateOpts.
func (opts CreateOpts) ToServerGroupCreateMap() (map[string]interface{}, error) {
if opts.Name == "" {
return nil, errors.New("Missing field required for server group creation: Name")
}
if len(opts.Policies) < 1 {
return nil, errors.New("Missing field required for server group creation: Policies")
}
serverGroup := make(map[string]interface{})
serverGroup["name"] = opts.Name
serverGroup["policies"] = opts.Policies
return map[string]interface{}{"server_group": serverGroup}, nil
}
// Create requests the creation of a new Server Group
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToServerGroupCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
return res
}
// Get returns data about a previously created ServerGroup.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
return res
}
// Delete requests the deletion of a previously allocated ServerGroup.
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = client.Delete(deleteURL(client, id), nil)
return res
}

View File

@@ -1,87 +0,0 @@
package servergroups
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// A ServerGroup creates a policy for instance placement in the cloud
type ServerGroup struct {
// ID is the unique ID of the Server Group.
ID string `mapstructure:"id"`
// Name is the common name of the server group.
Name string `mapstructure:"name"`
// Polices are the group policies.
Policies []string `mapstructure:"policies"`
// Members are the members of the server group.
Members []string `mapstructure:"members"`
// Metadata includes a list of all user-specified key-value pairs attached to the Server Group.
Metadata map[string]interface{}
}
// ServerGroupsPage stores a single, only page of ServerGroups
// results from a List call.
type ServerGroupsPage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a ServerGroupsPage is empty.
func (page ServerGroupsPage) IsEmpty() (bool, error) {
va, err := ExtractServerGroups(page)
return len(va) == 0, err
}
// ExtractServerGroups interprets a page of results as a slice of
// ServerGroups.
func ExtractServerGroups(page pagination.Page) ([]ServerGroup, error) {
casted := page.(ServerGroupsPage).Body
var response struct {
ServerGroups []ServerGroup `mapstructure:"server_groups"`
}
err := mapstructure.WeakDecode(casted, &response)
return response.ServerGroups, err
}
type ServerGroupResult struct {
gophercloud.Result
}
// Extract is a method that attempts to interpret any Server Group resource
// response as a ServerGroup struct.
func (r ServerGroupResult) Extract() (*ServerGroup, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
ServerGroup *ServerGroup `json:"server_group" mapstructure:"server_group"`
}
err := mapstructure.WeakDecode(r.Body, &res)
return res.ServerGroup, err
}
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
// as a ServerGroup.
type CreateResult struct {
ServerGroupResult
}
// GetResult is the response from a Get operation. Call its Extract method to interpret it
// as a ServerGroup.
type GetResult struct {
ServerGroupResult
}
// 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 servergroups
import "github.com/rackspace/gophercloud"
const resourcePath = "os-server-groups"
func resourceURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(resourcePath)
}
func listURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func createURL(c *gophercloud.ServiceClient) string {
return resourceURL(c)
}
func getURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(resourcePath, id)
}
func deleteURL(c *gophercloud.ServiceClient, id string) string {
return getURL(c, id)
}

View File

@@ -1,5 +0,0 @@
/*
Package startstop provides functionality to start and stop servers that have
been provisioned by the OpenStack Compute service.
*/
package startstop

View File

@@ -1,27 +0,0 @@
package startstop
import (
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func mockStartServerResponse(t *testing.T, id string) {
th.Mux.HandleFunc("/servers/"+id+"/action", 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, `{"os-start": null}`)
w.WriteHeader(http.StatusAccepted)
})
}
func mockStopServerResponse(t *testing.T, id string) {
th.Mux.HandleFunc("/servers/"+id+"/action", 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, `{"os-stop": null}`)
w.WriteHeader(http.StatusAccepted)
})
}

View File

@@ -1,23 +0,0 @@
package startstop
import "github.com/rackspace/gophercloud"
func actionURL(client *gophercloud.ServiceClient, id string) string {
return client.ServiceURL("servers", id, "action")
}
// Start is the operation responsible for starting a Compute server.
func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var res gophercloud.ErrResult
reqBody := map[string]interface{}{"os-start": nil}
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
return res
}
// Stop is the operation responsible for stopping a Compute server.
func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var res gophercloud.ErrResult
reqBody := map[string]interface{}{"os-stop": nil}
_, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil)
return res
}

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,7 +0,0 @@
/*
This is package created is to hold fixtures (which imports testing),
so that importing volumeattach package does not inadvertently import testing into production code
More information here:
https://github.com/rackspace/gophercloud/issues/473
*/
package testing

View File

@@ -1,110 +0,0 @@
// +build fixtures
package testing
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"
}
}
`
// 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)
})
}