Merge pull request #4939 from kazegusuri/update_gophercloud

Update github.com/rackspace/gophercloud to HEAD
pull/6/head
Brian Grant 2015-03-05 10:58:23 -08:00
commit 527fc30e90
189 changed files with 10661 additions and 909 deletions

4
Godeps/Godeps.json generated
View File

@ -234,8 +234,8 @@
},
{
"ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v1.0.0-336-g2a6e319",
"Rev": "2a6e3190447abe5d000f951595ead1cf98df72d8"
"Comment": "v1.0.0-490-g32d0a89",
"Rev": "32d0a893a8ef70abe76dc5153e2925b39cbea7f7"
},
{
"ImportPath": "github.com/russross/blackfriday",

View File

@ -10,3 +10,4 @@ Contributors
| Ash Wilson | <ash.wilson@rackspace.com>
| Jamie Hannaford | <jamie.hannaford@rackspace.com>
| Don Schenck | don.schenck@rackspace.com>
| Joe Topjian | <joe@topjian.net>

View File

@ -0,0 +1,107 @@
// +build acceptance compute servers
package v2
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
)
func createFIPServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s\n", name)
pwd := tools.MakeNewPassword("")
server, err := servers.Create(client, servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
AdminPass: pwd,
}).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
th.AssertEquals(t, pwd, server.AdminPass)
return server, err
}
func createFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingip.FloatingIP, error) {
pool := os.Getenv("OS_POOL_NAME")
fip, err := floatingip.Create(client, &floatingip.CreateOpts{
Pool: pool,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Obtained Floating IP: %v", fip.IP)
return fip, err
}
func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
err := floatingip.Associate(client, serverId, fip.IP).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
defer func() {
err = floatingip.Disassociate(client, serverId, fip.IP).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Disassociated floating IP %v from instance %v", fip.IP, serverId)
}()
floatingIp, err := floatingip.Get(client, fip.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
}
func TestFloatingIP(t *testing.T) {
pool := os.Getenv("OS_POOL_NAME")
if pool == "" {
t.Fatalf("OS_POOL_NAME must be set")
}
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := createFIPServer(t, client, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer func() {
servers.Delete(client, server.ID)
t.Logf("Server deleted.")
}()
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
fip, err := createFloatingIP(t, client)
if err != nil {
t.Fatalf("Unable to create floating IP: %v", err)
}
defer func() {
err = floatingip.Delete(client, fip.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Floating IP deleted.")
}()
associateFloatingIP(t, client, server.ID, fip)
}

View File

@ -0,0 +1,125 @@
// +build acceptance compute servers
package v2
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack"
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
)
func newBlockClient(t *testing.T) (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := openstack.AuthenticatedClient(ao)
th.AssertNoErr(t, err)
return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
}
func createVAServer(t *testing.T, computeClient *gophercloud.ServiceClient, choices *ComputeChoices) (*servers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s\n", name)
pwd := tools.MakeNewPassword("")
server, err := servers.Create(computeClient, servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
AdminPass: pwd,
}).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
th.AssertEquals(t, pwd, server.AdminPass)
return server, err
}
func createVAVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) {
volume, err := volumes.Create(blockClient, &volumes.CreateOpts{
Size: 1,
Name: "gophercloud-test-volume",
}).Extract()
th.AssertNoErr(t, err)
defer func() {
err = volumes.WaitForStatus(blockClient, volume.ID, "available", 60)
th.AssertNoErr(t, err)
}()
return volume, err
}
func createVolumeAttachment(t *testing.T, computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverId string, volumeId string) {
va, err := volumeattach.Create(computeClient, serverId, &volumeattach.CreateOpts{
VolumeID: volumeId,
}).Extract()
th.AssertNoErr(t, err)
defer func() {
err = volumes.WaitForStatus(blockClient, volumeId, "in-use", 60)
th.AssertNoErr(t, err)
err = volumeattach.Delete(computeClient, serverId, va.ID).ExtractErr()
th.AssertNoErr(t, err)
err = volumes.WaitForStatus(blockClient, volumeId, "available", 60)
th.AssertNoErr(t, err)
}()
}
func TestAttachVolume(t *testing.T) {
choices, err := ComputeChoicesFromEnv()
if err != nil {
t.Fatal(err)
}
computeClient, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
blockClient, err := newBlockClient(t)
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
server, err := createVAServer(t, computeClient, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer func() {
servers.Delete(computeClient, server.ID)
t.Logf("Server deleted.")
}()
if err = waitForStatus(computeClient, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
volume, err := createVAVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer func() {
err = volumes.Delete(blockClient, volume.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Volume deleted.")
}()
createVolumeAttachment(t, computeClient, blockClient, server.ID, volume.ID)
}

View File

@ -0,0 +1,116 @@
// +build acceptance networking fwaas
package fwaas
import (
"testing"
"time"
"github.com/rackspace/gophercloud"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func firewallSetup(t *testing.T) string {
base.Setup(t)
return createPolicy(t, &policies.CreateOpts{})
}
func firewallTeardown(t *testing.T, policyID string) {
defer base.Teardown()
deletePolicy(t, policyID)
}
func TestFirewall(t *testing.T) {
policyID := firewallSetup(t)
defer firewallTeardown(t, policyID)
firewallID := createFirewall(t, &firewalls.CreateOpts{
Name: "gophercloud test",
Description: "acceptance test",
PolicyID: policyID,
})
waitForFirewallToBeActive(t, firewallID)
listFirewalls(t)
updateFirewall(t, firewallID, &firewalls.UpdateOpts{
Description: "acceptance test updated",
})
waitForFirewallToBeActive(t, firewallID)
deleteFirewall(t, firewallID)
waitForFirewallToBeDeleted(t, firewallID)
}
func createFirewall(t *testing.T, opts *firewalls.CreateOpts) string {
f, err := firewalls.Create(base.Client, *opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created firewall: %#v", opts)
return f.ID
}
func listFirewalls(t *testing.T) {
err := firewalls.List(base.Client, firewalls.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
firewallList, err := firewalls.ExtractFirewalls(page)
if err != nil {
t.Errorf("Failed to extract firewalls: %v", err)
return false, err
}
for _, r := range firewallList {
t.Logf("Listing firewalls: ID [%s]", r.ID)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateFirewall(t *testing.T, firewallID string, opts *firewalls.UpdateOpts) {
f, err := firewalls.Update(base.Client, firewallID, *opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated firewall ID [%s]", f.ID)
}
func getFirewall(t *testing.T, firewallID string) *firewalls.Firewall {
f, err := firewalls.Get(base.Client, firewallID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting firewall ID [%s]", f.ID)
return f
}
func deleteFirewall(t *testing.T, firewallID string) {
res := firewalls.Delete(base.Client, firewallID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted firewall %s", firewallID)
}
func waitForFirewallToBeActive(t *testing.T, firewallID string) {
for i := 0; i < 10; i++ {
fw := getFirewall(t, firewallID)
if fw.Status == "ACTIVE" {
break
}
time.Sleep(time.Second)
}
}
func waitForFirewallToBeDeleted(t *testing.T, firewallID string) {
for i := 0; i < 10; i++ {
err := firewalls.Get(base.Client, firewallID).Err
if err != nil {
httpStatus := err.(*gophercloud.UnexpectedResponseCodeError)
if httpStatus.Actual == 404 {
return
}
}
time.Sleep(time.Second)
}
}

View File

@ -0,0 +1,107 @@
// +build acceptance networking fwaas
package fwaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func firewallPolicySetup(t *testing.T) string {
base.Setup(t)
return createRule(t, &rules.CreateOpts{
Protocol: "tcp",
Action: "allow",
})
}
func firewallPolicyTeardown(t *testing.T, ruleID string) {
defer base.Teardown()
deleteRule(t, ruleID)
}
func TestFirewallPolicy(t *testing.T) {
ruleID := firewallPolicySetup(t)
defer firewallPolicyTeardown(t, ruleID)
policyID := createPolicy(t, &policies.CreateOpts{
Name: "gophercloud test",
Description: "acceptance test",
Rules: []string{
ruleID,
},
})
listPolicies(t)
updatePolicy(t, policyID, &policies.UpdateOpts{
Description: "acceptance test updated",
})
getPolicy(t, policyID)
removeRuleFromPolicy(t, policyID, ruleID)
addRuleToPolicy(t, policyID, ruleID)
deletePolicy(t, policyID)
}
func createPolicy(t *testing.T, opts *policies.CreateOpts) string {
p, err := policies.Create(base.Client, *opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created policy: %#v", opts)
return p.ID
}
func listPolicies(t *testing.T) {
err := policies.List(base.Client, policies.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
policyList, err := policies.ExtractPolicies(page)
if err != nil {
t.Errorf("Failed to extract policies: %v", err)
return false, err
}
for _, p := range policyList {
t.Logf("Listing policies: ID [%s]", p.ID)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updatePolicy(t *testing.T, policyID string, opts *policies.UpdateOpts) {
p, err := policies.Update(base.Client, policyID, *opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated policy ID [%s]", p.ID)
}
func removeRuleFromPolicy(t *testing.T, policyID string, ruleID string) {
err := policies.RemoveRule(base.Client, policyID, ruleID)
th.AssertNoErr(t, err)
t.Logf("Removed rule [%s] from policy ID [%s]", ruleID, policyID)
}
func addRuleToPolicy(t *testing.T, policyID string, ruleID string) {
err := policies.InsertRule(base.Client, policyID, ruleID, "", "")
th.AssertNoErr(t, err)
t.Logf("Inserted rule [%s] into policy ID [%s]", ruleID, policyID)
}
func getPolicy(t *testing.T, policyID string) {
p, err := policies.Get(base.Client, policyID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting policy ID [%s]", p.ID)
}
func deletePolicy(t *testing.T, policyID string) {
res := policies.Delete(base.Client, policyID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted policy %s", policyID)
}

View File

@ -0,0 +1,84 @@
// +build acceptance networking fwaas
package fwaas
import (
"testing"
base "github.com/rackspace/gophercloud/acceptance/openstack/networking/v2"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestFirewallRules(t *testing.T) {
base.Setup(t)
defer base.Teardown()
ruleID := createRule(t, &rules.CreateOpts{
Name: "gophercloud_test",
Description: "acceptance test",
Protocol: "tcp",
Action: "allow",
DestinationIPAddress: "192.168.0.0/24",
DestinationPort: "22",
})
listRules(t)
destinationIPAddress := "192.168.1.0/24"
destinationPort := ""
sourcePort := "1234"
updateRule(t, ruleID, &rules.UpdateOpts{
DestinationIPAddress: &destinationIPAddress,
DestinationPort: &destinationPort,
SourcePort: &sourcePort,
})
getRule(t, ruleID)
deleteRule(t, ruleID)
}
func createRule(t *testing.T, opts *rules.CreateOpts) string {
r, err := rules.Create(base.Client, *opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created rule: %#v", opts)
return r.ID
}
func listRules(t *testing.T) {
err := rules.List(base.Client, rules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
ruleList, err := rules.ExtractRules(page)
if err != nil {
t.Errorf("Failed to extract rules: %v", err)
return false, err
}
for _, r := range ruleList {
t.Logf("Listing rules: ID [%s]", r.ID)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateRule(t *testing.T, ruleID string, opts *rules.UpdateOpts) {
r, err := rules.Update(base.Client, ruleID, *opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated rule ID [%s]", r.ID)
}
func getRule(t *testing.T, ruleID string) {
r, err := rules.Get(base.Client, ruleID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting rule ID [%s]", r.ID)
}
func deleteRule(t *testing.T, ruleID string) {
res := rules.Delete(base.Client, ruleID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted rule %s", ruleID)
}

View File

@ -87,3 +87,51 @@ func TestContainers(t *testing.T) {
}
}
}
func TestListAllContainers(t *testing.T) {
// Create a new client to execute the HTTP requests. See common.go for newClient body.
client := newClient(t)
numContainers := 20
// Create a slice of random container names.
cNames := make([]string, numContainers)
for i := 0; i < numContainers; i++ {
cNames[i] = tools.RandomString("gophercloud-test-container-", 8)
}
// Create numContainers containers.
for i := 0; i < len(cNames); i++ {
res := containers.Create(client, cNames[i], nil)
th.AssertNoErr(t, res.Err)
}
// Delete the numContainers containers after function completion.
defer func() {
for i := 0; i < len(cNames); i++ {
res := containers.Delete(client, cNames[i])
th.AssertNoErr(t, res.Err)
}
}()
// List all the numContainer names that were just created. To just list those,
// the 'prefix' parameter is used.
allPages, err := containers.List(client, &containers.ListOpts{Full: true, Limit: 5, Prefix: "gophercloud-test-container-"}).AllPages()
th.AssertNoErr(t, err)
containerInfoList, err := containers.ExtractInfo(allPages)
th.AssertNoErr(t, err)
for _, n := range containerInfoList {
t.Logf("Container: Name [%s] Count [%d] Bytes [%d]",
n.Name, n.Count, n.Bytes)
}
th.AssertEquals(t, numContainers, len(containerInfoList))
// List the info for all the numContainer containers that were created.
allPages, err = containers.List(client, &containers.ListOpts{Full: false, Limit: 2, Prefix: "gophercloud-test-container-"}).AllPages()
th.AssertNoErr(t, err)
containerNamesList, err := containers.ExtractNames(allPages)
th.AssertNoErr(t, err)
for _, n := range containerNamesList {
t.Logf("Container: Name [%s]", n)
}
th.AssertEquals(t, numContainers, len(containerNamesList))
}

View File

@ -0,0 +1,20 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestBuildInfo(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
bi, err := buildinfo.Get(client).Extract()
th.AssertNoErr(t, err)
t.Logf("retrieved build info: %+v\n", bi)
}

View File

@ -0,0 +1,44 @@
// +build acceptance
package v1
import (
"fmt"
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
th "github.com/rackspace/gophercloud/testhelper"
)
var template = fmt.Sprintf(`
{
"heat_template_version": "2013-05-23",
"description": "Simple template to test heat commands",
"parameters": {},
"resources": {
"hello_world": {
"type":"OS::Nova::Server",
"properties": {
"flavor": "%s",
"image": "%s",
"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
}
}
}
}`, os.Getenv("OS_FLAVOR_ID"), os.Getenv("OS_IMAGE_ID"))
func newClient(t *testing.T) *gophercloud.ServiceClient {
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := openstack.AuthenticatedClient(ao)
th.AssertNoErr(t, err)
c, err := openstack.NewOrchestrationV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
th.AssertNoErr(t, err)
return c
}

View File

@ -0,0 +1,13 @@
{
"heat_template_version": "2013-05-23",
"resources": {
"compute_instance": {
"type": "OS::Nova::Server",
"properties": {
"flavor": "m1.small",
"image": "cirros-0.3.2-x86_64-disk",
"name": "Single Compute Instance"
}
}
}
}

View File

@ -0,0 +1,68 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStackEvents(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName := "postman_stack_2"
resourceName := "hello_world"
var eventID string
createOpts := stacks.CreateOpts{
Name: stackName,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
err = stackevents.List(client, stackName, stack.ID, nil).EachPage(func(page pagination.Page) (bool, error) {
events, err := stackevents.ExtractEvents(page)
th.AssertNoErr(t, err)
t.Logf("listed events: %+v\n", events)
eventID = events[0].ID
return false, nil
})
th.AssertNoErr(t, err)
err = stackevents.ListResourceEvents(client, stackName, stack.ID, resourceName, nil).EachPage(func(page pagination.Page) (bool, error) {
resourceEvents, err := stackevents.ExtractEvents(page)
th.AssertNoErr(t, err)
t.Logf("listed resource events: %+v\n", resourceEvents)
return false, nil
})
th.AssertNoErr(t, err)
event, err := stackevents.Get(client, stackName, stack.ID, resourceName, eventID).Extract()
th.AssertNoErr(t, err)
t.Logf("retrieved event: %+v\n", event)
}

View File

@ -0,0 +1,62 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStackResources(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName := "postman_stack_2"
createOpts := stacks.CreateOpts{
Name: stackName,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
resourceName := "hello_world"
resource, err := stackresources.Get(client, stackName, stack.ID, resourceName).Extract()
th.AssertNoErr(t, err)
t.Logf("Got stack resource: %+v\n", resource)
metadata, err := stackresources.Metadata(client, stackName, stack.ID, resourceName).Extract()
th.AssertNoErr(t, err)
t.Logf("Got stack resource metadata: %+v\n", metadata)
err = stackresources.List(client, stackName, stack.ID, stackresources.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
resources, err := stackresources.ExtractResources(page)
th.AssertNoErr(t, err)
t.Logf("resources: %+v\n", resources)
return false, nil
})
th.AssertNoErr(t, err)
}

View File

@ -0,0 +1,81 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStacks(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName1 := "gophercloud-test-stack-2"
createOpts := stacks.CreateOpts{
Name: stackName1,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName1, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName1)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
updateOpts := stacks.UpdateOpts{
Template: template,
Timeout: 20,
}
err = stacks.Update(client, stackName1, stack.ID, updateOpts).ExtractErr()
th.AssertNoErr(t, err)
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "UPDATE_COMPLETE" {
return true, nil
}
return false, nil
})
t.Logf("Updated stack")
err = stacks.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
stackList, err := stacks.ExtractStacks(page)
th.AssertNoErr(t, err)
t.Logf("Got stack list: %+v\n", stackList)
return true, nil
})
th.AssertNoErr(t, err)
getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Got stack: %+v\n", getStack)
abandonedStack, err := stacks.Abandon(client, stackName1, stack.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Abandonded stack %+v\n", abandonedStack)
th.AssertNoErr(t, err)
}

View File

@ -0,0 +1,77 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStackTemplates(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName := "postman_stack_2"
createOpts := stacks.CreateOpts{
Name: stackName,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
tmpl, err := stacktemplates.Get(client, stackName, stack.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("retrieved template: %+v\n", tmpl)
validateOpts := stacktemplates.ValidateOpts{
Template: map[string]interface{}{
"heat_template_version": "2013-05-23",
"description": "Simple template to test heat commands",
"parameters": map[string]interface{}{
"flavor": map[string]interface{}{
"default": "m1.tiny",
"type": "string",
},
},
"resources": map[string]interface{}{
"hello_world": map[string]interface{}{
"type": "OS::Nova::Server",
"properties": map[string]interface{}{
"key_name": "heat_key",
"flavor": map[string]interface{}{
"get_param": "flavor",
},
"image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n",
},
},
},
},
}
validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("validated template: %+v\n", validatedTemplate)
}

View File

@ -0,0 +1,130 @@
// +build acceptance compute servers
package v2
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack"
osVolumes "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
osVolumeAttach "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
osServers "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/rackspace"
"github.com/rackspace/gophercloud/rackspace/blockstorage/v1/volumes"
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
"github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach"
th "github.com/rackspace/gophercloud/testhelper"
)
func newBlockClient(t *testing.T) (*gophercloud.ServiceClient, error) {
ao, err := rackspace.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := rackspace.AuthenticatedClient(ao)
th.AssertNoErr(t, err)
return openstack.NewBlockStorageV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("RS_REGION_NAME"),
})
}
func createVAServer(t *testing.T, computeClient *gophercloud.ServiceClient, choices *serverOpts) (*osServers.Server, error) {
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s\n", name)
pwd := tools.MakeNewPassword("")
server, err := servers.Create(computeClient, osServers.CreateOpts{
Name: name,
FlavorRef: choices.flavorID,
ImageRef: choices.imageID,
AdminPass: pwd,
}).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
th.AssertEquals(t, pwd, server.AdminPass)
return server, err
}
func createVAVolume(t *testing.T, blockClient *gophercloud.ServiceClient) (*volumes.Volume, error) {
volume, err := volumes.Create(blockClient, &osVolumes.CreateOpts{
Size: 80,
Name: "gophercloud-test-volume",
}).Extract()
th.AssertNoErr(t, err)
defer func() {
err = osVolumes.WaitForStatus(blockClient, volume.ID, "available", 60)
th.AssertNoErr(t, err)
}()
return volume, err
}
func createVolumeAttachment(t *testing.T, computeClient *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, serverID string, volumeID string) {
va, err := volumeattach.Create(computeClient, serverID, &osVolumeAttach.CreateOpts{
VolumeID: volumeID,
}).Extract()
th.AssertNoErr(t, err)
defer func() {
err = osVolumes.WaitForStatus(blockClient, volumeID, "in-use", 60)
th.AssertNoErr(t, err)
err = volumeattach.Delete(computeClient, serverID, va.ID).ExtractErr()
th.AssertNoErr(t, err)
err = osVolumes.WaitForStatus(blockClient, volumeID, "available", 60)
th.AssertNoErr(t, err)
}()
t.Logf("Attached volume to server: %+v", va)
}
func TestAttachVolume(t *testing.T) {
choices, err := optionsFromEnv()
if err != nil {
t.Fatal(err)
}
computeClient, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
blockClient, err := newBlockClient(t)
if err != nil {
t.Fatalf("Unable to create a blockstorage client: %v", err)
}
server, err := createVAServer(t, computeClient, choices)
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
defer func() {
servers.Delete(computeClient, server.ID)
t.Logf("Server deleted.")
}()
if err = osServers.WaitForStatus(computeClient, server.ID, "ACTIVE", 300); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
volume, err := createVAVolume(t, blockClient)
if err != nil {
t.Fatalf("Unable to create volume: %v", err)
}
defer func() {
err = volumes.Delete(blockClient, volume.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Volume deleted.")
}()
createVolumeAttachment(t, computeClient, blockClient, server.ID, volume.ID)
}

View File

@ -36,11 +36,15 @@ func TestCDNObjects(t *testing.T) {
raxCDNClient, err := createClient(t, true)
th.AssertNoErr(t, err)
enableResult := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
th.AssertNoErr(t, enableResult.Err)
t.Logf("Headers from Enable CDN Container request: %+v\n", enableResult.Header)
enableHeader, err := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900}).Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Enable CDN Container request: %+v\n", enableHeader)
deleteResult := raxCDNObjects.Delete(raxCDNClient, "gophercloud-test", "test-object", nil)
th.AssertNoErr(t, deleteResult.Err)
t.Logf("Headers from Delete CDN Object request: %+v\n", deleteResult.Err)
objCDNURL, err := raxCDNObjects.CDNURL(raxCDNClient, "gophercloud-test", "test-object")
th.AssertNoErr(t, err)
t.Logf("%s CDN URL: %s\n", "test_object", objCDNURL)
deleteHeader, err := raxCDNObjects.Delete(raxCDNClient, "gophercloud-test", "test-object", nil).Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Delete CDN Object request: %+v\n", deleteHeader)
}

View File

@ -0,0 +1,20 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/buildinfo"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestBuildInfo(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
bi, err := buildinfo.Get(client).Extract()
th.AssertNoErr(t, err)
t.Logf("retrieved build info: %+v\n", bi)
}

View File

@ -0,0 +1,45 @@
// +build acceptance
package v1
import (
"fmt"
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
var template = fmt.Sprintf(`
{
"heat_template_version": "2013-05-23",
"description": "Simple template to test heat commands",
"parameters": {},
"resources": {
"hello_world": {
"type":"OS::Nova::Server",
"properties": {
"flavor": "%s",
"image": "%s",
"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n"
}
}
}
}
`, os.Getenv("RS_FLAVOR_ID"), os.Getenv("RS_IMAGE_ID"))
func newClient(t *testing.T) *gophercloud.ServiceClient {
ao, err := rackspace.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
client, err := rackspace.AuthenticatedClient(ao)
th.AssertNoErr(t, err)
c, err := rackspace.NewOrchestrationV1(client, gophercloud.EndpointOpts{
Region: os.Getenv("RS_REGION_NAME"),
})
th.AssertNoErr(t, err)
return c
}

View File

@ -0,0 +1,70 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
osStackEvents "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents"
osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stackevents"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStackEvents(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName := "postman_stack_2"
resourceName := "hello_world"
var eventID string
createOpts := osStacks.CreateOpts{
Name: stackName,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
err = stackevents.List(client, stackName, stack.ID, nil).EachPage(func(page pagination.Page) (bool, error) {
events, err := osStackEvents.ExtractEvents(page)
th.AssertNoErr(t, err)
t.Logf("listed events: %+v\n", events)
eventID = events[0].ID
return false, nil
})
th.AssertNoErr(t, err)
err = stackevents.ListResourceEvents(client, stackName, stack.ID, resourceName, nil).EachPage(func(page pagination.Page) (bool, error) {
resourceEvents, err := osStackEvents.ExtractResourceEvents(page)
th.AssertNoErr(t, err)
t.Logf("listed resource events: %+v\n", resourceEvents)
return false, nil
})
th.AssertNoErr(t, err)
event, err := stackevents.Get(client, stackName, stack.ID, resourceName, eventID).Extract()
th.AssertNoErr(t, err)
t.Logf("retrieved event: %+v\n", event)
}

View File

@ -0,0 +1,64 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
osStackResources "github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources"
osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stackresources"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStackResources(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName := "postman_stack_2"
createOpts := osStacks.CreateOpts{
Name: stackName,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
resourceName := "hello_world"
resource, err := stackresources.Get(client, stackName, stack.ID, resourceName).Extract()
th.AssertNoErr(t, err)
t.Logf("Got stack resource: %+v\n", resource)
metadata, err := stackresources.Metadata(client, stackName, stack.ID, resourceName).Extract()
th.AssertNoErr(t, err)
t.Logf("Got stack resource metadata: %+v\n", metadata)
err = stackresources.List(client, stackName, stack.ID, nil).EachPage(func(page pagination.Page) (bool, error) {
resources, err := osStackResources.ExtractResources(page)
th.AssertNoErr(t, err)
t.Logf("resources: %+v\n", resources)
return false, nil
})
th.AssertNoErr(t, err)
}

View File

@ -0,0 +1,82 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStacks(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName1 := "gophercloud-test-stack-2"
createOpts := osStacks.CreateOpts{
Name: stackName1,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName1, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName1)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
updateOpts := osStacks.UpdateOpts{
Template: template,
Timeout: 20,
}
err = stacks.Update(client, stackName1, stack.ID, updateOpts).ExtractErr()
th.AssertNoErr(t, err)
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "UPDATE_COMPLETE" {
return true, nil
}
return false, nil
})
t.Logf("Updated stack")
err = stacks.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
stackList, err := osStacks.ExtractStacks(page)
th.AssertNoErr(t, err)
t.Logf("Got stack list: %+v\n", stackList)
return true, nil
})
th.AssertNoErr(t, err)
getStack, err := stacks.Get(client, stackName1, stack.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Got stack: %+v\n", getStack)
abandonedStack, err := stacks.Abandon(client, stackName1, stack.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Abandonded stack %+v\n", abandonedStack)
th.AssertNoErr(t, err)
}

View File

@ -0,0 +1,79 @@
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
osStacks "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks"
osStacktemplates "github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacks"
"github.com/rackspace/gophercloud/rackspace/orchestration/v1/stacktemplates"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestStackTemplates(t *testing.T) {
// Create a provider client for making the HTTP requests.
// See common.go in this directory for more information.
client := newClient(t)
stackName := "postman_stack_2"
createOpts := osStacks.CreateOpts{
Name: stackName,
Template: template,
Timeout: 5,
}
stack, err := stacks.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created stack: %+v\n", stack)
defer func() {
err := stacks.Delete(client, stackName, stack.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted stack (%s)", stackName)
}()
err = gophercloud.WaitFor(60, func() (bool, error) {
getStack, err := stacks.Get(client, stackName, stack.ID).Extract()
if err != nil {
return false, err
}
if getStack.Status == "CREATE_COMPLETE" {
return true, nil
}
return false, nil
})
tmpl, err := stacktemplates.Get(client, stackName, stack.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("retrieved template: %+v\n", tmpl)
validateOpts := osStacktemplates.ValidateOpts{
Template: map[string]interface{}{
"heat_template_version": "2013-05-23",
"description": "Simple template to test heat commands",
"parameters": map[string]interface{}{
"flavor": map[string]interface{}{
"default": "m1.tiny",
"type": "string",
},
},
"resources": map[string]interface{}{
"hello_world": map[string]interface{}{
"type": "OS::Nova::Server",
"properties": map[string]interface{}{
"key_name": "heat_key",
"flavor": map[string]interface{}{
"get_param": "flavor",
},
"image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
"user_data": "#!/bin/bash -xv\necho \"hello world\" &gt; /root/hello-world.txt\n",
},
},
},
},
}
validatedTemplate, err := stacktemplates.Validate(client, validateOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("validated template: %+v\n", validatedTemplate)
}

View File

@ -42,7 +42,5 @@ type AuthOptions struct {
// re-authenticate automatically if/when your token expires. If you set it to
// false, it will not cache these settings, but re-authentication will not be
// possible. This setting defaults to false.
//
// This setting is speculative and is currently not respected!
AllowReauth bool
}

View File

@ -3,8 +3,6 @@ package apiversions
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// List lists all the Cinder API versions available to end-users.
@ -18,11 +16,9 @@ func List(c *gophercloud.ServiceClient) pagination.Pager {
// type from the result, call the Extract method on the GetResult.
func Get(client *gophercloud.ServiceClient, v string) GetResult {
var res GetResult
_, err := perigee.Request("GET", getURL(client, v), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("GET", getURL(client, v), gophercloud.RequestOpts{
OkCodes: []int{200},
Results: &res.Body,
JSONResponse: &res.Body,
})
res.Err = err
return res
}

View File

@ -5,8 +5,6 @@ import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// CreateOptsBuilder allows extensions to add additional parameters to the
@ -69,11 +67,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return res
}
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{
OkCodes: []int{200, 201},
ReqBody: &reqBody,
Results: &res.Body,
JSONBody: &reqBody,
JSONResponse: &res.Body,
})
return res
}
@ -81,8 +78,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
// Delete will delete the existing Snapshot with the provided ID.
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{202, 204},
})
return res
@ -92,10 +88,9 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
// object from the response, call the Extract method on the GetResult.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{200},
JSONResponse: &res.Body,
})
return res
}
@ -178,11 +173,10 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet
return res
}
_, res.Err = perigee.Request("PUT", updateMetadataURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("PUT", updateMetadataURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{200},
ReqBody: &reqBody,
Results: &res.Body,
JSONBody: &reqBody,
JSONResponse: &res.Body,
})
return res
}

View File

@ -45,7 +45,15 @@ func MockGetResponse(t *testing.T) {
{
"volume": {
"display_name": "vol-001",
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
"attachments": [
{
"device": "/dev/vde",
"server_id": "a740d24b-dc5b-4d59-ac75-53971c2920ba",
"id": "d6da11e5-2ed3-413e-88d8-b772ba62193d",
"volume_id": "d6da11e5-2ed3-413e-88d8-b772ba62193d"
}
]
}
}
`)

View File

@ -5,8 +5,6 @@ import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// CreateOptsBuilder allows extensions to add additional parameters to the
@ -85,11 +83,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return res
}
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{
OkCodes: []int{200, 201},
JSONBody: &reqBody,
JSONResponse: &res.Body,
})
return res
}
@ -97,8 +94,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
// Delete will delete the existing Volume with the provided ID.
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{202, 204},
})
return res
@ -108,9 +104,8 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
// from the response, call the Extract method on the GetResult.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -157,6 +152,7 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa
createPage := func(r pagination.PageResult) pagination.Page {
return ListResult{pagination.SinglePageBase(r)}
}
return pagination.NewPager(client, url, createPage)
}
@ -207,11 +203,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
return res
}
_, res.Err = perigee.Request("PUT", updateURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("PUT", updateURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{200},
ReqBody: &reqBody,
Results: &res.Body,
JSONBody: &reqBody,
JSONResponse: &res.Body,
})
return res
}

View File

@ -45,6 +45,32 @@ func TestList(t *testing.T) {
}
}
func TestListAll(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
MockListResponse(t)
allPages, err := List(client.ServiceClient(), &ListOpts{}).AllPages()
th.AssertNoErr(t, err)
actual, err := ExtractVolumes(allPages)
th.AssertNoErr(t, err)
expected := []Volume{
Volume{
ID: "289da7f8-6440-407c-9fb4-7db01ec49164",
Name: "vol-001",
},
Volume{
ID: "96c3bda7-c82a-4f50-be73-ca7621794835",
Name: "vol-002",
},
}
th.CheckDeepEquals(t, expected, actual)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
@ -56,6 +82,7 @@ func TestGet(t *testing.T) {
th.AssertEquals(t, v.Name, "vol-001")
th.AssertEquals(t, v.ID, "d32019d3-bc6e-4319-9c1d-6722fc136a22")
th.AssertEquals(t, v.Attachments[0]["device"], "/dev/vde")
}
func TestCreate(t *testing.T) {

View File

@ -16,7 +16,7 @@ type Volume struct {
Name string `mapstructure:"display_name"`
// Instances onto which the volume is attached.
Attachments []string `mapstructure:"attachments"`
Attachments []map[string]interface{} `mapstructure:"attachments"`
// This parameter is no longer used.
AvailabilityZone string `mapstructure:"availability_zone"`

View File

@ -1,7 +1,6 @@
package volumetypes
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -45,11 +44,11 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return res
}
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
_, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{200, 201},
ReqBody: &reqBody,
Results: &res.Body,
JSONBody: &reqBody,
JSONResponse: &res.Body,
})
return res
}
@ -57,7 +56,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
// Delete will delete the volume type with the provided ID.
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
_, res.Err = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{202},
})
@ -68,10 +67,10 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
// type from the result, call the Extract method on the GetResult.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, err := perigee.Request("GET", getURL(client, id), perigee.Options{
_, err := client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{200},
Results: &res.Body,
JSONResponse: &res.Body,
})
res.Err = err
return res

View File

@ -1,18 +1,13 @@
package base
import (
"github.com/rackspace/gophercloud"
"github.com/racker/perigee"
)
import "github.com/rackspace/gophercloud"
// Get retrieves the home document, allowing the user to discover the
// entire API.
func Get(c *gophercloud.ServiceClient) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", getURL(c), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -21,10 +16,9 @@ func Get(c *gophercloud.ServiceClient) GetResult {
// Ping retrieves a ping to the server.
func Ping(c *gophercloud.ServiceClient) PingResult {
var res PingResult
_, res.Err = perigee.Request("GET", pingURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("GET", pingURL(c), gophercloud.RequestOpts{
OkCodes: []int{204},
OmitAccept: true,
MoreHeaders: map[string]string{"Accept": ""},
})
return res
}

View File

@ -1,7 +1,6 @@
package flavors
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -18,9 +17,8 @@ func List(c *gophercloud.ServiceClient) pagination.Pager {
// Get retrieves a specific flavor based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res

View File

@ -3,7 +3,6 @@ package serviceassets
import (
"strings"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
)
@ -44,8 +43,7 @@ func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder
}
var res DeleteResult
_, res.Err = perigee.Request("DELETE", url, perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{
OkCodes: []int{202},
})
return res

View File

@ -4,7 +4,6 @@ import (
"fmt"
"strings"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -178,12 +177,11 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
}
// Send request to API
resp, err := perigee.Request("POST", createURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
resp, err := c.Request("POST", createURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
OkCodes: []int{202},
})
res.Header = resp.HttpResponse.Header
res.Header = resp.Header
res.Err = err
return res
}
@ -201,9 +199,8 @@ func Get(c *gophercloud.ServiceClient, idOrURL string) GetResult {
}
var res GetResult
_, res.Err = perigee.Request("GET", url, perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", url, gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -359,13 +356,12 @@ func Update(c *gophercloud.ServiceClient, idOrURL string, opts UpdateOpts) Updat
reqBody[i] = patch.ToCDNServiceUpdateMap()
}
resp, err := perigee.Request("PATCH", url, perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
resp, err := c.Request("PATCH", url, gophercloud.RequestOpts{
JSONBody: &reqBody,
OkCodes: []int{202},
})
var result UpdateResult
result.Header = resp.HttpResponse.Header
result.Header = resp.Header
result.Err = err
return result
}
@ -383,8 +379,7 @@ func Delete(c *gophercloud.ServiceClient, idOrURL string) DeleteResult {
}
var res DeleteResult
_, res.Err = perigee.Request("DELETE", url, perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{
OkCodes: []int{202},
})
return res

View File

@ -68,7 +68,7 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp
&utils.Version{ID: v30, Priority: 30, Suffix: "/v3/"},
}
chosen, endpoint, err := utils.ChooseVersion(client.IdentityBase, client.IdentityEndpoint, versions)
chosen, endpoint, err := utils.ChooseVersion(client, versions)
if err != nil {
return err
}
@ -107,6 +107,11 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
return err
}
if options.AllowReauth {
client.ReauthFunc = func() error {
return AuthenticateV2(client, options)
}
}
client.TokenID = token.ID
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
return V2EndpointURL(catalog, opts)
@ -133,6 +138,11 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
}
client.TokenID = token.ID
if options.AllowReauth {
client.ReauthFunc = func() error {
return AuthenticateV3(client, options)
}
}
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
return V3EndpointURL(v3Client, opts)
}
@ -214,3 +224,13 @@ func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("orchestration")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}

View File

@ -1,7 +1,6 @@
package extensions
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -9,9 +8,8 @@ import (
// Get retrieves information for a specific extension using its alias.
func Get(c *gophercloud.ServiceClient, alias string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", ExtensionURL(c, alias), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", ExtensionURL(c, alias), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res

View File

@ -6,8 +6,6 @@ import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/racker/perigee"
)
// SourceType represents the type of medium being used to create the volume.
@ -101,10 +99,9 @@ func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) s
return res
}
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: reqBody,
Results: &res.Body,
_, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{
JSONBody: reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200, 202},
})
return res

View File

@ -3,8 +3,6 @@ package defsecrules
import (
"errors"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -75,10 +73,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return result
}
_, result.Err = perigee.Request("POST", rootURL(client), perigee.Options{
Results: &result.Body,
ReqBody: &reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: &reqBody,
OkCodes: []int{200},
})
@ -89,9 +86,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", resourceURL(client, id), perigee.Options{
Results: &result.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
OkCodes: []int{200},
})
@ -102,8 +98,7 @@ func Get(client *gophercloud.ServiceClient, id string) GetResult {
func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})

View File

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

View File

@ -0,0 +1,174 @@
// +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)
})
}
// 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

@ -0,0 +1,105 @@
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
}
// 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
}
// 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.Request("POST", createURL(client), gophercloud.RequestOpts{
JSONBody: reqBody,
JSONResponse: &res.Body,
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.Request("GET", getURL(client, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
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.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{202},
})
return res
}
// association / disassociation
// Associate pairs an allocated floating IP with an instance
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.Request("POST", associateURL(client, serverId), gophercloud.RequestOpts{
JSONBody: reqBody,
OkCodes: []int{202},
})
return res
}
// Disassociate decouples an allocated floating IP from an instance
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.Request("POST", disassociateURL(client, serverId), gophercloud.RequestOpts{
JSONBody: reqBody,
OkCodes: []int{202},
})
return res
}

View File

@ -0,0 +1,80 @@
package floatingip
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleListSuccessfully(t)
count := 0
err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractFloatingIPs(page)
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, ExpectedFloatingIPsSlice, actual)
return true, nil
})
th.AssertNoErr(t, err)
th.CheckEquals(t, 1, count)
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleCreateSuccessfully(t)
actual, err := Create(client.ServiceClient(), CreateOpts{
Pool: "nova",
}).Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, &CreatedFloatingIP, actual)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleGetSuccessfully(t)
actual, err := Get(client.ServiceClient(), "2").Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, &SecondFloatingIP, actual)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDeleteSuccessfully(t)
err := Delete(client.ServiceClient(), "1").ExtractErr()
th.AssertNoErr(t, err)
}
func TestAssociate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleAssociateSuccessfully(t)
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
fip := "10.10.10.2"
err := Associate(client.ServiceClient(), serverId, fip).ExtractErr()
th.AssertNoErr(t, err)
}
func TestDisassociate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDisassociateSuccessfully(t)
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
fip := "10.10.10.2"
err := Disassociate(client.ServiceClient(), serverId, fip).ExtractErr()
th.AssertNoErr(t, err)
}

View File

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

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

@ -0,0 +1,60 @@
package floatingip
import (
"testing"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func TestListURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
th.CheckEquals(t, c.Endpoint+"os-floating-ips", listURL(c))
}
func TestCreateURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
th.CheckEquals(t, c.Endpoint+"os-floating-ips", createURL(c))
}
func TestGetURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
id := "1"
th.CheckEquals(t, c.Endpoint+"os-floating-ips/"+id, getURL(c, id))
}
func TestDeleteURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
id := "1"
th.CheckEquals(t, c.Endpoint+"os-floating-ips/"+id, deleteURL(c, id))
}
func TestAssociateURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/action", associateURL(c, serverId))
}
func TestDisassociateURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/action", disassociateURL(c, serverId))
}

View File

@ -3,7 +3,6 @@ package keypairs
import (
"errors"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
@ -82,10 +81,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return res
}
_, res.Err = perigee.Request("POST", createURL(client), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: reqBody,
Results: &res.Body,
_, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{
JSONBody: reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -94,9 +92,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
// Get returns public data about a previously uploaded KeyPair.
func Get(client *gophercloud.ServiceClient, name string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(client, name), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = client.Request("GET", getURL(client, name), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -105,8 +102,7 @@ func Get(client *gophercloud.ServiceClient, name string) GetResult {
// 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 = perigee.Request("DELETE", deleteURL(client, name), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", deleteURL(client, name), gophercloud.RequestOpts{
OkCodes: []int{202},
})
return res

View File

@ -38,7 +38,7 @@ func mockListGroupsResponse(t *testing.T) {
}
func mockListGroupsByServerResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("%s/servers/%s%s", rootPath, serverID, rootPath)
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)

View File

@ -3,8 +3,6 @@ package secgroups
import (
"errors"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -80,10 +78,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return result
}
_, result.Err = perigee.Request("POST", rootURL(client), perigee.Options{
Results: &result.Body,
ReqBody: &reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: &reqBody,
OkCodes: []int{200},
})
@ -126,10 +123,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
return result
}
_, result.Err = perigee.Request("PUT", resourceURL(client, id), perigee.Options{
Results: &result.Body,
ReqBody: &reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("PUT", resourceURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: &reqBody,
OkCodes: []int{200},
})
@ -140,9 +136,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", resourceURL(client, id), perigee.Options{
Results: &result.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
OkCodes: []int{200},
})
@ -153,8 +148,7 @@ func Get(client *gophercloud.ServiceClient, id string) GetResult {
func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{202},
})
@ -222,7 +216,7 @@ func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
rule["cidr"] = opts.CIDR
}
if opts.FromGroupID != "" {
rule["from_group_id"] = opts.FromGroupID
rule["group_id"] = opts.FromGroupID
}
return map[string]interface{}{"security_group_rule": rule}, nil
@ -240,10 +234,9 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) C
return result
}
_, result.Err = perigee.Request("POST", rootRuleURL(client), perigee.Options{
Results: &result.Body,
ReqBody: &reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", rootRuleURL(client), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: &reqBody,
OkCodes: []int{200},
})
@ -254,8 +247,7 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) C
func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = perigee.Request("DELETE", resourceRuleURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("DELETE", resourceRuleURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{202},
})
@ -273,10 +265,9 @@ func actionMap(prefix, groupName string) map[string]map[string]string {
func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = perigee.Request("POST", serverActionURL(client, serverID), perigee.Options{
Results: &result.Body,
ReqBody: actionMap("add", groupName),
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", serverActionURL(client, serverID), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: actionMap("add", groupName),
OkCodes: []int{202},
})
@ -287,10 +278,9 @@ func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName str
func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult {
var result gophercloud.ErrResult
_, result.Err = perigee.Request("POST", serverActionURL(client, serverID), perigee.Options{
Results: &result.Body,
ReqBody: actionMap("remove", groupName),
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", serverActionURL(client, serverID), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: actionMap("remove", groupName),
OkCodes: []int{202},
})

View File

@ -16,7 +16,7 @@ func rootURL(c *gophercloud.ServiceClient) string {
}
func listByServerURL(c *gophercloud.ServiceClient, serverID string) string {
return c.ServiceURL(secgrouppath, "servers", serverID, secgrouppath)
return c.ServiceURL("servers", serverID, secgrouppath)
}
func rootRuleURL(c *gophercloud.ServiceClient) string {

View File

@ -1,9 +1,6 @@
package startstop
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
)
import "github.com/rackspace/gophercloud"
func actionURL(client *gophercloud.ServiceClient, id string) string {
return client.ServiceURL("servers", id, "action")
@ -15,9 +12,8 @@ func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
reqBody := map[string]interface{}{"os-start": nil}
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: reqBody,
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: reqBody,
OkCodes: []int{202},
})
@ -30,9 +26,8 @@ func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult {
reqBody := map[string]interface{}{"os-stop": nil}
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: reqBody,
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: reqBody,
OkCodes: []int{202},
})

View File

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

View File

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

View File

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

View File

@ -0,0 +1,65 @@
package volumeattach
import (
"testing"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleListSuccessfully(t)
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
count := 0
err := List(client.ServiceClient(), serverId).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractVolumeAttachments(page)
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, ExpectedVolumeAttachmentSlice, actual)
return true, nil
})
th.AssertNoErr(t, err)
th.CheckEquals(t, 1, count)
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleCreateSuccessfully(t)
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
actual, err := Create(client.ServiceClient(), serverId, CreateOpts{
Device: "/dev/vdc",
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
}).Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleGetSuccessfully(t)
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
actual, err := Get(client.ServiceClient(), serverId, aId).Extract()
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, &SecondVolumeAttachment, actual)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDeleteSuccessfully(t)
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
err := Delete(client.ServiceClient(), serverId, aId).ExtractErr()
th.AssertNoErr(t, err)
}

View File

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

View File

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

View File

@ -0,0 +1,46 @@
package volumeattach
import (
"testing"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func TestListURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments", listURL(c, serverId))
}
func TestCreateURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments", createURL(c, serverId))
}
func TestGetURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments/"+aId, getURL(c, serverId, aId))
}
func TestDeleteURL(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
c := client.ServiceClient()
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
th.CheckEquals(t, c.Endpoint+"servers/"+serverId+"/os-volume_attachments/"+aId, deleteURL(c, serverId, aId))
}

View File

@ -1,7 +1,6 @@
package flavors
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -64,9 +63,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat
// Use ExtractFlavor to convert its result into a Flavor.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var gr GetResult
gr.Err = perigee.Get(getURL(client, id), perigee.Options{
Results: &gr.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, gr.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
JSONResponse: &gr.Body,
})
return gr
}

View File

@ -3,8 +3,6 @@ package images
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// ListOptsBuilder allows extensions to add additional parameters to the
@ -22,7 +20,7 @@ type ListOpts struct {
// UUID of the Image at which to set a marker.
Marker string `q:"marker"`
// The name of the Image.
Name string `q:"name:"`
Name string `q:"name"`
// The name of the Server (in URL format).
Server string `q:"server"`
// The current status of the Image.
@ -62,9 +60,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat
// Use ExtractImage() to interpret the result as an openstack Image.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
Results: &result.Body,
_, result.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
OkCodes: []int{200},
})
return result

View File

@ -285,6 +285,11 @@ var (
Created: "2014-09-25T13:10:02Z",
TenantID: "fcad67a6189847c4aecfa3c81a05783b",
Metadata: map[string]interface{}{},
SecurityGroups: []map[string]interface{}{
map[string]interface{}{
"name": "default",
},
},
}
// ServerDerp is a Server struct that should correspond to the second server in ServerListBody.
@ -336,6 +341,11 @@ var (
Created: "2014-09-25T13:04:41Z",
TenantID: "fcad67a6189847c4aecfa3c81a05783b",
Metadata: map[string]interface{}{},
SecurityGroups: []map[string]interface{}{
map[string]interface{}{
"name": "default",
},
},
}
)

View File

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -217,10 +216,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return res
}
_, res.Err = perigee.Request("POST", listURL(client), perigee.Options{
Results: &res.Body,
ReqBody: reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", listURL(client), gophercloud.RequestOpts{
JSONResponse: &res.Body,
JSONBody: reqBody,
OkCodes: []int{202},
})
return res
@ -229,8 +227,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
// Delete requests that a server previously provisioned be removed from your account.
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res
@ -239,9 +236,8 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
// Get requests details on a single server, by ID.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", getURL(client, id), perigee.Options{
Results: &result.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
OkCodes: []int{200, 203},
})
return result
@ -284,10 +280,9 @@ func (opts UpdateOpts) ToServerUpdateMap() map[string]interface{} {
// Update requests that various attributes of the indicated server be changed.
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
var result UpdateResult
_, result.Err = perigee.Request("PUT", updateURL(client, id), perigee.Options{
Results: &result.Body,
ReqBody: opts.ToServerUpdateMap(),
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("PUT", updateURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: opts.ToServerUpdateMap(),
})
return result
}
@ -304,9 +299,8 @@ func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword stri
var res ActionResult
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
ReqBody: req,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: req,
OkCodes: []int{202},
})
@ -373,13 +367,12 @@ func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) Acti
return res
}
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
ReqBody: struct {
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: struct {
C map[string]string `json:"reboot"`
}{
map[string]string{"type": string(how)},
},
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{202},
})
@ -475,10 +468,9 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuild
return result
}
_, result.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
ReqBody: &reqBody,
Results: &result.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &result.Body,
OkCodes: []int{202},
})
@ -522,9 +514,8 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder
return res
}
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
ReqBody: reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: reqBody,
OkCodes: []int{202},
})
@ -536,9 +527,8 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder
func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult {
var res ActionResult
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
ReqBody: map[string]interface{}{"confirmResize": nil},
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: map[string]interface{}{"confirmResize": nil},
OkCodes: []int{204},
})
@ -550,9 +540,8 @@ func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult {
func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult {
var res ActionResult
_, res.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
ReqBody: map[string]interface{}{"revertResize": nil},
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONBody: map[string]interface{}{"revertResize": nil},
OkCodes: []int{202},
})
@ -597,10 +586,9 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder
return result
}
_, result.Err = perigee.Request("POST", actionURL(client, id), perigee.Options{
Results: &result.Body,
ReqBody: &reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: &reqBody,
OkCodes: []int{200},
})
@ -637,10 +625,9 @@ func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetad
res.Err = err
return res
}
_, res.Err = perigee.Request("PUT", metadataURL(client, id), perigee.Options{
ReqBody: metadata,
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("PUT", metadataURL(client, id), gophercloud.RequestOpts{
JSONBody: metadata,
JSONResponse: &res.Body,
})
return res
}
@ -648,9 +635,8 @@ func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetad
// Metadata requests all the metadata for the given server ID.
func Metadata(client *gophercloud.ServiceClient, id string) GetMetadataResult {
var res GetMetadataResult
_, res.Err = perigee.Request("GET", metadataURL(client, id), perigee.Options{
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("GET", metadataURL(client, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
})
return res
}
@ -671,10 +657,9 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet
res.Err = err
return res
}
_, res.Err = perigee.Request("POST", metadataURL(client, id), perigee.Options{
ReqBody: metadata,
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", metadataURL(client, id), gophercloud.RequestOpts{
JSONBody: metadata,
JSONResponse: &res.Body,
})
return res
}
@ -710,10 +695,9 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu
return res
}
_, res.Err = perigee.Request("PUT", metadatumURL(client, id, key), perigee.Options{
ReqBody: metadatum,
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("PUT", metadatumURL(client, id, key), gophercloud.RequestOpts{
JSONBody: metadatum,
JSONResponse: &res.Body,
})
return res
}
@ -721,9 +705,8 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu
// Metadatum requests the key-value pair with the given key for the given server ID.
func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumResult {
var res GetMetadatumResult
_, res.Err = perigee.Request("GET", metadatumURL(client, id, key), perigee.Options{
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("GET", metadatumURL(client, id, key), gophercloud.RequestOpts{
JSONResponse: &res.Body,
})
return res
}
@ -731,9 +714,8 @@ func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumRe
// DeleteMetadatum will delete the key-value pair with the given key for the given server ID.
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult {
var res DeleteMetadatumResult
_, res.Err = perigee.Request("DELETE", metadatumURL(client, id, key), perigee.Options{
Results: &res.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", metadatumURL(client, id, key), gophercloud.RequestOpts{
JSONResponse: &res.Body,
})
return res
}

View File

@ -39,6 +39,19 @@ func TestListServers(t *testing.T) {
}
}
func TestListAllServers(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleServerListSuccessfully(t)
allPages, err := List(client.ServiceClient(), ListOpts{}).AllPages()
th.AssertNoErr(t, err)
actual, err := ExtractServers(allPages)
th.AssertNoErr(t, err)
th.CheckDeepEquals(t, ServerHerp, actual[0])
th.CheckDeepEquals(t, ServerDerp, actual[1])
}
func TestCreateServer(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

View File

@ -139,6 +139,9 @@ type Server struct {
// AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
// Note that this is the ONLY time this field will be valid.
AdminPass string `json:"adminPass" mapstructure:"adminPass"`
// SecurityGroups includes the security groups that this instance has applied to it
SecurityGroups []map[string]interface{} `json:"security_groups" mapstructure:"security_groups"`
}
// ServerPage abstracts the raw results of making a List() request against the API.

View File

@ -1,7 +1,6 @@
package roles
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -21,8 +20,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager {
func AddUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
var result UserRoleResult
_, result.Err = perigee.Request("PUT", userRoleURL(client, tenantID, userID, roleID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("PUT", userRoleURL(client, tenantID, userID, roleID), gophercloud.RequestOpts{
OkCodes: []int{200, 201},
})
@ -35,8 +33,7 @@ func AddUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID str
func DeleteUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult {
var result UserRoleResult
_, result.Err = perigee.Request("DELETE", userRoleURL(client, tenantID, userID, roleID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("DELETE", userRoleURL(client, tenantID, userID, roleID), gophercloud.RequestOpts{
OkCodes: []int{204},
})

View File

@ -1,9 +1,6 @@
package tokens
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
)
import "github.com/rackspace/gophercloud"
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
type AuthOptionsBuilder interface {
@ -78,9 +75,9 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) CreateRe
}
var result CreateResult
_, result.Err = perigee.Request("POST", CreateURL(client), perigee.Options{
ReqBody: &request,
Results: &result.Body,
_, result.Err = client.Request("POST", CreateURL(client), gophercloud.RequestOpts{
JSONBody: &request,
JSONResponse: &result.Body,
OkCodes: []int{200, 203},
})
return result

View File

@ -3,7 +3,6 @@ package users
import (
"errors"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -91,10 +90,9 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
return res
}
_, res.Err = perigee.Request("POST", rootURL(client), perigee.Options{
Results: &res.Body,
ReqBody: reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{
JSONResponse: &res.Body,
JSONBody: reqBody,
OkCodes: []int{200, 201},
})
@ -105,9 +103,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", ResourceURL(client, id), perigee.Options{
Results: &result.Body,
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("GET", ResourceURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
OkCodes: []int{200},
})
@ -149,10 +146,9 @@ func (opts UpdateOpts) ToUserUpdateMap() map[string]interface{} {
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
var result UpdateResult
_, result.Err = perigee.Request("PUT", ResourceURL(client, id), perigee.Options{
Results: &result.Body,
ReqBody: opts.ToUserUpdateMap(),
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("PUT", ResourceURL(client, id), gophercloud.RequestOpts{
JSONResponse: &result.Body,
JSONBody: opts.ToUserUpdateMap(),
OkCodes: []int{200},
})
@ -163,8 +159,7 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
var result DeleteResult
_, result.Err = perigee.Request("DELETE", ResourceURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, result.Err = client.Request("DELETE", ResourceURL(client, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})

View File

@ -1,7 +1,6 @@
package endpoints
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -57,10 +56,9 @@ func Create(client *gophercloud.ServiceClient, opts EndpointOpts) CreateResult {
reqBody.Endpoint.Region = gophercloud.MaybeString(opts.Region)
var result CreateResult
_, result.Err = perigee.Request("POST", listURL(client), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &result.Body,
_, result.Err = client.Request("POST", listURL(client), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &result.Body,
OkCodes: []int{201},
})
return result
@ -113,10 +111,9 @@ func Update(client *gophercloud.ServiceClient, endpointID string, opts EndpointO
reqBody.Endpoint.ServiceID = gophercloud.MaybeString(opts.ServiceID)
var result UpdateResult
_, result.Err = perigee.Request("PATCH", endpointURL(client, endpointID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &result.Body,
_, result.Err = client.Request("PATCH", endpointURL(client, endpointID), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &result.Body,
OkCodes: []int{200},
})
return result
@ -125,8 +122,7 @@ func Update(client *gophercloud.ServiceClient, endpointID string, opts EndpointO
// Delete removes an endpoint from the service catalog.
func Delete(client *gophercloud.ServiceClient, endpointID string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", endpointURL(client, endpointID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", endpointURL(client, endpointID), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -1,7 +1,6 @@
package services
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -19,10 +18,9 @@ func Create(client *gophercloud.ServiceClient, serviceType string) CreateResult
req := request{Type: serviceType}
var result CreateResult
_, result.Err = perigee.Request("POST", listURL(client), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: &req,
Results: &result.Body,
_, result.Err = client.Request("POST", listURL(client), gophercloud.RequestOpts{
JSONBody: &req,
JSONResponse: &result.Body,
OkCodes: []int{201},
})
return result
@ -53,9 +51,8 @@ func List(client *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
// Get returns additional information about a service, given its ID.
func Get(client *gophercloud.ServiceClient, serviceID string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", serviceURL(client, serviceID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
Results: &result.Body,
_, result.Err = client.Request("GET", serviceURL(client, serviceID), gophercloud.RequestOpts{
JSONResponse: &result.Body,
OkCodes: []int{200},
})
return result
@ -70,10 +67,9 @@ func Update(client *gophercloud.ServiceClient, serviceID string, serviceType str
req := request{Type: serviceType}
var result UpdateResult
_, result.Err = perigee.Request("PATCH", serviceURL(client, serviceID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
ReqBody: &req,
Results: &result.Body,
_, result.Err = client.Request("PATCH", serviceURL(client, serviceID), gophercloud.RequestOpts{
JSONBody: &req,
JSONResponse: &result.Body,
OkCodes: []int{200},
})
return result
@ -83,8 +79,7 @@ func Update(client *gophercloud.ServiceClient, serviceID string, serviceType str
// It either deletes all associated endpoints, or fails until all endpoints are deleted.
func Delete(client *gophercloud.ServiceClient, serviceID string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", serviceURL(client, serviceID), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
_, res.Err = client.Request("DELETE", serviceURL(client, serviceID), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -1,7 +1,8 @@
package tokens
import (
"github.com/racker/perigee"
"net/http"
"github.com/rackspace/gophercloud"
)
@ -233,38 +234,38 @@ func Create(c *gophercloud.ServiceClient, options gophercloud.AuthOptions, scope
}
var result CreateResult
var response *perigee.Response
response, result.Err = perigee.Request("POST", tokenURL(c), perigee.Options{
ReqBody: &req,
Results: &result.Body,
var response *http.Response
response, result.Err = c.Request("POST", tokenURL(c), gophercloud.RequestOpts{
JSONBody: &req,
JSONResponse: &result.Body,
OkCodes: []int{201},
})
if result.Err != nil {
return result
}
result.Header = response.HttpResponse.Header
result.Header = response.Header
return result
}
// Get validates and retrieves information about another token.
func Get(c *gophercloud.ServiceClient, token string) GetResult {
var result GetResult
var response *perigee.Response
response, result.Err = perigee.Request("GET", tokenURL(c), perigee.Options{
var response *http.Response
response, result.Err = c.Request("GET", tokenURL(c), gophercloud.RequestOpts{
MoreHeaders: subjectTokenHeaders(c, token),
Results: &result.Body,
JSONResponse: &result.Body,
OkCodes: []int{200, 203},
})
if result.Err != nil {
return result
}
result.Header = response.HttpResponse.Header
result.Header = response.Header
return result
}
// Validate determines if a specified token is valid or not.
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
response, err := perigee.Request("HEAD", tokenURL(c), perigee.Options{
response, err := c.Request("HEAD", tokenURL(c), gophercloud.RequestOpts{
MoreHeaders: subjectTokenHeaders(c, token),
OkCodes: []int{204, 404},
})
@ -278,7 +279,7 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
// Revoke immediately makes specified token invalid.
func Revoke(c *gophercloud.ServiceClient, token string) RevokeResult {
var res RevokeResult
_, res.Err = perigee.Request("DELETE", tokenURL(c), perigee.Options{
_, res.Err = c.Request("DELETE", tokenURL(c), gophercloud.RequestOpts{
MoreHeaders: subjectTokenHeaders(c, token),
OkCodes: []int{204},
})

View File

@ -0,0 +1,3 @@
// Package fwaas provides information and interaction with the Firewall
// as a Service extension for the OpenStack Networking service.
package fwaas

View File

@ -0,0 +1,11 @@
package firewalls
import "fmt"
func err(str string) error {
return fmt.Errorf("%s", str)
}
var (
errPolicyRequired = err("A policy ID is required")
)

View File

@ -0,0 +1,227 @@
package firewalls
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// AdminState gives users a solid type to work with for create and update
// operations. It is recommended that users use the `Up` and `Down` enums.
type AdminState *bool
// Shared gives users a solid type to work with for create and update
// operations. It is recommended that users use the `Yes` and `No` enums.
type Shared *bool
// Convenience vars for AdminStateUp and Shared values.
var (
iTrue = true
iFalse = false
Up AdminState = &iTrue
Down AdminState = &iFalse
Yes Shared = &iTrue
No Shared = &iFalse
)
// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
ToFirewallListQuery() (string, error)
}
// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the firewall attributes you want to see returned. SortKey allows you to sort
// by a particular firewall attribute. SortDir sets the direction, and is either
// `asc' or `desc'. Marker and Limit are used for pagination.
type ListOpts struct {
TenantID string `q:"tenant_id"`
Name string `q:"name"`
Description string `q:"description"`
AdminStateUp bool `q:"admin_state_up"`
Shared bool `q:"shared"`
PolicyID string `q:"firewall_policy_id"`
ID string `q:"id"`
Limit int `q:"limit"`
Marker string `q:"marker"`
SortKey string `q:"sort_key"`
SortDir string `q:"sort_dir"`
}
// ToFirewallListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToFirewallListQuery() (string, error) {
q, err := gophercloud.BuildQueryString(opts)
if err != nil {
return "", err
}
return q.String(), nil
}
// List returns a Pager which allows you to iterate over a collection of
// firewalls. It accepts a ListOpts struct, which allows you to filter
// and sort the returned collection for greater efficiency.
//
// Default policy settings return only those firewalls that are owned by the
// tenant who submits the request, unless an admin user submits the request.
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
url := rootURL(c)
if opts != nil {
query, err := opts.ToFirewallListQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
return FirewallPage{pagination.LinkedPageBase{PageResult: r}}
})
}
// CreateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Create operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type CreateOptsBuilder interface {
ToFirewallCreateMap() (map[string]interface{}, error)
}
// CreateOpts contains all the values needed to create a new firewall.
type CreateOpts struct {
// Only required if the caller has an admin role and wants to create a firewall
// for another tenant.
TenantID string
Name string
Description string
AdminStateUp *bool
Shared *bool
PolicyID string
}
// ToFirewallCreateMap casts a CreateOpts struct to a map.
func (opts CreateOpts) ToFirewallCreateMap() (map[string]interface{}, error) {
if opts.PolicyID == "" {
return nil, errPolicyRequired
}
f := make(map[string]interface{})
if opts.TenantID != "" {
f["tenant_id"] = opts.TenantID
}
if opts.Name != "" {
f["name"] = opts.Name
}
if opts.Description != "" {
f["description"] = opts.Description
}
if opts.Shared != nil {
f["shared"] = *opts.Shared
}
if opts.AdminStateUp != nil {
f["admin_state_up"] = *opts.AdminStateUp
}
if opts.PolicyID != "" {
f["firewall_policy_id"] = opts.PolicyID
}
return map[string]interface{}{"firewall": f}, nil
}
// Create accepts a CreateOpts struct and uses the values to create a new firewall
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToFirewallCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
}
// Get retrieves a particular firewall based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
}
// UpdateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Update operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type UpdateOptsBuilder interface {
ToFirewallUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts contains the values used when updating a firewall.
type UpdateOpts struct {
// Name of the firewall.
Name string
Description string
AdminStateUp *bool
Shared *bool
PolicyID string
}
// ToFirewallUpdateMap casts a CreateOpts struct to a map.
func (opts UpdateOpts) ToFirewallUpdateMap() (map[string]interface{}, error) {
f := make(map[string]interface{})
if opts.Name != "" {
f["name"] = opts.Name
}
if opts.Description != "" {
f["description"] = opts.Description
}
if opts.Shared != nil {
f["shared"] = *opts.Shared
}
if opts.AdminStateUp != nil {
f["admin_state_up"] = *opts.AdminStateUp
}
if opts.PolicyID != "" {
f["firewall_policy_id"] = opts.PolicyID
}
return map[string]interface{}{"firewall": f}, nil
}
// Update allows firewalls to be updated.
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
var res UpdateResult
reqBody, err := opts.ToFirewallUpdateMap()
if err != nil {
res.Err = err
return res
}
// Send request to API
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
}
// Delete will permanently delete a particular firewall based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res
}

View File

@ -0,0 +1,246 @@
package firewalls
import (
"fmt"
"net/http"
"testing"
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestURLs(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewalls", rootURL(fake.ServiceClient()))
}
func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewalls", 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, `
{
"firewalls":[
{
"status": "ACTIVE",
"name": "fw1",
"admin_state_up": false,
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
"firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
"id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
"description": "OpenStack firewall 1"
},
{
"status": "PENDING_UPDATE",
"name": "fw2",
"admin_state_up": true,
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
"firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e299",
"id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
"description": "OpenStack firewall 2"
}
]
}
`)
})
count := 0
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractFirewalls(page)
if err != nil {
t.Errorf("Failed to extract members: %v", err)
return false, err
}
expected := []Firewall{
Firewall{
Status: "ACTIVE",
Name: "fw1",
AdminStateUp: false,
TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
Description: "OpenStack firewall 1",
},
Firewall{
Status: "PENDING_UPDATE",
Name: "fw2",
AdminStateUp: true,
TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
PolicyID: "34be8c83-4d42-4dca-a74e-b77fffb8e299",
ID: "fb5b5315-64f6-4ea3-8e58-981cc37c6f99",
Description: "OpenStack firewall 2",
},
}
th.CheckDeepEquals(t, expected, actual)
return true, nil
})
if count != 1 {
t.Errorf("Expected 1 page, got %d", count)
}
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"firewall":{
"name": "fw",
"description": "OpenStack firewall",
"admin_state_up": true,
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `
{
"firewall":{
"status": "PENDING_CREATE",
"name": "fw",
"description": "OpenStack firewall",
"admin_state_up": true,
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
}
}
`)
})
options := CreateOpts{
TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
Name: "fw",
Description: "OpenStack firewall",
AdminStateUp: Up,
PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
}
_, err := Create(fake.ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewalls/fb5b5315-64f6-4ea3-8e58-981cc37c6f61", 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, `
{
"firewall": {
"status": "ACTIVE",
"name": "fw",
"admin_state_up": true,
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
"firewall_policy_id": "34be8c83-4d42-4dca-a74e-b77fffb8e28a",
"id": "fb5b5315-64f6-4ea3-8e58-981cc37c6f61",
"description": "OpenStack firewall"
}
}
`)
})
fw, err := Get(fake.ServiceClient(), "fb5b5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "ACTIVE", fw.Status)
th.AssertEquals(t, "fw", fw.Name)
th.AssertEquals(t, "OpenStack firewall", fw.Description)
th.AssertEquals(t, true, fw.AdminStateUp)
th.AssertEquals(t, "34be8c83-4d42-4dca-a74e-b77fffb8e28a", fw.PolicyID)
th.AssertEquals(t, "fb5b5315-64f6-4ea3-8e58-981cc37c6f61", fw.ID)
th.AssertEquals(t, "b4eedccc6fb74fa8a7ad6b08382b852b", fw.TenantID)
}
func TestUpdate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "PUT")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"firewall":{
"name": "fw",
"description": "updated fw",
"admin_state_up":false,
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"firewall": {
"status": "ACTIVE",
"name": "fw",
"admin_state_up": false,
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
"id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
"description": "OpenStack firewall",
}
}
`)
})
options := UpdateOpts{
Name: "fw",
Description: "updated fw",
AdminStateUp: Down,
PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
}
_, err := Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", options).Extract()
th.AssertNoErr(t, err)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewalls/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.WriteHeader(http.StatusNoContent)
})
res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
th.AssertNoErr(t, res.Err)
}

View File

@ -0,0 +1,101 @@
package firewalls
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
type Firewall struct {
ID string `json:"id" mapstructure:"id"`
Name string `json:"name" mapstructure:"name"`
Description string `json:"description" mapstructure:"description"`
AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
Status string `json:"status" mapstructure:"status"`
PolicyID string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
}
type commonResult struct {
gophercloud.Result
}
// Extract is a function that accepts a result and extracts a firewall.
func (r commonResult) Extract() (*Firewall, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
Firewall *Firewall `json:"firewall"`
}
err := mapstructure.Decode(r.Body, &res)
return res.Firewall, err
}
// FirewallPage is the page returned by a pager when traversing over a
// collection of firewalls.
type FirewallPage struct {
pagination.LinkedPageBase
}
// NextPageURL is invoked when a paginated collection of firewalls has reached
// the end of a page and the pager seeks to traverse over a new one. In order
// to do this, it needs to construct the next page's URL.
func (p FirewallPage) NextPageURL() (string, error) {
type resp struct {
Links []gophercloud.Link `mapstructure:"firewalls_links"`
}
var r resp
err := mapstructure.Decode(p.Body, &r)
if err != nil {
return "", err
}
return gophercloud.ExtractNextURL(r.Links)
}
// IsEmpty checks whether a FirewallPage struct is empty.
func (p FirewallPage) IsEmpty() (bool, error) {
is, err := ExtractFirewalls(p)
if err != nil {
return true, nil
}
return len(is) == 0, nil
}
// ExtractFirewalls accepts a Page struct, specifically a RouterPage struct,
// and extracts the elements into a slice of Router structs. In other words,
// a generic collection is mapped into a relevant slice.
func ExtractFirewalls(page pagination.Page) ([]Firewall, error) {
var resp struct {
Firewalls []Firewall `mapstructure:"firewalls" json:"firewalls"`
}
err := mapstructure.Decode(page.(FirewallPage).Body, &resp)
return resp.Firewalls, err
}
// GetResult represents the result of a get operation.
type GetResult struct {
commonResult
}
// UpdateResult represents the result of an update operation.
type UpdateResult struct {
commonResult
}
// DeleteResult represents the result of a delete operation.
type DeleteResult struct {
gophercloud.ErrResult
}
// CreateResult represents the result of a create operation.
type CreateResult struct {
commonResult
}

View File

@ -0,0 +1,16 @@
package firewalls
import "github.com/rackspace/gophercloud"
const (
rootPath = "fw"
resourcePath = "firewalls"
)
func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rootPath, resourcePath)
}
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rootPath, resourcePath, id)
}

