diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8bd449a499..14ef93c948 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -130,8 +130,8 @@ }, { "ImportPath": "github.com/rackspace/gophercloud", - "Comment": "v1.0.0-473-g7ca169d", - "Rev": "7ca169d371b29e3dbab9e631c3a6151896b06330" + "Comment": "v1.0.0-558-ce0f487", + "Rev": "ce0f487f6747ab43c4e4404722df25349385bebd" }, { "ImportPath": "github.com/skarademir/naturalsort", diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md index 93b798e5a2..9748c1ad2b 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md @@ -205,7 +205,7 @@ On of the easiest ways to get readily involved in our project is to let us know about your experiences using our SDK. Feedback like this is incredibly useful to us, because it allows us to refine and change features based on what our users want and expect of us. There are a bunch of ways to get in contact! You -can [ping us](mailto:sdk-support@rackspace.com) via e-mail, talk to us on irc +can [ping us](https://developer.rackspace.com/support/) via e-mail, talk to us on irc (#rackspace-dev on freenode), [tweet us](https://twitter.com/rackspace), or submit an issue on our [bug tracker](/issues). Things you might like to tell us are: diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/README.md b/Godeps/_workspace/src/github.com/rackspace/gophercloud/README.md index 9f7552b0d2..05453bfede 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/README.md +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/README.md @@ -151,11 +151,10 @@ Engaging the community and lowering barriers for contributors is something we care a lot about. For this reason, we've taken the time to write a [contributing guide](./CONTRIBUTING.md) for folks interested in getting involved in our project. If you're not sure how you can get involved, feel free to submit an issue or -[e-mail us](mailto:sdk-support@rackspace.com) privately. You don't need to be a +[contact us](https://developer.rackspace.com/support/). You don't need to be a Go expert - all members of the community are welcome! ## Help and feedback If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](/issues) or e-mail us directly at -[sdk-support@rackspace.com](mailto:sdk-support@rackspace.com). +to submit an issue to our [bug tracker](/issues) or [contact us directly](https://developer.rackspace.com/support/). diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/UPGRADING.md b/Godeps/_workspace/src/github.com/rackspace/gophercloud/UPGRADING.md index a702cfc509..76a94d5703 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/UPGRADING.md +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/UPGRADING.md @@ -8,7 +8,7 @@ extensible, maintainable and easy-to-use. Below we've compiled upgrade instructions for the various services that existed before. If you have a specific issue that is not addressed below, please [submit an issue](/issues/new) or -[e-mail our support team](mailto:sdk-support@rackspace.com). +[e-mail our support team](https://developer.rackspace.com/support/). * [Authentication](#authentication) * [Servers](#servers) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/compute_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/compute_test.go index 33e49fea9b..c1bbf7961f 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/compute_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/compute_test.go @@ -56,6 +56,9 @@ type ComputeChoices struct { // FlavorIDResize contains the ID of a different flavor available on the same OpenStack installation, that is distinct // from FlavorID. FlavorIDResize string + + // NetworkName is the name of a network to launch the instance on. + NetworkName string } // ComputeChoicesFromEnv populates a ComputeChoices struct from environment variables. @@ -64,6 +67,7 @@ func ComputeChoicesFromEnv() (*ComputeChoices, error) { imageID := os.Getenv("OS_IMAGE_ID") flavorID := os.Getenv("OS_FLAVOR_ID") flavorIDResize := os.Getenv("OS_FLAVOR_ID_RESIZE") + networkName := os.Getenv("OS_NETWORK_NAME") missing := make([]string, 0, 3) if imageID == "" { @@ -75,6 +79,9 @@ func ComputeChoicesFromEnv() (*ComputeChoices, error) { if flavorIDResize == "" { missing = append(missing, "OS_FLAVOR_ID_RESIZE") } + if networkName == "" { + networkName = "public" + } notDistinct := "" if flavorID == flavorIDResize { @@ -93,5 +100,5 @@ func ComputeChoicesFromEnv() (*ComputeChoices, error) { return nil, fmt.Errorf(text) } - return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize}, nil + return &ComputeChoices{ImageID: imageID, FlavorID: flavorID, FlavorIDResize: flavorIDResize, NetworkName: networkName}, nil } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/servers_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/servers_test.go index d52a9d3537..7b928e9ef5 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/servers_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/servers_test.go @@ -57,7 +57,6 @@ func networkingClient() (*gophercloud.ServiceClient, error) { } return openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ - Name: "neutron", Region: os.Getenv("OS_REGION_NAME"), }) } @@ -74,7 +73,10 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp t.Fatalf("Unable to create a networking client: %v", err) } - pager := networks.List(networkingClient, networks.ListOpts{Name: "public", Limit: 1}) + pager := networks.List(networkingClient, networks.ListOpts{ + Name: choices.NetworkName, + Limit: 1, + }) pager.EachPage(func(page pagination.Page) (bool, error) { networks, err := networks.ExtractNetworks(page) if err != nil { @@ -138,6 +140,32 @@ func TestCreateDestroyServer(t *testing.T) { if err = waitForStatus(client, server, "ACTIVE"); err != nil { t.Fatalf("Unable to wait for server: %v", err) } + + pager := servers.ListAddresses(client, server.ID) + pager.EachPage(func(page pagination.Page) (bool, error) { + networks, err := servers.ExtractAddresses(page) + if err != nil { + return false, err + } + + for n, a := range networks { + t.Logf("%s: %+v\n", n, a) + } + return true, nil + }) + + pager = servers.ListAddressesByNetwork(client, server.ID, choices.NetworkName) + pager.EachPage(func(page pagination.Page) (bool, error) { + addresses, err := servers.ExtractNetworkAddresses(page) + if err != nil { + return false, err + } + + for _, a := range addresses { + t.Logf("%+v\n", a) + } + return true, nil + }) } func TestUpdateServer(t *testing.T) { diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/tenantnetworks_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/tenantnetworks_test.go new file mode 100644 index 0000000000..a92e8bf531 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/tenantnetworks_test.go @@ -0,0 +1,109 @@ +// +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/tenantnetworks" + "github.com/rackspace/gophercloud/openstack/compute/v2/servers" + th "github.com/rackspace/gophercloud/testhelper" +) + +func getNetworkID(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) { + allPages, err := tenantnetworks.List(client).AllPages() + if err != nil { + t.Fatalf("Unable to list networks: %v", err) + } + + networkList, err := tenantnetworks.ExtractNetworks(allPages) + if err != nil { + t.Fatalf("Unable to list networks: %v", err) + } + + networkID := "" + for _, network := range networkList { + t.Logf("Network: %v", network) + if network.Name == networkName { + networkID = network.ID + } + } + + t.Logf("Found network ID for %s: %s\n", networkName, networkID) + + return networkID, nil +} + +func createNetworkServer(t *testing.T, client *gophercloud.ServiceClient, choices *ComputeChoices, networkID string) (*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("") + + networks := make([]servers.Network, 1) + networks[0] = servers.Network{ + UUID: networkID, + } + + server, err := servers.Create(client, servers.CreateOpts{ + Name: name, + FlavorRef: choices.FlavorID, + ImageRef: choices.ImageID, + AdminPass: pwd, + Networks: networks, + }).Extract() + if err != nil { + t.Fatalf("Unable to create server: %v", err) + } + + th.AssertEquals(t, pwd, server.AdminPass) + + return server, err +} + +func TestTenantNetworks(t *testing.T) { + networkName := os.Getenv("OS_NETWORK_NAME") + if networkName == "" { + t.Fatalf("OS_NETWORK_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) + } + + networkID, err := getNetworkID(t, client, networkName) + if err != nil { + t.Fatalf("Unable to get network ID: %v", err) + } + + server, err := createNetworkServer(t, client, choices, networkID) + 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) + } + + allPages, err := tenantnetworks.List(client).AllPages() + allNetworks, err := tenantnetworks.ExtractNetworks(allPages) + th.AssertNoErr(t, err) + t.Logf("Retrieved all %d networks: %+v", len(allNetworks), allNetworks) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/containers_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/containers_test.go index d6832f1914..8328a4fa6f 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/containers_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/objectstorage/v1/containers_test.go @@ -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)) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/volumeattach_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/volumeattach_test.go new file mode 100644 index 0000000000..9848e2eba5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/compute/v2/volumeattach_test.go @@ -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) + +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/networking/v2/security_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/networking/v2/security_test.go new file mode 100644 index 0000000000..ec029913e3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/networking/v2/security_test.go @@ -0,0 +1,165 @@ +// +build acceptance networking security + +package v2 + +import ( + "testing" + + osGroups "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups" + osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules" + osNetworks "github.com/rackspace/gophercloud/openstack/networking/v2/networks" + osPorts "github.com/rackspace/gophercloud/openstack/networking/v2/ports" + "github.com/rackspace/gophercloud/pagination" + rsNetworks "github.com/rackspace/gophercloud/rackspace/networking/v2/networks" + rsPorts "github.com/rackspace/gophercloud/rackspace/networking/v2/ports" + rsGroups "github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups" + rsRules "github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules" + th "github.com/rackspace/gophercloud/testhelper" +) + +func TestSecurityGroups(t *testing.T) { + Setup(t) + defer Teardown() + + // create security group + groupID := createSecGroup(t) + + // delete security group + defer deleteSecGroup(t, groupID) + + // list security group + listSecGroups(t) + + // get security group + getSecGroup(t, groupID) +} + +func TestSecurityGroupRules(t *testing.T) { + Setup(t) + defer Teardown() + + // create security group + groupID := createSecGroup(t) + + defer deleteSecGroup(t, groupID) + + // create security group rule + ruleID := createSecRule(t, groupID) + + // delete security group rule + defer deleteSecRule(t, ruleID) + + // list security group rule + listSecRules(t) + + // get security group rule + getSecRule(t, ruleID) +} + +func createSecGroup(t *testing.T) string { + sg, err := rsGroups.Create(Client, osGroups.CreateOpts{ + Name: "new-webservers", + Description: "security group for webservers", + }).Extract() + + th.AssertNoErr(t, err) + + t.Logf("Created security group %s", sg.ID) + + return sg.ID +} + +func listSecGroups(t *testing.T) { + err := rsGroups.List(Client, osGroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + list, err := osGroups.ExtractGroups(page) + if err != nil { + t.Errorf("Failed to extract secgroups: %v", err) + return false, err + } + + for _, sg := range list { + t.Logf("Listing security group: ID [%s] Name [%s]", sg.ID, sg.Name) + } + + return true, nil + }) + + th.AssertNoErr(t, err) +} + +func getSecGroup(t *testing.T, id string) { + sg, err := rsGroups.Get(Client, id).Extract() + th.AssertNoErr(t, err) + t.Logf("Getting security group: ID [%s] Name [%s] Description [%s]", sg.ID, sg.Name, sg.Description) +} + +func createSecGroupPort(t *testing.T, groupID string) (string, string) { + n, err := rsNetworks.Create(Client, osNetworks.CreateOpts{Name: "tmp_network"}).Extract() + th.AssertNoErr(t, err) + t.Logf("Created network %s", n.ID) + + opts := osPorts.CreateOpts{ + NetworkID: n.ID, + Name: "my_port", + SecurityGroups: []string{groupID}, + } + p, err := rsPorts.Create(Client, opts).Extract() + th.AssertNoErr(t, err) + t.Logf("Created port %s with security group %s", p.ID, groupID) + + return n.ID, p.ID +} + +func deleteSecGroup(t *testing.T, groupID string) { + res := rsGroups.Delete(Client, groupID) + th.AssertNoErr(t, res.Err) + t.Logf("Deleted security group %s", groupID) +} + +func createSecRule(t *testing.T, groupID string) string { + r, err := rsRules.Create(Client, osRules.CreateOpts{ + Direction: "ingress", + PortRangeMin: 80, + EtherType: "IPv4", + PortRangeMax: 80, + Protocol: "tcp", + SecGroupID: groupID, + }).Extract() + + th.AssertNoErr(t, err) + + t.Logf("Created security group rule %s", r.ID) + + return r.ID +} + +func listSecRules(t *testing.T) { + err := rsRules.List(Client, osRules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + list, err := osRules.ExtractRules(page) + if err != nil { + t.Errorf("Failed to extract sec rules: %v", err) + return false, err + } + + for _, r := range list { + t.Logf("Listing security rule: ID [%s]", r.ID) + } + + return true, nil + }) + + th.AssertNoErr(t, err) +} + +func getSecRule(t *testing.T, id string) { + r, err := rsRules.Get(Client, id).Extract() + th.AssertNoErr(t, err) + t.Logf("Getting security rule: ID [%s] Direction [%s] EtherType [%s] Protocol [%s]", + r.ID, r.Direction, r.EtherType, r.Protocol) +} + +func deleteSecRule(t *testing.T, id string) { + res := rsRules.Delete(Client, id) + th.AssertNoErr(t, res.Err) + t.Logf("Deleted security rule %s", id) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/cloudnetworks_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/cloudnetworks_test.go new file mode 100644 index 0000000000..2c6287e9f7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/cloudnetworks_test.go @@ -0,0 +1,36 @@ +// +build acceptance + +package v3 + +import ( + "fmt" + "testing" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks" + th "github.com/rackspace/gophercloud/testhelper" +) + +func TestCloudNetworks(t *testing.T) { + c := newClient(t) + cnID := testListNetworks(t, c) + testGetNetworks(t, c, cnID) +} + +func testListNetworks(t *testing.T, c *gophercloud.ServiceClient) string { + allPages, err := cloudnetworks.List(c).AllPages() + th.AssertNoErr(t, err) + allcn, err := cloudnetworks.ExtractCloudNetworks(allPages) + fmt.Printf("Listing all cloud networks: %+v\n\n", allcn) + var cnID string + if len(allcn) > 0 { + cnID = allcn[0].ID + } + return cnID +} + +func testGetNetworks(t *testing.T, c *gophercloud.ServiceClient, id string) { + cn, err := cloudnetworks.Get(c, id).Extract() + th.AssertNoErr(t, err) + fmt.Printf("Retrieved cloud network: %+v\n\n", cn) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/common.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/common.go new file mode 100644 index 0000000000..8c75314174 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/common.go @@ -0,0 +1,26 @@ +// +build acceptance + +package v3 + +import ( + "os" + "testing" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/rackspace" + th "github.com/rackspace/gophercloud/testhelper" +) + +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.NewRackConnectV3(client, gophercloud.EndpointOpts{ + Region: os.Getenv("RS_REGION_NAME"), + }) + th.AssertNoErr(t, err) + return c +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/lbpools_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/lbpools_test.go new file mode 100644 index 0000000000..85ac931b9c --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/lbpools_test.go @@ -0,0 +1,71 @@ +// +build acceptance + +package v3 + +import ( + "fmt" + "testing" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools" + th "github.com/rackspace/gophercloud/testhelper" +) + +func TestLBPools(t *testing.T) { + c := newClient(t) + pID := testListPools(t, c) + testGetPools(t, c, pID) + nID := testListNodes(t, c, pID) + testListNodeDetails(t, c, pID) + testGetNode(t, c, pID, nID) + testGetNodeDetails(t, c, pID, nID) +} + +func testListPools(t *testing.T, c *gophercloud.ServiceClient) string { + allPages, err := lbpools.List(c).AllPages() + th.AssertNoErr(t, err) + allp, err := lbpools.ExtractPools(allPages) + fmt.Printf("Listing all LB pools: %+v\n\n", allp) + var pID string + if len(allp) > 0 { + pID = allp[0].ID + } + return pID +} + +func testGetPools(t *testing.T, c *gophercloud.ServiceClient, pID string) { + p, err := lbpools.Get(c, pID).Extract() + th.AssertNoErr(t, err) + fmt.Printf("Retrieved LB pool: %+v\n\n", p) +} + +func testListNodes(t *testing.T, c *gophercloud.ServiceClient, pID string) string { + allPages, err := lbpools.ListNodes(c, pID).AllPages() + th.AssertNoErr(t, err) + alln, err := lbpools.ExtractNodes(allPages) + fmt.Printf("Listing all LB pool nodes for pool (%s): %+v\n\n", pID, alln) + var nID string + if len(alln) > 0 { + nID = alln[0].ID + } + return nID +} + +func testListNodeDetails(t *testing.T, c *gophercloud.ServiceClient, pID string) { + allPages, err := lbpools.ListNodesDetails(c, pID).AllPages() + th.AssertNoErr(t, err) + alln, err := lbpools.ExtractNodesDetails(allPages) + fmt.Printf("Listing all LB pool nodes details for pool (%s): %+v\n\n", pID, alln) +} + +func testGetNode(t *testing.T, c *gophercloud.ServiceClient, pID, nID string) { + n, err := lbpools.GetNode(c, pID, nID).Extract() + th.AssertNoErr(t, err) + fmt.Printf("Retrieved LB node: %+v\n\n", n) +} + +func testGetNodeDetails(t *testing.T, c *gophercloud.ServiceClient, pID, nID string) { + n, err := lbpools.GetNodeDetails(c, pID, nID).Extract() + th.AssertNoErr(t, err) + fmt.Printf("Retrieved LB node details: %+v\n\n", n) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/publicips_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/publicips_test.go new file mode 100644 index 0000000000..8dc62703ba --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/rackconnect/v3/publicips_test.go @@ -0,0 +1,45 @@ +// +build acceptance + +package v3 + +import ( + "fmt" + "testing" + + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips" + th "github.com/rackspace/gophercloud/testhelper" +) + +func TestPublicIPs(t *testing.T) { + c := newClient(t) + ipID := testListIPs(t, c) + sID := testGetIP(t, c, ipID) + testListIPsForServer(t, c, sID) +} + +func testListIPs(t *testing.T, c *gophercloud.ServiceClient) string { + allPages, err := publicips.List(c).AllPages() + th.AssertNoErr(t, err) + allip, err := publicips.ExtractPublicIPs(allPages) + fmt.Printf("Listing all public IPs: %+v\n\n", allip) + var ipID string + if len(allip) > 0 { + ipID = allip[0].ID + } + return ipID +} + +func testGetIP(t *testing.T, c *gophercloud.ServiceClient, ipID string) string { + ip, err := publicips.Get(c, ipID).Extract() + th.AssertNoErr(t, err) + fmt.Printf("Retrieved public IP (%s): %+v\n\n", ipID, ip) + return ip.CloudServer.ID +} + +func testListIPsForServer(t *testing.T, c *gophercloud.ServiceClient, sID string) { + allPages, err := publicips.ListForServer(c, sID).AllPages() + th.AssertNoErr(t, err) + allip, err := publicips.ExtractPublicIPs(allPages) + fmt.Printf("Listing all public IPs for server (%s): %+v\n\n", sID, allip) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/apiversions/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/apiversions/requests.go index f5a793c35c..bb2c259158 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/apiversions/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/apiversions/requests.go @@ -16,9 +16,6 @@ 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 - _, res.Err = client.Request("GET", getURL(client, v), gophercloud.RequestOpts{ - OkCodes: []int{200}, - JSONResponse: &res.Body, - }) + _, res.Err = client.Get(getURL(client, v), &res.Body, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots/requests.go index 1b313a6080..d2f10aa6b5 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/snapshots/requests.go @@ -67,10 +67,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - JSONBody: &reqBody, - JSONResponse: &res.Body, + _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res } @@ -78,9 +76,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 = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{202, 204}, - }) + _, res.Err = client.Delete(deleteURL(client, id), nil) return res } @@ -88,10 +84,7 @@ 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 = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{200}, - JSONResponse: &res.Body, - }) + _, res.Err = client.Get(getURL(client, id), &res.Body, nil) return res } @@ -173,10 +166,8 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet return res } - _, res.Err = client.Request("PUT", updateMetadataURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{200}, - JSONBody: &reqBody, - JSONResponse: &res.Body, + _, res.Err = client.Put(updateMetadataURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests.go index e620ca6140..253aaf7c54 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests.go @@ -83,10 +83,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - JSONBody: &reqBody, - JSONResponse: &res.Body, + _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res } @@ -94,9 +92,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 = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{202, 204}, - }) + _, res.Err = client.Delete(deleteURL(client, id), nil) return res } @@ -104,10 +100,7 @@ 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 = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = client.Get(getURL(client, id), &res.Body, nil) return res } @@ -152,6 +145,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) } @@ -202,10 +196,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return res } - _, res.Err = client.Request("PUT", updateURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{200}, - JSONBody: &reqBody, - JSONResponse: &res.Body, + _, res.Err = client.Put(updateURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests_test.go index 7643375a72..c484cf08df 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/requests_test.go @@ -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() diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes/requests.go index 6fedaa6896..1673d13aaf 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumetypes/requests.go @@ -44,11 +44,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{ - MoreHeaders: client.AuthenticatedHeaders(), - OkCodes: []int{200, 201}, - JSONBody: &reqBody, - JSONResponse: &res.Body, + _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res } @@ -56,10 +53,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 = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{ - MoreHeaders: client.AuthenticatedHeaders(), - OkCodes: []int{202}, - }) + _, res.Err = client.Delete(deleteURL(client, id), nil) return res } @@ -67,11 +61,7 @@ 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 := client.Request("GET", getURL(client, id), gophercloud.RequestOpts{ - MoreHeaders: client.AuthenticatedHeaders(), - OkCodes: []int{200}, - JSONResponse: &res.Body, - }) + _, err := client.Get(getURL(client, id), &res.Body, nil) res.Err = err return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests.go index b63dc95a71..dd221bc983 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests.go @@ -6,17 +6,14 @@ import "github.com/rackspace/gophercloud" // entire API. func Get(c *gophercloud.ServiceClient) GetResult { var res GetResult - _, res.Err = c.Request("GET", getURL(c), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c), &res.Body, nil) return res } // Ping retrieves a ping to the server. func Ping(c *gophercloud.ServiceClient) PingResult { var res PingResult - _, res.Err = c.Request("GET", pingURL(c), gophercloud.RequestOpts{ + _, res.Err = c.Get(pingURL(c), nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, MoreHeaders: map[string]string{"Accept": ""}, }) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests_test.go index a8d95f9ace..2c20a71103 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/base/requests_test.go @@ -17,18 +17,18 @@ func TestGetHomeDocument(t *testing.T) { expected := HomeDocument{ "rel/cdn": map[string]interface{}{ - "href-template": "services{?marker,limit}", - "href-vars": map[string]interface{}{ - "marker": "param/marker", - "limit": "param/limit", - }, - "hints": map[string]interface{}{ - "allow": []string{"GET"}, - "formats": map[string]interface{}{ - "application/json": map[string]interface{}{}, - }, - }, - }, + "href-template": "services{?marker,limit}", + "href-vars": map[string]interface{}{ + "marker": "param/marker", + "limit": "param/limit", + }, + "hints": map[string]interface{}{ + "allow": []string{"GET"}, + "formats": map[string]interface{}{ + "application/json": map[string]interface{}{}, + }, + }, + }, } th.CheckDeepEquals(t, expected, *actual) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/fixtures.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/fixtures.go index f413b6bc41..d7ec1a00d5 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/fixtures.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/fixtures.go @@ -1,24 +1,24 @@ package flavors import ( - "fmt" - "net/http" - "testing" + "fmt" + "net/http" + "testing" - th "github.com/rackspace/gophercloud/testhelper" - fake "github.com/rackspace/gophercloud/testhelper/client" + th "github.com/rackspace/gophercloud/testhelper" + fake "github.com/rackspace/gophercloud/testhelper/client" ) // HandleListCDNFlavorsSuccessfully creates an HTTP handler at `/flavors` on the test handler mux // that responds with a `List` response. func HandleListCDNFlavorsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.Mux.HandleFunc("/flavors", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` { "flavors": [ { @@ -44,19 +44,19 @@ func HandleListCDNFlavorsSuccessfully(t *testing.T) { ] } `) - }) + }) } // HandleGetCDNFlavorSuccessfully creates an HTTP handler at `/flavors/{id}` on the test handler mux // that responds with a `Get` response. func HandleGetCDNFlavorSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.Mux.HandleFunc("/flavors/asia", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ` + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` { "id" : "asia", "providers" : [ @@ -78,5 +78,5 @@ func HandleGetCDNFlavorSuccessfully(t *testing.T) { ] } `) - }) + }) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests.go index 138fd97631..8755a95b8f 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests.go @@ -17,9 +17,6 @@ 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 = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests_test.go index 7ddf1b1c66..f731738279 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/flavors/requests_test.go @@ -1,90 +1,89 @@ package flavors import ( - "testing" + "testing" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" - th "github.com/rackspace/gophercloud/testhelper" - fake "github.com/rackspace/gophercloud/testhelper/client" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" + fake "github.com/rackspace/gophercloud/testhelper/client" ) func TestList(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() - HandleListCDNFlavorsSuccessfully(t) + HandleListCDNFlavorsSuccessfully(t) - count := 0 + count := 0 - err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { - count++ - actual, err := ExtractFlavors(page) - if err != nil { - t.Errorf("Failed to extract flavors: %v", err) - return false, err - } + err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractFlavors(page) + if err != nil { + t.Errorf("Failed to extract flavors: %v", err) + return false, err + } - expected := []Flavor{ - Flavor{ - ID: "europe", - Providers: []Provider{ - Provider{ - Provider: "Fastly", - Links: []gophercloud.Link{ - gophercloud.Link{ - Href: "http://www.fastly.com", - Rel: "provider_url", - }, - }, - }, - }, - Links: []gophercloud.Link{ - gophercloud.Link{ - Href: "https://www.poppycdn.io/v1.0/flavors/europe", - Rel: "self", - }, - }, - }, - } + expected := []Flavor{ + Flavor{ + ID: "europe", + Providers: []Provider{ + Provider{ + Provider: "Fastly", + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "http://www.fastly.com", + Rel: "provider_url", + }, + }, + }, + }, + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "https://www.poppycdn.io/v1.0/flavors/europe", + Rel: "self", + }, + }, + }, + } - th.CheckDeepEquals(t, expected, actual) + th.CheckDeepEquals(t, expected, actual) - return true, nil - }) - th.AssertNoErr(t, err) - th.CheckEquals(t, 1, count) + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, count) } func TestGet(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() - HandleGetCDNFlavorSuccessfully(t) + HandleGetCDNFlavorSuccessfully(t) - expected := &Flavor{ - ID: "asia", - Providers: []Provider{ - Provider{ - Provider: "ChinaCache", - Links: []gophercloud.Link{ - gophercloud.Link{ - Href: "http://www.chinacache.com", - Rel: "provider_url", - }, - }, - }, - }, - Links: []gophercloud.Link{ - gophercloud.Link{ - Href: "https://www.poppycdn.io/v1.0/flavors/asia", - Rel: "self", - }, - }, - } + expected := &Flavor{ + ID: "asia", + Providers: []Provider{ + Provider{ + Provider: "ChinaCache", + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "http://www.chinacache.com", + Rel: "provider_url", + }, + }, + }, + }, + Links: []gophercloud.Link{ + gophercloud.Link{ + Href: "https://www.poppycdn.io/v1.0/flavors/asia", + Rel: "self", + }, + }, + } - - actual, err := Get(fake.ServiceClient(), "asia").Extract() - th.AssertNoErr(t, err) - th.AssertDeepEquals(t, expected, actual) + actual, err := Get(fake.ServiceClient(), "asia").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/fixtures.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/fixtures.go index 38e7fc55f8..5c6b5d00e4 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/fixtures.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/fixtures.go @@ -1,19 +1,19 @@ package serviceassets import ( - "net/http" - "testing" + "net/http" + "testing" - th "github.com/rackspace/gophercloud/testhelper" - fake "github.com/rackspace/gophercloud/testhelper/client" + th "github.com/rackspace/gophercloud/testhelper" + fake "github.com/rackspace/gophercloud/testhelper/client" ) // HandleDeleteCDNAssetSuccessfully creates an HTTP handler at `/services/{id}/assets` on the test handler mux // that responds with a `Delete` response. func HandleDeleteCDNAssetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - w.WriteHeader(http.StatusAccepted) - }) + th.Mux.HandleFunc("/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0/assets", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + w.WriteHeader(http.StatusAccepted) + }) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests.go index a80aa0db28..1ddc65fafd 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests.go @@ -43,8 +43,6 @@ func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder } var res DeleteResult - _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = c.Delete(url, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests_test.go index 32896eef7a..dde7bc171d 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets/requests_test.go @@ -1,18 +1,18 @@ package serviceassets import ( - "testing" + "testing" - th "github.com/rackspace/gophercloud/testhelper" - fake "github.com/rackspace/gophercloud/testhelper/client" + th "github.com/rackspace/gophercloud/testhelper" + fake "github.com/rackspace/gophercloud/testhelper/client" ) func TestDelete(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() + th.SetupHTTP() + defer th.TeardownHTTP() - HandleDeleteCDNAssetSuccessfully(t) + HandleDeleteCDNAssetSuccessfully(t) - err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr() - th.AssertNoErr(t, err) + err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr() + th.AssertNoErr(t, err) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/services/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/services/requests.go index 78a3087c98..8b37928e21 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/services/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/cdn/v1/services/requests.go @@ -177,10 +177,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { } // Send request to API - resp, err := c.Request("POST", createURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) + resp, err := c.Post(createURL(c), &reqBody, nil, nil) res.Header = resp.Header res.Err = err return res @@ -199,10 +196,7 @@ func Get(c *gophercloud.ServiceClient, idOrURL string) GetResult { } var res GetResult - _, res.Err = c.Request("GET", url, gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(url, &res.Body, nil) return res } @@ -379,8 +373,6 @@ func Delete(c *gophercloud.ServiceClient, idOrURL string) DeleteResult { } var res DeleteResult - _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = c.Delete(url, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/client.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/client.go index 6818d9d734..1193b19a7a 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/client.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/client.go @@ -109,6 +109,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc if options.AllowReauth { client.ReauthFunc = func() error { + client.TokenID = "" return AuthenticateV2(client, options) } } @@ -132,10 +133,36 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc v3Client.Endpoint = endpoint } - token, err := tokens3.Create(v3Client, options, nil).Extract() + var scope *tokens3.Scope + if options.TenantID != "" { + scope = &tokens3.Scope{ + ProjectID: options.TenantID, + } + options.TenantID = "" + options.TenantName = "" + } else { + if options.TenantName != "" { + scope = &tokens3.Scope{ + ProjectName: options.TenantName, + DomainID: options.DomainID, + DomainName: options.DomainName, + } + options.TenantName = "" + } + } + + result := tokens3.Create(v3Client, options, scope) + + token, err := result.ExtractToken() if err != nil { return err } + + catalog, err := result.ExtractServiceCatalog() + if err != nil { + return err + } + client.TokenID = token.ID if options.AllowReauth { @@ -144,7 +171,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { - return V3EndpointURL(v3Client, opts) + return V3EndpointURL(catalog, opts) } return nil diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/errors.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/errors.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go old mode 100644 new mode 100755 index dfd81c99a0..0b71085015 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go @@ -8,10 +8,7 @@ import ( // Get retrieves information for a specific extension using its alias. func Get(c *gophercloud.ServiceClient, alias string) GetResult { var res GetResult - _, res.Err = c.Request("GET", ExtensionURL(c, alias), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(ExtensionURL(c, alias), &res.Body, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/results.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/urls_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/common/extensions/urls_test.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go index b64014f36b..c0ba368db7 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -99,10 +99,8 @@ func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) s return res } - _, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{ - JSONBody: reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 202}, + _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/requests.go index 294bae3578..9f27ef172c 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules/requests.go @@ -73,10 +73,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return result } - _, result.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: &reqBody, - OkCodes: []int{200}, + _, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return result @@ -85,22 +83,13 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes // Get will return details for a particular default rule. func Get(client *gophercloud.ServiceClient, id string) GetResult { var result GetResult - - _, result.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - OkCodes: []int{200}, - }) - + _, result.Err = client.Get(resourceURL(client, id), &result.Body, nil) return result } // Delete will permanently delete a default rule from the project. func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { var result gophercloud.ErrResult - - _, result.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) - + _, result.Err = client.Delete(resourceURL(client, id), nil) return result } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go index d154038010..8abb72dcde 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go @@ -45,10 +45,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{ - JSONBody: reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -56,19 +54,14 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes // 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}, - }) + _, res.Err = client.Get(getURL(client, id), &res.Body, nil) return res } // Delete requests the deletion of a previous allocated FloatingIP. func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { var res DeleteResult - _, res.Err = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = client.Delete(deleteURL(client, id), nil) return res } @@ -82,10 +75,7 @@ func Associate(client *gophercloud.ServiceClient, serverId, fip string) Associat addFloatingIp["address"] = fip reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp} - _, res.Err = client.Request("POST", associateURL(client, serverId), gophercloud.RequestOpts{ - JSONBody: reqBody, - OkCodes: []int{202}, - }) + _, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil) return res } @@ -97,9 +87,6 @@ func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) Disas removeFloatingIp["address"] = fip reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp} - _, res.Err = client.Request("POST", disassociateURL(client, serverId), gophercloud.RequestOpts{ - JSONBody: reqBody, - OkCodes: []int{202}, - }) + _, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go index 287e4127c5..c56ee67ea2 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go @@ -81,10 +81,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", createURL(client), gophercloud.RequestOpts{ - JSONBody: reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -92,18 +90,13 @@ 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 = client.Request("GET", getURL(client, name), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = client.Get(getURL(client, name), &res.Body, nil) return res } // Delete requests the deletion of a previous stored KeyPair from the server. func Delete(client *gophercloud.ServiceClient, name string) DeleteResult { var res DeleteResult - _, res.Err = client.Request("DELETE", deleteURL(client, name), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = client.Delete(deleteURL(client, name), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go index 8f0a7a032f..4cef480222 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go @@ -78,10 +78,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return result } - _, result.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: &reqBody, - OkCodes: []int{200}, + _, result.Err = client.Post(rootURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return result @@ -123,10 +121,8 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder return result } - _, result.Err = client.Request("PUT", resourceURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: &reqBody, - OkCodes: []int{200}, + _, result.Err = client.Put(resourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return result @@ -135,23 +131,14 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder // Get will return details for a particular security group. func Get(client *gophercloud.ServiceClient, id string) GetResult { var result GetResult - - _, result.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - OkCodes: []int{200}, - }) - + _, result.Err = client.Get(resourceURL(client, id), &result.Body, nil) return result } // Delete will permanently delete a security group from the project. func Delete(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { var result gophercloud.ErrResult - - _, result.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, result.Err = client.Delete(resourceURL(client, id), nil) return result } @@ -234,10 +221,8 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) C return result } - _, result.Err = client.Request("POST", rootRuleURL(client), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: &reqBody, - OkCodes: []int{200}, + _, result.Err = client.Post(rootRuleURL(client), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return result @@ -246,11 +231,7 @@ func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) C // DeleteRule will permanently delete a rule from a security group. func DeleteRule(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { var result gophercloud.ErrResult - - _, result.Err = client.Request("DELETE", resourceRuleURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, result.Err = client.Delete(resourceRuleURL(client, id), nil) return result } @@ -264,25 +245,13 @@ func actionMap(prefix, groupName string) map[string]map[string]string { // rules of the group on the server. func AddServerToGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult { var result gophercloud.ErrResult - - _, result.Err = client.Request("POST", serverActionURL(client, serverID), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: actionMap("add", groupName), - OkCodes: []int{202}, - }) - + _, result.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &result.Body, nil) return result } // RemoveServerFromGroup will disassociate a server from a security group. func RemoveServerFromGroup(client *gophercloud.ServiceClient, serverID, groupName string) gophercloud.ErrResult { var result gophercloud.ErrResult - - _, result.Err = client.Request("POST", serverActionURL(client, serverID), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: actionMap("remove", groupName), - OkCodes: []int{202}, - }) - + _, result.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &result.Body, nil) return result } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go index 04b5909372..0e090e69f2 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go @@ -9,27 +9,15 @@ func actionURL(client *gophercloud.ServiceClient, id string) string { // Start is the operation responsible for starting a Compute server. func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { var res gophercloud.ErrResult - reqBody := map[string]interface{}{"os-start": nil} - - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) return res } // Stop is the operation responsible for stopping a Compute server. func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { var res gophercloud.ErrResult - reqBody := map[string]interface{}{"os-stop": nil} - - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/doc.go new file mode 100644 index 0000000000..65c46ff507 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/doc.go @@ -0,0 +1,2 @@ +// Package tenantnetworks provides the ability for tenants to see information about the networks they have access to +package tenantnetworks diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/fixtures.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/fixtures.go new file mode 100644 index 0000000000..0cfa72ab06 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/fixtures.go @@ -0,0 +1,84 @@ +// +build fixtures + +package tenantnetworks + +import ( + "fmt" + "net/http" + "testing" + "time" + + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" +) + +// ListOutput is a sample response to a List call. +const ListOutput = ` +{ + "networks": [ + { + "cidr": "10.0.0.0/29", + "id": "20c8acc0-f747-4d71-a389-46d078ebf047", + "label": "mynet_0" + }, + { + "cidr": "10.0.0.10/29", + "id": "20c8acc0-f747-4d71-a389-46d078ebf000", + "label": "mynet_1" + } + ] +} +` + +// GetOutput is a sample response to a Get call. +const GetOutput = ` +{ + "network": { + "cidr": "10.0.0.10/29", + "id": "20c8acc0-f747-4d71-a389-46d078ebf000", + "label": "mynet_1" + } +} +` + +// FirstNetwork is the first result in ListOutput. +var nilTime time.Time +var FirstNetwork = Network{ + CIDR: "10.0.0.0/29", + ID: "20c8acc0-f747-4d71-a389-46d078ebf047", + Name: "mynet_0", +} + +// SecondNetwork is the second result in ListOutput. +var SecondNetwork = Network{ + CIDR: "10.0.0.10/29", + ID: "20c8acc0-f747-4d71-a389-46d078ebf000", + Name: "mynet_1", +} + +// ExpectedNetworkSlice is the slice of results that should be parsed +// from ListOutput, in the expected order. +var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork} + +// HandleListSuccessfully configures the test server to respond to a List request. +func HandleListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-tenant-networks", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, ListOutput) + }) +} + +// HandleGetSuccessfully configures the test server to respond to a Get request +// for an existing network. +func HandleGetSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/os-tenant-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", client.TokenID) + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, GetOutput) + }) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests.go new file mode 100644 index 0000000000..3ec13d384b --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests.go @@ -0,0 +1,22 @@ +package tenantnetworks + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// List returns a Pager that allows you to iterate over a collection of Network. +func List(client *gophercloud.ServiceClient) pagination.Pager { + url := listURL(client) + createPage := func(r pagination.PageResult) pagination.Page { + return NetworkPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(client, url, createPage) +} + +// Get returns data about a previously created Network. +func Get(client *gophercloud.ServiceClient, id string) GetResult { + var res GetResult + _, res.Err = client.Get(getURL(client, id), &res.Body, nil) + return res +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests_test.go new file mode 100644 index 0000000000..fc4ee4f4ba --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/requests_test.go @@ -0,0 +1,37 @@ +package tenantnetworks + +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 := ExtractNetworks(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedNetworkSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, count) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleGetSuccessfully(t) + + actual, err := Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &SecondNetwork, actual) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/results.go new file mode 100644 index 0000000000..805009247a --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/results.go @@ -0,0 +1,68 @@ +package tenantnetworks + +import ( + "github.com/mitchellh/mapstructure" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// A Network represents a nova-network that an instance communicates on +type Network struct { + // CIDR is the IPv4 subnet. + CIDR string `mapstructure:"cidr"` + + // ID is the UUID of the network. + ID string `mapstructure:"id"` + + // Name is the common name that the network has. + Name string `mapstructure:"label"` +} + +// NetworkPage stores a single, only page of Networks +// results from a List call. +type NetworkPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a NetworkPage is empty. +func (page NetworkPage) IsEmpty() (bool, error) { + va, err := ExtractNetworks(page) + return len(va) == 0, err +} + +// ExtractNetworks interprets a page of results as a slice of Networks +func ExtractNetworks(page pagination.Page) ([]Network, error) { + networks := page.(NetworkPage).Body + var res struct { + Networks []Network `mapstructure:"networks"` + } + + err := mapstructure.WeakDecode(networks, &res) + + return res.Networks, err +} + +type NetworkResult struct { + gophercloud.Result +} + +// Extract is a method that attempts to interpret any Network resource +// response as a Network struct. +func (r NetworkResult) Extract() (*Network, error) { + if r.Err != nil { + return nil, r.Err + } + + var res struct { + Network *Network `json:"network" mapstructure:"network"` + } + + err := mapstructure.Decode(r.Body, &res) + return res.Network, err +} + +// GetResult is the response from a Get operation. Call its Extract method to interpret it +// as a Network. +type GetResult struct { + NetworkResult +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls.go new file mode 100644 index 0000000000..2401a5d038 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls.go @@ -0,0 +1,17 @@ +package tenantnetworks + +import "github.com/rackspace/gophercloud" + +const resourcePath = "os-tenant-networks" + +func resourceURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func listURL(c *gophercloud.ServiceClient) string { + return resourceURL(c) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(resourcePath, id) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls_test.go new file mode 100644 index 0000000000..39c464e9fb --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks/urls_test.go @@ -0,0 +1,25 @@ +package tenantnetworks + +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-tenant-networks", listURL(c)) +} + +func TestGetURL(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + c := client.ServiceClient() + id := "1" + + th.CheckEquals(t, c.Endpoint+"os-tenant-networks/"+id, getURL(c, id)) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go index 79709fdbe4..b4ebedea86 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go @@ -54,10 +54,8 @@ func Create(client *gophercloud.ServiceClient, serverId string, opts CreateOptsB return res } - _, res.Err = client.Request("POST", createURL(client, serverId), gophercloud.RequestOpts{ - JSONBody: reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = client.Post(createURL(client, serverId), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -65,18 +63,13 @@ func Create(client *gophercloud.ServiceClient, serverId string, opts CreateOptsB // 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}, - }) + _, res.Err = client.Get(getURL(client, serverId, aId), &res.Body, nil) return res } // Delete requests the deletion of a previous stored VolumeAttachment from the server. func Delete(client *gophercloud.ServiceClient, serverId, aId string) DeleteResult { var res DeleteResult - _, res.Err = client.Request("DELETE", deleteURL(client, serverId, aId), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = client.Delete(deleteURL(client, serverId, aId), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go index 1d33f58ad5..586be67ac2 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go @@ -62,9 +62,7 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat // Get instructs OpenStack to provide details on a single flavor, identified by its ID. // Use ExtractFlavor to convert its result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) GetResult { - var gr GetResult - _, gr.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &gr.Body, - }) - return gr + var res GetResult + _, res.Err = client.Get(getURL(client, id), &res.Body, nil) + return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go index 9e9c3b1d4e..5eb19b5aa3 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go @@ -60,9 +60,6 @@ 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 = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - OkCodes: []int{200}, - }) + _, result.Err = client.Get(getURL(client, id), &result.Body, nil) return result } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go index 6125d530b3..e47bc0e8b0 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go @@ -567,3 +567,87 @@ func HandleMetadataUpdateSuccessfully(t *testing.T) { w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`)) }) } + +// ListAddressesExpected represents an expected repsonse from a ListAddresses request. +var ListAddressesExpected = map[string][]Address{ + "public": []Address{ + Address{ + Version: 4, + Address: "80.56.136.39", + }, + Address{ + Version: 6, + Address: "2001:4800:790e:510:be76:4eff:fe04:82a8", + }, + }, + "private": []Address{ + Address{ + Version: 4, + Address: "10.880.3.154", + }, + }, +} + +// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request. +func HandleAddressListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/asdfasdfasdf/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, `{ + "addresses": { + "public": [ + { + "version": 4, + "addr": "50.56.176.35" + }, + { + "version": 6, + "addr": "2001:4800:780e:510:be76:4eff:fe04:84a8" + } + ], + "private": [ + { + "version": 4, + "addr": "10.180.3.155" + } + ] + } + }`) + }) +} + +// ListNetworkAddressesExpected represents an expected repsonse from a ListAddressesByNetwork request. +var ListNetworkAddressesExpected = []Address{ + Address{ + Version: 4, + Address: "50.56.176.35", + }, + Address{ + Version: 6, + Address: "2001:4800:780e:510:be76:4eff:fe04:84a8", + }, +} + +// HandleNetworkAddressListSuccessfully sets up the test server to respond to a ListAddressesByNetwork request. +func HandleNetworkAddressListSuccessfully(t *testing.T) { + th.Mux.HandleFunc("/servers/asdfasdfasdf/ips/public", 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, `{ + "public": [ + { + "version": 4, + "addr": "50.56.176.35" + }, + { + "version": 6, + "addr": "2001:4800:780e:510:be76:4eff:fe04:84a8" + } + ] + }`) + }) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go index b7c1611d3c..e0950e4e09 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go @@ -216,29 +216,22 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", listURL(client), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - JSONBody: reqBody, - OkCodes: []int{202}, - }) + _, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil) return res } // 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 = client.Request("DELETE", deleteURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = client.Delete(deleteURL(client, id), nil) return res } // Get requests details on a single server, by ID. func Get(client *gophercloud.ServiceClient, id string) GetResult { var result GetResult - _, result.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - OkCodes: []int{200, 203}, + _, result.Err = client.Get(getURL(client, id), &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, }) return result } @@ -280,9 +273,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 = client.Request("PUT", updateURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: opts.ToServerUpdateMap(), + reqBody := opts.ToServerUpdateMap() + _, result.Err = client.Put(updateURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return result } @@ -298,12 +291,7 @@ func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword stri req.ChangePassword.AdminPass = newPassword var res ActionResult - - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: req, - OkCodes: []int{202}, - }) - + _, res.Err = client.Post(actionURL(client, id), req, nil, nil) return res } @@ -367,15 +355,13 @@ func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) Acti return res } - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: struct { - C map[string]string `json:"reboot"` - }{ - map[string]string{"type": string(how)}, - }, - OkCodes: []int{202}, - }) + reqBody := struct { + C map[string]string `json:"reboot"` + }{ + map[string]string{"type": string(how)}, + } + _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) return res } @@ -468,12 +454,7 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuild return result } - _, result.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &result.Body, - OkCodes: []int{202}, - }) - + _, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, nil) return result } @@ -514,11 +495,7 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder return res } - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) return res } @@ -527,11 +504,10 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult { var res ActionResult - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: map[string]interface{}{"confirmResize": nil}, - OkCodes: []int{204}, + reqBody := map[string]interface{}{"confirmResize": nil} + _, res.Err = client.Post(actionURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ + OkCodes: []int{201, 202, 204}, }) - return res } @@ -539,12 +515,8 @@ func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult { // See Resize() for more details. func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult { var res ActionResult - - _, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONBody: map[string]interface{}{"revertResize": nil}, - OkCodes: []int{202}, - }) - + reqBody := map[string]interface{}{"revertResize": nil} + _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) return res } @@ -586,10 +558,8 @@ func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder return result } - _, result.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: &reqBody, - OkCodes: []int{200}, + _, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return result @@ -625,9 +595,8 @@ func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetad res.Err = err return res } - _, res.Err = client.Request("PUT", metadataURL(client, id), gophercloud.RequestOpts{ - JSONBody: metadata, - JSONResponse: &res.Body, + _, res.Err = client.Put(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -635,9 +604,7 @@ 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 = client.Request("GET", metadataURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - }) + _, res.Err = client.Get(metadataURL(client, id), &res.Body, nil) return res } @@ -657,9 +624,8 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet res.Err = err return res } - _, res.Err = client.Request("POST", metadataURL(client, id), gophercloud.RequestOpts{ - JSONBody: metadata, - JSONResponse: &res.Body, + _, res.Err = client.Post(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -695,9 +661,8 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu return res } - _, res.Err = client.Request("PUT", metadatumURL(client, id, key), gophercloud.RequestOpts{ - JSONBody: metadatum, - JSONResponse: &res.Body, + _, res.Err = client.Put(metadatumURL(client, id, key), metadatum, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -714,8 +679,25 @@ 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 = client.Request("DELETE", metadatumURL(client, id, key), gophercloud.RequestOpts{ + _, res.Err = client.Delete(metadatumURL(client, id, key), &gophercloud.RequestOpts{ JSONResponse: &res.Body, }) return res } + +// ListAddresses makes a request against the API to list the servers IP addresses. +func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { + createPageFn := func(r pagination.PageResult) pagination.Page { + return AddressPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(client, listAddressesURL(client, id), createPageFn) +} + +// ListAddressesByNetwork makes a request against the API to list the servers IP addresses +// for the given network. +func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { + createPageFn := func(r pagination.PageResult) pagination.Page { + return NetworkAddressPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), createPageFn) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests_test.go index 017e793ccd..62b89e0c94 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests_test.go @@ -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() @@ -264,3 +277,51 @@ func TestUpdateMetadata(t *testing.T) { th.AssertNoErr(t, err) th.AssertDeepEquals(t, expected, actual) } + +func TestListAddresses(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleAddressListSuccessfully(t) + + expected := ListAddressesExpected + pages := 0 + err := ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := ExtractAddresses(page) + th.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 networks, got %d", len(actual)) + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, pages) +} + +func TestListAddressesByNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleNetworkAddressListSuccessfully(t) + + expected := ListNetworkAddressesExpected + pages := 0 + err := ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := ExtractNetworkAddresses(page) + th.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 addresses, got %d", len(actual)) + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, pages) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go index 1b22f219b2..e2be6baa78 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go @@ -194,7 +194,6 @@ func ExtractServers(page pagination.Page) ([]Server, error) { err = decoder.Decode(casted) - //err := mapstructure.Decode(casted, &response) return response.Servers, err } @@ -272,3 +271,77 @@ func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (inte } return data, nil } + +// Address represents an IP address. +type Address struct { + Version int `mapstructure:"version"` + Address string `mapstructure:"addr"` +} + +// AddressPage abstracts the raw results of making a ListAddresses() request against the API. +// As OpenStack extensions may freely alter the response bodies of structures returned +// to the client, you may only safely access the data provided through the ExtractAddresses call. +type AddressPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if an AddressPage contains no networks. +func (r AddressPage) IsEmpty() (bool, error) { + addresses, err := ExtractAddresses(r) + if err != nil { + return true, err + } + return len(addresses) == 0, nil +} + +// ExtractAddresses interprets the results of a single page from a ListAddresses() call, +// producing a map of addresses. +func ExtractAddresses(page pagination.Page) (map[string][]Address, error) { + casted := page.(AddressPage).Body + + var response struct { + Addresses map[string][]Address `mapstructure:"addresses"` + } + + err := mapstructure.Decode(casted, &response) + if err != nil { + return nil, err + } + + return response.Addresses, err +} + +// NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API. +// As OpenStack extensions may freely alter the response bodies of structures returned +// to the client, you may only safely access the data provided through the ExtractAddresses call. +type NetworkAddressPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a NetworkAddressPage contains no addresses. +func (r NetworkAddressPage) IsEmpty() (bool, error) { + addresses, err := ExtractNetworkAddresses(r) + if err != nil { + return true, err + } + return len(addresses) == 0, nil +} + +// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call, +// producing a slice of addresses. +func ExtractNetworkAddresses(page pagination.Page) ([]Address, error) { + casted := page.(NetworkAddressPage).Body + + var response map[string][]Address + err := mapstructure.Decode(casted, &response) + if err != nil { + return nil, err + } + + var key string + for k := range response { + key = k + } + + return response[key], err +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go index 4bc6586a50..8998354939 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go @@ -37,3 +37,11 @@ func metadatumURL(client *gophercloud.ServiceClient, id, key string) string { func metadataURL(client *gophercloud.ServiceClient, id string) string { return client.ServiceURL("servers", id, "metadata") } + +func listAddressesURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "ips") +} + +func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string { + return client.ServiceURL("servers", id, "ips", network) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location.go index 5a311e4085..29d02c43f9 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location.go @@ -5,9 +5,7 @@ import ( "github.com/rackspace/gophercloud" tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens" - endpoints3 "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints" - services3 "github.com/rackspace/gophercloud/openstack/identity/v3/services" - "github.com/rackspace/gophercloud/pagination" + tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" ) // V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired @@ -52,73 +50,42 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt return "", gophercloud.ErrEndpointNotFound } -// V3EndpointURL discovers the endpoint URL for a specific service using multiple calls against -// an identity v3 service endpoint. The specified EndpointOpts are used to identify a unique, +// V3EndpointURL discovers the endpoint URL for a specific service from a Catalog acquired +// during the v3 identity service. The specified EndpointOpts are used to identify a unique, // unambiguous endpoint to return. It's an error both when multiple endpoints match the provided // criteria and when none do. The minimum that can be specified is a Type, but you will also often // need to specify a Name and/or a Region depending on what's available on your OpenStack // deployment. -func V3EndpointURL(v3Client *gophercloud.ServiceClient, opts gophercloud.EndpointOpts) (string, error) { - // Discover the service we're interested in. - var services = make([]services3.Service, 0, 1) - servicePager := services3.List(v3Client, services3.ListOpts{ServiceType: opts.Type}) - err := servicePager.EachPage(func(page pagination.Page) (bool, error) { - part, err := services3.ExtractServices(page) - if err != nil { - return false, err - } - - for _, service := range part { - if service.Name == opts.Name { - services = append(services, service) +func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpts) (string, error) { + // Extract Endpoints from the catalog entries that match the requested Type, Interface, + // Name if provided, and Region if provided. + var endpoints = make([]tokens3.Endpoint, 0, 1) + for _, entry := range catalog.Entries { + if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) { + for _, endpoint := range entry.Endpoints { + if opts.Availability != gophercloud.AvailabilityAdmin && + opts.Availability != gophercloud.AvailabilityPublic && + opts.Availability != gophercloud.AvailabilityInternal { + return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability) + } + if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && + (opts.Region == "" || endpoint.Region == opts.Region) { + endpoints = append(endpoints, endpoint) + } } } - - return true, nil - }) - if err != nil { - return "", err } - if len(services) == 0 { - return "", gophercloud.ErrServiceNotFound - } - if len(services) > 1 { - return "", fmt.Errorf("Discovered %d matching services: %#v", len(services), services) - } - service := services[0] - - // Enumerate the endpoints available for this service. - var endpoints []endpoints3.Endpoint - endpointPager := endpoints3.List(v3Client, endpoints3.ListOpts{ - Availability: opts.Availability, - ServiceID: service.ID, - }) - err = endpointPager.EachPage(func(page pagination.Page) (bool, error) { - part, err := endpoints3.ExtractEndpoints(page) - if err != nil { - return false, err - } - - for _, endpoint := range part { - if opts.Region == "" || endpoint.Region == opts.Region { - endpoints = append(endpoints, endpoint) - } - } - - return true, nil - }) - if err != nil { - return "", err - } - - if len(endpoints) == 0 { - return "", gophercloud.ErrEndpointNotFound - } + // Report an error if the options were ambiguous. if len(endpoints) > 1 { return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints) } - endpoint := endpoints[0] - return gophercloud.NormalizeURL(endpoint.URL), nil + // Extract the URL from the matching Endpoint. + for _, endpoint := range endpoints { + return gophercloud.NormalizeURL(endpoint.URL), nil + } + + // Report an error if there were no matching endpoints. + return "", gophercloud.ErrEndpointNotFound } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location_test.go index 4e0569ac1f..8e65918abf 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/endpoint_location_test.go @@ -1,15 +1,13 @@ package openstack import ( - "fmt" - "net/http" "strings" "testing" "github.com/rackspace/gophercloud" tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens" + tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" th "github.com/rackspace/gophercloud/testhelper" - fake "github.com/rackspace/gophercloud/testhelper/client" ) // Service catalog fixtures take too much vertical space! @@ -107,119 +105,124 @@ func TestV2EndpointBadAvailability(t *testing.T) { Region: "same", Availability: "wat", }) - th.CheckEquals(t, err.Error(), "Unexpected availability in endpoint query: wat") + th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error()) } -func setupV3Responses(t *testing.T) { - // Mock the service query. - th.Mux.HandleFunc("/services", 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") - fmt.Fprintf(w, ` - { - "links": { - "next": null, - "previous": null +var catalog3 = tokens3.ServiceCatalog{ + Entries: []tokens3.CatalogEntry{ + tokens3.CatalogEntry{ + Type: "same", + Name: "same", + Endpoints: []tokens3.Endpoint{ + tokens3.Endpoint{ + ID: "1", + Region: "same", + Interface: "public", + URL: "https://public.correct.com/", }, - "services": [ - { - "description": "Correct", - "id": "1234", - "name": "same", - "type": "same" - }, - { - "description": "Bad Name", - "id": "9876", - "name": "different", - "type": "same" - } - ] - } - `) - }) - - // Mock the endpoint query. - th.Mux.HandleFunc("/endpoints", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - th.TestFormValues(t, r, map[string]string{ - "service_id": "1234", - "interface": "public", - }) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ` - { - "endpoints": [ - { - "id": "12", - "interface": "public", - "name": "the-right-one", - "region": "same", - "service_id": "1234", - "url": "https://correct:9000/" - }, - { - "id": "14", - "interface": "public", - "name": "bad-region", - "region": "different", - "service_id": "1234", - "url": "https://bad-region:9001/" - } - ], - "links": { - "next": null, - "previous": null - } - } - `) - }) + tokens3.Endpoint{ + ID: "2", + Region: "same", + Interface: "admin", + URL: "https://admin.correct.com/", + }, + tokens3.Endpoint{ + ID: "3", + Region: "same", + Interface: "internal", + URL: "https://internal.correct.com/", + }, + tokens3.Endpoint{ + ID: "4", + Region: "different", + Interface: "public", + URL: "https://badregion.com/", + }, + }, + }, + tokens3.CatalogEntry{ + Type: "same", + Name: "different", + Endpoints: []tokens3.Endpoint{ + tokens3.Endpoint{ + ID: "5", + Region: "same", + Interface: "public", + URL: "https://badname.com/", + }, + tokens3.Endpoint{ + ID: "6", + Region: "different", + Interface: "public", + URL: "https://badname.com/+badregion", + }, + }, + }, + tokens3.CatalogEntry{ + Type: "different", + Name: "different", + Endpoints: []tokens3.Endpoint{ + tokens3.Endpoint{ + ID: "7", + Region: "same", + Interface: "public", + URL: "https://badtype.com/+badname", + }, + tokens3.Endpoint{ + ID: "8", + Region: "different", + Interface: "public", + URL: "https://badtype.com/+badregion+badname", + }, + }, + }, + }, } func TestV3EndpointExact(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - setupV3Responses(t) + expectedURLs := map[gophercloud.Availability]string{ + gophercloud.AvailabilityPublic: "https://public.correct.com/", + gophercloud.AvailabilityAdmin: "https://admin.correct.com/", + gophercloud.AvailabilityInternal: "https://internal.correct.com/", + } - actual, err := V3EndpointURL(fake.ServiceClient(), gophercloud.EndpointOpts{ + for availability, expected := range expectedURLs { + actual, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ + Type: "same", + Name: "same", + Region: "same", + Availability: availability, + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, expected, actual) + } +} + +func TestV3EndpointNone(t *testing.T) { + _, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ + Type: "nope", + Availability: gophercloud.AvailabilityPublic, + }) + th.CheckEquals(t, gophercloud.ErrEndpointNotFound, err) +} + +func TestV3EndpointMultiple(t *testing.T) { + _, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ + Type: "same", + Region: "same", + Availability: gophercloud.AvailabilityPublic, + }) + if !strings.HasPrefix(err.Error(), "Discovered 2 matching endpoints:") { + t.Errorf("Received unexpected error: %v", err) + } +} + +func TestV3EndpointBadAvailability(t *testing.T) { + _, err := V3EndpointURL(&catalog3, gophercloud.EndpointOpts{ Type: "same", Name: "same", Region: "same", - Availability: gophercloud.AvailabilityPublic, + Availability: "wat", }) - th.AssertNoErr(t, err) - th.CheckEquals(t, actual, "https://correct:9000/") -} - -func TestV3EndpointNoService(t *testing.T) { - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/services", 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") - fmt.Fprintf(w, ` - { - "links": { - "next": null, - "previous": null - }, - "services": [] - } - `) - }) - - _, err := V3EndpointURL(fake.ServiceClient(), gophercloud.EndpointOpts{ - Type: "nope", - Name: "same", - Region: "same", - Availability: gophercloud.AvailabilityPublic, - }) - th.CheckEquals(t, gophercloud.ErrServiceNotFound, err) + th.CheckEquals(t, "Unexpected availability in endpoint query: wat", err.Error()) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles/requests.go index bbdf76a46c..9a333140b2 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles/requests.go @@ -19,11 +19,7 @@ func List(client *gophercloud.ServiceClient) pagination.Pager { // ID is a required argument. func AddUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult { var result UserRoleResult - - _, result.Err = client.Request("PUT", userRoleURL(client, tenantID, userID, roleID), gophercloud.RequestOpts{ - OkCodes: []int{200, 201}, - }) - + _, result.Err = client.Put(userRoleURL(client, tenantID, userID, roleID), nil, nil, nil) return result } @@ -32,10 +28,6 @@ func AddUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID str // tenant ID is a required argument. func DeleteUserRole(client *gophercloud.ServiceClient, tenantID, userID, roleID string) UserRoleResult { var result UserRoleResult - - _, result.Err = client.Request("DELETE", userRoleURL(client, tenantID, userID, roleID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) - + _, result.Err = client.Delete(userRoleURL(client, tenantID, userID, roleID), nil) return result } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go index db1ac8284f..efa054fb39 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go @@ -75,10 +75,8 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) CreateRe } var result CreateResult - _, result.Err = client.Request("POST", CreateURL(client), gophercloud.RequestOpts{ - JSONBody: &request, - JSONResponse: &result.Body, - OkCodes: []int{200, 203}, + _, result.Err = client.Post(CreateURL(client), request, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, }) return result } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/users/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/users/requests.go index 2afe62a613..88be45ecc0 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/users/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v2/users/requests.go @@ -90,10 +90,8 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes return res } - _, res.Err = client.Request("POST", rootURL(client), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - JSONBody: reqBody, - OkCodes: []int{200, 201}, + _, res.Err = client.Post(rootURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res @@ -102,12 +100,7 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes // Get requests details on a single user, either by ID. func Get(client *gophercloud.ServiceClient, id string) GetResult { var result GetResult - - _, result.Err = client.Request("GET", ResourceURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - OkCodes: []int{200}, - }) - + _, result.Err = client.Get(ResourceURL(client, id), &result.Body, nil) return result } @@ -145,24 +138,17 @@ func (opts UpdateOpts) ToUserUpdateMap() map[string]interface{} { // Update is the operation responsible for updating exist users by their UUID. func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult { var result UpdateResult - - _, result.Err = client.Request("PUT", ResourceURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - JSONBody: opts.ToUserUpdateMap(), - OkCodes: []int{200}, + reqBody := opts.ToUserUpdateMap() + _, result.Err = client.Put(ResourceURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) - return result } // Delete is the operation responsible for permanently deleting an API user. func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { var result DeleteResult - - _, result.Err = client.Request("DELETE", ResourceURL(client, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) - + _, result.Err = client.Delete(ResourceURL(client, id), nil) return result } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/endpoints/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/endpoints/requests.go index 3e09b2aef0..99a495d594 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/endpoints/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/endpoints/requests.go @@ -56,11 +56,7 @@ func Create(client *gophercloud.ServiceClient, opts EndpointOpts) CreateResult { reqBody.Endpoint.Region = gophercloud.MaybeString(opts.Region) var result CreateResult - _, result.Err = client.Request("POST", listURL(client), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &result.Body, - OkCodes: []int{201}, - }) + _, result.Err = client.Post(listURL(client), reqBody, &result.Body, nil) return result } @@ -122,8 +118,6 @@ 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 = client.Request("DELETE", endpointURL(client, endpointID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = client.Delete(endpointURL(client, endpointID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/services/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/services/requests.go index c6820c700b..3ee924f3ee 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/services/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/services/requests.go @@ -18,11 +18,7 @@ func Create(client *gophercloud.ServiceClient, serviceType string) CreateResult req := request{Type: serviceType} var result CreateResult - _, result.Err = client.Request("POST", listURL(client), gophercloud.RequestOpts{ - JSONBody: &req, - JSONResponse: &result.Body, - OkCodes: []int{201}, - }) + _, result.Err = client.Post(listURL(client), req, &result.Body, nil) return result } @@ -51,10 +47,7 @@ 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 = client.Request("GET", serviceURL(client, serviceID), gophercloud.RequestOpts{ - JSONResponse: &result.Body, - OkCodes: []int{200}, - }) + _, result.Err = client.Get(serviceURL(client, serviceID), &result.Body, nil) return result } @@ -79,8 +72,6 @@ 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 = client.Request("DELETE", serviceURL(client, serviceID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = client.Delete(serviceURL(client, serviceID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go index bbd3c56231..d449ca36e8 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go @@ -235,11 +235,7 @@ func Create(c *gophercloud.ServiceClient, options gophercloud.AuthOptions, scope var result CreateResult var response *http.Response - response, result.Err = c.Request("POST", tokenURL(c), gophercloud.RequestOpts{ - JSONBody: &req, - JSONResponse: &result.Body, - OkCodes: []int{201}, - }) + response, result.Err = c.Post(tokenURL(c), req, &result.Body, nil) if result.Err != nil { return result } @@ -251,10 +247,9 @@ func Create(c *gophercloud.ServiceClient, options gophercloud.AuthOptions, scope func Get(c *gophercloud.ServiceClient, token string) GetResult { var result GetResult var response *http.Response - response, result.Err = c.Request("GET", tokenURL(c), gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - JSONResponse: &result.Body, - OkCodes: []int{200, 203}, + response, result.Err = c.Get(tokenURL(c), &result.Body, &gophercloud.RequestOpts{ + MoreHeaders: subjectTokenHeaders(c, token), + OkCodes: []int{200, 203}, }) if result.Err != nil { return result @@ -279,9 +274,8 @@ 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 = c.Request("DELETE", tokenURL(c), gophercloud.RequestOpts{ + _, res.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ MoreHeaders: subjectTokenHeaders(c, token), - OkCodes: []int{204}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go index d1fff4c2a5..d134f7d4d0 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go @@ -7,13 +7,58 @@ import ( "github.com/rackspace/gophercloud" ) +// Endpoint represents a single API endpoint offered by a service. +// It matches either a public, internal or admin URL. +// If supported, it contains a region specifier, again if provided. +// The significance of the Region field will depend upon your provider. +type Endpoint struct { + ID string `mapstructure:"id"` + Region string `mapstructure:"region"` + Interface string `mapstructure:"interface"` + URL string `mapstructure:"url"` +} + +// CatalogEntry provides a type-safe interface to an Identity API V3 service catalog listing. +// Each class of service, such as cloud DNS or block storage services, could have multiple +// CatalogEntry representing it (one by interface type, e.g public, admin or internal). +// +// Note: when looking for the desired service, try, whenever possible, to key off the type field. +// Otherwise, you'll tie the representation of the service to a specific provider. +type CatalogEntry struct { + + // Service ID + ID string `mapstructure:"id"` + + // Name will contain the provider-specified name for the service. + Name string `mapstructure:"name"` + + // Type will contain a type string if OpenStack defines a type for the service. + // Otherwise, for provider-specific services, the provider may assign their own type strings. + Type string `mapstructure:"type"` + + // Endpoints will let the caller iterate over all the different endpoints that may exist for + // the service. + Endpoints []Endpoint `mapstructure:"endpoints"` +} + +// ServiceCatalog provides a view into the service catalog from a previous, successful authentication. +type ServiceCatalog struct { + Entries []CatalogEntry +} + // commonResult is the deferred result of a Create or a Get call. type commonResult struct { gophercloud.Result } -// Extract interprets a commonResult as a Token. +// Extract is a shortcut for ExtractToken. +// This function is deprecated and still present for backward compatibility. func (r commonResult) Extract() (*Token, error) { + return r.ExtractToken() +} + +// ExtractToken interprets a commonResult as a Token. +func (r commonResult) ExtractToken() (*Token, error) { if r.Err != nil { return nil, r.Err } @@ -40,7 +85,28 @@ func (r commonResult) Extract() (*Token, error) { return &token, err } -// CreateResult is the deferred response from a Create call. +// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. +func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { + if result.Err != nil { + return nil, result.Err + } + + var response struct { + Token struct { + Entries []CatalogEntry `mapstructure:"catalog"` + } `mapstructure:"token"` + } + + err := mapstructure.Decode(result.Body, &response) + if err != nil { + return nil, err + } + + return &ServiceCatalog{Entries: response.Token.Entries}, nil +} + +// CreateResult defers the interpretation of a created token. +// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog. type CreateResult struct { commonResult } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/delegate_test.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/doc.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/requests.go index 2f04593db9..097ae37f24 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/requests.go @@ -1,6 +1,10 @@ package external -import "github.com/rackspace/gophercloud/openstack/networking/v2/networks" +import ( + "time" + + "github.com/rackspace/gophercloud/openstack/networking/v2/networks" +) // 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. @@ -25,6 +29,15 @@ type CreateOpts struct { // ToNetworkCreateMap casts a CreateOpts struct to a map. func (o CreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) { + + // DO NOT REMOVE. Though this line seemingly does nothing of value, it is a + // splint to prevent the unit test from failing on Go Tip. We suspect it is a + // compiler issue that will hopefully be worked out prior to our next release. + // Again, for all the unit tests to pass, this line is necessary and sufficient + // at the moment. We should reassess after the Go 1.5 release to determine + // if this line is still needed. + time.Sleep(0 * time.Millisecond) + outer, err := o.Parent.ToNetworkCreateMap() if err != nil { return nil, err diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results.go index 1c173c07a3..54dbf4bb69 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/external/results.go @@ -68,7 +68,7 @@ func ExtractUpdate(r networks.UpdateResult) (*NetworkExternal, error) { } // ExtractList accepts a Page struct, specifically a NetworkPage struct, and -// extracts the elements into a slice of NetworkExtAttrs structs. In other +// extracts the elements into a slice of NetworkExternal structs. In other // words, a generic collection is mapped into a relevant slice. func ExtractList(page pagination.Page) ([]NetworkExternal, error) { var resp struct { diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests.go index 69f3dcad13..12d587f389 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls/requests.go @@ -139,21 +139,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -209,10 +202,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd } // Send request to API - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -220,8 +211,6 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd // 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}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests.go index 95081dfa03..fe07d9abb1 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies/requests.go @@ -128,21 +128,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -198,10 +191,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd } // Send request to API - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -209,9 +200,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd // 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}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } @@ -230,10 +219,8 @@ func InsertRule(c *gophercloud.ServiceClient, policyID, ruleID, beforeID, afterI // 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}, + _, res.Err = c.Put(insertURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res.Err } @@ -249,10 +236,8 @@ func RemoveRule(c *gophercloud.ServiceClient, policyID, ruleID string) error { // 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}, + _, res.Err = c.Put(removeURL(c, policyID), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res.Err } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests.go index 37801068bc..57a0e8baff 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules/requests.go @@ -163,21 +163,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -277,10 +270,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd } // Send request to API - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -289,8 +280,6 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd // 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}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 46f2b22cb1..49d6f0b7a5 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -107,23 +107,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { TenantID: opts.TenantID, }} - // Send request to API - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // 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 = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -159,10 +150,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu // Send request to API var res UpdateResult - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -173,8 +162,6 @@ 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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go old mode 100644 new mode 100755 index 12640dee1e..077a71755a --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -81,21 +81,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { } var res CreateResult - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular router 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -133,10 +126,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu // Send request to API var res UpdateResult - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -145,9 +136,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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } @@ -197,10 +186,8 @@ func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) I body := request{SubnetID: opts.SubnetID, PortID: opts.PortID} - _, res.Err = c.Request("PUT", addInterfaceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &body, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(addInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -229,10 +216,8 @@ func RemoveInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts body := request{SubnetID: opts.SubnetID, PortID: opts.PortID} - _, res.Err = c.Request("PUT", removeInterfaceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &body, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(removeInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests_test.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers/results.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests.go index 023a04dc04..848938f983 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members/requests.go @@ -79,21 +79,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { }} var res CreateResult - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular pool member 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -116,10 +109,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu // Send request to API var res UpdateResult - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, }) return res } @@ -127,8 +118,6 @@ 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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests.go index de6f68862f..71b21ef16e 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors/requests.go @@ -176,22 +176,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { AdminStateUp: opts.AdminStateUp, }} - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular health monitor 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -258,10 +250,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu AdminStateUp: opts.AdminStateUp, }} - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 202}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, }) return res @@ -270,8 +260,6 @@ 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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests.go index e7e6d944d0..2bb0acc447 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools/requests.go @@ -99,21 +99,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { }} var res CreateResult - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular pool 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -145,10 +138,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu // Send request to API var res UpdateResult - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -156,9 +147,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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } @@ -178,11 +167,7 @@ func AssociateMonitor(c *gophercloud.ServiceClient, poolID, monitorID string) As reqBody := request{hm{ID: monitorID}} var res AssociateResult - _, res.Err = c.Request("POST", associateURL(c, poolID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(associateURL(c, poolID), reqBody, &res.Body, nil) return res } @@ -191,8 +176,6 @@ 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 = c.Request("DELETE", disassociateURL(c, poolID, monitorID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(disassociateURL(c, poolID, monitorID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests.go index 5b0bfd9a0b..6216f873e3 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips/requests.go @@ -178,22 +178,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { reqBody.VirtualIP.Persistence = opts.Persistence } - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular virtual IP 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } @@ -249,10 +241,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu } var res UpdateResult - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 202}, + _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 202}, }) return res @@ -261,8 +251,6 @@ 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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/doc.go old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results.go old mode 100644 new mode 100755 index 3453584587..f07d6285db --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results.go @@ -73,7 +73,7 @@ func ExtractGet(r networks.GetResult) (*NetworkExtAttrs, error) { Network *NetworkExtAttrs `json:"network"` } - err := mapstructure.Decode(r.Body, &res) + err := mapstructure.WeakDecode(r.Body, &res) return res.Network, err } @@ -89,7 +89,7 @@ func ExtractCreate(r networks.CreateResult) (*NetworkExtAttrs, error) { Network *NetworkExtAttrs `json:"network"` } - err := mapstructure.Decode(r.Body, &res) + err := mapstructure.WeakDecode(r.Body, &res) return res.Network, err } @@ -105,7 +105,7 @@ func ExtractUpdate(r networks.UpdateResult) (*NetworkExtAttrs, error) { Network *NetworkExtAttrs `json:"network"` } - err := mapstructure.Decode(r.Body, &res) + err := mapstructure.WeakDecode(r.Body, &res) return res.Network, err } @@ -118,7 +118,7 @@ func ExtractList(page pagination.Page) ([]NetworkExtAttrs, error) { Networks []NetworkExtAttrs `mapstructure:"networks" json:"networks"` } - err := mapstructure.Decode(page.(networks.NetworkPage).Body, &resp) + err := mapstructure.WeakDecode(page.(networks.NetworkPage).Body, &resp) return resp.Networks, err } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results_test.go index 9801b2e5e3..80816926da 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/provider/results_test.go @@ -49,7 +49,7 @@ func TestList(t *testing.T) { "tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e", "shared": true, "id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324", - "provider:segmentation_id": null, + "provider:segmentation_id": 1234567890, "provider:physical_network": null, "provider:network_type": "local" } @@ -91,7 +91,7 @@ func TestList(t *testing.T) { ID: "db193ab3-96e3-4cb3-8fc5-05f4296d0324", NetworkType: "local", PhysicalNetwork: "", - SegmentationID: "", + SegmentationID: "1234567890", }, } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/doc.go index 8ef455ffb3..31f744ccd7 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/doc.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/doc.go @@ -15,13 +15,13 @@ // // For ingress traffic (to an instance) // - Only traffic matched with security group rules are allowed. -// - When there is no rule defined, all traffic are dropped. +// - When there is no rule defined, all traffic is dropped. // // For egress traffic (from an instance) // - Only traffic matched with security group rules are allowed. // - When there is no rule defined, all egress traffic are dropped. // - When a new security group is created, rules to allow all egress traffic -// are automatically added. +// is automatically added. // // "default security group" is defined for each tenant. // - For the default security group a rule which allows intercommunication diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go index c07508bd7b..55e4b3b804 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go @@ -74,30 +74,20 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { Description: opts.Description, }} - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular security group 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } // 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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go index 108acf670d..0b2d10b0ef 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go @@ -150,30 +150,20 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult { RemoteIPPrefix: opts.RemoteIPPrefix, }} - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } // Get retrieves a particular security group 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}, - }) + _, res.Err = c.Get(resourceURL(c, id), &res.Body, nil) return res } // 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 = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests.go index b0db67e7d0..7be3227400 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/networks/requests.go @@ -79,10 +79,7 @@ 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 = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) return res } @@ -134,12 +131,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - // Send request to API - _, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil) return res } @@ -184,10 +176,8 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild } // Send request to API - _, res.Err = c.Request("PUT", updateURL(c, networkID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 201}, + _, res.Err = c.Put(updateURL(c, networkID), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res @@ -196,8 +186,6 @@ 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 = c.Request("DELETE", deleteURL(c, networkID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(deleteURL(c, networkID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go index 01d550fc1a..781a3c3e74 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/ports/requests.go @@ -79,10 +79,7 @@ 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 = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) return res } @@ -155,13 +152,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - // Response - _, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil) return res } @@ -220,10 +211,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd return res } - _, res.Err = c.Request("PUT", updateURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 201}, + _, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res } @@ -231,8 +220,6 @@ 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 = c.Request("DELETE", deleteURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(deleteURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests.go index 63ac2901b7..6e01f059d7 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/requests.go @@ -78,10 +78,7 @@ 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 = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) return res } @@ -171,12 +168,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - _, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) - + _, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil) return res } @@ -229,10 +221,8 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) Upd return res } - _, res.Err = c.Request("PUT", updateURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 201}, + _, res.Err = c.Put(updateURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201}, }) return res @@ -241,8 +231,6 @@ 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 = c.Request("DELETE", deleteURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(deleteURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts/requests.go index 3e404c3b6b..a645115705 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts/requests.go @@ -95,7 +95,7 @@ func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) UpdateResult { resp, err := c.Request("POST", updateURL(c), gophercloud.RequestOpts{ MoreHeaders: h, - OkCodes: []int{204}, + OkCodes: []int{201, 202, 204}, }) res.Header = resp.Header res.Err = err diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/fixtures.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/fixtures.go index 9c84bce0a4..e60735248f 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/fixtures.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/fixtures.go @@ -55,6 +55,14 @@ func HandleListContainerInfoSuccessfully(t *testing.T) { "name": "marktwain" } ]`) + case "janeausten": + fmt.Fprintf(w, `[ + { + "count": 1, + "bytes": 14, + "name": "marktwain" + } + ]`) case "marktwain": fmt.Fprintf(w, `[]`) default: @@ -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: diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests.go index a29d7da5d6..bbf8cdb952 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests.go @@ -122,9 +122,7 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", deleteURL(c, containerName), gophercloud.RequestOpts{ - OkCodes: []int{202, 204}, - }) + _, res.Err = c.Delete(deleteURL(c, containerName), nil) return res } @@ -180,7 +178,7 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB resp, err := c.Request("POST", updateURL(c, containerName), gophercloud.RequestOpts{ MoreHeaders: h, - OkCodes: []int{202, 204}, + OkCodes: []int{201, 202, 204}, }) res.Header = resp.Header res.Err = err diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests_test.go index f650696e48..0ccd5a7786 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers/requests_test.go @@ -29,6 +29,18 @@ func TestListContainerInfo(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListAllContainerInfo(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListContainerInfoSuccessfully(t) + + allPages, err := List(fake.ServiceClient(), &ListOpts{Full: true}).AllPages() + th.AssertNoErr(t, err) + actual, err := ExtractInfo(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedListInfo, actual) +} + func TestListContainerNames(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() @@ -51,6 +63,18 @@ func TestListContainerNames(t *testing.T) { th.CheckEquals(t, count, 1) } +func TestListAllContainerNames(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + HandleListContainerNamesSuccessfully(t) + + allPages, err := List(fake.ServiceClient(), &ListOpts{Full: false}).AllPages() + th.AssertNoErr(t, err) + actual, err := ExtractNames(allPages) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, ExpectedListNames, actual) +} + func TestCreateContainer(t *testing.T) { th.SetupHTTP() defer th.TeardownHTTP() diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects/requests.go index 30ea94cc2d..7eedde2550 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects/requests.go @@ -211,7 +211,6 @@ func Create(c *gophercloud.ServiceClient, containerName, objectName string, cont ropts := gophercloud.RequestOpts{ RawBody: content, MoreHeaders: h, - OkCodes: []int{201, 202}, } resp, err := c.Request("PUT", url, ropts) @@ -310,9 +309,7 @@ func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts url += query } - resp, err := c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + resp, err := c.Delete(url, nil) res.Header = resp.Header res.Err = err return res @@ -412,7 +409,6 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts url := updateURL(c, containerName, objectName) resp, err := c.Request("POST", url, gophercloud.RequestOpts{ MoreHeaders: h, - OkCodes: []int{202}, }) res.Header = resp.Header res.Err = err diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo/requests.go index 379f34f30b..9e03e5cc85 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/buildinfo/requests.go @@ -5,9 +5,6 @@ import "github.com/rackspace/gophercloud" // Get retreives data for the given stack template. func Get(c *gophercloud.ServiceClient) GetResult { var res GetResult - _, res.Err = c.Request("GET", getURL(c), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c), &res.Body, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/requests.go index 37eab1e763..53c3916020 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/requests.go @@ -11,7 +11,6 @@ func Find(c *gophercloud.ServiceClient, stackName string) FindResult { _, res.Err = c.Request("GET", findURL(c, stackName), gophercloud.RequestOpts{ JSONResponse: &res.Body, - OkCodes: []int{200}, }) return res } @@ -197,9 +196,8 @@ func ListResourceEvents(client *gophercloud.ServiceClient, stackName, stackID, r // Get retreives data for the given stack resource. func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName, eventID string) GetResult { var res GetResult - _, res.Err = c.Request("GET", getURL(c, stackName, stackID, resourceName, eventID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Get(getURL(c, stackName, stackID, resourceName, eventID), &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/results.go index bf233ae2ad..3c8f1da491 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackevents/results.go @@ -1,6 +1,8 @@ package stackevents import ( + "fmt" + "reflect" "time" "github.com/mitchellh/mapstructure" @@ -106,7 +108,15 @@ func ExtractEvents(page pagination.Page) ([]Event, error) { return nil, err } - events := casted.(map[string]interface{})["events"].([]interface{}) + var events []interface{} + switch casted.(type) { + case map[string]interface{}: + events = casted.(map[string]interface{})["events"].([]interface{}) + case map[string][]interface{}: + events = casted.(map[string][]interface{})["events"] + default: + return res.Res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } for i, eventRaw := range events { event := eventRaw.(map[string]interface{}) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/requests.go index 2a66edc8c2..ee9c3c250c 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/requests.go @@ -12,7 +12,6 @@ func Find(c *gophercloud.ServiceClient, stackName string) FindResult { // Send request to API _, res.Err = c.Request("GET", findURL(c, stackName), gophercloud.RequestOpts{ JSONResponse: &res.Body, - OkCodes: []int{200}, }) return res } @@ -71,9 +70,8 @@ func Get(c *gophercloud.ServiceClient, stackName, stackID, resourceName string) var res GetResult // Send request to API - _, res.Err = c.Request("GET", getURL(c, stackName, stackID, resourceName), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Get(getURL(c, stackName, stackID, resourceName), &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -83,9 +81,8 @@ func Metadata(c *gophercloud.ServiceClient, stackName, stackID, resourceName str var res MetadataResult // Send request to API - _, res.Err = c.Request("GET", metadataURL(c, stackName, stackID, resourceName), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Get(metadataURL(c, stackName, stackID, resourceName), &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -106,9 +103,8 @@ func Schema(c *gophercloud.ServiceClient, resourceType string) SchemaResult { var res SchemaResult // Send request to API - _, res.Err = c.Request("GET", schemaURL(c, resourceType), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Get(schemaURL(c, resourceType), &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -118,9 +114,8 @@ func Template(c *gophercloud.ServiceClient, resourceType string) TemplateResult var res TemplateResult // Send request to API - _, res.Err = c.Request("GET", templateURL(c, resourceType), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Get(templateURL(c, resourceType), &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/results.go index 13f5dd21fb..69f21daef3 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stackresources/results.go @@ -1,6 +1,8 @@ package stackresources import ( + "fmt" + "reflect" "time" "github.com/mitchellh/mapstructure" @@ -94,7 +96,15 @@ func ExtractResources(page pagination.Page) ([]Resource, error) { } err := mapstructure.Decode(casted, &response) - resources := casted.(map[string]interface{})["resources"].([]interface{}) + var resources []interface{} + switch casted.(type) { + case map[string]interface{}: + resources = casted.(map[string]interface{})["resources"].([]interface{}) + case map[string][]interface{}: + resources = casted.(map[string][]interface{})["resources"] + default: + return response.Resources, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } for i, resourceRaw := range resources { resource := resourceRaw.(map[string]interface{}) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/requests.go index c0388c366d..0dd6af2cfa 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/requests.go @@ -111,12 +111,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - // Send request to API - _, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil) return res } @@ -221,12 +216,7 @@ func Adopt(c *gophercloud.ServiceClient, opts AdoptOptsBuilder) AdoptResult { return res } - // Send request to API - _, res.Err = c.Request("POST", adoptURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{201}, - }) + _, res.Err = c.Post(adoptURL(c), reqBody, &res.Body, nil) return res } @@ -302,12 +292,7 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retreives a stack based on the stack name and stack ID. func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult { var res GetResult - - // Send request to API - _, res.Err = c.Request("GET", getURL(c, stackName, stackID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c, stackName, stackID), &res.Body, nil) return res } @@ -388,22 +373,14 @@ func Update(c *gophercloud.ServiceClient, stackName, stackID string, opts Update return res } - // Send request to API - _, res.Err = c.Request("PUT", updateURL(c, stackName, stackID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) + _, res.Err = c.Put(updateURL(c, stackName, stackID), reqBody, nil, nil) return res } // Delete deletes a stack based on the stack name and stack ID. func Delete(c *gophercloud.ServiceClient, stackName, stackID string) DeleteResult { var res DeleteResult - - // Send request to API - _, res.Err = c.Request("DELETE", deleteURL(c, stackName, stackID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(deleteURL(c, stackName, stackID), nil) return res } @@ -498,10 +475,8 @@ func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) PreviewResul } // Send request to API - _, res.Err = c.Request("POST", previewURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Post(previewURL(c), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } @@ -510,9 +485,7 @@ func Preview(c *gophercloud.ServiceClient, opts PreviewOptsBuilder) PreviewResul // resources intact, and returns data describing the stack and its resources. func Abandon(c *gophercloud.ServiceClient, stackName, stackID string) AbandonResult { var res AbandonResult - - // Send request to API - _, res.Err = c.Request("DELETE", abandonURL(c, stackName, stackID), gophercloud.RequestOpts{ + _, res.Err = c.Delete(abandonURL(c, stackName, stackID), &gophercloud.RequestOpts{ JSONResponse: &res.Body, OkCodes: []int{200}, }) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/results.go index ff971e8b8b..04d3f8ea96 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/results.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/results.go @@ -2,6 +2,8 @@ package stacks import ( "encoding/json" + "fmt" + "reflect" "time" "github.com/mitchellh/mapstructure" @@ -73,6 +75,8 @@ type ListedStack struct { // ExtractStacks extracts and returns a slice of ListedStack. It is used while iterating // over a stacks.List call. func ExtractStacks(page pagination.Page) ([]ListedStack, error) { + casted := page.(StackPage).Body + var res struct { Stacks []ListedStack `mapstructure:"stacks"` } @@ -82,7 +86,16 @@ func ExtractStacks(page pagination.Page) ([]ListedStack, error) { return nil, err } - rawStacks := (((page.(StackPage).Body).(map[string]interface{}))["stacks"]).([]interface{}) + var rawStacks []interface{} + switch casted.(type) { + case map[string]interface{}: + rawStacks = casted.(map[string]interface{})["stacks"].([]interface{}) + case map[string][]interface{}: + rawStacks = casted.(map[string][]interface{})["stacks"] + default: + return res.Stacks, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } + for i := range rawStacks { thisStack := (rawStacks[i]).(map[string]interface{}) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates/requests.go index f57e226efb..ad1e468d19 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacktemplates/requests.go @@ -11,7 +11,6 @@ func Get(c *gophercloud.ServiceClient, stackName, stackID string) GetResult { var res GetResult _, res.Err = c.Request("GET", getURL(c, stackName, stackID), gophercloud.RequestOpts{ JSONResponse: &res.Body, - OkCodes: []int{200}, }) return res } @@ -52,10 +51,8 @@ func Validate(c *gophercloud.ServiceClient, opts ValidateOptsBuilder) ValidateRe return res } - _, res.Err = c.Request("POST", validateURL(c), gophercloud.RequestOpts{ - JSONBody: reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Post(validateURL(c), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked.go index 461fa499af..e9bd8dec97 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked.go @@ -59,3 +59,9 @@ func (current LinkedPageBase) NextPageURL() (string, error) { } } } + +// GetBody returns the linked page's body. This method is needed to satisfy the +// Page interface. +func (current LinkedPageBase) GetBody() interface{} { + return current.Body +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked_test.go index 4d3248e6ac..1ac0f73164 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/linked_test.go @@ -105,3 +105,16 @@ func TestEnumerateLinked(t *testing.T) { t.Errorf("Expected 3 calls, but was %d", callCount) } } + +func TestAllPagesLinked(t *testing.T) { + pager := createLinked(t) + defer testhelper.TeardownHTTP() + + page, err := pager.AllPages() + testhelper.AssertNoErr(t, err) + + expected := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + actual, err := ExtractLinkedInts(page) + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, expected, actual) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker.go index e7688c217b..f355afc54b 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker.go @@ -32,3 +32,9 @@ func (current MarkerPageBase) NextPageURL() (string, error) { return currentURL.String(), nil } + +// GetBody returns the linked page's body. This method is needed to satisfy the +// Page interface. +func (current MarkerPageBase) GetBody() interface{} { + return current.Body +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker_test.go index 3b1df1d68b..f4d55be810 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/marker_test.go @@ -111,3 +111,16 @@ func TestEnumerateMarker(t *testing.T) { testhelper.AssertNoErr(t, err) testhelper.AssertEquals(t, callCount, 3) } + +func TestAllPagesMarker(t *testing.T) { + pager := createMarkerPaged(t) + defer testhelper.TeardownHTTP() + + page, err := pager.AllPages() + testhelper.AssertNoErr(t, err) + + expected := []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii"} + actual, err := ExtractMarkerStrings(page) + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, expected, actual) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/pager.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/pager.go index 5c20e16c6c..ea47c695dc 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/pager.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/pager.go @@ -2,6 +2,10 @@ package pagination import ( "errors" + "fmt" + "net/http" + "reflect" + "strings" "github.com/rackspace/gophercloud" ) @@ -25,6 +29,9 @@ type Page interface { // IsEmpty returns true if this Page has no items in it. IsEmpty() (bool, error) + + // GetBody returns the Page Body. This is used in the `AllPages` method. + GetBody() interface{} } // Pager knows how to advance through a specific resource collection, one page at a time. @@ -113,3 +120,105 @@ func (p Pager) EachPage(handler func(Page) (bool, error)) error { } } } + +// AllPages returns all the pages from a `List` operation in a single page, +// allowing the user to retrieve all the pages at once. +func (p Pager) AllPages() (Page, error) { + // pagesSlice holds all the pages until they get converted into as Page Body. + var pagesSlice []interface{} + // body will contain the final concatenated Page body. + var body reflect.Value + + // Grab a test page to ascertain the page body type. + testPage, err := p.fetchNextPage(p.initialURL) + if err != nil { + return nil, err + } + // Store the page type so we can use reflection to create a new mega-page of + // that type. + pageType := reflect.TypeOf(testPage) + + // Switch on the page body type. Recognized types are `map[string]interface{}`, + // `[]byte`, and `[]interface{}`. + switch testPage.GetBody().(type) { + case map[string]interface{}: + // key is the map key for the page body if the body type is `map[string]interface{}`. + var key string + // Iterate over the pages to concatenate the bodies. + err := p.EachPage(func(page Page) (bool, error) { + b := page.GetBody().(map[string]interface{}) + for k := range b { + // If it's a linked page, we don't want the `links`, we want the other one. + if !strings.HasSuffix(k, "links") { + key = k + } + } + pagesSlice = append(pagesSlice, b[key].([]interface{})...) + return true, nil + }) + if err != nil { + return nil, err + } + // Set body to value of type `map[string]interface{}` + body = reflect.MakeMap(reflect.MapOf(reflect.TypeOf(key), reflect.TypeOf(pagesSlice))) + body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) + case []byte: + // Iterate over the pages to concatenate the bodies. + err := p.EachPage(func(page Page) (bool, error) { + b := page.GetBody().([]byte) + pagesSlice = append(pagesSlice, b) + // seperate pages with a comma + pagesSlice = append(pagesSlice, []byte{10}) + return true, nil + }) + if err != nil { + return nil, err + } + // Remove the trailing comma. + pagesSlice = pagesSlice[:len(pagesSlice)-1] + var b []byte + // Combine the slice of slices in to a single slice. + for _, slice := range pagesSlice { + b = append(b, slice.([]byte)...) + } + // Set body to value of type `bytes`. + body = reflect.New(reflect.TypeOf(b)).Elem() + body.SetBytes(b) + case []interface{}: + // Iterate over the pages to concatenate the bodies. + err := p.EachPage(func(page Page) (bool, error) { + b := page.GetBody().([]interface{}) + pagesSlice = append(pagesSlice, b...) + return true, nil + }) + if err != nil { + return nil, err + } + // Set body to value of type `[]interface{}` + body = reflect.MakeSlice(reflect.TypeOf(pagesSlice), len(pagesSlice), len(pagesSlice)) + for i, s := range pagesSlice { + body.Index(i).Set(reflect.ValueOf(s)) + } + default: + return nil, fmt.Errorf("Page body has unrecognized type.") + } + + // Each `Extract*` function is expecting a specific type of page coming back, + // otherwise the type assertion in those functions will fail. pageType is needed + // to create a type in this method that has the same type that the `Extract*` + // function is expecting and set the Body of that object to the concatenated + // pages. + page := reflect.New(pageType) + // Set the page body to be the concatenated pages. + page.Elem().FieldByName("Body").Set(body) + // Set any additional headers that were pass along. The `objectstorage` pacakge, + // for example, passes a Content-Type header. + h := make(http.Header) + for k, v := range p.Headers { + h.Add(k, v) + } + page.Elem().FieldByName("Header").Set(reflect.ValueOf(h)) + // Type assert the page to a Page interface so that the type assertion in the + // `Extract*` methods will work. + return page.Elem().Interface().(Page), err +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single.go index 4dd3c5c425..f78d4ab5d9 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single.go @@ -7,3 +7,9 @@ type SinglePageBase PageResult func (current SinglePageBase) NextPageURL() (string, error) { return "", nil } + +// GetBody returns the single page's body. This method is needed to satisfy the +// Page interface. +func (current SinglePageBase) GetBody() interface{} { + return current.Body +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single_test.go index 8817d570f2..4af0fee69a 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/single_test.go @@ -69,3 +69,16 @@ func TestEnumerateSinglePaged(t *testing.T) { testhelper.CheckNoErr(t, err) testhelper.CheckEquals(t, 1, callCount) } + +func TestAllPagesSingle(t *testing.T) { + pager := setupSinglePaged() + defer testhelper.TeardownHTTP() + + page, err := pager.AllPages() + testhelper.AssertNoErr(t, err) + + expected := []int{1, 2, 3} + actual, err := ExtractSingleInts(page) + testhelper.AssertNoErr(t, err) + testhelper.CheckDeepEquals(t, expected, actual) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go index 200ee0b1fd..0dff2cfc30 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go @@ -11,7 +11,7 @@ import ( ) // DefaultUserAgent is the default User-Agent string set in the request header. -const DefaultUserAgent = "gophercloud/v1.0" +const DefaultUserAgent = "gophercloud/1.0.0" // UserAgent represents a User-Agent header. type UserAgent struct { @@ -169,7 +169,6 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) ( if options.MoreHeaders != nil { for k, v := range options.MoreHeaders { - fmt.Printf("Applying header [%s: %v]\n", k, v) if v != "" { req.Header.Set(k, v) } else { @@ -197,25 +196,28 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) ( } } - // Validate the response code, if requested to do so. - if options.OkCodes != nil { - var ok bool - for _, code := range options.OkCodes { - if resp.StatusCode == code { - ok = true - break - } + // Allow default OkCodes if none explicitly set + if options.OkCodes == nil { + options.OkCodes = defaultOkCodes(method) + } + + // Validate the HTTP response status. + var ok bool + for _, code := range options.OkCodes { + if resp.StatusCode == code { + ok = true + break } - if !ok { - body, _ := ioutil.ReadAll(resp.Body) - resp.Body.Close() - return resp, &UnexpectedResponseCodeError{ - URL: url, - Method: method, - Expected: options.OkCodes, - Actual: resp.StatusCode, - Body: body, - } + } + if !ok { + body, _ := ioutil.ReadAll(resp.Body) + resp.Body.Close() + return resp, &UnexpectedResponseCodeError{ + URL: url, + Method: method, + Expected: options.OkCodes, + Actual: resp.StatusCode, + Body: body, } } @@ -227,3 +229,72 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) ( return resp, nil } + +func defaultOkCodes(method string) []int { + switch { + case method == "GET": + return []int{200} + case method == "POST": + return []int{201, 202} + case method == "PUT": + return []int{201, 202} + case method == "DELETE": + return []int{202, 204} + } + + return []int{} +} + +func (client *ProviderClient) Get(url string, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + if JSONResponse != nil { + opts.JSONResponse = JSONResponse + } + return client.Request("GET", url, *opts) +} + +func (client *ProviderClient) Post(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + if v, ok := (JSONBody).(io.Reader); ok { + opts.RawBody = v + } else if JSONBody != nil { + opts.JSONBody = JSONBody + } + + if JSONResponse != nil { + opts.JSONResponse = JSONResponse + } + + return client.Request("POST", url, *opts) +} + +func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + if v, ok := (JSONBody).(io.Reader); ok { + opts.RawBody = v + } else if JSONBody != nil { + opts.JSONBody = JSONBody + } + + if JSONResponse != nil { + opts.JSONResponse = JSONResponse + } + + return client.Request("PUT", url, *opts) +} + +func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + return client.Request("DELETE", url, *opts) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client_test.go index 8fd20f8f81..d79d862b2c 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client_test.go @@ -18,18 +18,18 @@ func TestAuthenticatedHeaders(t *testing.T) { func TestUserAgent(t *testing.T) { p := &ProviderClient{} - p.UserAgent.Prepend("custom-user-agent/v2.4") - expected := "custom-user-agent/v2.4 gophercloud/v1.0" + p.UserAgent.Prepend("custom-user-agent/2.4.0") + expected := "custom-user-agent/2.4.0 gophercloud/1.0.0" actual := p.UserAgent.Join() th.CheckEquals(t, expected, actual) - p.UserAgent.Prepend("another-custom-user-agent/v0.3", "a-third-ua/v5.9") - expected = "another-custom-user-agent/v0.3 a-third-ua/v5.9 custom-user-agent/v2.4 gophercloud/v1.0" + p.UserAgent.Prepend("another-custom-user-agent/0.3.0", "a-third-ua/5.9.0") + expected = "another-custom-user-agent/0.3.0 a-third-ua/5.9.0 custom-user-agent/2.4.0 gophercloud/1.0.0" actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) p.UserAgent = UserAgent{} - expected = "gophercloud/v1.0" + expected = "gophercloud/1.0.0" actual = p.UserAgent.Join() th.CheckEquals(t, expected, actual) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/cdn/v1/base/delegate_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/cdn/v1/base/delegate_test.go index 3c058016a0..731fc6dd00 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/cdn/v1/base/delegate_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/cdn/v1/base/delegate_test.go @@ -21,7 +21,7 @@ func TestGetHomeDocument(t *testing.T) { "href-template": "services{?marker,limit}", "href-vars": map[string]interface{}{ "marker": "param/marker", - "limit": "param/limit", + "limit": "param/limit", }, "hints": map[string]interface{}{ "allow": []string{"GET"}, diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/client.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/client.go index 8f1f34f7b8..db3f305b52 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/client.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/client.go @@ -202,3 +202,13 @@ func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.Endpo } return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil } + +// NewRackConnectV3 creates a ServiceClient that may be used to access the v3 RackConnect service. +func NewRackConnectV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + eo.ApplyDefaults("rax:rackconnect") + url, err := client.EndpointLocator(eo) + if err != nil { + return nil, err + } + return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/networks/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/networks/requests.go index 3aefb0cdca..cebbffd36a 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/networks/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/networks/requests.go @@ -21,10 +21,7 @@ func List(c *gophercloud.ServiceClient) 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 = c.Request("GET", getURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) return res } @@ -78,10 +75,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { } // Send request to API - _, res.Err = c.Request("POST", createURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 201, 202}, + _, res.Err = c.Post(createURL(c), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, }) return res } @@ -89,8 +84,6 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { // 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 = c.Request("DELETE", deleteURL(c, networkID), gophercloud.RequestOpts{ - OkCodes: []int{204}, - }) + _, res.Err = c.Delete(deleteURL(c, networkID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate.go index 173868edf1..7810d156a0 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate.go @@ -54,6 +54,23 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts os.RebuildOptsBu return os.Rebuild(client, id, opts) } +// Resize instructs the provider to change the flavor of the server. +// Note that this implies rebuilding it. +// Unfortunately, one cannot pass rebuild parameters to the resize function. +// When the resize completes, the server will be in RESIZE_VERIFY state. +// While in this state, you can explore the use of the new server's configuration. +// If you like it, call ConfirmResize() to commit the resize permanently. +// Otherwise, call RevertResize() to restore the old configuration. +func Resize(client *gophercloud.ServiceClient, id string, opts os.ResizeOptsBuilder) os.ActionResult { + return os.Resize(client, id, opts) +} + +// ConfirmResize confirms a previous resize operation on a server. +// See Resize() for more details. +func ConfirmResize(client *gophercloud.ServiceClient, id string) os.ActionResult { + return os.ConfirmResize(client, id) +} + // WaitForStatus will continually poll a server until it successfully transitions to a specified // status. It will do this for at most the number of seconds specified. func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error { @@ -64,3 +81,36 @@ func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) er func ExtractServers(page pagination.Page) ([]os.Server, error) { return os.ExtractServers(page) } + +// ListAddresses makes a request against the API to list the servers IP addresses. +func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { + return os.ListAddresses(client, id) +} + +// ExtractAddresses interprets the results of a single page from a ListAddresses() call, producing a map of Address slices. +func ExtractAddresses(page pagination.Page) (map[string][]os.Address, error) { + return os.ExtractAddresses(page) +} + +// ListAddressesByNetwork makes a request against the API to list the servers IP addresses +// for the given network. +func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { + return os.ListAddressesByNetwork(client, id, network) +} + +// ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call, producing a map of Address slices. +func ExtractNetworkAddresses(page pagination.Page) ([]os.Address, error) { + return os.ExtractNetworkAddresses(page) +} + +// Metadata requests all the metadata for the given server ID. +func Metadata(client *gophercloud.ServiceClient, id string) os.GetMetadataResult { + return os.Metadata(client, id) +} + +// UpdateMetadata updates (or creates) all the metadata specified by opts for the given server ID. +// This operation does not affect already-existing metadata that is not specified +// by opts. +func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts os.UpdateMetadataOptsBuilder) os.UpdateMetadataResult { + return os.UpdateMetadata(client, id, opts) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate_test.go index c3d9cc0897..03e7acea84 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate_test.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/servers/delegate_test.go @@ -132,3 +132,51 @@ func TestRebuildServer(t *testing.T) { th.AssertNoErr(t, err) th.CheckDeepEquals(t, &GophercloudServer, actual) } + +func TestListAddresses(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + os.HandleAddressListSuccessfully(t) + + expected := os.ListAddressesExpected + pages := 0 + err := ListAddresses(client.ServiceClient(), "asdfasdfasdf").EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := ExtractAddresses(page) + th.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 networks, got %d", len(actual)) + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, pages) +} + +func TestListAddressesByNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + os.HandleNetworkAddressListSuccessfully(t) + + expected := os.ListNetworkAddressesExpected + pages := 0 + err := ListAddressesByNetwork(client.ServiceClient(), "asdfasdfasdf", "public").EachPage(func(page pagination.Page) (bool, error) { + pages++ + + actual, err := ExtractNetworkAddresses(page) + th.AssertNoErr(t, err) + + if len(actual) != 2 { + t.Fatalf("Expected 2 addresses, got %d", len(actual)) + } + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, pages) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces/requests.go index 3c81ef80f8..1ff7c5ae55 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/virtualinterfaces/requests.go @@ -28,10 +28,8 @@ func Create(c *gophercloud.ServiceClient, instanceID, networkID string) CreateRe } // Send request to API - _, res.Err = c.Request("POST", createURL(c, instanceID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200, 201, 202}, + _, res.Err = c.Post(createURL(c, instanceID), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 201, 202}, }) return res } @@ -40,7 +38,7 @@ func Create(c *gophercloud.ServiceClient, instanceID, networkID string) CreateRe // instanceID. func Delete(c *gophercloud.ServiceClient, instanceID, interfaceID string) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", deleteURL(c, instanceID, interfaceID), gophercloud.RequestOpts{ + _, res.Err = c.Delete(deleteURL(c, instanceID, interfaceID), &gophercloud.RequestOpts{ OkCodes: []int{200, 204}, }) return res diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/delegate.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/delegate.go new file mode 100644 index 0000000000..c6003e0e5b --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/delegate.go @@ -0,0 +1,27 @@ +package volumeattach + +import ( + "github.com/rackspace/gophercloud" + os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" + "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 os.List(client, serverID) +} + +// Create requests the creation of a new volume attachment on the server +func Create(client *gophercloud.ServiceClient, serverID string, opts os.CreateOptsBuilder) os.CreateResult { + return os.Create(client, serverID, opts) +} + +// Get returns public data about a previously created VolumeAttachment. +func Get(client *gophercloud.ServiceClient, serverID, aID string) os.GetResult { + return os.Get(client, serverID, aID) +} + +// Delete requests the deletion of a previous stored VolumeAttachment from the server. +func Delete(client *gophercloud.ServiceClient, serverID, aID string) os.DeleteResult { + return os.Delete(client, serverID, aID) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/delegate_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/delegate_test.go new file mode 100644 index 0000000000..e26416cba0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/delegate_test.go @@ -0,0 +1,66 @@ +package volumeattach + +import ( + "testing" + + os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach" + "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() + os.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 := os.ExtractVolumeAttachments(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, os.ExpectedVolumeAttachmentSlice, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, 1, count) +} + +func TestCreate(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + os.HandleCreateSuccessfully(t) + serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0" + + actual, err := Create(client.ServiceClient(), serverId, os.CreateOpts{ + Device: "/dev/vdc", + VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804", + }).Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, &os.CreatedVolumeAttachment, actual) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + os.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, &os.SecondVolumeAttachment, actual) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + os.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) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/doc.go new file mode 100644 index 0000000000..2164908e3a --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/volumeattach/doc.go @@ -0,0 +1,3 @@ +// Package volumeattach provides the ability to attach and detach volume +// to instances to Rackspace servers +package volumeattach diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/acl/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/acl/requests.go index 94d98e34cb..d4ce7c01f4 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/acl/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/acl/requests.go @@ -74,11 +74,7 @@ func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOp return res } - _, res.Err = client.Request("POST", rootURL(client, loadBalancerID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Post(rootURL(client, loadBalancerID), reqBody, nil, nil) return res } @@ -95,19 +91,14 @@ func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, itemIDs []int) url := rootURL(c, loadBalancerID) url += gophercloud.IDSliceToQueryString("id", itemIDs) - _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(url, nil) return res } // Delete will remove a single network item from a load balancer's access list. func Delete(c *gophercloud.ServiceClient, lbID, itemID int) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", resourceURL(c, lbID, itemID), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = c.Delete(resourceURL(c, lbID, itemID), nil) return res } @@ -115,8 +106,6 @@ func Delete(c *gophercloud.ServiceClient, lbID, itemID int) DeleteResult { // effectively resetting it and allowing all traffic. func DeleteAll(c *gophercloud.ServiceClient, lbID int) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", rootURL(c, lbID), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = c.Delete(rootURL(c, lbID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/lbs/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/lbs/requests.go index 49a46f6d4e..46f5f02a43 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/lbs/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/lbs/requests.go @@ -227,12 +227,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { return res } - _, res.Err = c.Request("POST", rootURL(c), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{202}, - }) - + _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil) return res } @@ -243,9 +238,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { func Get(c *gophercloud.ServiceClient, id int) GetResult { var res GetResult - _, res.Err = c.Request("GET", resourceURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Get(resourceURL(c, id), &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -269,21 +263,14 @@ func BulkDelete(c *gophercloud.ServiceClient, ids []int) DeleteResult { url := rootURL(c) url += gophercloud.IDSliceToQueryString("id", ids) - _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(url, nil) return res } // Delete removes a single load balancer. func Delete(c *gophercloud.ServiceClient, id int) DeleteResult { var res DeleteResult - - _, res.Err = c.Request("DELETE", resourceURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(resourceURL(c, id), nil) return res } @@ -363,11 +350,7 @@ func Update(c *gophercloud.ServiceClient, id int, opts UpdateOptsBuilder) Update return res } - _, res.Err = c.Request("PUT", resourceURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = c.Put(resourceURL(c, id), reqBody, nil, nil) return res } @@ -394,10 +377,7 @@ func ListAlgorithms(client *gophercloud.ServiceClient) pagination.Pager { func IsLoggingEnabled(client *gophercloud.ServiceClient, id int) (bool, error) { var body interface{} - _, err := client.Request("GET", loggingURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &body, - OkCodes: []int{200}, - }) + _, err := client.Get(loggingURL(client, id), &body, nil) if err != nil { return false, err } @@ -420,39 +400,22 @@ func toConnLoggingMap(state bool) map[string]map[string]bool { // EnableLogging will enable connection logging for a specified load balancer. func EnableLogging(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult { - reqBody := toConnLoggingMap(true) var res gophercloud.ErrResult - - _, res.Err = client.Request("PUT", loggingURL(client, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Put(loggingURL(client, id), toConnLoggingMap(true), nil, nil) return res } // DisableLogging will disable connection logging for a specified load balancer. func DisableLogging(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult { - reqBody := toConnLoggingMap(false) var res gophercloud.ErrResult - - _, res.Err = client.Request("PUT", loggingURL(client, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Put(loggingURL(client, id), toConnLoggingMap(false), nil, nil) return res } // GetErrorPage will retrieve the current error page for the load balancer. func GetErrorPage(client *gophercloud.ServiceClient, id int) ErrorPageResult { var res ErrorPageResult - - _, res.Err = client.Request("GET", errorPageURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = client.Get(errorPageURL(client, id), &res.Body, nil) return res } @@ -464,10 +427,8 @@ func SetErrorPage(client *gophercloud.ServiceClient, id int, html string) ErrorP type stringMap map[string]string reqBody := map[string]stringMap{"errorpage": stringMap{"content": html}} - _, res.Err = client.Request("PUT", errorPageURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - JSONBody: &reqBody, - OkCodes: []int{200}, + _, res.Err = client.Put(errorPageURL(client, id), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -476,23 +437,16 @@ func SetErrorPage(client *gophercloud.ServiceClient, id int, html string) ErrorP // DeleteErrorPage will delete the current error page for the load balancer. func DeleteErrorPage(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult { var res gophercloud.ErrResult - - _, res.Err = client.Request("DELETE", errorPageURL(client, id), gophercloud.RequestOpts{ + _, res.Err = client.Delete(errorPageURL(client, id), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return res } // GetStats will retrieve detailed stats related to the load balancer's usage. func GetStats(client *gophercloud.ServiceClient, id int) StatsResult { var res StatsResult - - _, res.Err = client.Request("GET", statsURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = client.Get(statsURL(client, id), &res.Body, nil) return res } @@ -507,10 +461,7 @@ func GetStats(client *gophercloud.ServiceClient, id int) StatsResult { func IsContentCached(client *gophercloud.ServiceClient, id int) (bool, error) { var body interface{} - _, err := client.Request("GET", cacheURL(client, id), gophercloud.RequestOpts{ - JSONResponse: &body, - OkCodes: []int{200}, - }) + _, err := client.Get(cacheURL(client, id), &body, nil) if err != nil { return false, err } @@ -533,26 +484,14 @@ func toCachingMap(state bool) map[string]map[string]bool { // EnableCaching will enable content-caching for the specified load balancer. func EnableCaching(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult { - reqBody := toCachingMap(true) var res gophercloud.ErrResult - - _, res.Err = client.Request("PUT", cacheURL(client, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Put(cacheURL(client, id), toCachingMap(true), nil, nil) return res } // DisableCaching will disable content-caching for the specified load balancer. func DisableCaching(client *gophercloud.ServiceClient, id int) gophercloud.ErrResult { - reqBody := toCachingMap(false) var res gophercloud.ErrResult - - _, res.Err = client.Request("PUT", cacheURL(client, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = client.Put(cacheURL(client, id), toCachingMap(false), nil, nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/monitors/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/monitors/requests.go index 917282c63b..d4ba27653c 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/monitors/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/monitors/requests.go @@ -141,33 +141,20 @@ func Update(c *gophercloud.ServiceClient, id int, opts UpdateOptsBuilder) Update return res } - _, res.Err = c.Request("PUT", rootURL(c, id), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = c.Put(rootURL(c, id), reqBody, nil, nil) return res } // Get is the operation responsible for showing details of a health monitor. func Get(c *gophercloud.ServiceClient, id int) GetResult { var res GetResult - - _, res.Err = c.Request("GET", rootURL(c, id), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = c.Get(rootURL(c, id), &res.Body, nil) return res } // Delete is the operation responsible for deleting a health monitor. func Delete(c *gophercloud.ServiceClient, id int) DeleteResult { var res DeleteResult - - _, res.Err = c.Request("DELETE", rootURL(c, id), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(rootURL(c, id), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go index 86fe5d7c8c..02af86b5c1 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/requests.go @@ -112,11 +112,8 @@ func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOp return res } - resp, err := client.Request("POST", rootURL(client, loadBalancerID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{202}, - }) + resp, err := client.Post(rootURL(client, loadBalancerID), reqBody, &res.Body, nil) + if err != nil { res.Err = err return res @@ -145,22 +142,14 @@ func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, nodeIDs []int) url := rootURL(c, loadBalancerID) url += gophercloud.IDSliceToQueryString("id", nodeIDs) - _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(url, nil) return res } // Get is the operation responsible for showing details for a single node. func Get(c *gophercloud.ServiceClient, lbID, nodeID int) GetResult { var res GetResult - - _, res.Err = c.Request("GET", resourceURL(c, lbID, nodeID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = c.Get(resourceURL(c, lbID, nodeID), &res.Body, nil) return res } @@ -213,20 +202,14 @@ func Update(c *gophercloud.ServiceClient, lbID, nodeID int, opts UpdateOptsBuild return res } - _, res.Err = c.Request("PUT", resourceURL(c, lbID, nodeID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - OkCodes: []int{202}, - }) - + _, res.Err = c.Put(resourceURL(c, lbID, nodeID), reqBody, nil, nil) return res } // Delete is the operation responsible for permanently deleting a node. func Delete(c *gophercloud.ServiceClient, lbID, nodeID int) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", resourceURL(c, lbID, nodeID), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = c.Delete(resourceURL(c, lbID, nodeID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/sessions/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/sessions/requests.go index 5572407edd..a93d766cd9 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/sessions/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/sessions/requests.go @@ -42,12 +42,7 @@ func Enable(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) Enab return res } - _, res.Err = c.Request("PUT", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{202}, - }) - + _, res.Err = c.Put(rootURL(c, lbID), reqBody, &res.Body, nil) return res } @@ -55,12 +50,7 @@ func Enable(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) Enab // persistence configuration for a particular load balancer. func Get(c *gophercloud.ServiceClient, lbID int) GetResult { var res GetResult - - _, res.Err = c.Request("GET", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = c.Get(rootURL(c, lbID), &res.Body, nil) return res } @@ -68,10 +58,6 @@ func Get(c *gophercloud.ServiceClient, lbID int) GetResult { // particular load balancer. func Disable(c *gophercloud.ServiceClient, lbID int) DisableResult { var res DisableResult - - _, res.Err = c.Request("DELETE", rootURL(c, lbID), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(rootURL(c, lbID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/ssl/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/ssl/requests.go index e9c6514286..bb53ef8960 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/ssl/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/ssl/requests.go @@ -85,12 +85,9 @@ func Update(c *gophercloud.ServiceClient, lbID int, opts UpdateOptsBuilder) Upda return res } - _, res.Err = c.Request("PUT", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Put(rootURL(c, lbID), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) - return res } @@ -98,12 +95,7 @@ func Update(c *gophercloud.ServiceClient, lbID int, opts UpdateOptsBuilder) Upda // Termination configuration for a load balancer. func Get(c *gophercloud.ServiceClient, lbID int) GetResult { var res GetResult - - _, res.Err = c.Request("GET", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = c.Get(rootURL(c, lbID), &res.Body, nil) return res } @@ -111,11 +103,9 @@ func Get(c *gophercloud.ServiceClient, lbID int) GetResult { // configuration for a load balancer. func Delete(c *gophercloud.ServiceClient, lbID int) DeleteResult { var res DeleteResult - - _, res.Err = c.Request("DELETE", rootURL(c, lbID), gophercloud.RequestOpts{ + _, res.Err = c.Delete(rootURL(c, lbID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return res } @@ -180,10 +170,8 @@ func CreateCert(c *gophercloud.ServiceClient, lbID int, opts CreateCertOptsBuild return res } - _, res.Err = c.Request("POST", certURL(c, lbID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{200}, + _, res.Err = c.Post(certURL(c, lbID), reqBody, &res.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, }) return res @@ -192,12 +180,7 @@ func CreateCert(c *gophercloud.ServiceClient, lbID int, opts CreateCertOptsBuild // GetCert will show the details of an existing SSL certificate. func GetCert(c *gophercloud.ServiceClient, lbID, certID int) GetCertResult { var res GetCertResult - - _, res.Err = c.Request("GET", certResourceURL(c, lbID, certID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = c.Get(certResourceURL(c, lbID, certID), &res.Body, nil) return res } @@ -247,12 +230,7 @@ func UpdateCert(c *gophercloud.ServiceClient, lbID, certID int, opts UpdateCertO return res } - _, res.Err = c.Request("PUT", certResourceURL(c, lbID, certID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{202}, - }) - + _, res.Err = c.Put(certResourceURL(c, lbID, certID), reqBody, &res.Body, nil) return res } @@ -261,7 +239,7 @@ func UpdateCert(c *gophercloud.ServiceClient, lbID, certID int, opts UpdateCertO func DeleteCert(c *gophercloud.ServiceClient, lbID, certID int) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", certResourceURL(c, lbID, certID), gophercloud.RequestOpts{ + _, res.Err = c.Delete(certResourceURL(c, lbID, certID), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/throttle/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/throttle/requests.go index 2680a892b6..0446b97a14 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/throttle/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/throttle/requests.go @@ -55,12 +55,7 @@ func Create(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) Crea return res } - _, res.Err = c.Request("PUT", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{202}, - }) - + _, res.Err = c.Put(rootURL(c, lbID), reqBody, &res.Body, nil) return res } @@ -68,12 +63,7 @@ func Create(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) Crea // throttling configuration for a load balancer. func Get(c *gophercloud.ServiceClient, lbID int) GetResult { var res GetResult - - _, res.Err = c.Request("GET", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - OkCodes: []int{200}, - }) - + _, res.Err = c.Get(rootURL(c, lbID), &res.Body, nil) return res } @@ -81,10 +71,6 @@ func Get(c *gophercloud.ServiceClient, lbID int) GetResult { // configuration for a load balancer. func Delete(c *gophercloud.ServiceClient, lbID int) DeleteResult { var res DeleteResult - - _, res.Err = c.Request("DELETE", rootURL(c, lbID), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(rootURL(c, lbID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/vips/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/vips/requests.go index d52a73afd4..2bc924f293 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/vips/requests.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/vips/requests.go @@ -67,12 +67,7 @@ func Create(c *gophercloud.ServiceClient, lbID int, opts CreateOptsBuilder) Crea return res } - _, res.Err = c.Request("POST", rootURL(c, lbID), gophercloud.RequestOpts{ - JSONBody: &reqBody, - JSONResponse: &res.Body, - OkCodes: []int{202}, - }) - + _, res.Err = c.Post(rootURL(c, lbID), reqBody, &res.Body, nil) return res } @@ -90,18 +85,13 @@ func BulkDelete(c *gophercloud.ServiceClient, loadBalancerID int, vipIDs []int) url := rootURL(c, loadBalancerID) url += gophercloud.IDSliceToQueryString("id", vipIDs) - _, res.Err = c.Request("DELETE", url, gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) - + _, res.Err = c.Delete(url, nil) return res } // Delete is the operation responsible for permanently deleting a VIP. func Delete(c *gophercloud.ServiceClient, lbID, vipID int) DeleteResult { var res DeleteResult - _, res.Err = c.Request("DELETE", resourceURL(c, lbID, vipID), gophercloud.RequestOpts{ - OkCodes: []int{202}, - }) + _, res.Err = c.Delete(resourceURL(c, lbID, vipID), nil) return res } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/ports/delegate.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/ports/delegate.go index 091b99e0f4..95728d1855 100644 --- a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/ports/delegate.go +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/ports/delegate.go @@ -24,6 +24,9 @@ func Get(c *gophercloud.ServiceClient, networkID string) os.GetResult { // Create accepts a CreateOpts struct and creates a new network using the values // provided. You must remember to provide a NetworkID value. +// +// NOTE: Currently the SecurityGroup option is not implemented to work with +// Rackspace. func Create(c *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult { return os.Create(c, opts) } diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/doc.go new file mode 100644 index 0000000000..31f744ccd7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/doc.go @@ -0,0 +1,32 @@ +// Package security contains functionality to work with security group and +// security group rules Neutron resources. +// +// Security groups and security group rules allows administrators and tenants +// the ability to specify the type of traffic and direction (ingress/egress) +// that is allowed to pass through a port. A security group is a container for +// security group rules. +// +// When a port is created in Networking it is associated with a security group. +// If a security group is not specified the port is associated with a 'default' +// security group. By default, this group drops all ingress traffic and allows +// all egress. Rules can be added to this group in order to change the behaviour. +// +// The basic characteristics of Neutron Security Groups are: +// +// For ingress traffic (to an instance) +// - Only traffic matched with security group rules are allowed. +// - When there is no rule defined, all traffic is dropped. +// +// For egress traffic (from an instance) +// - Only traffic matched with security group rules are allowed. +// - When there is no rule defined, all egress traffic are dropped. +// - When a new security group is created, rules to allow all egress traffic +// is automatically added. +// +// "default security group" is defined for each tenant. +// - For the default security group a rule which allows intercommunication +// among hosts associated with the default security group is defined by default. +// - As a result, all egress traffic and intercommunication in the default +// group are allowed and all ingress from outside of the default group is +// dropped by default (in the default security group). +package security diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups/delegate.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups/delegate.go new file mode 100644 index 0000000000..1e9a23a05a --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups/delegate.go @@ -0,0 +1,30 @@ +package groups + +import ( + "github.com/rackspace/gophercloud" + os "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups" + "github.com/rackspace/gophercloud/pagination" +) + +// List returns a Pager which allows you to iterate over a collection of +// security groups. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts os.ListOpts) pagination.Pager { + return os.List(c, opts) +} + +// Create is an operation which provisions a new security group with default +// security group rules for the IPv4 and IPv6 ether types. +func Create(c *gophercloud.ServiceClient, opts os.CreateOpts) os.CreateResult { + return os.Create(c, opts) +} + +// Get retrieves a particular security group based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) os.GetResult { + return os.Get(c, id) +} + +// Delete will permanently delete a particular security group based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) os.DeleteResult { + return os.Delete(c, id) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups/delegate_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups/delegate_test.go new file mode 100644 index 0000000000..45cd3ba8d4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/groups/delegate_test.go @@ -0,0 +1,206 @@ +package groups + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/rackspace/gophercloud/openstack/networking/v2/common" + osGroups "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups" + osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules" + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-groups", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "security_groups": [ + { + "description": "default", + "id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "name": "default", + "security_group_rules": [], + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + ] + } + `) + }) + + count := 0 + + List(fake.ServiceClient(), osGroups.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := osGroups.ExtractGroups(page) + if err != nil { + t.Errorf("Failed to extract secgroups: %v", err) + return false, err + } + + expected := []osGroups.SecGroup{ + osGroups.SecGroup{ + Description: "default", + ID: "85cc3048-abc3-43cc-89b3-377341426ac5", + Name: "default", + Rules: []osRules.SecGroupRule{}, + TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", + }, + } + + 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/security-groups", 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, ` + { + "security_group": { + "name": "new-webservers", + "description": "security group for webservers" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` + { + "security_group": { + "description": "security group for webservers", + "id": "2076db17-a522-4506-91de-c6dd8e837028", + "name": "new-webservers", + "security_group_rules": [ + { + "direction": "egress", + "ethertype": "IPv4", + "id": "38ce2d8e-e8f1-48bd-83c2-d33cb9f50c3d", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + }, + { + "direction": "egress", + "ethertype": "IPv6", + "id": "565b9502-12de-4ffd-91e9-68885cff6ae1", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "2076db17-a522-4506-91de-c6dd8e837028", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + ], + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + } + `) + }) + + opts := osGroups.CreateOpts{Name: "new-webservers", Description: "security group for webservers"} + _, err := Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-groups/85cc3048-abc3-43cc-89b3-377341426ac5", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "security_group": { + "description": "default", + "id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "name": "default", + "security_group_rules": [ + { + "direction": "egress", + "ethertype": "IPv6", + "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + }, + { + "direction": "egress", + "ethertype": "IPv4", + "id": "93aa42e5-80db-4581-9391-3a608bd0e448", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + ], + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + } + `) + }) + + sg, err := Get(fake.ServiceClient(), "85cc3048-abc3-43cc-89b3-377341426ac5").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "default", sg.Description) + th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sg.ID) + th.AssertEquals(t, "default", sg.Name) + th.AssertEquals(t, 2, len(sg.Rules)) + th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sg.TenantID) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-groups/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) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules/delegate.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules/delegate.go new file mode 100644 index 0000000000..23b4b318e2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules/delegate.go @@ -0,0 +1,30 @@ +package rules + +import ( + "github.com/rackspace/gophercloud" + os "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules" + "github.com/rackspace/gophercloud/pagination" +) + +// List returns a Pager which allows you to iterate over a collection of +// security group rules. It accepts a ListOpts struct, which allows you to filter +// and sort the returned collection for greater efficiency. +func List(c *gophercloud.ServiceClient, opts os.ListOpts) pagination.Pager { + return os.List(c, opts) +} + +// Create is an operation which provisions a new security group with default +// security group rules for the IPv4 and IPv6 ether types. +func Create(c *gophercloud.ServiceClient, opts os.CreateOpts) os.CreateResult { + return os.Create(c, opts) +} + +// Get retrieves a particular security group based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) os.GetResult { + return os.Get(c, id) +} + +// Delete will permanently delete a particular security group based on its unique ID. +func Delete(c *gophercloud.ServiceClient, id string) os.DeleteResult { + return os.Delete(c, id) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules/delegate_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules/delegate_test.go new file mode 100644 index 0000000000..3563fbeaa6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/networking/v2/security/rules/delegate_test.go @@ -0,0 +1,236 @@ +package rules + +import ( + "fmt" + "net/http" + "testing" + + fake "github.com/rackspace/gophercloud/openstack/networking/v2/common" + osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules" + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" +) + +func TestList(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-group-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, ` + { + "security_group_rules": [ + { + "direction": "egress", + "ethertype": "IPv6", + "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + }, + { + "direction": "egress", + "ethertype": "IPv4", + "id": "93aa42e5-80db-4581-9391-3a608bd0e448", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + ] + } + `) + }) + + count := 0 + + List(fake.ServiceClient(), osRules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := osRules.ExtractRules(page) + if err != nil { + t.Errorf("Failed to extract secrules: %v", err) + return false, err + } + + expected := []osRules.SecGroupRule{ + osRules.SecGroupRule{ + Direction: "egress", + EtherType: "IPv6", + ID: "3c0e45ff-adaf-4124-b083-bf390e5482ff", + PortRangeMax: 0, + PortRangeMin: 0, + Protocol: "", + RemoteGroupID: "", + RemoteIPPrefix: "", + SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", + TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", + }, + osRules.SecGroupRule{ + Direction: "egress", + EtherType: "IPv4", + ID: "93aa42e5-80db-4581-9391-3a608bd0e448", + PortRangeMax: 0, + PortRangeMin: 0, + Protocol: "", + RemoteGroupID: "", + RemoteIPPrefix: "", + SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", + TenantID: "e4f50856753b4dc6afee5fa6b9b6c550", + }, + } + + 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/security-group-rules", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "POST") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Content-Type", "application/json") + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "security_group_rule": { + "direction": "ingress", + "port_range_min": 80, + "ethertype": "IPv4", + "port_range_max": 80, + "protocol": "tcp", + "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a" + } + } + `) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + fmt.Fprintf(w, ` + { + "security_group_rule": { + "direction": "ingress", + "ethertype": "IPv4", + "id": "2bc0accf-312e-429a-956e-e4407625eb62", + "port_range_max": 80, + "port_range_min": 80, + "protocol": "tcp", + "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "remote_ip_prefix": null, + "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + } + `) + }) + + opts := osRules.CreateOpts{ + Direction: "ingress", + PortRangeMin: 80, + EtherType: "IPv4", + PortRangeMax: 80, + Protocol: "tcp", + RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5", + SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a", + } + _, err := Create(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) +} + +func TestRequiredCreateOpts(t *testing.T) { + res := Create(fake.ServiceClient(), osRules.CreateOpts{Direction: "something"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: "something"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: osRules.Ether4}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } + res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: osRules.Ether4, SecGroupID: "something", Protocol: "foo"}) + if res.Err == nil { + t.Fatalf("Expected error, got none") + } +} + +func TestGet(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, ` + { + "security_group_rule": { + "direction": "egress", + "ethertype": "IPv6", + "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff", + "port_range_max": null, + "port_range_min": null, + "protocol": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5", + "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550" + } + } + `) + }) + + sr, err := Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract() + th.AssertNoErr(t, err) + + th.AssertEquals(t, "egress", sr.Direction) + th.AssertEquals(t, "IPv6", sr.EtherType) + th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID) + th.AssertEquals(t, 0, sr.PortRangeMax) + th.AssertEquals(t, 0, sr.PortRangeMin) + th.AssertEquals(t, "", sr.Protocol) + th.AssertEquals(t, "", sr.RemoteGroupID) + th.AssertEquals(t, "", sr.RemoteIPPrefix) + th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID) + th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID) +} + +func TestDelete(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + + th.Mux.HandleFunc("/v2.0/security-group-rules/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) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/requests.go new file mode 100644 index 0000000000..58843030ae --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/requests.go @@ -0,0 +1,24 @@ +package cloudnetworks + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// List returns all cloud networks that are associated with RackConnect. The ID +// returned for each network is the same as the ID returned by the networks package. +func List(c *gophercloud.ServiceClient) pagination.Pager { + url := listURL(c) + createPage := func(r pagination.PageResult) pagination.Page { + return CloudNetworkPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} + +// Get retrieves a specific cloud network (that is associated with RackConnect) +// based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) GetResult { + var res GetResult + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) + return res +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/requests_test.go new file mode 100644 index 0000000000..10d15dd11f --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/requests_test.go @@ -0,0 +1,87 @@ +package cloudnetworks + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" + fake "github.com/rackspace/gophercloud/testhelper/client" +) + +func TestListCloudNetworks(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/cloud_networks", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[{ + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "updated": "2014-05-25T02:28:44Z" + }]`) + }) + + expected := []CloudNetwork{ + CloudNetwork{ + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + Name: "RC-CLOUD", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + } + + count := 0 + err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractCloudNetworks(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestGetCloudNetwork(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/cloud_networks/07426958-1ebf-4c38-b032-d456820ca21a", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "updated": "2014-05-25T02:28:44Z" + }`) + }) + + expected := &CloudNetwork{ + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + Name: "RC-CLOUD", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + } + + actual, err := Get(fake.ServiceClient(), "07426958-1ebf-4c38-b032-d456820ca21a").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expected, actual) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/results.go new file mode 100644 index 0000000000..f554a0d75b --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/results.go @@ -0,0 +1,113 @@ +package cloudnetworks + +import ( + "fmt" + "reflect" + "time" + + "github.com/mitchellh/mapstructure" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// CloudNetwork represents a network associated with a RackConnect configuration. +type CloudNetwork struct { + // Specifies the ID of the newtork. + ID string `mapstructure:"id"` + // Specifies the user-provided name of the network. + Name string `mapstructure:"name"` + // Specifies the IP range for this network. + CIDR string `mapstructure:"cidr"` + // Specifies the time the network was created. + CreatedAt time.Time `mapstructure:"-"` + // Specifies the time the network was last updated. + UpdatedAt time.Time `mapstructure:"-"` +} + +// CloudNetworkPage is the page returned by a pager when traversing over a +// collection of CloudNetworks. +type CloudNetworkPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a CloudNetworkPage contains no CloudNetworks. +func (r CloudNetworkPage) IsEmpty() (bool, error) { + cns, err := ExtractCloudNetworks(r) + if err != nil { + return true, err + } + return len(cns) == 0, nil +} + +// ExtractCloudNetworks extracts and returns CloudNetworks. It is used while iterating over +// a cloudnetworks.List call. +func ExtractCloudNetworks(page pagination.Page) ([]CloudNetwork, error) { + var res []CloudNetwork + casted := page.(CloudNetworkPage).Body + err := mapstructure.Decode(casted, &res) + + var rawNets []interface{} + switch casted.(type) { + case interface{}: + rawNets = casted.([]interface{}) + default: + return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } + + for i := range rawNets { + thisNet := (rawNets[i]).(map[string]interface{}) + + if t, ok := thisNet["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CreatedAt = creationTime + } + + if t, ok := thisNet["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].UpdatedAt = updatedTime + } + } + + return res, err +} + +// GetResult represents the result of a Get operation. +type GetResult struct { + gophercloud.Result +} + +// Extract is a function that extracts a CloudNetwork from a GetResult. +func (r GetResult) Extract() (*CloudNetwork, error) { + if r.Err != nil { + return nil, r.Err + } + var res CloudNetwork + + err := mapstructure.Decode(r.Body, &res) + + b := r.Body.(map[string]interface{}) + + if date, ok := b["created"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.CreatedAt = t + } + + if date, ok := b["updated"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.UpdatedAt = t + } + + return &res, err +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/urls.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/urls.go new file mode 100644 index 0000000000..bd6b098dad --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/cloudnetworks/urls.go @@ -0,0 +1,11 @@ +package cloudnetworks + +import "github.com/rackspace/gophercloud" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("cloud_networks") +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("cloud_networks", id) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/doc.go new file mode 100644 index 0000000000..3a8279e109 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/doc.go @@ -0,0 +1,4 @@ +// Package rackconnect allows Rackspace cloud accounts to leverage version 3 of +// RackConnect, Rackspace's hybrid connectivity solution connecting dedicated +// and cloud servers. +package rackconnect diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/doc.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/doc.go new file mode 100644 index 0000000000..f4319b8ff3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/doc.go @@ -0,0 +1,14 @@ +// Package lbpools provides access to load balancer pools associated with a +// RackConnect configuration. Load Balancer Pools must be configured in advance +// by your Network Security team to be eligible for use with RackConnect. +// If you do not see a pool that you expect to see, contact your Support team +// for further assistance. The Load Balancer Pool id returned by these calls is +// automatically generated by the RackConnect automation and will remain constant +// unless the Load Balancer Pool is renamed on your hardware load balancer. +// All Load Balancer Pools will currently return a status of ACTIVE. Future +// features may introduce additional statuses. +// Node status values are ADDING, ACTIVE, REMOVING, ADD_FAILED, and REMOVE_FAILED. +// The cloud_servers node count will only include Cloud Servers from the specified +// cloud account. Any dedicated servers or cloud servers from another cloud account +// on the same RackConnect Configuration will be counted as external nodes. +package lbpools diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/requests.go new file mode 100644 index 0000000000..c300c56c1e --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/requests.go @@ -0,0 +1,146 @@ +package lbpools + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// List returns all load balancer pools that are associated with RackConnect. +func List(c *gophercloud.ServiceClient) pagination.Pager { + url := listURL(c) + createPage := func(r pagination.PageResult) pagination.Page { + return PoolPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} + +// Get retrieves a specific load balancer pool (that is associated with RackConnect) +// based on its unique ID. +func Get(c *gophercloud.ServiceClient, id string) GetResult { + var res GetResult + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) + return res +} + +// ListNodes returns all load balancer pool nodes that are associated with RackConnect +// for the given LB pool ID. +func ListNodes(c *gophercloud.ServiceClient, id string) pagination.Pager { + url := listNodesURL(c, id) + createPage := func(r pagination.PageResult) pagination.Page { + return NodePage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} + +// CreateNode adds the cloud server with the given serverID to the load balancer +// pool with the given poolID. +func CreateNode(c *gophercloud.ServiceClient, poolID, serverID string) CreateNodeResult { + var res CreateNodeResult + reqBody := map[string]interface{}{ + "cloud_server": map[string]string{ + "id": serverID, + }, + } + _, res.Err = c.Post(createNodeURL(c, poolID), reqBody, &res.Body, nil) + return res +} + +// ListNodesDetails returns all load balancer pool nodes that are associated with RackConnect +// for the given LB pool ID with all their details. +func ListNodesDetails(c *gophercloud.ServiceClient, id string) pagination.Pager { + url := listNodesDetailsURL(c, id) + createPage := func(r pagination.PageResult) pagination.Page { + return NodeDetailsPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} + +// GetNode retrieves a specific LB pool node (that is associated with RackConnect) +// based on its unique ID and the LB pool's unique ID. +func GetNode(c *gophercloud.ServiceClient, poolID, nodeID string) GetNodeResult { + var res GetNodeResult + _, res.Err = c.Get(nodeURL(c, poolID, nodeID), &res.Body, nil) + return res +} + +// DeleteNode removes the node with the given nodeID from the LB pool with the +// given poolID. +func DeleteNode(c *gophercloud.ServiceClient, poolID, nodeID string) DeleteNodeResult { + var res DeleteNodeResult + _, res.Err = c.Delete(deleteNodeURL(c, poolID, nodeID), nil) + return res +} + +// GetNodeDetails retrieves a specific LB pool node's details based on its unique +// ID and the LB pool's unique ID. +func GetNodeDetails(c *gophercloud.ServiceClient, poolID, nodeID string) GetNodeDetailsResult { + var res GetNodeDetailsResult + _, res.Err = c.Get(nodeDetailsURL(c, poolID, nodeID), &res.Body, nil) + return res +} + +// NodeOpts are options for bulk adding/deleting nodes to LB pools. +type NodeOpts struct { + ServerID string + PoolID string +} + +// NodesOpts are a slice of NodeOpts, passed as options for bulk operations. +type NodesOpts []NodeOpts + +// ToLBPoolCreateNodesMap serializes a NodesOpts into a map to send in the request. +func (o NodesOpts) ToLBPoolCreateNodesMap() ([]map[string]interface{}, error) { + m := make([]map[string]interface{}, len(o)) + for i := range o { + m[i] = map[string]interface{}{ + "cloud_server": map[string]string{ + "id": o[i].ServerID, + }, + "load_balancer_pool": map[string]string{ + "id": o[i].PoolID, + }, + } + } + return m, nil +} + +// CreateNodes adds the cloud servers with the given serverIDs to the corresponding +// load balancer pools with the given poolIDs. +func CreateNodes(c *gophercloud.ServiceClient, opts NodesOpts) CreateNodesResult { + var res CreateNodesResult + reqBody, err := opts.ToLBPoolCreateNodesMap() + if err != nil { + res.Err = err + return res + } + + _, res.Err = c.Post(createNodesURL(c), reqBody, &res.Body, nil) + return res +} + +// DeleteNodes removes the cloud servers with the given serverIDs to the corresponding +// load balancer pools with the given poolIDs. +func DeleteNodes(c *gophercloud.ServiceClient, opts NodesOpts) DeleteNodesResult { + var res DeleteNodesResult + reqBody, err := opts.ToLBPoolCreateNodesMap() + if err != nil { + res.Err = err + return res + } + + _, res.Err = c.Request("DELETE", createNodesURL(c), gophercloud.RequestOpts{ + JSONBody: &reqBody, + OkCodes: []int{204}, + }) + return res +} + +// ListNodesDetailsForServer is similar to ListNodesDetails but only returns nodes +// for the given serverID. +func ListNodesDetailsForServer(c *gophercloud.ServiceClient, serverID string) pagination.Pager { + url := listNodesForServerURL(c, serverID) + createPage := func(r pagination.PageResult) pagination.Page { + return NodeDetailsForServerPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/requests_test.go new file mode 100644 index 0000000000..48ebcece13 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/requests_test.go @@ -0,0 +1,876 @@ +package lbpools + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" + fake "github.com/rackspace/gophercloud/testhelper/client" +) + +func TestListPools(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[ + { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + "name": "RCv3Test", + "node_counts": { + "cloud_servers": 3, + "external": 4, + "total": 7 + }, + "port": 80, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.5" + }, + { + "id": "33021100-4abf-4836-9080-465a6d87ab68", + "name": "RCv3Test2", + "node_counts": { + "cloud_servers": 1, + "external": 0, + "total": 1 + }, + "port": 80, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.7" + }, + { + "id": "b644350a-301b-47b5-a411-c6e0f933c347", + "name": "RCv3Test3", + "node_counts": { + "cloud_servers": 2, + "external": 3, + "total": 5 + }, + "port": 443, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.15" + } + ]`) + }) + + expected := []Pool{ + Pool{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + Name: "RCv3Test", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 3, + External: 4, + Total: 7, + }, + Port: 80, + Status: "ACTIVE", + VirtualIP: "203.0.113.5", + }, + Pool{ + ID: "33021100-4abf-4836-9080-465a6d87ab68", + Name: "RCv3Test2", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 1, + External: 0, + Total: 1, + }, + Port: 80, + Status: "ACTIVE", + VirtualIP: "203.0.113.7", + }, + Pool{ + ID: "b644350a-301b-47b5-a411-c6e0f933c347", + Name: "RCv3Test3", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 2, + External: 3, + Total: 5, + }, + Port: 443, + Status: "ACTIVE", + VirtualIP: "203.0.113.15", + }, + } + + count := 0 + err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractPools(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestGetLBPool(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, `{ + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + "name": "RCv3Test", + "node_counts": { + "cloud_servers": 3, + "external": 4, + "total": 7 + }, + "port": 80, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.5" + }`) + }) + + expected := &Pool{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + Name: "RCv3Test", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 3, + External: 4, + Total: 7, + }, + Port: 80, + Status: "ACTIVE", + VirtualIP: "203.0.113.5", + } + + actual, err := Get(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expected, actual) +} + +func TestListNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[ + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + }, + "status": "ACTIVE", + "updated": "2014-05-30T03:24:18Z" + }, + { + "created": "2014-05-31T08:23:12Z", + "cloud_server": { + "id": "f28b870f-a063-498a-8b12-7025e5b1caa6" + }, + "id": "b70481dd-7edf-4dbb-a44b-41cc7679d4fb", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + }, + "status": "ADDING", + "updated": "2014-05-31T08:23:26Z" + }, + { + "created": "2014-05-31T08:23:18Z", + "cloud_server": { + "id": "a3d3a6b3-e4e4-496f-9a3d-5c987163e458" + }, + "id": "ced9ddc8-6fae-4e72-9457-16ead52b5515", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + }, + "status": "ADD_FAILED", + "status_detail": "Unable to communicate with network device", + "updated": "2014-05-31T08:24:36Z" + } + ]`) + }) + + expected := []Node{ + Node{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + }, + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + }, + Node{ + CreatedAt: time.Date(2014, 5, 31, 8, 23, 12, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "f28b870f-a063-498a-8b12-7025e5b1caa6", + }, + ID: "b70481dd-7edf-4dbb-a44b-41cc7679d4fb", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + Status: "ADDING", + UpdatedAt: time.Date(2014, 5, 31, 8, 23, 26, 0, time.UTC), + }, + Node{ + CreatedAt: time.Date(2014, 5, 31, 8, 23, 18, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "a3d3a6b3-e4e4-496f-9a3d-5c987163e458", + }, + ID: "ced9ddc8-6fae-4e72-9457-16ead52b5515", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + Status: "ADD_FAILED", + StatusDetail: "Unable to communicate with network device", + UpdatedAt: time.Date(2014, 5, 31, 8, 24, 36, 0, time.UTC), + }, + } + + count := 0 + err := ListNodes(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2").EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractNodes(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestCreateNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes", 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, "Accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + } + } + `) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, ` + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + }, + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + } + `) + }) + + expected := &Node{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + }, + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + } + + actual, err := CreateNode(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "d95ae0c4-6ab8-4873-b82f-f8433840cff2").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expected, actual) +} + +func TestListNodesDetails(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/details", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, ` + [ + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "cloud_network": { + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "private_ip_v4": "192.168.100.5", + "updated": "2014-05-25T02:28:44Z" + }, + "created": "2014-05-30T02:18:42Z", + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + "name": "RCv3TestServer1", + "updated": "2014-05-30T02:19:18Z" + }, + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + "name": "RCv3Test", + "node_counts": { + "cloud_servers": 3, + "external": 4, + "total": 7 + }, + "port": 80, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.5" + }, + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + } + ] + `) + }) + + expected := []NodeDetails{ + NodeDetails{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + CloudNetwork struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + CloudNetwork: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + Name: "RC-CLOUD", + PrivateIPv4: "192.168.100.5", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC), + Name: "RCv3TestServer1", + UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC), + }, + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: Pool{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + Name: "RCv3Test", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 3, + External: 4, + Total: 7, + }, + Port: 80, + Status: "ACTIVE", + VirtualIP: "203.0.113.5", + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + }, + } + count := 0 + err := ListNodesDetails(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2").EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractNodesDetails(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestGetNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/1860451d-fb89-45b8-b54e-151afceb50e5", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + }, + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + } + `) + }) + + expected := &Node{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + }, + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + } + + actual, err := GetNode(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "1860451d-fb89-45b8-b54e-151afceb50e5").Extract() + th.AssertNoErr(t, err) + + th.AssertDeepEquals(t, expected, actual) +} + +func TestDeleteNode(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/1860451d-fb89-45b8-b54e-151afceb50e5", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := DeleteNode(client.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "1860451d-fb89-45b8-b54e-151afceb50e5").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestGetNodeDetails(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2/nodes/d95ae0c4-6ab8-4873-b82f-f8433840cff2/details", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, ` + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "cloud_network": { + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "private_ip_v4": "192.168.100.5", + "updated": "2014-05-25T02:28:44Z" + }, + "created": "2014-05-30T02:18:42Z", + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + "name": "RCv3TestServer1", + "updated": "2014-05-30T02:19:18Z" + }, + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + "name": "RCv3Test", + "node_counts": { + "cloud_servers": 3, + "external": 4, + "total": 7 + }, + "port": 80, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.5" + }, + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + } + `) + }) + + expected := &NodeDetails{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + CloudNetwork struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + CloudNetwork: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + Name: "RC-CLOUD", + PrivateIPv4: "192.168.100.5", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC), + Name: "RCv3TestServer1", + UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC), + }, + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: Pool{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + Name: "RCv3Test", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 3, + External: 4, + Total: 7, + }, + Port: 80, + Status: "ACTIVE", + VirtualIP: "203.0.113.5", + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + } + + actual, err := GetNodeDetails(fake.ServiceClient(), "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", "d95ae0c4-6ab8-4873-b82f-f8433840cff2").Extract() + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) +} + +func TestCreateNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/nodes", 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, "Accept", "application/json") + th.TestJSONRequest(t, r, ` + [ + { + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + } + }, + { + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "load_balancer_pool": { + "id": "33021100-4abf-4836-9080-465a6d87ab68" + } + } + ] + `) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, ` + [ + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + }, + "status": "ADDING", + "status_detail": null, + "updated": null + }, + { + "created": "2014-05-31T08:23:12Z", + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "id": "b70481dd-7edf-4dbb-a44b-41cc7679d4fb", + "load_balancer_pool": { + "id": "33021100-4abf-4836-9080-465a6d87ab68" + }, + "status": "ADDING", + "status_detail": null, + "updated": null + } + ] + `) + }) + + expected := []Node{ + Node{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + }, + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + Status: "ADDING", + }, + Node{ + CreatedAt: time.Date(2014, 5, 31, 8, 23, 12, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + }, + ID: "b70481dd-7edf-4dbb-a44b-41cc7679d4fb", + LoadBalancerPool: struct { + ID string `mapstructure:"id"` + }{ + ID: "33021100-4abf-4836-9080-465a6d87ab68", + }, + Status: "ADDING", + }, + } + + opts := NodesOpts{ + NodeOpts{ + ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + PoolID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + NodeOpts{ + ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + PoolID: "33021100-4abf-4836-9080-465a6d87ab68", + }, + } + actual, err := CreateNodes(fake.ServiceClient(), opts).Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestDeleteNodes(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/nodes", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + th.TestJSONRequest(t, r, ` + [ + { + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2" + } + }, + { + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + }, + "load_balancer_pool": { + "id": "33021100-4abf-4836-9080-465a6d87ab68" + } + } + ] + `) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + opts := NodesOpts{ + NodeOpts{ + ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + PoolID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + }, + NodeOpts{ + ServerID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + PoolID: "33021100-4abf-4836-9080-465a6d87ab68", + }, + } + err := DeleteNodes(client.ServiceClient(), opts).ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListNodesForServerDetails(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/load_balancer_pools/nodes/details", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, ` + [ + { + "created": "2014-05-30T03:23:42Z", + "id": "1860451d-fb89-45b8-b54e-151afceb50e5", + "load_balancer_pool": { + "id": "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + "name": "RCv3Test", + "node_counts": { + "cloud_servers": 3, + "external": 4, + "total": 7 + }, + "port": 80, + "status": "ACTIVE", + "status_detail": null, + "virtual_ip": "203.0.113.5" + }, + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + } + ] + `) + }) + + expected := []NodeDetailsForServer{ + NodeDetailsForServer{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + ID: "1860451d-fb89-45b8-b54e-151afceb50e5", + LoadBalancerPool: Pool{ + ID: "d6d3aa7c-dfa5-4e61-96ee-1d54ac1075d2", + Name: "RCv3Test", + NodeCounts: struct { + CloudServers int `mapstructure:"cloud_servers"` + External int `mapstructure:"external"` + Total int `mapstructure:"total"` + }{ + CloudServers: 3, + External: 4, + Total: 7, + }, + Port: 80, + Status: "ACTIVE", + VirtualIP: "203.0.113.5", + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + }, + } + count := 0 + err := ListNodesDetailsForServer(fake.ServiceClient(), "07426958-1ebf-4c38-b032-d456820ca21a").EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractNodesDetailsForServer(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/results.go new file mode 100644 index 0000000000..e5e914b1e2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/results.go @@ -0,0 +1,505 @@ +package lbpools + +import ( + "fmt" + "reflect" + "time" + + "github.com/mitchellh/mapstructure" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// Pool represents a load balancer pool associated with a RackConnect configuration. +type Pool struct { + // The unique ID of the load balancer pool. + ID string `mapstructure:"id"` + // The name of the load balancer pool. + Name string `mapstructure:"name"` + // The node counts associated witht the load balancer pool. + NodeCounts struct { + // The number of nodes associated with this LB pool for this account. + CloudServers int `mapstructure:"cloud_servers"` + // The number of nodes associated with this LB pool from other accounts. + External int `mapstructure:"external"` + // The total number of nodes associated with this LB pool. + Total int `mapstructure:"total"` + } `mapstructure:"node_counts"` + // The port of the LB pool + Port int `mapstructure:"port"` + // The status of the LB pool + Status string `mapstructure:"status"` + // The details of the status of the LB pool + StatusDetail string `mapstructure:"status_detail"` + // The virtual IP of the LB pool + VirtualIP string `mapstructure:"virtual_ip"` +} + +// PoolPage is the page returned by a pager when traversing over a +// collection of Pools. +type PoolPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a PoolPage contains no Pools. +func (r PoolPage) IsEmpty() (bool, error) { + cns, err := ExtractPools(r) + if err != nil { + return true, err + } + return len(cns) == 0, nil +} + +// ExtractPools extracts and returns Pools. It is used while iterating over +// an lbpools.List call. +func ExtractPools(page pagination.Page) ([]Pool, error) { + var res []Pool + err := mapstructure.Decode(page.(PoolPage).Body, &res) + return res, err +} + +// GetResult represents the result of a Get operation. +type GetResult struct { + gophercloud.Result +} + +// Extract is a function that extracts an LBPool from a GetResult. +func (r GetResult) Extract() (*Pool, error) { + if r.Err != nil { + return nil, r.Err + } + var res Pool + err := mapstructure.Decode(r.Body, &res) + return &res, err +} + +// Node represents a load balancer pool node associated with a RackConnect configuration. +type Node struct { + // The unique ID of the LB node. + ID string `mapstructure:"id"` + // The cloud server (node) of the load balancer pool. + CloudServer struct { + // The cloud server ID. + ID string `mapstructure:"id"` + } `mapstructure:"cloud_server"` + // The load balancer pool. + LoadBalancerPool struct { + // The LB pool ID. + ID string `mapstructure:"id"` + } `mapstructure:"load_balancer_pool"` + // The status of the LB pool. + Status string `mapstructure:"status"` + // The details of the status of the LB pool. + StatusDetail string `mapstructure:"status_detail"` + // The time the LB node was created. + CreatedAt time.Time `mapstructure:"-"` + // The time the LB node was last updated. + UpdatedAt time.Time `mapstructure:"-"` +} + +// NodePage is the page returned by a pager when traversing over a +// collection of Nodes. +type NodePage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a NodePage contains no Nodes. +func (r NodePage) IsEmpty() (bool, error) { + n, err := ExtractNodes(r) + if err != nil { + return true, err + } + return len(n) == 0, nil +} + +// ExtractNodes extracts and returns a slice of Nodes. It is used while iterating over +// an lbpools.ListNodes call. +func ExtractNodes(page pagination.Page) ([]Node, error) { + var res []Node + casted := page.(NodePage).Body + err := mapstructure.Decode(casted, &res) + + var rawNodes []interface{} + switch casted.(type) { + case interface{}: + rawNodes = casted.([]interface{}) + default: + return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } + + for i := range rawNodes { + thisNode := (rawNodes[i]).(map[string]interface{}) + + if t, ok := thisNode["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CreatedAt = creationTime + } + + if t, ok := thisNode["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].UpdatedAt = updatedTime + } + } + + return res, err +} + +// NodeResult represents a result that can be extracted as a Node. +type NodeResult struct { + gophercloud.Result +} + +// CreateNodeResult represents the result of an CreateNode operation. +type CreateNodeResult struct { + NodeResult +} + +// GetNodeResult represents the result of an GetNode operation. +type GetNodeResult struct { + NodeResult +} + +// Extract is a function that extracts a Node from a NodeResult. +func (r NodeResult) Extract() (*Node, error) { + if r.Err != nil { + return nil, r.Err + } + var res Node + err := mapstructure.Decode(r.Body, &res) + + b := r.Body.(map[string]interface{}) + + if date, ok := b["created"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.CreatedAt = t + } + + if date, ok := b["updated"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.UpdatedAt = t + } + + return &res, err +} + +// NodeDetails represents a load balancer pool node associated with a RackConnect configuration +// with all its details. +type NodeDetails struct { + // The unique ID of the LB node. + ID string `mapstructure:"id"` + // The cloud server (node) of the load balancer pool. + CloudServer struct { + // The cloud server ID. + ID string `mapstructure:"id"` + // The name of the server. + Name string `mapstructure:"name"` + // The cloud network for the cloud server. + CloudNetwork struct { + // The network ID. + ID string `mapstructure:"id"` + // The network name. + Name string `mapstructure:"name"` + // The network's private IPv4 address. + PrivateIPv4 string `mapstructure:"private_ip_v4"` + // The IP range for the network. + CIDR string `mapstructure:"cidr"` + // The datetime the network was created. + CreatedAt time.Time `mapstructure:"-"` + // The last datetime the network was updated. + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + // The datetime the server was created. + CreatedAt time.Time `mapstructure:"-"` + // The datetime the server was last updated. + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_server"` + // The load balancer pool. + LoadBalancerPool Pool `mapstructure:"load_balancer_pool"` + // The status of the LB pool. + Status string `mapstructure:"status"` + // The details of the status of the LB pool. + StatusDetail string `mapstructure:"status_detail"` + // The time the LB node was created. + CreatedAt time.Time `mapstructure:"-"` + // The time the LB node was last updated. + UpdatedAt time.Time `mapstructure:"-"` +} + +// NodeDetailsPage is the page returned by a pager when traversing over a +// collection of NodeDetails. +type NodeDetailsPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a NodeDetailsPage contains no NodeDetails. +func (r NodeDetailsPage) IsEmpty() (bool, error) { + n, err := ExtractNodesDetails(r) + if err != nil { + return true, err + } + return len(n) == 0, nil +} + +// ExtractNodesDetails extracts and returns a slice of NodeDetails. It is used while iterating over +// an lbpools.ListNodesDetails call. +func ExtractNodesDetails(page pagination.Page) ([]NodeDetails, error) { + var res []NodeDetails + casted := page.(NodeDetailsPage).Body + err := mapstructure.Decode(casted, &res) + + var rawNodesDetails []interface{} + switch casted.(type) { + case interface{}: + rawNodesDetails = casted.([]interface{}) + default: + return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } + + for i := range rawNodesDetails { + thisNodeDetails := (rawNodesDetails[i]).(map[string]interface{}) + + if t, ok := thisNodeDetails["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CreatedAt = creationTime + } + + if t, ok := thisNodeDetails["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].UpdatedAt = updatedTime + } + + if cs, ok := thisNodeDetails["cloud_server"].(map[string]interface{}); ok { + if t, ok := cs["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.CreatedAt = creationTime + } + if t, ok := cs["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.UpdatedAt = updatedTime + } + if cn, ok := cs["cloud_network"].(map[string]interface{}); ok { + if t, ok := cn["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.CloudNetwork.CreatedAt = creationTime + } + if t, ok := cn["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.CloudNetwork.UpdatedAt = updatedTime + } + } + } + } + + return res, err +} + +// GetNodeDetailsResult represents the result of an NodeDetails operation. +type GetNodeDetailsResult struct { + gophercloud.Result +} + +// Extract is a function that extracts a NodeDetails from a NodeDetailsResult. +func (r GetNodeDetailsResult) Extract() (*NodeDetails, error) { + if r.Err != nil { + return nil, r.Err + } + var res NodeDetails + err := mapstructure.Decode(r.Body, &res) + + b := r.Body.(map[string]interface{}) + + if date, ok := b["created"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.CreatedAt = t + } + + if date, ok := b["updated"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.UpdatedAt = t + } + + if cs, ok := b["cloud_server"].(map[string]interface{}); ok { + if t, ok := cs["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.CreatedAt = creationTime + } + if t, ok := cs["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.UpdatedAt = updatedTime + } + if cn, ok := cs["cloud_network"].(map[string]interface{}); ok { + if t, ok := cn["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.CloudNetwork.CreatedAt = creationTime + } + if t, ok := cn["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.CloudNetwork.UpdatedAt = updatedTime + } + } + } + + return &res, err +} + +// DeleteNodeResult represents the result of a DeleteNode operation. +type DeleteNodeResult struct { + gophercloud.ErrResult +} + +// CreateNodesResult represents the result of a CreateNodes operation. +type CreateNodesResult struct { + gophercloud.Result +} + +// Extract is a function that extracts a slice of Nodes from a CreateNodesResult. +func (r CreateNodesResult) Extract() ([]Node, error) { + if r.Err != nil { + return nil, r.Err + } + var res []Node + err := mapstructure.Decode(r.Body, &res) + + b := r.Body.([]interface{}) + for i := range b { + if date, ok := b[i].(map[string]interface{})["created"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res[i].CreatedAt = t + } + if date, ok := b[i].(map[string]interface{})["updated"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res[i].UpdatedAt = t + } + } + + return res, err +} + +// DeleteNodesResult represents the result of a DeleteNodes operation. +type DeleteNodesResult struct { + gophercloud.ErrResult +} + +// NodeDetailsForServer represents a load balancer pool node associated with a RackConnect configuration +// with all its details for a particular server. +type NodeDetailsForServer struct { + // The unique ID of the LB node. + ID string `mapstructure:"id"` + // The load balancer pool. + LoadBalancerPool Pool `mapstructure:"load_balancer_pool"` + // The status of the LB pool. + Status string `mapstructure:"status"` + // The details of the status of the LB pool. + StatusDetail string `mapstructure:"status_detail"` + // The time the LB node was created. + CreatedAt time.Time `mapstructure:"-"` + // The time the LB node was last updated. + UpdatedAt time.Time `mapstructure:"-"` +} + +// NodeDetailsForServerPage is the page returned by a pager when traversing over a +// collection of NodeDetailsForServer. +type NodeDetailsForServerPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a NodeDetailsForServerPage contains no NodeDetailsForServer. +func (r NodeDetailsForServerPage) IsEmpty() (bool, error) { + n, err := ExtractNodesDetailsForServer(r) + if err != nil { + return true, err + } + return len(n) == 0, nil +} + +// ExtractNodesDetailsForServer extracts and returns a slice of NodeDetailsForServer. It is used while iterating over +// an lbpools.ListNodesDetailsForServer call. +func ExtractNodesDetailsForServer(page pagination.Page) ([]NodeDetailsForServer, error) { + var res []NodeDetailsForServer + casted := page.(NodeDetailsForServerPage).Body + err := mapstructure.Decode(casted, &res) + + var rawNodesDetails []interface{} + switch casted.(type) { + case interface{}: + rawNodesDetails = casted.([]interface{}) + default: + return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } + + for i := range rawNodesDetails { + thisNodeDetails := (rawNodesDetails[i]).(map[string]interface{}) + + if t, ok := thisNodeDetails["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CreatedAt = creationTime + } + + if t, ok := thisNodeDetails["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].UpdatedAt = updatedTime + } + } + + return res, err +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/urls.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/urls.go new file mode 100644 index 0000000000..c238239f61 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/lbpools/urls.go @@ -0,0 +1,49 @@ +package lbpools + +import "github.com/rackspace/gophercloud" + +var root = "load_balancer_pools" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(root) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(root, id) +} + +func listNodesURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(root, id, "nodes") +} + +func createNodeURL(c *gophercloud.ServiceClient, id string) string { + return listNodesURL(c, id) +} + +func listNodesDetailsURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(root, id, "nodes", "details") +} + +func nodeURL(c *gophercloud.ServiceClient, poolID, nodeID string) string { + return c.ServiceURL(root, poolID, "nodes", nodeID) +} + +func deleteNodeURL(c *gophercloud.ServiceClient, poolID, nodeID string) string { + return nodeURL(c, poolID, nodeID) +} + +func nodeDetailsURL(c *gophercloud.ServiceClient, poolID, nodeID string) string { + return c.ServiceURL(root, poolID, "nodes", nodeID, "details") +} + +func createNodesURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(root, "nodes") +} + +func deleteNodesURL(c *gophercloud.ServiceClient) string { + return createNodesURL(c) +} + +func listNodesForServerURL(c *gophercloud.ServiceClient, serverID string) string { + return c.ServiceURL(root, "nodes", "details?cloud_server_id="+serverID) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/requests.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/requests.go new file mode 100644 index 0000000000..1164260109 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/requests.go @@ -0,0 +1,50 @@ +package publicips + +import ( + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// List returns all public IPs. +func List(c *gophercloud.ServiceClient) pagination.Pager { + url := listURL(c) + createPage := func(r pagination.PageResult) pagination.Page { + return PublicIPPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} + +// Create adds a public IP to the server with the given serverID. +func Create(c *gophercloud.ServiceClient, serverID string) CreateResult { + var res CreateResult + reqBody := map[string]interface{}{ + "cloud_server": map[string]string{ + "id": serverID, + }, + } + _, res.Err = c.Post(createURL(c), reqBody, &res.Body, nil) + return res +} + +// ListForServer returns all public IPs for the server with the given serverID. +func ListForServer(c *gophercloud.ServiceClient, serverID string) pagination.Pager { + url := listForServerURL(c, serverID) + createPage := func(r pagination.PageResult) pagination.Page { + return PublicIPPage{pagination.SinglePageBase(r)} + } + return pagination.NewPager(c, url, createPage) +} + +// Get retrieves the public IP with the given id. +func Get(c *gophercloud.ServiceClient, id string) GetResult { + var res GetResult + _, res.Err = c.Get(getURL(c, id), &res.Body, nil) + return res +} + +// Delete removes the public IP with the given id. +func Delete(c *gophercloud.ServiceClient, id string) DeleteResult { + var res DeleteResult + _, res.Err = c.Delete(deleteURL(c, id), nil) + return res +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/requests_test.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/requests_test.go new file mode 100644 index 0000000000..61da2b03d9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/requests_test.go @@ -0,0 +1,378 @@ +package publicips + +import ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/rackspace/gophercloud/pagination" + th "github.com/rackspace/gophercloud/testhelper" + "github.com/rackspace/gophercloud/testhelper/client" + fake "github.com/rackspace/gophercloud/testhelper/client" +) + +func TestListIPs(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/public_ips", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `[ + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "cloud_network": { + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "private_ip_v4": "192.168.100.5", + "updated": "2014-05-25T02:28:44Z" + }, + "created": "2014-05-30T02:18:42Z", + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + "name": "RCv3TestServer1", + "updated": "2014-05-30T02:19:18Z" + }, + "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450", + "public_ip_v4": "203.0.113.110", + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + } + ]`) + }) + + expected := []PublicIP{ + PublicIP{ + ID: "2d0f586b-37a7-4ae0-adac-2743d5feb450", + PublicIPv4: "203.0.113.110", + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + CloudNetwork struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + CloudNetwork: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + Name: "RC-CLOUD", + PrivateIPv4: "192.168.100.5", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC), + Name: "RCv3TestServer1", + UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC), + }, + Status: "ACTIVE", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + }, + } + + count := 0 + err := List(fake.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractPublicIPs(page) + th.AssertNoErr(t, err) + + th.CheckDeepEquals(t, expected, actual) + + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} + +func TestCreateIP(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/public_ips", 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, "Accept", "application/json") + th.TestJSONRequest(t, r, ` + { + "cloud_server": { + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2" + } + } + `) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + fmt.Fprintf(w, ` + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "cloud_network": { + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "private_ip_v4": "192.168.100.5", + "updated": "2014-05-25T02:28:44Z" + }, + "created": "2014-05-30T02:18:42Z", + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + "name": "RCv3TestServer1", + "updated": "2014-05-30T02:19:18Z" + }, + "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450", + "status": "ADDING" + }`) + }) + + expected := &PublicIP{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + CloudNetwork struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + CloudNetwork: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + Name: "RC-CLOUD", + PrivateIPv4: "192.168.100.5", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC), + Name: "RCv3TestServer1", + UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC), + }, + ID: "2d0f586b-37a7-4ae0-adac-2743d5feb450", + Status: "ADDING", + } + + actual, err := Create(fake.ServiceClient(), "d95ae0c4-6ab8-4873-b82f-f8433840cff2").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestGetIP(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/public_ips/2d0f586b-37a7-4ae0-adac-2743d5feb450", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, ` + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "cloud_network": { + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "private_ip_v4": "192.168.100.5", + "updated": "2014-05-25T02:28:44Z" + }, + "created": "2014-05-30T02:18:42Z", + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + "name": "RCv3TestServer1", + "updated": "2014-05-30T02:19:18Z" + }, + "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450", + "public_ip_v4": "203.0.113.110", + "status": "ACTIVE", + "status_detail": null, + "updated": "2014-05-30T03:24:18Z" + }`) + }) + + expected := &PublicIP{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + CloudNetwork struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + CloudNetwork: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + Name: "RC-CLOUD", + PrivateIPv4: "192.168.100.5", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC), + Name: "RCv3TestServer1", + UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC), + }, + ID: "2d0f586b-37a7-4ae0-adac-2743d5feb450", + Status: "ACTIVE", + PublicIPv4: "203.0.113.110", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + } + + actual, err := Get(fake.ServiceClient(), "2d0f586b-37a7-4ae0-adac-2743d5feb450").Extract() + th.AssertNoErr(t, err) + th.AssertDeepEquals(t, expected, actual) +} + +func TestDeleteIP(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/public_ips/2d0f586b-37a7-4ae0-adac-2743d5feb450", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "DELETE") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusNoContent) + }) + + err := Delete(client.ServiceClient(), "2d0f586b-37a7-4ae0-adac-2743d5feb450").ExtractErr() + th.AssertNoErr(t, err) +} + +func TestListForServer(t *testing.T) { + th.SetupHTTP() + defer th.TeardownHTTP() + th.Mux.HandleFunc("/public_ips", func(w http.ResponseWriter, r *http.Request) { + th.TestMethod(t, r, "GET") + th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) + th.TestHeader(t, r, "Accept", "application/json") + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, ` + [ + { + "created": "2014-05-30T03:23:42Z", + "cloud_server": { + "cloud_network": { + "cidr": "192.168.100.0/24", + "created": "2014-05-25T01:23:42Z", + "id": "07426958-1ebf-4c38-b032-d456820ca21a", + "name": "RC-CLOUD", + "private_ip_v4": "192.168.100.5", + "updated": "2014-05-25T02:28:44Z" + }, + "created": "2014-05-30T02:18:42Z", + "id": "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + "name": "RCv3TestServer1", + "updated": "2014-05-30T02:19:18Z" + }, + "id": "2d0f586b-37a7-4ae0-adac-2743d5feb450", + "public_ip_v4": "203.0.113.110", + "status": "ACTIVE", + "updated": "2014-05-30T03:24:18Z" + } + ]`) + }) + + expected := []PublicIP{ + PublicIP{ + CreatedAt: time.Date(2014, 5, 30, 3, 23, 42, 0, time.UTC), + CloudServer: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + CloudNetwork struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "d95ae0c4-6ab8-4873-b82f-f8433840cff2", + CloudNetwork: struct { + ID string `mapstructure:"id"` + Name string `mapstructure:"name"` + PrivateIPv4 string `mapstructure:"private_ip_v4"` + CIDR string `mapstructure:"cidr"` + CreatedAt time.Time `mapstructure:"-"` + UpdatedAt time.Time `mapstructure:"-"` + }{ + ID: "07426958-1ebf-4c38-b032-d456820ca21a", + CIDR: "192.168.100.0/24", + CreatedAt: time.Date(2014, 5, 25, 1, 23, 42, 0, time.UTC), + Name: "RC-CLOUD", + PrivateIPv4: "192.168.100.5", + UpdatedAt: time.Date(2014, 5, 25, 2, 28, 44, 0, time.UTC), + }, + CreatedAt: time.Date(2014, 5, 30, 2, 18, 42, 0, time.UTC), + Name: "RCv3TestServer1", + UpdatedAt: time.Date(2014, 5, 30, 2, 19, 18, 0, time.UTC), + }, + ID: "2d0f586b-37a7-4ae0-adac-2743d5feb450", + Status: "ACTIVE", + PublicIPv4: "203.0.113.110", + UpdatedAt: time.Date(2014, 5, 30, 3, 24, 18, 0, time.UTC), + }, + } + count := 0 + err := ListForServer(fake.ServiceClient(), "d95ae0c4-6ab8-4873-b82f-f8433840cff2").EachPage(func(page pagination.Page) (bool, error) { + count++ + actual, err := ExtractPublicIPs(page) + th.AssertNoErr(t, err) + th.CheckDeepEquals(t, expected, actual) + return true, nil + }) + th.AssertNoErr(t, err) + th.CheckEquals(t, count, 1) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/results.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/results.go new file mode 100644 index 0000000000..132cf770a0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/results.go @@ -0,0 +1,221 @@ +package publicips + +import ( + "fmt" + "reflect" + "time" + + "github.com/mitchellh/mapstructure" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/pagination" +) + +// PublicIP represents a public IP address. +type PublicIP struct { + // The unique ID of the public IP. + ID string `mapstructure:"id"` + // The IPv4 address of the public IP. + PublicIPv4 string `mapstructure:"public_ip_v4"` + // The cloud server (node) of the public IP. + CloudServer struct { + // The cloud server ID. + ID string `mapstructure:"id"` + // The name of the server. + Name string `mapstructure:"name"` + // The cloud network for the cloud server. + CloudNetwork struct { + // The network ID. + ID string `mapstructure:"id"` + // The network name. + Name string `mapstructure:"name"` + // The network's private IPv4 address. + PrivateIPv4 string `mapstructure:"private_ip_v4"` + // The IP range for the network. + CIDR string `mapstructure:"cidr"` + // The datetime the network was created. + CreatedAt time.Time `mapstructure:"-"` + // The last datetime the network was updated. + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_network"` + // The datetime the server was created. + CreatedAt time.Time `mapstructure:"-"` + // The datetime the server was last updated. + UpdatedAt time.Time `mapstructure:"-"` + } `mapstructure:"cloud_server"` + // The status of the public IP. + Status string `mapstructure:"status"` + // The details of the status of the public IP. + StatusDetail string `mapstructure:"status_detail"` + // The time the public IP was created. + CreatedAt time.Time `mapstructure:"-"` + // The time the public IP was last updated. + UpdatedAt time.Time `mapstructure:"-"` +} + +// PublicIPPage is the page returned by a pager when traversing over a +// collection of PublicIPs. +type PublicIPPage struct { + pagination.SinglePageBase +} + +// IsEmpty returns true if a PublicIPPage contains no PublicIPs. +func (r PublicIPPage) IsEmpty() (bool, error) { + n, err := ExtractPublicIPs(r) + if err != nil { + return true, err + } + return len(n) == 0, nil +} + +// ExtractPublicIPs extracts and returns a slice of PublicIPs. It is used while iterating over +// a publicips.List call. +func ExtractPublicIPs(page pagination.Page) ([]PublicIP, error) { + var res []PublicIP + casted := page.(PublicIPPage).Body + err := mapstructure.Decode(casted, &res) + + var rawNodesDetails []interface{} + switch casted.(type) { + case interface{}: + rawNodesDetails = casted.([]interface{}) + default: + return res, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted)) + } + + for i := range rawNodesDetails { + thisNodeDetails := (rawNodesDetails[i]).(map[string]interface{}) + + if t, ok := thisNodeDetails["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CreatedAt = creationTime + } + + if t, ok := thisNodeDetails["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].UpdatedAt = updatedTime + } + + if cs, ok := thisNodeDetails["cloud_server"].(map[string]interface{}); ok { + if t, ok := cs["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.CreatedAt = creationTime + } + if t, ok := cs["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.UpdatedAt = updatedTime + } + if cn, ok := cs["cloud_network"].(map[string]interface{}); ok { + if t, ok := cn["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.CloudNetwork.CreatedAt = creationTime + } + if t, ok := cn["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return res, err + } + res[i].CloudServer.CloudNetwork.UpdatedAt = updatedTime + } + } + } + } + + return res, err +} + +// PublicIPResult represents a result that can be extracted into a PublicIP. +type PublicIPResult struct { + gophercloud.Result +} + +// CreateResult represents the result of a Create operation. +type CreateResult struct { + PublicIPResult +} + +// GetResult represents the result of a Get operation. +type GetResult struct { + PublicIPResult +} + +// Extract is a function that extracts a PublicIP from a PublicIPResult. +func (r PublicIPResult) Extract() (*PublicIP, error) { + if r.Err != nil { + return nil, r.Err + } + var res PublicIP + err := mapstructure.Decode(r.Body, &res) + + b := r.Body.(map[string]interface{}) + + if date, ok := b["created"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.CreatedAt = t + } + + if date, ok := b["updated"]; ok && date != nil { + t, err := time.Parse(time.RFC3339, date.(string)) + if err != nil { + return nil, err + } + res.UpdatedAt = t + } + + if cs, ok := b["cloud_server"].(map[string]interface{}); ok { + if t, ok := cs["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.CreatedAt = creationTime + } + if t, ok := cs["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.UpdatedAt = updatedTime + } + if cn, ok := cs["cloud_network"].(map[string]interface{}); ok { + if t, ok := cn["created"].(string); ok && t != "" { + creationTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.CloudNetwork.CreatedAt = creationTime + } + if t, ok := cn["updated"].(string); ok && t != "" { + updatedTime, err := time.Parse(time.RFC3339, t) + if err != nil { + return &res, err + } + res.CloudServer.CloudNetwork.UpdatedAt = updatedTime + } + } + } + + return &res, err +} + +// DeleteResult represents the result of a Delete operation. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/urls.go b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/urls.go new file mode 100644 index 0000000000..6f310be4e8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/rackconnect/v3/publicips/urls.go @@ -0,0 +1,25 @@ +package publicips + +import "github.com/rackspace/gophercloud" + +var root = "public_ips" + +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(root) +} + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(root) +} + +func listForServerURL(c *gophercloud.ServiceClient, serverID string) string { + return c.ServiceURL(root + "?cloud_server_id=" + serverID) +} + +func getURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL(root, id) +} + +func deleteURL(c *gophercloud.ServiceClient, id string) string { + return getURL(c, id) +} diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/acceptancetest b/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/acceptancetest old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/bootstrap b/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/bootstrap old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/cibuild b/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/cibuild old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/test b/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/test old mode 100644 new mode 100755 diff --git a/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/unittest b/Godeps/_workspace/src/github.com/rackspace/gophercloud/script/unittest old mode 100644 new mode 100755