View File

@ -0,0 +1,258 @@
package policies
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// Binary gives users a solid type to work with for create and update
// operations. It is recommended that users use the `Yes` and `No` enums
type Binary *bool
// Convenience vars for Audited and Shared values.
var (
iTrue = true
iFalse = false
Yes Binary = &iTrue
No Binary = &iFalse
)
// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
ToPolicyListQuery() (string, error)
}
// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the firewall policy attributes you want to see returned. SortKey allows you
// to sort by a particular firewall policy attribute. SortDir sets the direction,
// and is either `asc' or `desc'. Marker and Limit are used for pagination.
type ListOpts struct {
TenantID string `q:"tenant_id"`
Name string `q:"name"`
Description string `q:"description"`
Shared bool `q:"shared"`
Audited bool `q:"audited"`
ID string `q:"id"`
Limit int `q:"limit"`
Marker string `q:"marker"`
SortKey string `q:"sort_key"`
SortDir string `q:"sort_dir"`
}
// ToPolicyListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToPolicyListQuery() (string, error) {
q, err := gophercloud.BuildQueryString(opts)
if err != nil {
return "", err
}
return q.String(), nil
}
// List returns a Pager which allows you to iterate over a collection of
// firewall policies. It accepts a ListOpts struct, which allows you to filter
// and sort the returned collection for greater efficiency.
//
// Default policy settings return only those firewall policies that are owned by the
// tenant who submits the request, unless an admin user submits the request.
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
url := rootURL(c)
if opts != nil {
query, err := opts.ToPolicyListQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
return PolicyPage{pagination.LinkedPageBase{PageResult: r}}
})
}
// CreateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Create operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type CreateOptsBuilder interface {
ToPolicyCreateMap() (map[string]interface{}, error)
}
// CreateOpts contains all the values needed to create a new firewall policy.
type CreateOpts struct {
// Only required if the caller has an admin role and wants to create a firewall policy
// for another tenant.
TenantID string
Name string
Description string
Shared *bool
Audited *bool
Rules []string
}
// ToPolicyCreateMap casts a CreateOpts struct to a map.
func (opts CreateOpts) ToPolicyCreateMap() (map[string]interface{}, error) {
p := make(map[string]interface{})
if opts.TenantID != "" {
p["tenant_id"] = opts.TenantID
}
if opts.Name != "" {
p["name"] = opts.Name
}
if opts.Description != "" {
p["description"] = opts.Description
}
if opts.Shared != nil {
p["shared"] = *opts.Shared
}
if opts.Audited != nil {
p["audited"] = *opts.Audited
}
if opts.Rules != nil {
p["firewall_rules"] = opts.Rules
}
return map[string]interface{}{"firewall_policy": p}, nil
}
// Create accepts a CreateOpts struct and uses the values to create a new firewall policy
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToPolicyCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
}
// Get retrieves a particular firewall policy based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
}
// UpdateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Update operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type UpdateOptsBuilder interface {
ToPolicyUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts contains the values used when updating a firewall policy.
type UpdateOpts struct {
// Name of the firewall policy.
Name string
Description string
Shared *bool
Audited *bool
Rules []string
}
// ToPolicyUpdateMap casts a CreateOpts struct to a map.
func (opts UpdateOpts) ToPolicyUpdateMap() (map[string]interface{}, error) {
p := make(map[string]interface{})
if opts.Name != "" {
p["name"] = opts.Name
}
if opts.Description != "" {
p["description"] = opts.Description
}
if opts.Shared != nil {
p["shared"] = *opts.Shared
}
if opts.Audited != nil {
p["audited"] = *opts.Audited
}
if opts.Rules != nil {
p["firewall_rules"] = opts.Rules
}
return map[string]interface{}{"firewall_policy": p}, nil
}
// Update allows firewall policies to be updated.
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
var res UpdateResult
reqBody, err := opts.ToPolicyUpdateMap()
if err != nil {
res.Err = err
return res
}
// Send request to API
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
}
// Delete will permanently delete a particular firewall policy based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res
}
func InsertRule(c *gophercloud.ServiceClient, policyID, ruleID, beforeID, afterID string) error {
type request struct {
RuleId string `json:"firewall_rule_id"`
Before string `json:"insert_before,omitempty"`
After string `json:"insert_after,omitempty"`
}
reqBody := request{
RuleId: ruleID,
Before: beforeID,
After: afterID,
}
// Send request to API
var res commonResult
_, res.Err = c.Request("PUT", insertURL(c, policyID), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res.Err
}
func RemoveRule(c *gophercloud.ServiceClient, policyID, ruleID string) error {
type request struct {
RuleId string `json:"firewall_rule_id"`
}
reqBody := request{
RuleId: ruleID,
}
// Send request to API
var res commonResult
_, res.Err = c.Request("PUT", removeURL(c, policyID), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res.Err
}

View File

@ -0,0 +1,279 @@
package policies
import (
"fmt"
"net/http"
"testing"
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestURLs(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_policies", rootURL(fake.ServiceClient()))
}
func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_policies", 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, `
{
"firewall_policies": [
{
"name": "policy1",
"firewall_rules": [
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
"c9e77ca0-1bc8-497d-904d-948107873dc6"
],
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
"audited": true,
"shared": false,
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
"description": "Firewall policy 1"
},
{
"name": "policy2",
"firewall_rules": [
"03d2a6ad-633f-431a-8463-4370d06a22c8"
],
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
"audited": false,
"shared": true,
"id": "c854fab5-bdaf-4a86-9359-78de93e5df01",
"description": "Firewall policy 2"
}
]
}
`)
})
count := 0
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractPolicies(page)
if err != nil {
t.Errorf("Failed to extract members: %v", err)
return false, err
}
expected := []Policy{
Policy{
Name: "policy1",
Rules: []string{
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
"c9e77ca0-1bc8-497d-904d-948107873dc6",
},
TenantID: "9145d91459d248b1b02fdaca97c6a75d",
Audited: true,
Shared: false,
ID: "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
Description: "Firewall policy 1",
},
Policy{
Name: "policy2",
Rules: []string{
"03d2a6ad-633f-431a-8463-4370d06a22c8",
},
TenantID: "9145d91459d248b1b02fdaca97c6a75d",
Audited: false,
Shared: true,
ID: "c854fab5-bdaf-4a86-9359-78de93e5df01",
Description: "Firewall policy 2",
},
}
th.CheckDeepEquals(t, expected, actual)
return true, nil
})
if count != 1 {
t.Errorf("Expected 1 page, got %d", count)
}
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_policies", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"firewall_policy":{
"name": "policy",
"firewall_rules": [
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
"11a58c87-76be-ae7c-a74e-b77fffb88a32"
],
"description": "Firewall policy",
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
"audited": true,
"shared": false
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `
{
"firewall_policy":{
"name": "policy",
"firewall_rules": [
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
"11a58c87-76be-ae7c-a74e-b77fffb88a32"
],
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
"audited": false,
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
"description": "Firewall policy"
}
}
`)
})
options := CreateOpts{
TenantID: "9145d91459d248b1b02fdaca97c6a75d",
Name: "policy",
Description: "Firewall policy",
Shared: No,
Audited: Yes,
Rules: []string{
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
"11a58c87-76be-ae7c-a74e-b77fffb88a32",
},
}
_, err := Create(fake.ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_policies/bcab5315-64f6-4ea3-8e58-981cc37c6f61", 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, `
{
"firewall_policy":{
"name": "www",
"firewall_rules": [
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
"c9e77ca0-1bc8-497d-904d-948107873dc6",
"03d2a6ad-633f-431a-8463-4370d06a22c8"
],
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
"audited": false,
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
"description": "Firewall policy web"
}
}
`)
})
policy, err := Get(fake.ServiceClient(), "bcab5315-64f6-4ea3-8e58-981cc37c6f61").Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "www", policy.Name)
th.AssertEquals(t, "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", policy.ID)
th.AssertEquals(t, "Firewall policy web", policy.Description)
th.AssertEquals(t, 3, len(policy.Rules))
th.AssertEquals(t, "75452b36-268e-4e75-aaf4-f0e7ed50bc97", policy.Rules[0])
th.AssertEquals(t, "c9e77ca0-1bc8-497d-904d-948107873dc6", policy.Rules[1])
th.AssertEquals(t, "03d2a6ad-633f-431a-8463-4370d06a22c8", policy.Rules[2])
th.AssertEquals(t, "9145d91459d248b1b02fdaca97c6a75d", policy.TenantID)
}
func TestUpdate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_policies/f2b08c1e-aa81-4668-8ae1-1401bcb0576c", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "PUT")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"firewall_policy":{
"name": "policy",
"firewall_rules": [
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
"11a58c87-76be-ae7c-a74e-b77fffb88a32"
],
"description": "Firewall policy"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"firewall_policy":{
"name": "policy",
"firewall_rules": [
"75452b36-268e-4e75-aaf4-f0e7ed50bc97",
"c9e77ca0-1bc8-497d-904d-948107873dc6",
"03d2a6ad-633f-431a-8463-4370d06a22c8"
],
"tenant_id": "9145d91459d248b1b02fdaca97c6a75d",
"audited": false,
"id": "f2b08c1e-aa81-4668-8ae1-1401bcb0576c",
"description": "Firewall policy"
}
}
`)
})
options := UpdateOpts{
Name: "policy",
Description: "Firewall policy",
Rules: []string{
"98a58c87-76be-ae7c-a74e-b77fffb88d95",
"11a58c87-76be-ae7c-a74e-b77fffb88a32",
},
}
_, err := Update(fake.ServiceClient(), "f2b08c1e-aa81-4668-8ae1-1401bcb0576c", options).Extract()
th.AssertNoErr(t, err)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_policies/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.WriteHeader(http.StatusNoContent)
})
res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
th.AssertNoErr(t, res.Err)
}

View File

@ -0,0 +1,101 @@
package policies
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
type Policy struct {
ID string `json:"id" mapstructure:"id"`
Name string `json:"name" mapstructure:"name"`
Description string `json:"description" mapstructure:"description"`
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
Audited bool `json:"audited" mapstructure:"audited"`
Shared bool `json:"shared" mapstructure:"shared"`
Rules []string `json:"firewall_rules,omitempty" mapstructure:"firewall_rules"`
}
type commonResult struct {
gophercloud.Result
}
// Extract is a function that accepts a result and extracts a firewall policy.
func (r commonResult) Extract() (*Policy, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
Policy *Policy `json:"firewall_policy" mapstructure:"firewall_policy"`
}
err := mapstructure.Decode(r.Body, &res)
return res.Policy, err
}
// PolicyPage is the page returned by a pager when traversing over a
// collection of firewall policies.
type PolicyPage struct {
pagination.LinkedPageBase
}
// NextPageURL is invoked when a paginated collection of firewall policies has
// reached the end of a page and the pager seeks to traverse over a new one.
// In order to do this, it needs to construct the next page's URL.
func (p PolicyPage) NextPageURL() (string, error) {
type resp struct {
Links []gophercloud.Link `mapstructure:"firewall_policies_links"`
}
var r resp
err := mapstructure.Decode(p.Body, &r)
if err != nil {
return "", err
}
return gophercloud.ExtractNextURL(r.Links)
}
// IsEmpty checks whether a PolicyPage struct is empty.
func (p PolicyPage) IsEmpty() (bool, error) {
is, err := ExtractPolicies(p)
if err != nil {
return true, nil
}
return len(is) == 0, nil
}
// ExtractPolicies accepts a Page struct, specifically a RouterPage struct,
// and extracts the elements into a slice of Router structs. In other words,
// a generic collection is mapped into a relevant slice.
func ExtractPolicies(page pagination.Page) ([]Policy, error) {
var resp struct {
Policies []Policy `mapstructure:"firewall_policies" json:"firewall_policies"`
}
err := mapstructure.Decode(page.(PolicyPage).Body, &resp)
return resp.Policies, err
}
// GetResult represents the result of a get operation.
type GetResult struct {
commonResult
}
// UpdateResult represents the result of an update operation.
type UpdateResult struct {
commonResult
}
// DeleteResult represents the result of a delete operation.
type DeleteResult struct {
gophercloud.ErrResult
}
// CreateResult represents the result of a create operation.
type CreateResult struct {
commonResult
}

View File

@ -0,0 +1,26 @@
package policies
import "github.com/rackspace/gophercloud"
const (
rootPath = "fw"
resourcePath = "firewall_policies"
insertPath = "insert_rule"
removePath = "remove_rule"
)
func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rootPath, resourcePath)
}
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rootPath, resourcePath, id)
}
func insertURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rootPath, resourcePath, id, insertPath)
}
func removeURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rootPath, resourcePath, id, removePath)
}

View File

@ -0,0 +1,12 @@
package rules
import "fmt"
func err(str string) error {
return fmt.Errorf("%s", str)
}
var (
errProtocolRequired = err("A protocol is required (tcp, udp, icmp or any)")
errActionRequired = err("An action is required (allow or deny)")
)

View File

@ -0,0 +1,296 @@
package rules
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// Binary gives users a solid type to work with for create and update
// operations. It is recommended that users use the `Yes` and `No` enums
type Binary *bool
// Convenience vars for Enabled and Shared values.
var (
iTrue = true
iFalse = false
Yes Binary = &iTrue
No Binary = &iFalse
)
// ListOptsBuilder allows extensions to add additional parameters to the
// List request.
type ListOptsBuilder interface {
ToRuleListQuery() (string, error)
}
// ListOpts allows the filtering and sorting of paginated collections through
// the API. Filtering is achieved by passing in struct field values that map to
// the Firewall rule attributes you want to see returned. SortKey allows you to
// sort by a particular firewall rule attribute. SortDir sets the direction, and is
// either `asc' or `desc'. Marker and Limit are used for pagination.
type ListOpts struct {
TenantID string `q:"tenant_id"`
Name string `q:"name"`
Description string `q:"description"`
Protocol string `q:"protocol"`
Action string `q:"action"`
IPVersion int `q:"ip_version"`
SourceIPAddress string `q:"source_ip_address"`
DestinationIPAddress string `q:"destination_ip_address"`
SourcePort string `q:"source_port"`
DestinationPort string `q:"destination_port"`
Enabled bool `q:"enabled"`
ID string `q:"id"`
Limit int `q:"limit"`
Marker string `q:"marker"`
SortKey string `q:"sort_key"`
SortDir string `q:"sort_dir"`
}
// ToRuleListQuery formats a ListOpts into a query string.
func (opts ListOpts) ToRuleListQuery() (string, error) {
q, err := gophercloud.BuildQueryString(opts)
if err != nil {
return "", err
}
return q.String(), nil
}
// List returns a Pager which allows you to iterate over a collection of
// firewall rules. It accepts a ListOpts struct, which allows you to filter
// and sort the returned collection for greater efficiency.
//
// Default policy settings return only those firewall rules that are owned by the
// tenant who submits the request, unless an admin user submits the request.
func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
url := rootURL(c)
if opts != nil {
query, err := opts.ToRuleListQuery()
if err != nil {
return pagination.Pager{Err: err}
}
url += query
}
return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page {
return RulePage{pagination.LinkedPageBase{PageResult: r}}
})
}
// CreateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Create operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type CreateOptsBuilder interface {
ToRuleCreateMap() (map[string]interface{}, error)
}
// CreateOpts contains all the values needed to create a new firewall rule.
type CreateOpts struct {
// Mandatory for create
Protocol string
Action string
// Optional
TenantID string
Name string
Description string
IPVersion int
SourceIPAddress string
DestinationIPAddress string
SourcePort string
DestinationPort string
Shared *bool
Enabled *bool
}
// ToRuleCreateMap casts a CreateOpts struct to a map.
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
if opts.Protocol == "" {
return nil, errProtocolRequired
}
if opts.Action == "" {
return nil, errActionRequired
}
r := make(map[string]interface{})
r["protocol"] = opts.Protocol
r["action"] = opts.Action
if opts.TenantID != "" {
r["tenant_id"] = opts.TenantID
}
if opts.Name != "" {
r["name"] = opts.Name
}
if opts.Description != "" {
r["description"] = opts.Description
}
if opts.IPVersion != 0 {
r["ip_version"] = opts.IPVersion
}
if opts.SourceIPAddress != "" {
r["source_ip_address"] = opts.SourceIPAddress
}
if opts.DestinationIPAddress != "" {
r["destination_ip_address"] = opts.DestinationIPAddress
}
if opts.SourcePort != "" {
r["source_port"] = opts.SourcePort
}
if opts.DestinationPort != "" {
r["destination_port"] = opts.DestinationPort
}
if opts.Shared != nil {
r["shared"] = *opts.Shared
}
if opts.Enabled != nil {
r["enabled"] = *opts.Enabled
}
return map[string]interface{}{"firewall_rule": r}, nil
}
// Create accepts a CreateOpts struct and uses the values to create a new firewall rule
func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
reqBody, err := opts.ToRuleCreateMap()
if err != nil {
res.Err = err
return res
}
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
}
// Get retrieves a particular firewall rule based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
}
// UpdateOptsBuilder is the interface options structs have to satisfy in order
// to be used in the main Update operation in this package. Since many
// extensions decorate or modify the common logic, it is useful for them to
// satisfy a basic interface in order for them to be used.
type UpdateOptsBuilder interface {
ToRuleUpdateMap() (map[string]interface{}, error)
}
// UpdateOpts contains the values used when updating a firewall rule.
// Optional
type UpdateOpts struct {
Protocol string
Action string
Name string
Description string
IPVersion int
SourceIPAddress *string
DestinationIPAddress *string
SourcePort *string
DestinationPort *string
Shared *bool
Enabled *bool
}
// ToRuleUpdateMap casts a UpdateOpts struct to a map.
func (opts UpdateOpts) ToRuleUpdateMap() (map[string]interface{}, error) {
r := make(map[string]interface{})
if opts.Protocol != "" {
r["protocol"] = opts.Protocol
}
if opts.Action != "" {
r["action"] = opts.Action
}
if opts.Name != "" {
r["name"] = opts.Name
}
if opts.Description != "" {
r["description"] = opts.Description
}
if opts.IPVersion != 0 {
r["ip_version"] = opts.IPVersion
}
if opts.SourceIPAddress != nil {
s := *opts.SourceIPAddress
if s == "" {
r["source_ip_address"] = nil
} else {
r["source_ip_address"] = s
}
}
if opts.DestinationIPAddress != nil {
s := *opts.DestinationIPAddress
if s == "" {
r["destination_ip_address"] = nil
} else {
r["destination_ip_address"] = s
}
}
if opts.SourcePort != nil {
s := *opts.SourcePort
if s == "" {
r["source_port"] = nil
} else {
r["source_port"] = s
}
}
if opts.DestinationPort != nil {
s := *opts.DestinationPort
if s == "" {
r["destination_port"] = nil
} else {
r["destination_port"] = s
}
}
if opts.Shared != nil {
r["shared"] = *opts.Shared
}
if opts.Enabled != nil {
r["enabled"] = *opts.Enabled
}
return map[string]interface{}{"firewall_rule": r}, nil
}
// Update allows firewall policies to be updated.
func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
var res UpdateResult
reqBody, err := opts.ToRuleUpdateMap()
if err != nil {
res.Err = err
return res
}
// Send request to API
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
}
// Delete will permanently delete a particular firewall rule based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res
}

View File

@ -0,0 +1,328 @@
package rules
import (
"fmt"
"net/http"
"testing"
fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestURLs(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.AssertEquals(t, th.Endpoint()+"v2.0/fw/firewall_rules", rootURL(fake.ServiceClient()))
}
func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_rules", 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, `
{
"firewall_rules": [
{
"protocol": "tcp",
"description": "ssh rule",
"source_port": null,
"source_ip_address": null,
"destination_ip_address": "192.168.1.0/24",
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
"position": 2,
"destination_port": "22",
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
"name": "ssh_form_any",
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
"enabled": true,
"action": "allow",
"ip_version": 4,
"shared": false
},
{
"protocol": "udp",
"description": "udp rule",
"source_port": null,
"source_ip_address": null,
"destination_ip_address": null,
"firewall_policy_id": "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
"position": 1,
"destination_port": null,
"id": "ab7bd950-6c56-4f5e-a307-45967078f890",
"name": "deny_all_udp",
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
"enabled": true,
"action": "deny",
"ip_version": 4,
"shared": false
}
]
}
`)
})
count := 0
List(fake.ServiceClient(), ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractRules(page)
if err != nil {
t.Errorf("Failed to extract members: %v", err)
return false, err
}
expected := []Rule{
Rule{
Protocol: "tcp",
Description: "ssh rule",
SourcePort: "",
SourceIPAddress: "",
DestinationIPAddress: "192.168.1.0/24",
PolicyID: "e2a5fb51-698c-4898-87e8-f1eee6b50919",
Position: 2,
DestinationPort: "22",
ID: "f03bd950-6c56-4f5e-a307-45967078f507",
Name: "ssh_form_any",
TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61",
Enabled: true,
Action: "allow",
IPVersion: 4,
Shared: false,
},
Rule{
Protocol: "udp",
Description: "udp rule",
SourcePort: "",
SourceIPAddress: "",
DestinationIPAddress: "",
PolicyID: "98d7fb51-698c-4123-87e8-f1eee6b5ab7e",
Position: 1,
DestinationPort: "",
ID: "ab7bd950-6c56-4f5e-a307-45967078f890",
Name: "deny_all_udp",
TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61",
Enabled: true,
Action: "deny",
IPVersion: 4,
Shared: false,
},
}
th.CheckDeepEquals(t, expected, actual)
return true, nil
})
if count != 1 {
t.Errorf("Expected 1 page, got %d", count)
}
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_rules", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"firewall_rule": {
"protocol": "tcp",
"description": "ssh rule",
"destination_ip_address": "192.168.1.0/24",
"destination_port": "22",
"name": "ssh_form_any",
"action": "allow",
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `
{
"firewall_rule":{
"protocol": "tcp",
"description": "ssh rule",
"source_port": null,
"source_ip_address": null,
"destination_ip_address": "192.168.1.0/24",
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
"position": 2,
"destination_port": "22",
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
"name": "ssh_form_any",
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
"enabled": true,
"action": "allow",
"ip_version": 4,
"shared": false
}
}
`)
})
options := CreateOpts{
TenantID: "80cf934d6ffb4ef5b244f1c512ad1e61",
Protocol: "tcp",
Description: "ssh rule",
DestinationIPAddress: "192.168.1.0/24",
DestinationPort: "22",
Name: "ssh_form_any",
Action: "allow",
}
_, err := Create(fake.ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", 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, `
{
"firewall_rule":{
"protocol": "tcp",
"description": "ssh rule",
"source_port": null,
"source_ip_address": null,
"destination_ip_address": "192.168.1.0/24",
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
"position": 2,
"destination_port": "22",
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
"name": "ssh_form_any",
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
"enabled": true,
"action": "allow",
"ip_version": 4,
"shared": false
}
}
`)
})
rule, err := Get(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507").Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "tcp", rule.Protocol)
th.AssertEquals(t, "ssh rule", rule.Description)
th.AssertEquals(t, "192.168.1.0/24", rule.DestinationIPAddress)
th.AssertEquals(t, "e2a5fb51-698c-4898-87e8-f1eee6b50919", rule.PolicyID)
th.AssertEquals(t, 2, rule.Position)
th.AssertEquals(t, "22", rule.DestinationPort)
th.AssertEquals(t, "f03bd950-6c56-4f5e-a307-45967078f507", rule.ID)
th.AssertEquals(t, "ssh_form_any", rule.Name)
th.AssertEquals(t, "80cf934d6ffb4ef5b244f1c512ad1e61", rule.TenantID)
th.AssertEquals(t, true, rule.Enabled)
th.AssertEquals(t, "allow", rule.Action)
th.AssertEquals(t, 4, rule.IPVersion)
th.AssertEquals(t, false, rule.Shared)
}
func TestUpdate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_rules/f03bd950-6c56-4f5e-a307-45967078f507", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "PUT")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"firewall_rule":{
"protocol": "tcp",
"description": "ssh rule",
"destination_ip_address": "192.168.1.0/24",
"destination_port": "22",
"source_ip_address": null,
"source_port": null,
"name": "ssh_form_any",
"action": "allow",
"enabled": false
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"firewall_rule":{
"protocol": "tcp",
"description": "ssh rule",
"source_port": null,
"source_ip_address": null,
"destination_ip_address": "192.168.1.0/24",
"firewall_policy_id": "e2a5fb51-698c-4898-87e8-f1eee6b50919",
"position": 2,
"destination_port": "22",
"id": "f03bd950-6c56-4f5e-a307-45967078f507",
"name": "ssh_form_any",
"tenant_id": "80cf934d6ffb4ef5b244f1c512ad1e61",
"enabled": false,
"action": "allow",
"ip_version": 4,
"shared": false
}
}
`)
})
destinationIPAddress := "192.168.1.0/24"
destinationPort := "22"
empty := ""
options := UpdateOpts{
Protocol: "tcp",
Description: "ssh rule",
DestinationIPAddress: &destinationIPAddress,
DestinationPort: &destinationPort,
Name: "ssh_form_any",
SourceIPAddress: &empty,
SourcePort: &empty,
Action: "allow",
Enabled: No,
}
_, err := Update(fake.ServiceClient(), "f03bd950-6c56-4f5e-a307-45967078f507", options).Extract()
th.AssertNoErr(t, err)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/fw/firewall_rules/4ec89077-d057-4a2b-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.WriteHeader(http.StatusNoContent)
})
res := Delete(fake.ServiceClient(), "4ec89077-d057-4a2b-911f-60a3b47ee304")
th.AssertNoErr(t, res.Err)
}

View File

@ -0,0 +1,110 @@
package rules
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// Rule represents a firewall rule
type Rule struct {
ID string `json:"id" mapstructure:"id"`
Name string `json:"name,omitempty" mapstructure:"name"`
Description string `json:"description,omitempty" mapstructure:"description"`
Protocol string `json:"protocol" mapstructure:"protocol"`
Action string `json:"action" mapstructure:"action"`
IPVersion int `json:"ip_version,omitempty" mapstructure:"ip_version"`
SourceIPAddress string `json:"source_ip_address,omitempty" mapstructure:"source_ip_address"`
DestinationIPAddress string `json:"destination_ip_address,omitempty" mapstructure:"destination_ip_address"`
SourcePort string `json:"source_port,omitempty" mapstructure:"source_port"`
DestinationPort string `json:"destination_port,omitempty" mapstructure:"destination_port"`
Shared bool `json:"shared,omitempty" mapstructure:"shared"`
Enabled bool `json:"enabled,omitempty" mapstructure:"enabled"`
PolicyID string `json:"firewall_policy_id" mapstructure:"firewall_policy_id"`
Position int `json:"position" mapstructure:"position"`
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
}
// RulePage is the page returned by a pager when traversing over a
// collection of firewall rules.
type RulePage struct {
pagination.LinkedPageBase
}
// NextPageURL is invoked when a paginated collection of firewall rules has
// reached the end of a page and the pager seeks to traverse over a new one.
// In order to do this, it needs to construct the next page's URL.
func (p RulePage) NextPageURL() (string, error) {
type resp struct {
Links []gophercloud.Link `mapstructure:"firewall_rules_links"`
}
var r resp
err := mapstructure.Decode(p.Body, &r)
if err != nil {
return "", err
}
return gophercloud.ExtractNextURL(r.Links)
}
// IsEmpty checks whether a RulePage struct is empty.
func (p RulePage) IsEmpty() (bool, error) {
is, err := ExtractRules(p)
if err != nil {
return true, nil
}
return len(is) == 0, nil
}
// ExtractRules accepts a Page struct, specifically a RouterPage struct,
// and extracts the elements into a slice of Router structs. In other words,
// a generic collection is mapped into a relevant slice.
func ExtractRules(page pagination.Page) ([]Rule, error) {
var resp struct {
Rules []Rule `mapstructure:"firewall_rules" json:"firewall_rules"`
}
err := mapstructure.Decode(page.(RulePage).Body, &resp)
return resp.Rules, err
}
type commonResult struct {
gophercloud.Result
}
// Extract is a function that accepts a result and extracts a firewall rule.
func (r commonResult) Extract() (*Rule, error) {
if r.Err != nil {
return nil, r.Err
}
var res struct {
Rule *Rule `json:"firewall_rule" mapstructure:"firewall_rule"`
}
err := mapstructure.Decode(r.Body, &res)
return res.Rule, err
}
// GetResult represents the result of a get operation.
type GetResult struct {
commonResult
}
// UpdateResult represents the result of an update operation.
type UpdateResult struct {
commonResult
}
// DeleteResult represents the result of a delete operation.
type DeleteResult struct {
gophercloud.ErrResult
}
// CreateResult represents the result of a create operation.
type CreateResult struct {
commonResult
}

View File

@ -0,0 +1,16 @@
package rules
import "github.com/rackspace/gophercloud"
const (
rootPath = "fw"
resourcePath = "firewall_rules"
)
func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rootPath, resourcePath)
}
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rootPath, resourcePath, id)
}

View File

@ -3,7 +3,6 @@ package floatingips
import (
"fmt"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -53,7 +52,6 @@ type CreateOpts struct {
var (
errFloatingNetworkIDRequired = fmt.Errorf("A NetworkID is required")
errPortIDRequired = fmt.Errorf("A PortID is required")
)
// Create accepts a CreateOpts struct and uses the values provided to create a
@ -88,16 +86,12 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
res.Err = errFloatingNetworkIDRequired
return res
}
if opts.PortID == "" {
res.Err = errPortIDRequired
return res
}
// Define structures
type floatingIP struct {
FloatingNetworkID string `json:"floating_network_id"`
FloatingIP string `json:"floating_ip_address,omitempty"`
PortID string `json:"port_id"`
PortID string `json:"port_id,omitempty"`
FixedIP string `json:"fixed_ip_address,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
}
@ -114,10 +108,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
}}
// Send request to API
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -127,9 +120,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular floating IP resource based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -167,10 +159,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Send request to API
var res UpdateResult
_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
@ -182,8 +173,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// internal ports.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -170,6 +170,55 @@ func TestCreate(t *testing.T) {
th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
}
func TestCreateEmptyPort(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/v2.0/floatingips", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Content-Type", "application/json")
th.TestHeader(t, r, "Accept", "application/json")
th.TestJSONRequest(t, r, `
{
"floatingip": {
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `
{
"floatingip": {
"router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72f",
"tenant_id": "4969c491a3c74ee4af974e6d800c62de",
"floating_network_id": "376da547-b977-4cfe-9cba-275c80debf57",
"fixed_ip_address": "10.0.0.3",
"floating_ip_address": "",
"id": "2f245a7b-796b-4f26-9cf9-9e82d248fda7"
}
}
`)
})
options := CreateOpts{
FloatingNetworkID: "376da547-b977-4cfe-9cba-275c80debf57",
}
ip, err := Create(fake.ServiceClient(), options).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "2f245a7b-796b-4f26-9cf9-9e82d248fda7", ip.ID)
th.AssertEquals(t, "4969c491a3c74ee4af974e6d800c62de", ip.TenantID)
th.AssertEquals(t, "376da547-b977-4cfe-9cba-275c80debf57", ip.FloatingNetworkID)
th.AssertEquals(t, "", ip.FloatingIP)
th.AssertEquals(t, "", ip.PortID)
th.AssertEquals(t, "10.0.0.3", ip.FixedIP)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()

View File

@ -3,7 +3,6 @@ package routers
import (
"errors"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -82,10 +81,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
}
var res CreateResult
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
@ -94,9 +92,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular router based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -136,10 +133,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Send request to API
var res UpdateResult
_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
@ -149,8 +145,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Delete will permanently delete a particular router based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res
@ -202,10 +197,9 @@ func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) I
body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
_, res.Err = perigee.Request("PUT", addInterfaceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &body,
Results: &res.Body,
_, res.Err = c.Request("PUT", addInterfaceURL(c, id), gophercloud.RequestOpts{
JSONBody: &body,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
@ -235,10 +229,9 @@ func RemoveInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts
body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
_, res.Err = perigee.Request("PUT", removeInterfaceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &body,
Results: &res.Body,
_, res.Err = c.Request("PUT", removeInterfaceURL(c, id), gophercloud.RequestOpts{
JSONBody: &body,
JSONResponse: &res.Body,
OkCodes: []int{200},
})

View File

@ -1,7 +1,6 @@
package members
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -80,10 +79,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
}}
var res CreateResult
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
@ -92,9 +90,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular pool member based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -119,10 +116,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Send request to API
var res UpdateResult
_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -131,8 +127,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Delete will permanently delete a particular member based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,7 +3,6 @@ package monitors
import (
"fmt"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -177,10 +176,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
AdminStateUp: opts.AdminStateUp,
}}
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -190,9 +188,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular health monitor based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -261,10 +258,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
AdminStateUp: opts.AdminStateUp,
}}
_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200, 202},
})
@ -274,8 +270,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Delete will permanently delete a particular monitor based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -1,7 +1,6 @@
package pools
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -100,10 +99,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
}}
var res CreateResult
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
@ -112,9 +110,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular pool based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -148,10 +145,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Send request to API
var res UpdateResult
_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -160,8 +156,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Delete will permanently delete a particular pool based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res
@ -183,10 +178,9 @@ func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) As
reqBody := request{hm{ID: monitorID}}
var res AssociateResult
_, res.Err = perigee.Request("POST", associateURL(c, poolID), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", associateURL(c, poolID), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
@ -197,8 +191,7 @@ func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) As
// check for the health of the members of the pool.
func DisassociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) AssociateResult {
var res AssociateResult
_, res.Err = perigee.Request("DELETE", disassociateURL(c, poolID, monitorID), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", disassociateURL(c, poolID, monitorID), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,7 +3,6 @@ package vips
import (
"fmt"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -179,10 +178,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
reqBody.VirtualIP.Persistence = opts.Persistence
}
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -192,9 +190,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular virtual IP based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -252,10 +249,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
}
var res UpdateResult
_, res.Err = perigee.Request("PUT", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200, 202},
})
@ -265,8 +261,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
// Delete will permanently delete a particular virtual IP based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,7 +3,6 @@ package groups
import (
"fmt"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -75,10 +74,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
Description: opts.Description,
}}
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -88,9 +86,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular security group based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -99,8 +96,7 @@ func Get(c *gophercloud.ServiceClient, id string) GetResult {
// Delete will permanently delete a particular security group based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,7 +3,6 @@ package rules
import (
"fmt"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
@ -151,10 +150,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
RemoteIPPrefix: opts.RemoteIPPrefix,
}}
_, res.Err = perigee.Request("POST", rootURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -164,9 +162,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
// Get retrieves a particular security group based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -175,8 +172,7 @@ func Get(c *gophercloud.ServiceClient, id string) GetResult {
// Delete will permanently delete a particular security group based on its unique ID.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", resourceURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,8 +3,6 @@ package networks
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// AdminState gives users a solid type to work with for create and update
@ -81,9 +79,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
// Get retrieves a specific network based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -138,10 +135,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
}
// Send request to API
_, res.Err = perigee.Request("POST", createURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
return res
@ -188,10 +184,9 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild
}
// Send request to API
_, res.Err = perigee.Request("PUT", updateURL(c, networkID), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", updateURL(c, networkID), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200, 201},
})
@ -201,8 +196,7 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild
// Delete accepts a unique ID and deletes the network associated with it.
func Delete(c *gophercloud.ServiceClient, networkID string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(c, networkID), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", deleteURL(c, networkID), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,8 +3,6 @@ package ports
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// AdminState gives users a solid type to work with for create and update
@ -81,9 +79,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
// Get retrieves a specific port based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -159,10 +156,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
}
// Response
_, res.Err = perigee.Request("POST", createURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -224,10 +220,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd
return res
}
_, res.Err = perigee.Request("PUT", updateURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", updateURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200, 201},
})
return res
@ -236,8 +231,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd
// Delete accepts a unique ID and deletes the port associated with it.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", deleteURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -3,8 +3,6 @@ package subnets
import (
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/racker/perigee"
)
// AdminState gives users a solid type to work with for create and update
@ -80,9 +78,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
// Get retrieves a specific subnet based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
_, res.Err = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{
JSONResponse: &res.Body,
OkCodes: []int{200},
})
return res
@ -174,10 +171,9 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
return res
}
_, res.Err = perigee.Request("POST", createURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{201},
})
@ -233,10 +229,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd
return res
}
_, res.Err = perigee.Request("PUT", updateURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
ReqBody: &reqBody,
Results: &res.Body,
_, res.Err = c.Request("PUT", updateURL(c, id), gophercloud.RequestOpts{
JSONBody: &reqBody,
JSONResponse: &res.Body,
OkCodes: []int{200, 201},
})
@ -246,8 +241,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd
// Delete accepts a unique ID and deletes the subnet associated with it.
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
var res DeleteResult
_, res.Err = perigee.Request("DELETE", deleteURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
_, res.Err = c.Request("DELETE", deleteURL(c, id), gophercloud.RequestOpts{
OkCodes: []int{204},
})
return res

View File

@ -1,9 +1,6 @@
package accounts
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
)
import "github.com/rackspace/gophercloud"
// GetOptsBuilder allows extensions to add additional headers to the Get
// request.
@ -42,11 +39,11 @@ func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) GetResult {
}
}
resp, err := perigee.Request("HEAD", getURL(c), perigee.Options{
resp, err := c.Request("HEAD", getURL(c), gophercloud.RequestOpts{
MoreHeaders: h,
OkCodes: []int{204},
})
res.Header = resp.HttpResponse.Header
res.Header = resp.Header
res.Err = err
return res
}
@ -83,7 +80,7 @@ func (opts UpdateOpts) ToAccountUpdateMap() (map[string]string, error) {
// To extract the headers returned, call the Extract method on the UpdateResult.
func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) UpdateResult {
var res UpdateResult
h := c.AuthenticatedHeaders()
h := make(map[string]string)
if opts != nil {
headers, err := opts.ToAccountUpdateMap()
@ -96,11 +93,11 @@ func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) UpdateResult {
}
}
resp, err := perigee.Request("POST", updateURL(c), perigee.Options{
resp, err := c.Request("POST", updateURL(c), gophercloud.RequestOpts{
MoreHeaders: h,
OkCodes: []int{204},
})
res.Header = resp.HttpResponse.Header
res.Header = resp.Header
res.Err = err
return res
}

View File

@ -54,6 +54,14 @@ func HandleListContainerInfoSuccessfully(t *testing.T) {
"bytes": 14,
"name": "marktwain"
}
]`)
case "janeausten":
fmt.Fprintf(w, `[
{
"count": 1,
"bytes": 14,
"name": "marktwain"
}
]`)
case "marktwain":
fmt.Fprintf(w, `[]`)
@ -77,6 +85,8 @@ func HandleListContainerNamesSuccessfully(t *testing.T) {
switch marker {
case "":
fmt.Fprintf(w, "janeausten\nmarktwain\n")
case "janeausten":
fmt.Fprintf(w, "marktwain\n")
case "marktwain":
fmt.Fprintf(w, ``)
default:

Some files were not shown because too many files have changed in this diff Show More