Commit 8357e152 authored by Thom May's avatar Thom May

Basic Rackspace cloud support

This enables all but Load Balancer support for the Rackspace public cloud platform.
parent 6f84bdab
......@@ -203,8 +203,8 @@
},
{
"ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v1.0.0",
"Rev": "da56de6a59e53fdd61be1b5d9b87df34c47ac420"
"Comment": "v1.0.0-336-g2a6e319",
"Rev": "2a6e3190447abe5d000f951595ead1cf98df72d8"
},
{
"ImportPath": "github.com/skynetservices/skydns/msg",
......
......@@ -4,6 +4,8 @@ install:
go:
- 1.1
- 1.2
- 1.3
- 1.4
- tip
script: script/cibuild
after_success:
......@@ -12,3 +14,4 @@ after_success:
- go get github.com/mattn/goveralls
- export PATH=$PATH:$HOME/gopath/bin/
- goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
sudo: false
......@@ -11,7 +11,7 @@ As a contributor you will need to setup your workspace in a slightly different
way than just downloading it. Here are the basic installation instructions:
1. Configure your `$GOPATH` and run `go get` as described in the main
[README](/#how-to-install).
[README](/README.md#how-to-install).
2. Move into the directory that houses your local repository:
......
......@@ -238,7 +238,7 @@ err := pager.EachPage(func(page pagination.Page) (bool, error) {
imageList, err := images.ExtractImages(page)
for _, i := range imageList {
// "i" will be a images.Image
// "i" will be an images.Image
}
})
```
......@@ -284,7 +284,7 @@ err := pager.EachPage(func(page pagination.Page) (bool, error) {
imageList, err := images.ExtractImages(page)
for _, i := range imageList {
// "i" will be a images.Image
// "i" will be an images.Image
}
})
```
......
......@@ -13,9 +13,9 @@ import (
func TestSnapshots(t *testing.T) {
client, err := newClient()
client, err := newClient(t)
th.AssertNoErr(t, err)
v, err := volumes.Create(client, &volumes.CreateOpts{
Name: "gophercloud-test-volume",
Size: 1,
......
......@@ -13,7 +13,7 @@ import (
th "github.com/rackspace/gophercloud/testhelper"
)
func newClient() (*gophercloud.ServiceClient, error) {
func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err)
......@@ -26,7 +26,7 @@ func newClient() (*gophercloud.ServiceClient, error) {
}
func TestVolumes(t *testing.T) {
client, err := newClient()
client, err := newClient(t)
th.AssertNoErr(t, err)
cv, err := volumes.Create(client, &volumes.CreateOpts{
......
......@@ -12,7 +12,7 @@ import (
)
func TestVolumeTypes(t *testing.T) {
client, err := newClient()
client, err := newClient(t)
th.AssertNoErr(t, err)
vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{
......
......@@ -5,10 +5,10 @@ package v2
import (
"testing"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/smashwilson/gophercloud/acceptance/tools"
)
func TestBootFromVolume(t *testing.T) {
......@@ -37,14 +37,19 @@ func TestBootFromVolume(t *testing.T) {
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: "3",
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
}
server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
serverCreateOpts,
bd,
}).Extract()
th.AssertNoErr(t, err)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
t.Logf("Created server: %+v\n", server)
//defer deleteServer(t, client, server)
defer servers.Delete(client, server.ID)
t.Logf("Deleting server [%s]...", name)
}
// +build acceptance
package v2
import (
"crypto/rand"
"crypto/rsa"
"testing"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
"code.google.com/p/go.crypto/ssh"
)
const keyName = "gophercloud_test_key_pair"
func TestCreateServerWithKeyPair(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
if testing.Short() {
t.Skip("Skipping test that requires server creation in short mode.")
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
publicKey := privateKey.PublicKey
pub, err := ssh.NewPublicKey(&publicKey)
th.AssertNoErr(t, err)
pubBytes := ssh.MarshalAuthorizedKey(pub)
pk := string(pubBytes)
kp, err := keypairs.Create(client, keypairs.CreateOpts{
Name: keyName,
PublicKey: pk,
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created key pair: %s\n", kp)
choices, err := ComputeChoicesFromEnv()
th.AssertNoErr(t, err)
name := tools.RandomString("Gophercloud-", 8)
t.Logf("Creating server [%s] with key pair.", name)
serverCreateOpts := servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
}
server, err := servers.Create(client, keypairs.CreateOptsExt{
serverCreateOpts,
keyName,
}).Extract()
th.AssertNoErr(t, err)
defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatalf("Unable to wait for server: %v", err)
}
server, err = servers.Get(client, server.ID).Extract()
t.Logf("Created server: %+v\n", server)
th.AssertNoErr(t, err)
th.AssertEquals(t, server.KeyName, keyName)
t.Logf("Deleting key pair [%s]...", kp.Name)
err = keypairs.Delete(client, keyName).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleting server [%s]...", name)
}
// +build acceptance compute defsecrules
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
dsr "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/defsecrules"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestSecDefRules(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
id := createDefRule(t, client)
listDefRules(t, client)
getDefRule(t, client, id)
deleteDefRule(t, client, id)
}
func createDefRule(t *testing.T, client *gophercloud.ServiceClient) string {
opts := dsr.CreateOpts{
FromPort: tools.RandomInt(80, 89),
ToPort: tools.RandomInt(90, 99),
IPProtocol: "TCP",
CIDR: "0.0.0.0/0",
}
rule, err := dsr.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created default rule %s", rule.ID)
return rule.ID
}
func listDefRules(t *testing.T, client *gophercloud.ServiceClient) {
err := dsr.List(client).EachPage(func(page pagination.Page) (bool, error) {
drList, err := dsr.ExtractDefaultRules(page)
th.AssertNoErr(t, err)
for _, dr := range drList {
t.Logf("Listing default rule %s: Name [%s] From Port [%s] To Port [%s] Protocol [%s]",
dr.ID, dr.FromPort, dr.ToPort, dr.IPProtocol)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getDefRule(t *testing.T, client *gophercloud.ServiceClient, id string) {
rule, err := dsr.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting rule %s: %#v", id, rule)
}
func deleteDefRule(t *testing.T, client *gophercloud.ServiceClient, id string) {
err := dsr.Delete(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted rule %s", id)
}
// +build acceptance compute secgroups
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestSecGroups(t *testing.T) {
client, err := newClient()
th.AssertNoErr(t, err)
serverID, needsDeletion := findServer(t, client)
groupID := createSecGroup(t, client)
listSecGroups(t, client)
newName := tools.RandomString("secgroup_", 5)
updateSecGroup(t, client, groupID, newName)
getSecGroup(t, client, groupID)
addRemoveRules(t, client, groupID)
addServerToSecGroup(t, client, serverID, newName)
removeServerFromSecGroup(t, client, serverID, newName)
if needsDeletion {
servers.Delete(client, serverID)
}
deleteSecGroup(t, client, groupID)
}
func createSecGroup(t *testing.T, client *gophercloud.ServiceClient) string {
opts := secgroups.CreateOpts{
Name: tools.RandomString("secgroup_", 5),
Description: "something",
}
group, err := secgroups.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created secgroup %s %s", group.ID, group.Name)
return group.ID
}
func listSecGroups(t *testing.T, client *gophercloud.ServiceClient) {
err := secgroups.List(client).EachPage(func(page pagination.Page) (bool, error) {
secGrpList, err := secgroups.ExtractSecurityGroups(page)
th.AssertNoErr(t, err)
for _, sg := range secGrpList {
t.Logf("Listing secgroup %s: Name [%s] Desc [%s] TenantID [%s]", sg.ID,
sg.Name, sg.Description, sg.TenantID)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func updateSecGroup(t *testing.T, client *gophercloud.ServiceClient, id, newName string) {
opts := secgroups.UpdateOpts{
Name: newName,
Description: tools.RandomString("dec_", 10),
}
group, err := secgroups.Update(client, id, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated %s's name to %s", group.ID, group.Name)
}
func getSecGroup(t *testing.T, client *gophercloud.ServiceClient, id string) {
group, err := secgroups.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting %s: %#v", id, group)
}
func addRemoveRules(t *testing.T, client *gophercloud.ServiceClient, id string) {
opts := secgroups.CreateRuleOpts{
ParentGroupID: id,
FromPort: 22,
ToPort: 22,
IPProtocol: "TCP",
CIDR: "0.0.0.0/0",
}
rule, err := secgroups.CreateRule(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Adding rule %s to group %s", rule.ID, id)
err = secgroups.DeleteRule(client, rule.ID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted rule %s from group %s", rule.ID, id)
}
func findServer(t *testing.T, client *gophercloud.ServiceClient) (string, bool) {
var serverID string
var needsDeletion bool
err := servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
sList, err := servers.ExtractServers(page)
th.AssertNoErr(t, err)
for _, s := range sList {
serverID = s.ID
needsDeletion = false
t.Logf("Found an existing server: ID [%s]", serverID)
break
}
return true, nil
})
th.AssertNoErr(t, err)
if serverID == "" {
t.Log("No server found, creating one")
choices, err := ComputeChoicesFromEnv()
th.AssertNoErr(t, err)
opts := &servers.CreateOpts{
Name: tools.RandomString("secgroup_test_", 5),
ImageRef: choices.ImageID,
FlavorRef: choices.FlavorID,
}
s, err := servers.Create(client, opts).Extract()
th.AssertNoErr(t, err)
serverID = s.ID
t.Logf("Created server %s, waiting for it to build", s.ID)
err = servers.WaitForStatus(client, serverID, "ACTIVE", 300)
th.AssertNoErr(t, err)
needsDeletion = true
}
return serverID, needsDeletion
}
func addServerToSecGroup(t *testing.T, client *gophercloud.ServiceClient, serverID, groupName string) {
err := secgroups.AddServerToGroup(client, serverID, groupName).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Adding group %s to server %s", groupName, serverID)
}
func removeServerFromSecGroup(t *testing.T, client *gophercloud.ServiceClient, serverID, groupName string) {
err := secgroups.RemoveServerFromGroup(client, serverID, groupName).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Removing group %s from server %s", groupName, serverID)
}
func deleteSecGroup(t *testing.T, client *gophercloud.ServiceClient, id string) {
err := secgroups.Delete(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted group %s", id)
}
......@@ -12,6 +12,7 @@ import (
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListServers(t *testing.T) {
......@@ -94,6 +95,8 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp
name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s\n", name)
pwd := tools.MakeNewPassword("")
server, err := servers.Create(client, servers.CreateOpts{
Name: name,
FlavorRef: choices.FlavorID,
......@@ -101,11 +104,14 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp
Networks: []servers.Network{
servers.Network{UUID: network.ID},
},
AdminPass: pwd,
}).Extract()
if err != nil {
t.Fatalf("Unable to create server: %v", err)
}
th.AssertEquals(t, pwd, server.AdminPass)
return server, err
}
......@@ -391,3 +397,54 @@ func TestActionResizeRevert(t *testing.T) {
t.Fatal(err)
}
}
func TestServerMetadata(t *testing.T) {
t.Parallel()
choices, err := ComputeChoicesFromEnv()
th.AssertNoErr(t, err)
client, err := newClient()
if err != nil {
t.Fatalf("Unable to create a compute client: %v", err)
}
server, err := createServer(t, client, choices)
if err != nil {
t.Fatal(err)
}
defer servers.Delete(client, server.ID)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
metadata, err := servers.UpdateMetadata(client, server.ID, servers.MetadataOpts{
"foo": "bar",
"this": "that",
}).Extract()
th.AssertNoErr(t, err)
t.Logf("UpdateMetadata result: %+v\n", metadata)
err = servers.DeleteMetadatum(client, server.ID, "foo").ExtractErr()
th.AssertNoErr(t, err)
metadata, err = servers.CreateMetadatum(client, server.ID, servers.MetadatumOpts{
"foo": "baz",
}).Extract()
th.AssertNoErr(t, err)
t.Logf("CreateMetadatum result: %+v\n", metadata)
metadata, err = servers.Metadatum(client, server.ID, "foo").Extract()
th.AssertNoErr(t, err)
t.Logf("Metadatum result: %+v\n", metadata)
th.AssertEquals(t, "baz", metadata["foo"])
metadata, err = servers.Metadata(client, server.ID).Extract()
th.AssertNoErr(t, err)
t.Logf("Metadata result: %+v\n", metadata)
metadata, err = servers.ResetMetadata(client, server.ID, servers.MetadataOpts{}).Extract()
th.AssertNoErr(t, err)
t.Logf("ResetMetadata result: %+v\n", metadata)
th.AssertDeepEquals(t, map[string]string{}, metadata)
}
// +build acceptance identity roles
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestRoles(t *testing.T) {
client := authenticatedClient(t)
tenantID := findTenant(t, client)
userID := createUser(t, client, tenantID)
roleID := listRoles(t, client)
addUserRole(t, client, tenantID, userID, roleID)
deleteUserRole(t, client, tenantID, userID, roleID)
deleteUser(t, client, userID)
}
func listRoles(t *testing.T, client *gophercloud.ServiceClient) string {
var roleID string
err := roles.List(client).EachPage(func(page pagination.Page) (bool, error) {
roleList, err := roles.ExtractRoles(page)
th.AssertNoErr(t, err)
for _, role := range roleList {
t.Logf("Listing role: ID [%s] Name [%s]", role.ID, role.Name)
roleID = role.ID
}
return true, nil
})
th.AssertNoErr(t, err)
return roleID
}
func addUserRole(t *testing.T, client *gophercloud.ServiceClient, tenantID, userID, roleID string) {
err := roles.AddUserRole(client, tenantID, userID, roleID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Added role %s to user %s", roleID, userID)
}
func deleteUserRole(t *testing.T, client *gophercloud.ServiceClient, tenantID, userID, roleID string) {
err := roles.DeleteUserRole(client, tenantID, userID, roleID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Removed role %s from user %s", roleID, userID)
}
// +build acceptance identity
package v2
import (
"strconv"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
"github.com/rackspace/gophercloud/openstack/identity/v2/users"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestUsers(t *testing.T) {
client := authenticatedClient(t)
tenantID := findTenant(t, client)
userID := createUser(t, client, tenantID)
listUsers(t, client)
getUser(t, client, userID)
updateUser(t, client, userID)
listUserRoles(t, client, tenantID, userID)
deleteUser(t, client, userID)
}
func findTenant(t *testing.T, client *gophercloud.ServiceClient) string {
var tenantID string
err := tenants.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
tenantList, err := tenants.ExtractTenants(page)
th.AssertNoErr(t, err)
for _, t := range tenantList {
tenantID = t.ID
break
}
return true, nil
})
th.AssertNoErr(t, err)
return tenantID
}
func createUser(t *testing.T, client *gophercloud.ServiceClient, tenantID string) string {
t.Log("Creating user")
opts := users.CreateOpts{
Name: tools.RandomString("user_", 5),
Enabled: users.Disabled,
TenantID: tenantID,
Email: "new_user@foo.com",
}
user, err := users.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created user %s on tenant %s", user.ID, tenantID)
return user.ID
}
func listUsers(t *testing.T, client *gophercloud.ServiceClient) {
err := users.List(client).EachPage(func(page pagination.Page) (bool, error) {
userList, err := users.ExtractUsers(page)
th.AssertNoErr(t, err)
for _, user := range userList {
t.Logf("Listing user: ID [%s] Name [%s] Email [%s] Enabled? [%s]",
user.ID, user.Name, user.Email, strconv.FormatBool(user.Enabled))
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
_, err := users.Get(client, userID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting user %s", userID)
}
func updateUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
opts := users.UpdateOpts{Name: tools.RandomString("new_name", 5), Email: "new@foo.com"}
user, err := users.Update(client, userID, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated user %s: Name [%s] Email [%s]", userID, user.Name, user.Email)
}
func listUserRoles(t *testing.T, client *gophercloud.ServiceClient, tenantID, userID string) {
count := 0
err := users.ListRoles(client, tenantID, userID).EachPage(func(page pagination.Page) (bool, error) {
count++
roleList, err := users.ExtractRoles(page)
th.AssertNoErr(t, err)
t.Logf("Listing roles for user %s", userID)
for _, r := range roleList {
t.Logf("- %s (%s)", r.Name, r.ID)
}
return true, nil
})
if count == 0 {
t.Logf("No roles for user %s", userID)
}
th.AssertNoErr(t, err)
}
func deleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
res := users.Delete(client, userID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted user %s", userID)
}
......@@ -82,7 +82,7 @@ func listPorts(t *testing.T) {
th.AssertNoErr(t, err)
for _, p := range portList {
t.Logf("Port: ID [%s] Name [%s] Status [%d] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v]",
t.Logf("Port: ID [%s] Name [%s] Status [%s] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v]",
p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups)
}
......
......@@ -17,7 +17,10 @@ func TestAccounts(t *testing.T) {
// Update an account's metadata.
updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: metadata})
th.AssertNoErr(t, updateres.Err)
t.Logf("Update Account Response: %+v\n", updateres)
updateHeaders, err := updateres.Extract()
th.AssertNoErr(t, err)
t.Logf("Update Account Response Headers: %+v\n", updateHeaders)
// Defer the deletion of the metadata set above.
defer func() {
......@@ -29,11 +32,14 @@ func TestAccounts(t *testing.T) {
th.AssertNoErr(t, updateres.Err)
}()
// Retrieve account metadata.
getres := accounts.Get(client, nil)
th.AssertNoErr(t, getres.Err)
// Extract the custom metadata from the 'Get' response.
am, err := getres.ExtractMetadata()
res := accounts.Get(client, nil)
h, err := res.Extract()
th.AssertNoErr(t, err)
t.Logf("Get Account Response Headers: %+v\n", h)
am, err := res.ExtractMetadata()
th.AssertNoErr(t, err)
for k := range metadata {
if am[k] != metadata[strings.Title(k)] {
......
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace/cdn/v1/base"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestBaseOps(t *testing.T) {
client := newClient(t)
t.Log("Retrieving Home Document")
testHomeDocumentGet(t, client)
t.Log("Pinging root URL")
testPing(t, client)
}
func testHomeDocumentGet(t *testing.T, client *gophercloud.ServiceClient) {
hd, err := base.Get(client).Extract()
th.AssertNoErr(t, err)
t.Logf("Retrieved home document: %+v", *hd)
}
func testPing(t *testing.T, client *gophercloud.ServiceClient) {
err := base.Ping(client).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Successfully pinged root URL")
}
// +build acceptance
package v1
import (
"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.NewCDNV1(client, gophercloud.EndpointOpts{})
th.AssertNoErr(t, err)
return c
}
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
os "github.com/rackspace/gophercloud/openstack/cdn/v1/flavors"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/cdn/v1/flavors"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestFlavor(t *testing.T) {
client := newClient(t)
t.Log("Listing Flavors")
id := testFlavorsList(t, client)
t.Log("Retrieving Flavor")
testFlavorGet(t, client, id)
}
func testFlavorsList(t *testing.T, client *gophercloud.ServiceClient) string {
var id string
err := flavors.List(client).EachPage(func(page pagination.Page) (bool, error) {
flavorList, err := os.ExtractFlavors(page)
th.AssertNoErr(t, err)
for _, flavor := range flavorList {
t.Logf("Listing flavor: ID [%s] Providers [%+v]", flavor.ID, flavor.Providers)
id = flavor.ID
}
return true, nil
})
th.AssertNoErr(t, err)
return id
}
func testFlavorGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
flavor, err := flavors.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Retrieved Flavor: %+v", *flavor)
}
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
os "github.com/rackspace/gophercloud/openstack/cdn/v1/services"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/cdn/v1/services"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestService(t *testing.T) {
client := newClient(t)
t.Log("Creating Service")
loc := testServiceCreate(t, client, "test-site-1")
t.Logf("Created service at location: %s", loc)
defer testServiceDelete(t, client, loc)
t.Log("Updating Service")
testServiceUpdate(t, client, loc)
t.Log("Retrieving Service")
testServiceGet(t, client, loc)
t.Log("Listing Services")
testServiceList(t, client)
}
func testServiceCreate(t *testing.T, client *gophercloud.ServiceClient, name string) string {
createOpts := os.CreateOpts{
Name: name,
Domains: []os.Domain{
os.Domain{
Domain: "www." + name + ".com",
},
},
Origins: []os.Origin{
os.Origin{
Origin: name + ".com",
Port: 80,
SSL: false,
},
},
FlavorID: "cdn",
}
l, err := services.Create(client, createOpts).Extract()
th.AssertNoErr(t, err)
return l
}
func testServiceGet(t *testing.T, client *gophercloud.ServiceClient, id string) {
s, err := services.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Retrieved service: %+v", *s)
}
func testServiceUpdate(t *testing.T, client *gophercloud.ServiceClient, id string) {
opts := os.UpdateOpts{
os.Append{
Value: os.Domain{Domain: "newDomain.com", Protocol: "http"},
},
}
loc, err := services.Update(client, id, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Successfully updated service at location: %s", loc)
}
func testServiceList(t *testing.T, client *gophercloud.ServiceClient) {
err := services.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
serviceList, err := os.ExtractServices(page)
th.AssertNoErr(t, err)
for _, service := range serviceList {
t.Logf("Listing service: %+v", service)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func testServiceDelete(t *testing.T, client *gophercloud.ServiceClient, id string) {
err := services.Delete(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Successfully deleted service (%s)", id)
}
// +build acceptance
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
osServiceAssets "github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets"
"github.com/rackspace/gophercloud/rackspace/cdn/v1/serviceassets"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestServiceAsset(t *testing.T) {
client := newClient(t)
t.Log("Creating Service")
loc := testServiceCreate(t, client, "test-site-2")
t.Logf("Created service at location: %s", loc)
t.Log("Deleting Service Assets")
testServiceAssetDelete(t, client, loc)
}
func testServiceAssetDelete(t *testing.T, client *gophercloud.ServiceClient, url string) {
deleteOpts := osServiceAssets.DeleteOpts{
All: true,
}
err := serviceassets.Delete(client, url, deleteOpts).ExtractErr()
th.AssertNoErr(t, err)
t.Log("Successfully deleted all Service Assets")
}
......@@ -5,11 +5,11 @@ package v2
import (
"testing"
"github.com/rackspace/gophercloud/acceptance/tools"
osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/rackspace/gophercloud/rackspace/compute/v2/bootfromvolume"
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/smashwilson/gophercloud/acceptance/tools"
)
func TestBootFromVolume(t *testing.T) {
......@@ -41,6 +41,9 @@ func TestBootFromVolume(t *testing.T) {
}).Extract()
th.AssertNoErr(t, err)
t.Logf("Created server: %+v\n", server)
//defer deleteServer(t, client, server)
t.Logf("Deleting server [%s]...", name)
defer deleteServer(t, client, server)
getServer(t, client, server)
listServers(t, client)
}
......@@ -39,11 +39,14 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName strin
name := tools.RandomString("Gophercloud-", 8)
pwd := tools.MakeNewPassword("")
opts := &servers.CreateOpts{
Name: name,
ImageRef: options.imageID,
FlavorRef: options.flavorID,
DiskConfig: diskconfig.Manual,
AdminPass: pwd,
}
if keyName != "" {
......@@ -59,6 +62,8 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName strin
th.AssertNoErr(t, err)
t.Logf("Server created successfully.")
th.CheckEquals(t, pwd, s.AdminPass)
return s
}
......@@ -95,6 +100,18 @@ func getServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Serve
logServer(t, details, -1)
}
func updateServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Get")
opts := os.UpdateOpts{
Name: "updated-server",
}
updatedServer, err := servers.Update(client, server.ID, opts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "updated-server", updatedServer.Name)
logServer(t, updatedServer, -1)
}
func listServers(t *testing.T, client *gophercloud.ServiceClient) {
t.Logf("> servers.List")
......@@ -120,7 +137,7 @@ func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server
original := server.AdminPass
t.Logf("Changing server password.")
err := servers.ChangeAdminPassword(client, server.ID, tools.MakeNewPassword(original)).Extract()
err := servers.ChangeAdminPassword(client, server.ID, tools.MakeNewPassword(original)).ExtractErr()
th.AssertNoErr(t, err)
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
......@@ -131,7 +148,7 @@ func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server
func rebootServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Reboot")
err := servers.Reboot(client, server.ID, os.HardReboot).Extract()
err := servers.Reboot(client, server.ID, os.HardReboot).ExtractErr()
th.AssertNoErr(t, err)
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
......@@ -192,6 +209,7 @@ func TestServerOperations(t *testing.T) {
defer deleteServer(t, client, server)
getServer(t, client, server)
updateServer(t, client, server)
listServers(t, client)
changeAdminPassword(t, client, server)
rebootServer(t, client, server)
......
// +build acceptance identity roles
package v2
import (
"testing"
"github.com/rackspace/gophercloud"
os "github.com/rackspace/gophercloud/openstack/identity/v2/extensions/admin/roles"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/identity/v2/roles"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestRoles(t *testing.T) {
client := authenticatedClient(t)
userID := createUser(t, client)
roleID := listRoles(t, client)
addUserRole(t, client, userID, roleID)
deleteUserRole(t, client, userID, roleID)
deleteUser(t, client, userID)
}
func listRoles(t *testing.T, client *gophercloud.ServiceClient) string {
var roleID string
err := roles.List(client).EachPage(func(page pagination.Page) (bool, error) {
roleList, err := os.ExtractRoles(page)
th.AssertNoErr(t, err)
for _, role := range roleList {
t.Logf("Listing role: ID [%s] Name [%s]", role.ID, role.Name)
roleID = role.ID
}
return true, nil
})
th.AssertNoErr(t, err)
return roleID
}
func addUserRole(t *testing.T, client *gophercloud.ServiceClient, userID, roleID string) {
err := roles.AddUserRole(client, userID, roleID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Added role %s to user %s", roleID, userID)
}
func deleteUserRole(t *testing.T, client *gophercloud.ServiceClient, userID, roleID string) {
err := roles.DeleteUserRole(client, userID, roleID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Removed role %s from user %s", roleID, userID)
}
// +build acceptance identity
package v2
import (
"strconv"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
os "github.com/rackspace/gophercloud/openstack/identity/v2/users"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/identity/v2/users"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestUsers(t *testing.T) {
client := authenticatedClient(t)
userID := createUser(t, client)
listUsers(t, client)
getUser(t, client, userID)
updateUser(t, client, userID)
resetApiKey(t, client, userID)
deleteUser(t, client, userID)
}
func createUser(t *testing.T, client *gophercloud.ServiceClient) string {
t.Log("Creating user")
opts := users.CreateOpts{
Username: tools.RandomString("user_", 5),
Enabled: os.Disabled,
Email: "new_user@foo.com",
}
user, err := users.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created user %s", user.ID)
return user.ID
}
func listUsers(t *testing.T, client *gophercloud.ServiceClient) {
err := users.List(client).EachPage(func(page pagination.Page) (bool, error) {
userList, err := os.ExtractUsers(page)
th.AssertNoErr(t, err)
for _, user := range userList {
t.Logf("Listing user: ID [%s] Username [%s] Email [%s] Enabled? [%s]",
user.ID, user.Username, user.Email, strconv.FormatBool(user.Enabled))
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
_, err := users.Get(client, userID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting user %s", userID)
}
func updateUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
opts := users.UpdateOpts{Username: tools.RandomString("new_name", 5), Email: "new@foo.com"}
user, err := users.Update(client, userID, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Updated user %s: Username [%s] Email [%s]", userID, user.Username, user.Email)
}
func deleteUser(t *testing.T, client *gophercloud.ServiceClient, userID string) {
res := users.Delete(client, userID)
th.AssertNoErr(t, res.Err)
t.Logf("Deleted user %s", userID)
}
func resetApiKey(t *testing.T, client *gophercloud.ServiceClient, userID string) {
key, err := users.ResetAPIKey(client, userID).Extract()
th.AssertNoErr(t, err)
if key.APIKey == "" {
t.Fatal("failed to reset API key for user")
}
t.Logf("Reset API key for user %s to %s", key.Username, key.APIKey)
}
// +build acceptance lbs
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/lb/v1/acl"
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestACL(t *testing.T) {
client := setup(t)
ids := createLB(t, client, 1)
lbID := ids[0]
createACL(t, client, lbID)
waitForLB(client, lbID, lbs.ACTIVE)
networkIDs := showACL(t, client, lbID)
deleteNetworkItem(t, client, lbID, networkIDs[0])
waitForLB(client, lbID, lbs.ACTIVE)
bulkDeleteACL(t, client, lbID, networkIDs[1:2])
waitForLB(client, lbID, lbs.ACTIVE)
deleteACL(t, client, lbID)
waitForLB(client, lbID, lbs.ACTIVE)
deleteLB(t, client, lbID)
}
func createACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
opts := acl.CreateOpts{
acl.CreateOpt{Address: "206.160.163.21", Type: acl.DENY},
acl.CreateOpt{Address: "206.160.165.11", Type: acl.DENY},
acl.CreateOpt{Address: "206.160.165.12", Type: acl.DENY},
acl.CreateOpt{Address: "206.160.165.13", Type: acl.ALLOW},
}
err := acl.Create(client, lbID, opts).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Created ACL items for LB %d", lbID)
}
func showACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) []int {
ids := []int{}
err := acl.List(client, lbID).EachPage(func(page pagination.Page) (bool, error) {
accessList, err := acl.ExtractAccessList(page)
th.AssertNoErr(t, err)
for _, i := range accessList {
t.Logf("Listing network item: ID [%s] Address [%s] Type [%s]", i.ID, i.Address, i.Type)
ids = append(ids, i.ID)
}
return true, nil
})
th.AssertNoErr(t, err)
return ids
}
func deleteNetworkItem(t *testing.T, client *gophercloud.ServiceClient, lbID, itemID int) {
err := acl.Delete(client, lbID, itemID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted network item %d", itemID)
}
func bulkDeleteACL(t *testing.T, client *gophercloud.ServiceClient, lbID int, items []int) {
err := acl.BulkDelete(client, lbID, items).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted network items %s", intsToStr(items))
}
func deleteACL(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
err := acl.DeleteAll(client, lbID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted ACL from LB %d", lbID)
}
// +build acceptance lbs
package v1
import (
"os"
"strconv"
"strings"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
func newProvider() (*gophercloud.ProviderClient, error) {
opts, err := rackspace.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
opts = tools.OnlyRS(opts)
return rackspace.AuthenticatedClient(opts)
}
func newClient() (*gophercloud.ServiceClient, error) {
provider, err := newProvider()
if err != nil {
return nil, err
}
return rackspace.NewLBV1(provider, gophercloud.EndpointOpts{
Region: os.Getenv("RS_REGION"),
})
}
func newComputeClient() (*gophercloud.ServiceClient, error) {
provider, err := newProvider()
if err != nil {
return nil, err
}
return rackspace.NewComputeV2(provider, gophercloud.EndpointOpts{
Region: os.Getenv("RS_REGION"),
})
}
func setup(t *testing.T) *gophercloud.ServiceClient {
client, err := newClient()
th.AssertNoErr(t, err)
return client
}
func intsToStr(ids []int) string {
strIDs := []string{}
for _, id := range ids {
strIDs = append(strIDs, strconv.Itoa(id))
}
return strings.Join(strIDs, ", ")
}
// +build acceptance lbs
package v1
import (
"strconv"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestLBs(t *testing.T) {
client := setup(t)
ids := createLB(t, client, 3)
id := ids[0]
listLBProtocols(t, client)
listLBAlgorithms(t, client)
listLBs(t, client)
getLB(t, client, id)
checkLBLogging(t, client, id)
checkErrorPage(t, client, id)
getStats(t, client, id)
updateLB(t, client, id)
deleteLB(t, client, id)
batchDeleteLBs(t, client, ids[1:])
}
func createLB(t *testing.T, client *gophercloud.ServiceClient, count int) []int {
ids := []int{}
for i := 0; i < count; i++ {
opts := lbs.CreateOpts{
Name: tools.RandomString("test_", 5),
Port: 80,
Protocol: "HTTP",
VIPs: []vips.VIP{
vips.VIP{Type: vips.PUBLIC},
},
}
lb, err := lbs.Create(client, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created LB %d - waiting for it to build...", lb.ID)
waitForLB(client, lb.ID, lbs.ACTIVE)
t.Logf("LB %d has reached ACTIVE state", lb.ID)
ids = append(ids, lb.ID)
}
return ids
}
func waitForLB(client *gophercloud.ServiceClient, id int, state lbs.Status) {
gophercloud.WaitFor(60, func() (bool, error) {
lb, err := lbs.Get(client, id).Extract()
if err != nil {
return false, err
}
if lb.Status != state {
return false, nil
}
return true, nil
})
}
func listLBProtocols(t *testing.T, client *gophercloud.ServiceClient) {
err := lbs.ListProtocols(client).EachPage(func(page pagination.Page) (bool, error) {
pList, err := lbs.ExtractProtocols(page)
th.AssertNoErr(t, err)
for _, p := range pList {
t.Logf("Listing protocol: Name [%s]", p.Name)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func listLBAlgorithms(t *testing.T, client *gophercloud.ServiceClient) {
err := lbs.ListAlgorithms(client).EachPage(func(page pagination.Page) (bool, error) {
aList, err := lbs.ExtractAlgorithms(page)
th.AssertNoErr(t, err)
for _, a := range aList {
t.Logf("Listing algorithm: Name [%s]", a.Name)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func listLBs(t *testing.T, client *gophercloud.ServiceClient) {
err := lbs.List(client, lbs.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
lbList, err := lbs.ExtractLBs(page)
th.AssertNoErr(t, err)
for _, lb := range lbList {
t.Logf("Listing LB: ID [%d] Name [%s] Protocol [%s] Status [%s] Node count [%d] Port [%d]",
lb.ID, lb.Name, lb.Protocol, lb.Status, lb.NodeCount, lb.Port)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
lb, err := lbs.Get(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting LB %d: Created [%s] VIPs [%#v] Logging [%#v] Persistence [%#v] SourceAddrs [%#v]",
lb.ID, lb.Created, lb.VIPs, lb.ConnectionLogging, lb.SessionPersistence, lb.SourceAddrs)
}
func updateLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
opts := lbs.UpdateOpts{
Name: tools.RandomString("new_", 5),
Protocol: "TCP",
HalfClosed: gophercloud.Enabled,
Algorithm: "RANDOM",
Port: 8080,
Timeout: 100,
HTTPSRedirect: gophercloud.Disabled,
}
err := lbs.Update(client, id, opts).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Updating LB %d - waiting for it to finish", id)
waitForLB(client, id, lbs.ACTIVE)
t.Logf("LB %d has reached ACTIVE state", id)
}
func deleteLB(t *testing.T, client *gophercloud.ServiceClient, id int) {
err := lbs.Delete(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted LB %d", id)
}
func batchDeleteLBs(t *testing.T, client *gophercloud.ServiceClient, ids []int) {
err := lbs.BulkDelete(client, ids).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted LB %s", intsToStr(ids))
}
func checkLBLogging(t *testing.T, client *gophercloud.ServiceClient, id int) {
err := lbs.EnableLogging(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Enabled logging for LB %d", id)
res, err := lbs.IsLoggingEnabled(client, id)
th.AssertNoErr(t, err)
t.Logf("LB %d log enabled? %s", id, strconv.FormatBool(res))
waitForLB(client, id, lbs.ACTIVE)
err = lbs.DisableLogging(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Disabled logging for LB %d", id)
}
func checkErrorPage(t *testing.T, client *gophercloud.ServiceClient, id int) {
content, err := lbs.SetErrorPage(client, id, "<html>New content!</html>").Extract()
t.Logf("Set error page for LB %d", id)
content, err = lbs.GetErrorPage(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Error page for LB %d: %s", id, content)
err = lbs.DeleteErrorPage(client, id).ExtractErr()
t.Logf("Deleted error page for LB %d", id)
}
func getStats(t *testing.T, client *gophercloud.ServiceClient, id int) {
waitForLB(client, id, lbs.ACTIVE)
stats, err := lbs.GetStats(client, id).Extract()
th.AssertNoErr(t, err)
t.Logf("Stats for LB %d: %#v", id, stats)
}
func checkCaching(t *testing.T, client *gophercloud.ServiceClient, id int) {
err := lbs.EnableCaching(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Enabled caching for LB %d", id)
res, err := lbs.IsContentCached(client, id)
th.AssertNoErr(t, err)
t.Logf("Is caching enabled for LB? %s", strconv.FormatBool(res))
err = lbs.DisableCaching(client, id).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Disabled caching for LB %d", id)
}
// +build acceptance lbs
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
"github.com/rackspace/gophercloud/rackspace/lb/v1/monitors"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestMonitors(t *testing.T) {
client := setup(t)
ids := createLB(t, client, 1)
lbID := ids[0]
getMonitor(t, client, lbID)
updateMonitor(t, client, lbID)
deleteMonitor(t, client, lbID)
deleteLB(t, client, lbID)
}
func getMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
hm, err := monitors.Get(client, lbID).Extract()
th.AssertNoErr(t, err)
t.Logf("Health monitor for LB %d: Type [%s] Delay [%d] Timeout [%d] AttemptLimit [%d]",
lbID, hm.Type, hm.Delay, hm.Timeout, hm.AttemptLimit)
}
func updateMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
opts := monitors.UpdateHTTPMonitorOpts{
AttemptLimit: 3,
Delay: 10,
Timeout: 10,
BodyRegex: "hello is it me you're looking for",
Path: "/foo",
StatusRegex: "200",
Type: monitors.HTTP,
}
err := monitors.Update(client, lbID, opts).ExtractErr()
th.AssertNoErr(t, err)
waitForLB(client, lbID, lbs.ACTIVE)
t.Logf("Updated monitor for LB %d", lbID)
}
func deleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
err := monitors.Delete(client, lbID).ExtractErr()
th.AssertNoErr(t, err)
waitForLB(client, lbID, lbs.ACTIVE)
t.Logf("Deleted monitor for LB %d", lbID)
}
// +build acceptance lbs
package v1
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/diskconfig"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
"github.com/rackspace/gophercloud/rackspace/lb/v1/nodes"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestNodes(t *testing.T) {
client := setup(t)
serverIP := findServer(t)
ids := createLB(t, client, 1)
lbID := ids[0]
nodeID := addNodes(t, client, lbID, serverIP)
listNodes(t, client, lbID)
getNode(t, client, lbID, nodeID)
updateNode(t, client, lbID, nodeID)
listEvents(t, client, lbID)
deleteNode(t, client, lbID, nodeID)
waitForLB(client, lbID, lbs.ACTIVE)
deleteLB(t, client, lbID)
}
func findServer(t *testing.T) string {
var serverIP string
client, err := newComputeClient()
th.AssertNoErr(t, err)
err = servers.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
sList, err := servers.ExtractServers(page)
th.AssertNoErr(t, err)
for _, s := range sList {
serverIP = s.AccessIPv4
t.Logf("Found an existing server: ID [%s] Public IP [%s]", s.ID, serverIP)
break
}
return true, nil
})
th.AssertNoErr(t, err)
if serverIP == "" {
t.Log("No server found, creating one")
imageRef := os.Getenv("RS_IMAGE_ID")
if imageRef == "" {
t.Fatalf("OS var RS_IMAGE_ID undefined")
}
flavorRef := os.Getenv("RS_FLAVOR_ID")
if flavorRef == "" {
t.Fatalf("OS var RS_FLAVOR_ID undefined")
}
opts := &servers.CreateOpts{
Name: tools.RandomString("lb_test_", 5),
ImageRef: imageRef,
FlavorRef: flavorRef,
DiskConfig: diskconfig.Manual,
}
s, err := servers.Create(client, opts).Extract()
th.AssertNoErr(t, err)
serverIP = s.AccessIPv4
t.Logf("Created server %s, waiting for it to build", s.ID)
err = servers.WaitForStatus(client, s.ID, "ACTIVE", 300)
th.AssertNoErr(t, err)
t.Logf("Server created successfully.")
}
return serverIP
}
func addNodes(t *testing.T, client *gophercloud.ServiceClient, lbID int, serverIP string) int {
opts := nodes.CreateOpts{
nodes.CreateOpt{
Address: serverIP,
Port: 80,
Condition: nodes.ENABLED,
Type: nodes.PRIMARY,
},
}
page := nodes.Create(client, lbID, opts)
nodeList, err := page.ExtractNodes()
th.AssertNoErr(t, err)
var nodeID int
for _, n := range nodeList {
nodeID = n.ID
}
if nodeID == 0 {
t.Fatalf("nodeID could not be extracted from create response")
}
t.Logf("Added node %d to LB %d", nodeID, lbID)
waitForLB(client, lbID, lbs.ACTIVE)
return nodeID
}
func listNodes(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
err := nodes.List(client, lbID, nil).EachPage(func(page pagination.Page) (bool, error) {
nodeList, err := nodes.ExtractNodes(page)
th.AssertNoErr(t, err)
for _, n := range nodeList {
t.Logf("Listing node: ID [%d] Address [%s:%d] Status [%s]", n.ID, n.Address, n.Port, n.Status)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func getNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
node, err := nodes.Get(client, lbID, nodeID).Extract()
th.AssertNoErr(t, err)
t.Logf("Getting node %d: Type [%s] Weight [%d]", nodeID, node.Type, node.Weight)
}
func updateNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
opts := nodes.UpdateOpts{
Weight: gophercloud.IntToPointer(10),
Condition: nodes.DRAINING,
Type: nodes.SECONDARY,
}
err := nodes.Update(client, lbID, nodeID, opts).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Updated node %d", nodeID)
waitForLB(client, lbID, lbs.ACTIVE)
}
func listEvents(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
pager := nodes.ListEvents(client, lbID, nodes.ListEventsOpts{})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
eventList, err := nodes.ExtractNodeEvents(page)
th.AssertNoErr(t, err)
for _, e := range eventList {
t.Logf("Listing events for node %d: Type [%s] Msg [%s] Severity [%s] Date [%s]",
e.NodeID, e.Type, e.DetailedMessage, e.Severity, e.Created)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func deleteNode(t *testing.T, client *gophercloud.ServiceClient, lbID int, nodeID int) {
err := nodes.Delete(client, lbID, nodeID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted node %d", nodeID)
}
// +build acceptance lbs
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace/lb/v1/sessions"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestSession(t *testing.T) {
client := setup(t)
ids := createLB(t, client, 1)
lbID := ids[0]
getSession(t, client, lbID)
enableSession(t, client, lbID)
waitForLB(client, lbID, "ACTIVE")
disableSession(t, client, lbID)
waitForLB(client, lbID, "ACTIVE")
deleteLB(t, client, lbID)
}
func getSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
sp, err := sessions.Get(client, lbID).Extract()
th.AssertNoErr(t, err)
t.Logf("Session config: Type [%s]", sp.Type)
}
func enableSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
opts := sessions.CreateOpts{Type: sessions.HTTPCOOKIE}
err := sessions.Enable(client, lbID, opts).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Enable %s sessions for %d", opts.Type, lbID)
}
func disableSession(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
err := sessions.Disable(client, lbID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Disable sessions for %d", lbID)
}
// +build acceptance lbs
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace/lb/v1/throttle"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestThrottle(t *testing.T) {
client := setup(t)
ids := createLB(t, client, 1)
lbID := ids[0]
getThrottleConfig(t, client, lbID)
createThrottleConfig(t, client, lbID)
waitForLB(client, lbID, "ACTIVE")
deleteThrottleConfig(t, client, lbID)
waitForLB(client, lbID, "ACTIVE")
deleteLB(t, client, lbID)
}
func getThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
sp, err := throttle.Get(client, lbID).Extract()
th.AssertNoErr(t, err)
t.Logf("Throttle config: MaxConns [%s]", sp.MaxConnections)
}
func createThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
opts := throttle.CreateOpts{
MaxConnections: 200,
MaxConnectionRate: 100,
MinConnections: 0,
RateInterval: 10,
}
err := throttle.Create(client, lbID, opts).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Enable throttling for %d", lbID)
}
func deleteThrottleConfig(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
err := throttle.Delete(client, lbID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Disable throttling for %d", lbID)
}
// +build acceptance lbs
package v1
import (
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/lb/v1/lbs"
"github.com/rackspace/gophercloud/rackspace/lb/v1/vips"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestVIPs(t *testing.T) {
client := setup(t)
ids := createLB(t, client, 1)
lbID := ids[0]
listVIPs(t, client, lbID)
vipIDs := addVIPs(t, client, lbID, 3)
deleteVIP(t, client, lbID, vipIDs[0])
bulkDeleteVIPs(t, client, lbID, vipIDs[1:])
waitForLB(client, lbID, lbs.ACTIVE)
deleteLB(t, client, lbID)
}
func listVIPs(t *testing.T, client *gophercloud.ServiceClient, lbID int) {
err := vips.List(client, lbID).EachPage(func(page pagination.Page) (bool, error) {
vipList, err := vips.ExtractVIPs(page)
th.AssertNoErr(t, err)
for _, vip := range vipList {
t.Logf("Listing VIP: ID [%s] Address [%s] Type [%s] Version [%s]",
vip.ID, vip.Address, vip.Type, vip.Version)
}
return true, nil
})
th.AssertNoErr(t, err)
}
func addVIPs(t *testing.T, client *gophercloud.ServiceClient, lbID, count int) []int {
ids := []int{}
for i := 0; i < count; i++ {
opts := vips.CreateOpts{
Type: vips.PUBLIC,
Version: vips.IPV6,
}
vip, err := vips.Create(client, lbID, opts).Extract()
th.AssertNoErr(t, err)
t.Logf("Created VIP %d", vip.ID)
waitForLB(client, lbID, lbs.ACTIVE)
ids = append(ids, vip.ID)
}
return ids
}
func deleteVIP(t *testing.T, client *gophercloud.ServiceClient, lbID, vipID int) {
err := vips.Delete(client, lbID, vipID).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted VIP %d", vipID)
waitForLB(client, lbID, lbs.ACTIVE)
}
func bulkDeleteVIPs(t *testing.T, client *gophercloud.ServiceClient, lbID int, ids []int) {
err := vips.BulkDelete(client, lbID, ids).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Deleted VIPs %s", intsToStr(ids))
}
package v2
import (
"os"
"testing"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/rackspace"
th "github.com/rackspace/gophercloud/testhelper"
)
var Client *gophercloud.ServiceClient
func NewClient() (*gophercloud.ServiceClient, error) {
opts, err := rackspace.AuthOptionsFromEnv()
if err != nil {
return nil, err
}
provider, err := rackspace.AuthenticatedClient(opts)
if err != nil {
return nil, err
}
return rackspace.NewNetworkV2(provider, gophercloud.EndpointOpts{
Name: "cloudNetworks",
Region: os.Getenv("RS_REGION"),
})
}
func Setup(t *testing.T) {
client, err := NewClient()
th.AssertNoErr(t, err)
Client = client
}
func Teardown() {
Client = nil
}
// +build acceptance networking
package v2
import (
"strconv"
"testing"
os "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestNetworkCRUDOperations(t *testing.T) {
Setup(t)
defer Teardown()
// Create a network
n, err := networks.Create(Client, os.CreateOpts{Name: "sample_network", AdminStateUp: os.Up}).Extract()
th.AssertNoErr(t, err)
defer networks.Delete(Client, n.ID)
th.AssertEquals(t, "sample_network", n.Name)
th.AssertEquals(t, true, n.AdminStateUp)
networkID := n.ID
// List networks
pager := networks.List(Client, os.ListOpts{Limit: 2})
err = pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
networkList, err := os.ExtractNetworks(page)
th.AssertNoErr(t, err)
for _, n := range networkList {
t.Logf("Network: ID [%s] Name [%s] Status [%s] Is shared? [%s]",
n.ID, n.Name, n.Status, strconv.FormatBool(n.Shared))
}
return true, nil
})
th.CheckNoErr(t, err)
// Get a network
if networkID == "" {
t.Fatalf("In order to retrieve a network, the NetworkID must be set")
}
n, err = networks.Get(Client, networkID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "ACTIVE", n.Status)
th.AssertDeepEquals(t, []string{}, n.Subnets)
th.AssertEquals(t, "sample_network", n.Name)
th.AssertEquals(t, true, n.AdminStateUp)
th.AssertEquals(t, false, n.Shared)
th.AssertEquals(t, networkID, n.ID)
// Update network
n, err = networks.Update(Client, networkID, os.UpdateOpts{Name: "new_network_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "new_network_name", n.Name)
// Delete network
res := networks.Delete(Client, networkID)
th.AssertNoErr(t, res.Err)
}
// +build acceptance networking
package v2
import (
"testing"
osNetworks "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
osPorts "github.com/rackspace/gophercloud/openstack/networking/v2/ports"
osSubnets "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
"github.com/rackspace/gophercloud/rackspace/networking/v2/ports"
"github.com/rackspace/gophercloud/rackspace/networking/v2/subnets"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestPortCRUD(t *testing.T) {
Setup(t)
defer Teardown()
// Setup network
t.Log("Setting up network")
networkID, err := createNetwork()
th.AssertNoErr(t, err)
defer networks.Delete(Client, networkID)
// Setup subnet
t.Logf("Setting up subnet on network %s", networkID)
subnetID, err := createSubnet(networkID)
th.AssertNoErr(t, err)
defer subnets.Delete(Client, subnetID)
// Create port
t.Logf("Create port based on subnet %s", subnetID)
portID := createPort(t, networkID, subnetID)
// List ports
t.Logf("Listing all ports")
listPorts(t)
// Get port
if portID == "" {
t.Fatalf("In order to retrieve a port, the portID must be set")
}
p, err := ports.Get(Client, portID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, portID, p.ID)
// Update port
p, err = ports.Update(Client, portID, osPorts.UpdateOpts{Name: "new_port_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "new_port_name", p.Name)
// Delete port
res := ports.Delete(Client, portID)
th.AssertNoErr(t, res.Err)
}
func createPort(t *testing.T, networkID, subnetID string) string {
enable := true
opts := osPorts.CreateOpts{
NetworkID: networkID,
Name: "my_port",
AdminStateUp: &enable,
FixedIPs: []osPorts.IP{osPorts.IP{SubnetID: subnetID}},
}
p, err := ports.Create(Client, opts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, networkID, p.NetworkID)
th.AssertEquals(t, "my_port", p.Name)
th.AssertEquals(t, true, p.AdminStateUp)
return p.ID
}
func listPorts(t *testing.T) {
count := 0
pager := ports.List(Client, osPorts.ListOpts{})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
count++
t.Logf("--- Page ---")
portList, err := osPorts.ExtractPorts(page)
th.AssertNoErr(t, err)
for _, p := range portList {
t.Logf("Port: ID [%s] Name [%s] Status [%s] MAC addr [%s] Fixed IPs [%#v] Security groups [%#v]",
p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups)
}
return true, nil
})
th.CheckNoErr(t, err)
if count == 0 {
t.Logf("No pages were iterated over when listing ports")
}
}
func createNetwork() (string, error) {
res, err := networks.Create(Client, osNetworks.CreateOpts{Name: "tmp_network", AdminStateUp: osNetworks.Up}).Extract()
return res.ID, err
}
func createSubnet(networkID string) (string, error) {
s, err := subnets.Create(Client, osSubnets.CreateOpts{
NetworkID: networkID,
CIDR: "192.168.199.0/24",
IPVersion: osSubnets.IPv4,
Name: "my_subnet",
EnableDHCP: osSubnets.Down,
}).Extract()
return s.ID, err
}
// +build acceptance networking
package v2
import (
"testing"
osNetworks "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
osSubnets "github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
"github.com/rackspace/gophercloud/pagination"
"github.com/rackspace/gophercloud/rackspace/networking/v2/networks"
"github.com/rackspace/gophercloud/rackspace/networking/v2/subnets"
th "github.com/rackspace/gophercloud/testhelper"
)
func TestListSubnets(t *testing.T) {
Setup(t)
defer Teardown()
pager := subnets.List(Client, osSubnets.ListOpts{Limit: 2})
err := pager.EachPage(func(page pagination.Page) (bool, error) {
t.Logf("--- Page ---")
subnetList, err := osSubnets.ExtractSubnets(page)
th.AssertNoErr(t, err)
for _, s := range subnetList {
t.Logf("Subnet: ID [%s] Name [%s] IP Version [%d] CIDR [%s] GatewayIP [%s]",
s.ID, s.Name, s.IPVersion, s.CIDR, s.GatewayIP)
}
return true, nil
})
th.CheckNoErr(t, err)
}
func TestSubnetCRUD(t *testing.T) {
Setup(t)
defer Teardown()
// Setup network
t.Log("Setting up network")
n, err := networks.Create(Client, osNetworks.CreateOpts{Name: "tmp_network", AdminStateUp: osNetworks.Up}).Extract()
th.AssertNoErr(t, err)
networkID := n.ID
defer networks.Delete(Client, networkID)
// Create subnet
t.Log("Create subnet")
enable := false
opts := osSubnets.CreateOpts{
NetworkID: networkID,
CIDR: "192.168.199.0/24",
IPVersion: osSubnets.IPv4,
Name: "my_subnet",
EnableDHCP: &enable,
}
s, err := subnets.Create(Client, opts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, networkID, s.NetworkID)
th.AssertEquals(t, "192.168.199.0/24", s.CIDR)
th.AssertEquals(t, 4, s.IPVersion)
th.AssertEquals(t, "my_subnet", s.Name)
th.AssertEquals(t, false, s.EnableDHCP)
subnetID := s.ID
// Get subnet
t.Log("Getting subnet")
s, err = subnets.Get(Client, subnetID).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, subnetID, s.ID)
// Update subnet
t.Log("Update subnet")
s, err = subnets.Update(Client, subnetID, osSubnets.UpdateOpts{Name: "new_subnet_name"}).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, "new_subnet_name", s.Name)
// Delete subnet
t.Log("Delete subnet")
res := subnets.Delete(Client, subnetID)
th.AssertNoErr(t, res.Err)
}
......@@ -13,11 +13,11 @@ func TestAccounts(t *testing.T) {
c, err := createClient(t, false)
th.AssertNoErr(t, err)
updateres := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": "mountains"}})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header)
updateHeaders, err := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}).Extract()
th.AssertNoErr(t, err)
t.Logf("Update Account Response Headers: %+v\n", updateHeaders)
defer func() {
updateres = raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": ""}})
updateres := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": ""}})
th.AssertNoErr(t, updateres.Err)
metadata, err := raxAccounts.Get(c).ExtractMetadata()
th.AssertNoErr(t, err)
......@@ -25,8 +25,13 @@ func TestAccounts(t *testing.T) {
th.CheckEquals(t, metadata["White"], "")
}()
metadata, err := raxAccounts.Get(c).ExtractMetadata()
th.AssertNoErr(t, err)
getResp := raxAccounts.Get(c)
th.AssertNoErr(t, getResp.Err)
getHeaders, _ := getResp.Extract()
t.Logf("Get Account Response Headers: %+v\n", getHeaders)
metadata, _ := getResp.ExtractMetadata()
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains")
......
......@@ -26,10 +26,11 @@ func TestCDNContainers(t *testing.T) {
raxCDNClient, err := createClient(t, true)
th.AssertNoErr(t, err)
r := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
th.AssertNoErr(t, r.Err)
t.Logf("Headers from Enable CDN Container request: %+v\n", r.Header)
enableRes := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
t.Logf("Header map from Enable CDN Container request: %+v\n", enableRes.Header)
enableHeader, err := enableRes.Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Enable CDN Container request: %+v\n", enableHeader)
t.Logf("Container Names available to the currently issued token:")
count := 0
......@@ -51,11 +52,15 @@ func TestCDNContainers(t *testing.T) {
t.Errorf("No CDN containers listed for your current token.")
}
updateres := raxCDNContainers.Update(raxCDNClient, "gophercloud-test", raxCDNContainers.UpdateOpts{CDNEnabled: false})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update CDN Container request: %+v\n", updateres.Header)
updateOpts := raxCDNContainers.UpdateOpts{XCDNEnabled: raxCDNContainers.Disabled, XLogRetention: raxCDNContainers.Enabled}
updateHeader, err := raxCDNContainers.Update(raxCDNClient, "gophercloud-test", updateOpts).Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Update CDN Container request: %+v\n", updateHeader)
metadata, err := raxCDNContainers.Get(raxCDNClient, "gophercloud-test").ExtractMetadata()
getRes := raxCDNContainers.Get(raxCDNClient, "gophercloud-test")
getHeader, err := getRes.Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Get CDN Container request (after update): %+v\n", metadata)
t.Logf("Headers from Get CDN Container request (after update): %+v\n", getHeader)
metadata, err := getRes.ExtractMetadata()
t.Logf("Metadata from Get CDN Container request (after update): %+v\n", metadata)
}
......@@ -57,29 +57,34 @@ func TestContainers(t *testing.T) {
t.Errorf("No containers listed for your current token.")
}
createres := raxContainers.Create(c, "gophercloud-test", nil)
th.AssertNoErr(t, createres.Err)
createHeader, err := raxContainers.Create(c, "gophercloud-test", nil).Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Create Container request: %+v\n", createHeader)
defer func() {
res := raxContainers.Delete(c, "gophercloud-test")
th.AssertNoErr(t, res.Err)
deleteres := raxContainers.Delete(c, "gophercloud-test")
deleteHeader, err := deleteres.Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Delete Container request: %+v\n", deleteres.Header)
t.Logf("Headers from Delete Container request: %+v\n", deleteHeader)
}()
updateres := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": "mountains"}})
th.AssertNoErr(t, updateres.Err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header)
updateHeader, err := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}).Extract()
th.AssertNoErr(t, err)
t.Logf("Headers from Update Container request: %+v\n", updateHeader)
defer func() {
res := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": ""}})
th.AssertNoErr(t, res.Err)
metadata, err := raxContainers.Get(c, "gophercloud-test").ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
t.Logf("Metadata from Get Container request (after update reverted): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "")
}()
getres := raxContainers.Get(c, "gophercloud-test")
t.Logf("Headers from Get Account request (after update): %+v\n", getres.Header)
metadata, err := getres.ExtractMetadata()
getHeader, err := getres.Extract()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
t.Logf("Headers from Get Container request (after update): %+v\n", getHeader)
metadata, err := getres.ExtractMetadata()
t.Logf("Metadata from Get Container request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains")
}
......@@ -21,6 +21,7 @@ func TestObjects(t *testing.T) {
th.AssertNoErr(t, res.Err)
defer func() {
t.Logf("Deleting container...")
res := raxContainers.Delete(c, "gophercloud-test")
th.AssertNoErr(t, res.Err)
}()
......@@ -29,7 +30,9 @@ func TestObjects(t *testing.T) {
options := &osObjects.CreateOpts{ContentType: "text/plain"}
createres := raxObjects.Create(c, "gophercloud-test", "o1", content, options)
th.AssertNoErr(t, createres.Err)
defer func() {
t.Logf("Deleting object o1...")
res := raxObjects.Delete(c, "gophercloud-test", "o1", nil)
th.AssertNoErr(t, res.Err)
}()
......@@ -80,6 +83,7 @@ func TestObjects(t *testing.T) {
copyres := raxObjects.Copy(c, "gophercloud-test", "o1", &raxObjects.CopyOpts{Destination: "gophercloud-test/o2"})
th.AssertNoErr(t, copyres.Err)
defer func() {
t.Logf("Deleting object o2...")
res := raxObjects.Delete(c, "gophercloud-test", "o2", nil)
th.AssertNoErr(t, res.Err)
}()
......@@ -99,7 +103,7 @@ func TestObjects(t *testing.T) {
metadata, err := raxObjects.Get(c, "gophercloud-test", "o2", nil).ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "")
th.CheckEquals(t, "", metadata["White"])
}()
getres := raxObjects.Get(c, "gophercloud-test", "o2", nil)
......@@ -108,5 +112,13 @@ func TestObjects(t *testing.T) {
metadata, err := getres.ExtractMetadata()
th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains")
th.CheckEquals(t, "mountains", metadata["White"])
createTempURLOpts := osObjects.CreateTempURLOpts{
Method: osObjects.GET,
TTL: 600,
}
tempURL, err := raxObjects.CreateTempURL(c, "gophercloud-test", "o1", createTempURLOpts)
th.AssertNoErr(t, err)
t.Logf("TempURL for object (%s): %s", "o1", tempURL)
}
// +build acceptance
// +build acceptance common
package tools
import (
"crypto/rand"
"errors"
mrand "math/rand"
"os"
"time"
......@@ -72,6 +73,12 @@ func RandomString(prefix string, n int) string {
return prefix + string(bytes)
}
// RandomInt will return a random integer between a specified range.
func RandomInt(min, max int) int {
mrand.Seed(time.Now().Unix())
return mrand.Intn(max-min) + min
}
// Elide returns the first bit of its input string with a suffix of "..." if it's longer than
// a comfortable 40 characters.
func Elide(value string) string {
......
package gophercloud
// AuthOptions allows anyone calling Authenticate to supply the required access
// credentials. Its fields are the union of those recognized by each identity
// implementation and provider.
/*
AuthOptions stores information needed to authenticate to an OpenStack cluster.
You can populate one manually, or use a provider's AuthOptionsFromEnv() function
to read relevant information from the standard environment variables. Pass one
to a provider's AuthenticatedClient function to authenticate and obtain a
ProviderClient representing an active session on that provider.
Its fields are the union of those recognized by each identity implementation and
provider.
*/
type AuthOptions struct {
// IdentityEndpoint specifies the HTTP endpoint that is required to work with
// the Identity API of the appropriate version. Required by the identity
// services, but often populated by a provider Client.
// the Identity API of the appropriate version. While it's ultimately needed by
// all of the identity services, it will often be populated by a provider-level
// function.
IdentityEndpoint string
// Username is required if using Identity V2 API. Consult with your provider's
// control panel to discover your account's username. In Identity V3, either
// UserID or a combination of Username and DomainID or DomainName.
// UserID or a combination of Username and DomainID or DomainName are needed.
Username, UserID string
// Exactly one of Password or ApiKey is required for the Identity V2 and V3
// Exactly one of Password or APIKey is required for the Identity V2 and V3
// APIs. Consult with your provider's control panel to discover your account's
// preferred method of authentication.
Password, APIKey string
......@@ -25,7 +33,7 @@ type AuthOptions struct {
// The TenantID and TenantName fields are optional for the Identity V2 API.
// Some providers allow you to specify a TenantName instead of the TenantId.
// Some require both. Your provider's authentication policies will determine
// Some require both. Your provider's authentication policies will determine
// how these fields influence authentication.
TenantID, TenantName string
......@@ -34,5 +42,7 @@ type AuthOptions struct {
// re-authenticate automatically if/when your token expires. If you set it to
// false, it will not cache these settings, but re-authentication will not be
// possible. This setting defaults to false.
//
// This setting is speculative and is currently not respected!
AllowReauth bool
}
......@@ -2,10 +2,9 @@ package gophercloud
import "time"
// AuthResults encapsulates the raw results from an authentication request. As OpenStack allows
// extensions to influence the structure returned in ways that Gophercloud cannot predict at
// compile-time, you should use type-safe accessors to work with the data represented by this type,
// such as ServiceCatalog and TokenID.
// AuthResults [deprecated] is a leftover type from the v0.x days. It was
// intended to describe common functionality among identity service results, but
// is not actually used anywhere.
type AuthResults interface {
// TokenID returns the token's ID value from the authentication response.
TokenID() (string, error)
......
......@@ -30,9 +30,38 @@ to work with and represent the state of API resources:
server, err := servers.Get(client, "{serverId}").Extract()
Another convention is to return Result structs for API operations, which allow
you to access the HTTP headers, response body, and associated errors with the
network transaction. To get a resource struct, you then call the Extract
method which is chained to the response.
Intermediate Result structs are returned for API operations, which allow
generic access to the HTTP headers, response body, and any errors associated
with the network transaction. To turn a result into a usable resource struct,
you must call the Extract method which is chained to the response, or an
Extract function from an applicable extension:
result := servers.Get(client, "{serverId}")
// Attempt to extract the disk configuration from the OS-DCF disk config
// extension:
config, err := diskconfig.ExtractGet(result)
All requests that enumerate a collection return a Pager struct that is used to
iterate through the results one page at a time. Use the EachPage method on that
Pager to handle each successive Page in a closure, then use the appropriate
extraction method from that request's package to interpret that Page as a slice
of results:
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
s, err := servers.ExtractServers(page)
if err != nil {
return false, err
}
// Handle the []servers.Server slice.
// Return "false" or an error to prematurely stop fetching new pages.
return true, nil
})
This top-level package contains utility functions and data types that are used
throughout the provider and service packages. Of particular note for end users
are the AuthOptions and EndpointOpts structs.
*/
package gophercloud
......@@ -3,58 +3,85 @@ package gophercloud
import "errors"
var (
// ErrServiceNotFound is returned when no service matches the EndpointOpts.
// ErrServiceNotFound is returned when no service in a service catalog matches
// the provided EndpointOpts. This is generally returned by provider service
// factory methods like "NewComputeV2()" and can mean that a service is not
// enabled for your account.
ErrServiceNotFound = errors.New("No suitable service could be found in the service catalog.")
// ErrEndpointNotFound is returned when no available endpoints match the provided EndpointOpts.
// ErrEndpointNotFound is returned when no available endpoints match the
// provided EndpointOpts. This is also generally returned by provider service
// factory methods, and usually indicates that a region was specified
// incorrectly.
ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.")
)
// Availability indicates whether a specific service endpoint is accessible.
// Identity v2 lists these as different kinds of URLs ("adminURL",
// "internalURL", and "publicURL"), while v3 lists them as "Interfaces".
// Availability indicates to whom a specific service endpoint is accessible:
// the internet at large, internal networks only, or only to administrators.
// Different identity services use different terminology for these. Identity v2
// lists them as different kinds of URLs within the service catalog ("adminURL",
// "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an
// endpoint's response.
type Availability string
const (
// AvailabilityAdmin makes an endpoint only available to administrators.
// AvailabilityAdmin indicates that an endpoint is only available to
// administrators.
AvailabilityAdmin Availability = "admin"
// AvailabilityPublic makes an endpoint available to everyone.
// AvailabilityPublic indicates that an endpoint is available to everyone on
// the internet.
AvailabilityPublic Availability = "public"
// AvailabilityInternal makes an endpoint only available within the cluster.
// AvailabilityInternal indicates that an endpoint is only available within
// the cluster's internal network.
AvailabilityInternal Availability = "internal"
)
// EndpointOpts contains options for finding an endpoint for an Openstack client.
// EndpointOpts specifies search criteria used by queries against an
// OpenStack service catalog. The options must contain enough information to
// unambiguously identify one, and only one, endpoint within the catalog.
//
// Usually, these are passed to service client factory functions in a provider
// package, like "rackspace.NewComputeV2()".
type EndpointOpts struct {
// Type is the service type for the client (e.g., "compute", "object-store").
// Required.
// Type [required] is the service type for the client (e.g., "compute",
// "object-store"). Generally, this will be supplied by the service client
// function, but a user-given value will be honored if provided.
Type string
// Name is the service name for the client (e.g., "nova") as it appears in
// the service catalog. Services can have the same Type but a different Name,
// which is why both Type and Name are sometimes needed. Optional.
// Name [optional] is the service name for the client (e.g., "nova") as it
// appears in the service catalog. Services can have the same Type but a
// different Name, which is why both Type and Name are sometimes needed.
Name string
// Region is the geographic region in which the service resides. Required only
// for services that span multiple regions.
// Region [required] is the geographic region in which the endpoint resides,
// generally specifying which datacenter should house your resources.
// Required only for services that span multiple regions.
Region string
// Availability is the visibility of the endpoint to be returned. Valid types
// are: AvailabilityPublic, AvailabilityInternal, or AvailabilityAdmin.
// Availability is not required, and defaults to AvailabilityPublic.
// Not all providers or services offer all Availability options.
// Availability [optional] is the visibility of the endpoint to be returned.
// Valid types include the constants AvailabilityPublic, AvailabilityInternal,
// or AvailabilityAdmin from this package.
//
// Availability is not required, and defaults to AvailabilityPublic. Not all
// providers or services offer all Availability options.
Availability Availability
}
// EndpointLocator is a function that describes how to locate a single endpoint
// from a service catalog for a specific ProviderClient. It should be set
// during ProviderClient authentication and used to discover related ServiceClients.
/*
EndpointLocator is an internal function to be used by provider implementations.
It provides an implementation that locates a single endpoint from a service
catalog for a specific ProviderClient based on user-provided EndpointOpts. The
provider then uses it to discover related ServiceClients.
*/
type EndpointLocator func(EndpointOpts) (string, error)
// ApplyDefaults sets EndpointOpts fields if not already set. Currently,
// EndpointOpts.Availability defaults to the public endpoint.
// ApplyDefaults is an internal method to be used by provider implementations.
//
// It sets EndpointOpts fields if not already set, including a default type.
// Currently, EndpointOpts.Availability defaults to the public endpoint.
func (eo *EndpointOpts) ApplyDefaults(t string) {
if eo.Type == "" {
eo.Type = t
......
package snapshots
import (
"fmt"
"net/http"
"testing"
"time"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func TestWaitForStatus(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/snapshots/1234", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"snapshot": {
"display_name": "snapshot-001",
"id": "1234",
"status":"available"
}
}`)
})
err := WaitForStatus(client.ServiceClient(), "1234", "available", 0)
if err == nil {
t.Errorf("Expected error: 'Time Out in WaitFor'")
}
err = WaitForStatus(client.ServiceClient(), "1234", "available", 3)
th.CheckNoErr(t, err)
}
......@@ -28,7 +28,7 @@ type Volume struct {
CreatedAt string `mapstructure:"created_at"`
// Human-readable description for the volume.
Description string `mapstructure:"display_discription"`
Description string `mapstructure:"display_description"`
// The type of volume to create, either SATA or SSD.
VolumeType string `mapstructure:"volume_type"`
......
package volumes
import (
"fmt"
"net/http"
"testing"
"time"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
func TestWaitForStatus(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
th.Mux.HandleFunc("/volumes/1234", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(3 * time.Second)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"volume": {
"display_name": "vol-001",
"id": "1234",
"status":"available"
}
}`)
})
err := WaitForStatus(client.ServiceClient(), "1234", "available", 0)
if err == nil {
t.Errorf("Expected error: 'Time Out in WaitFor'")
}
err = WaitForStatus(client.ServiceClient(), "1234", "available", 6)
th.CheckNoErr(t, err)
}
// Package base provides information and interaction with the base API
// resource in the OpenStack CDN service. This API resource allows for
// retrieving the Home Document and pinging the root URL.
package base
package base
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
// HandleGetSuccessfully creates an HTTP handler at `/` on the test handler mux
// that responds with a `Get` response.
func HandleGetSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/", 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.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"resources": {
"rel/cdn": {
"href-template": "services{?marker,limit}",
"href-vars": {
"marker": "param/marker",
"limit": "param/limit"
},
"hints": {
"allow": [
"GET"
],
"formats": {
"application/json": {}
}
}
}
}
}
`)
})
}
// HandlePingSuccessfully creates an HTTP handler at `/ping` on the test handler
// mux that responds with a `Ping` response.
func HandlePingSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.WriteHeader(http.StatusNoContent)
})
}
package base
import (
"github.com/rackspace/gophercloud"
"github.com/racker/perigee"
)
// Get retrieves the home document, allowing the user to discover the
// entire API.
func Get(c *gophercloud.ServiceClient) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
OkCodes: []int{200},
})
return res
}
// Ping retrieves a ping to the server.
func Ping(c *gophercloud.ServiceClient) PingResult {
var res PingResult
_, res.Err = perigee.Request("GET", pingURL(c), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
OkCodes: []int{204},
OmitAccept: true,
})
return res
}
package base
import (
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
func TestGetHomeDocument(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleGetSuccessfully(t)
actual, err := Get(fake.ServiceClient()).Extract()
th.CheckNoErr(t, err)
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{}{},
},
},
},
}
th.CheckDeepEquals(t, expected, *actual)
}
func TestPing(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandlePingSuccessfully(t)
err := Ping(fake.ServiceClient()).ExtractErr()
th.CheckNoErr(t, err)
}
package base
import (
"errors"
"github.com/rackspace/gophercloud"
)
// HomeDocument is a resource that contains all the resources for the CDN API.
type HomeDocument map[string]interface{}
// GetResult represents the result of a Get operation.
type GetResult struct {
gophercloud.Result
}
// Extract is a function that accepts a result and extracts a home document resource.
func (r GetResult) Extract() (*HomeDocument, error) {
if r.Err != nil {
return nil, r.Err
}
submap, ok := r.Body.(map[string]interface{})["resources"]
if !ok {
return nil, errors.New("Unexpected HomeDocument structure")
}
casted := HomeDocument(submap.(map[string]interface{}))
return &casted, nil
}
// PingResult represents the result of a Ping operation.
type PingResult struct {
gophercloud.ErrResult
}
package base
import "github.com/rackspace/gophercloud"
func getURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL()
}
func pingURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("ping")
}
// Package flavors provides information and interaction with the flavors API
// resource in the OpenStack CDN service. This API resource allows for
// listing flavors and retrieving a specific flavor.
//
// A flavor is a mapping configuration to a CDN provider.
package flavors
package flavors
import (
"fmt"
"net/http"
"testing"
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)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"flavors": [
{
"id": "europe",
"providers": [
{
"provider": "Fastly",
"links": [
{
"href": "http://www.fastly.com",
"rel": "provider_url"
}
]
}
],
"links": [
{
"href": "https://www.poppycdn.io/v1.0/flavors/europe",
"rel": "self"
}
]
}
]
}
`)
})
}
// 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)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"id" : "asia",
"providers" : [
{
"provider" : "ChinaCache",
"links": [
{
"href": "http://www.chinacache.com",
"rel": "provider_url"
}
]
}
],
"links": [
{
"href": "https://www.poppycdn.io/v1.0/flavors/asia",
"rel": "self"
}
]
}
`)
})
}
package flavors
import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List returns a single page of CDN flavors.
func List(c *gophercloud.ServiceClient) pagination.Pager {
url := listURL(c)
createPage := func(r pagination.PageResult) pagination.Page {
return FlavorPage{pagination.SinglePageBase(r)}
}
return pagination.NewPager(c, url, createPage)
}
// Get retrieves a specific flavor based on its unique ID.
func Get(c *gophercloud.ServiceClient, id string) GetResult {
var res GetResult
_, res.Err = perigee.Request("GET", getURL(c, id), perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
Results: &res.Body,
OkCodes: []int{200},
})
return res
}
package flavors
import (
"testing"
"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()
HandleListCDNFlavorsSuccessfully(t)
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
}
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)
return true, nil
})
th.AssertNoErr(t, err)
th.CheckEquals(t, 1, count)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
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",
},
},
}
actual, err := Get(fake.ServiceClient(), "asia").Extract()
th.AssertNoErr(t, err)
th.AssertDeepEquals(t, expected, actual)
}
package flavors
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// Provider represents a provider for a particular flavor.
type Provider struct {
// Specifies the name of the provider. The name must not exceed 64 bytes in
// length and is limited to unicode, digits, underscores, and hyphens.
Provider string `mapstructure:"provider"`
// Specifies a list with an href where rel is provider_url.
Links []gophercloud.Link `mapstructure:"links"`
}
// Flavor represents a mapping configuration to a CDN provider.
type Flavor struct {
// Specifies the name of the flavor. The name must not exceed 64 bytes in
// length and is limited to unicode, digits, underscores, and hyphens.
ID string `mapstructure:"id"`
// Specifies the list of providers mapped to this flavor.
Providers []Provider `mapstructure:"providers"`
// Specifies the self-navigating JSON document paths.
Links []gophercloud.Link `mapstructure:"links"`
}
// FlavorPage is the page returned by a pager when traversing over a
// collection of CDN flavors.
type FlavorPage struct {
pagination.SinglePageBase
}
// IsEmpty returns true if a FlavorPage contains no Flavors.
func (r FlavorPage) IsEmpty() (bool, error) {
flavors, err := ExtractFlavors(r)
if err != nil {
return true, err
}
return len(flavors) == 0, nil
}
// ExtractFlavors extracts and returns Flavors. It is used while iterating over
// a flavors.List call.
func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
var response struct {
Flavors []Flavor `json:"flavors"`
}
err := mapstructure.Decode(page.(FlavorPage).Body, &response)
return response.Flavors, err
}
// GetResult represents the result of a get operation.
type GetResult struct {
gophercloud.Result
}
// Extract is a function that extracts a flavor from a GetResult.
func (r GetResult) Extract() (*Flavor, error) {
if r.Err != nil {
return nil, r.Err
}
var res Flavor
err := mapstructure.Decode(r.Body, &res)
return &res, err
}
package flavors
import "github.com/rackspace/gophercloud"
func listURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("flavors")
}
func getURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL("flavors", id)
}
// Package serviceassets provides information and interaction with the
// serviceassets API resource in the OpenStack CDN service. This API resource
// allows for deleting cached assets.
//
// A service distributes assets across the network. Service assets let you
// interrogate properties about these assets and perform certain actions on them.
package serviceassets
package serviceassets
import (
"net/http"
"testing"
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)
})
}
package serviceassets
import (
"strings"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
)
// DeleteOptsBuilder allows extensions to add additional parameters to the Delete
// request.
type DeleteOptsBuilder interface {
ToCDNAssetDeleteParams() (string, error)
}
// DeleteOpts is a structure that holds options for deleting CDN service assets.
type DeleteOpts struct {
// If all is set to true, specifies that the delete occurs against all of the
// assets for the service.
All bool `q:"all"`
// Specifies the relative URL of the asset to be deleted.
URL string `q:"url"`
}
// ToCDNAssetDeleteParams formats a DeleteOpts into a query string.
func (opts DeleteOpts) ToCDNAssetDeleteParams() (string, error) {
q, err := gophercloud.BuildQueryString(opts)
if err != nil {
return "", err
}
return q.String(), nil
}
// Delete accepts a unique service ID or URL and deletes the CDN service asset associated with
// it. For example, both "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0" and
// "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
// are valid options for idOrURL.
func Delete(c *gophercloud.ServiceClient, idOrURL string, opts DeleteOptsBuilder) DeleteResult {
var url string
if strings.Contains(idOrURL, "/") {
url = idOrURL
} else {
url = deleteURL(c, idOrURL)
}
var res DeleteResult
_, res.Err = perigee.Request("DELETE", url, perigee.Options{
MoreHeaders: c.AuthenticatedHeaders(),
OkCodes: []int{202},
})
return res
}
package serviceassets
import (
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDeleteCDNAssetSuccessfully(t)
err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", nil).ExtractErr()
th.AssertNoErr(t, err)
}
package serviceassets
import "github.com/rackspace/gophercloud"
// DeleteResult represents the result of a Delete operation.
type DeleteResult struct {
gophercloud.ErrResult
}
package serviceassets
import "github.com/rackspace/gophercloud"
func deleteURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL("services", id, "assets")
}
// Package services provides information and interaction with the services API
// resource in the OpenStack CDN service. This API resource allows for
// listing, creating, updating, retrieving, and deleting services.
//
// A service represents an application that has its content cached to the edge
// nodes.
package services
package services
import "fmt"
func no(str string) error {
return fmt.Errorf("Required parameter %s not provided", str)
}
package services
import (
"testing"
"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()
HandleListCDNServiceSuccessfully(t)
count := 0
err := List(fake.ServiceClient(), &ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractServices(page)
if err != nil {
t.Errorf("Failed to extract services: %v", err)
return false, err
}
expected := []Service{
Service{
ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
Name: "mywebsite.com",
Domains: []Domain{
Domain{
Domain: "www.mywebsite.com",
},
},
Origins: []Origin{
Origin{
Origin: "mywebsite.com",
Port: 80,
SSL: false,
},
},
Caching: []CacheRule{
CacheRule{
Name: "default",
TTL: 3600,
},
CacheRule{
Name: "home",
TTL: 17200,
Rules: []TTLRule{
TTLRule{
Name: "index",
RequestURL: "/index.htm",
},
},
},
CacheRule{
Name: "images",
TTL: 12800,
Rules: []TTLRule{
TTLRule{
Name: "images",
RequestURL: "*.png",
},
},
},
},
Restrictions: []Restriction{
Restriction{
Name: "website only",
Rules: []RestrictionRule{
RestrictionRule{
Name: "mywebsite.com",
Referrer: "www.mywebsite.com",
},
},
},
},
FlavorID: "asia",
Status: "deployed",
Errors: []Error{},
Links: []gophercloud.Link{
gophercloud.Link{
Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
Rel: "self",
},
gophercloud.Link{
Href: "mywebsite.com.cdn123.poppycdn.net",
Rel: "access_url",
},
gophercloud.Link{
Href: "https://www.poppycdn.io/v1.0/flavors/asia",
Rel: "flavor",
},
},
},
Service{
ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
Name: "myothersite.com",
Domains: []Domain{
Domain{
Domain: "www.myothersite.com",
},
},
Origins: []Origin{
Origin{
Origin: "44.33.22.11",
Port: 80,
SSL: false,
},
Origin{
Origin: "77.66.55.44",
Port: 80,
SSL: false,
Rules: []OriginRule{
OriginRule{
Name: "videos",
RequestURL: "^/videos/*.m3u",
},
},
},
},
Caching: []CacheRule{
CacheRule{
Name: "default",
TTL: 3600,
},
},
Restrictions: []Restriction{},
FlavorID: "europe",
Status: "deployed",
Links: []gophercloud.Link{
gophercloud.Link{
Href: "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f1",
Rel: "self",
},
gophercloud.Link{
Href: "myothersite.com.poppycdn.net",
Rel: "access_url",
},
gophercloud.Link{
Href: "https://www.poppycdn.io/v1.0/flavors/europe",
Rel: "flavor",
},
},
},
}
th.CheckDeepEquals(t, expected, actual)
return true, nil
})
th.AssertNoErr(t, err)
if count != 1 {
t.Errorf("Expected 1 page, got %d", count)
}
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleCreateCDNServiceSuccessfully(t)
createOpts := CreateOpts{
Name: "mywebsite.com",
Domains: []Domain{
Domain{
Domain: "www.mywebsite.com",
},
Domain{
Domain: "blog.mywebsite.com",
},
},
Origins: []Origin{
Origin{
Origin: "mywebsite.com",
Port: 80,
SSL: false,
},
},
Restrictions: []Restriction{
Restriction{
Name: "website only",
Rules: []RestrictionRule{
RestrictionRule{
Name: "mywebsite.com",
Referrer: "www.mywebsite.com",
},
},
},
},
Caching: []CacheRule{
CacheRule{
Name: "default",
TTL: 3600,
},
},
FlavorID: "cdn",
}
expected := "https://global.cdn.api.rackspacecloud.com/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
actual, err := Create(fake.ServiceClient(), createOpts).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, expected, actual)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleGetCDNServiceSuccessfully(t)
expected := &Service{
ID: "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
Name: "mywebsite.com",
Domains: []Domain{
Domain{
Domain: "www.mywebsite.com",
Protocol: "http",
},
},
Origins: []Origin{
Origin{
Origin: "mywebsite.com",
Port: 80,
SSL: false,
},
},
Caching: []CacheRule{
CacheRule{
Name: "default",
TTL: 3600,
},
CacheRule{
Name: "home",
TTL: 17200,
Rules: []TTLRule{
TTLRule{
Name: "index",
RequestURL: "/index.htm",
},
},
},
CacheRule{
Name: "images",
TTL: 12800,
Rules: []TTLRule{
TTLRule{
Name: "images",
RequestURL: "*.png",
},
},
},
},
Restrictions: []Restriction{
Restriction{
Name: "website only",
Rules: []RestrictionRule{
RestrictionRule{
Name: "mywebsite.com",
Referrer: "www.mywebsite.com",
},
},
},
},
FlavorID: "cdn",
Status: "deployed",
Errors: []Error{},
Links: []gophercloud.Link{
gophercloud.Link{
Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0",
Rel: "self",
},
gophercloud.Link{
Href: "blog.mywebsite.com.cdn1.raxcdn.com",
Rel: "access_url",
},
gophercloud.Link{
Href: "https://global.cdn.api.rackspacecloud.com/v1.0/110011/flavors/cdn",
Rel: "flavor",
},
},
}
actual, err := Get(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").Extract()
th.AssertNoErr(t, err)
th.AssertDeepEquals(t, expected, actual)
}
func TestSuccessfulUpdate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleUpdateCDNServiceSuccessfully(t)
expected := "https://www.poppycdn.io/v1.0/services/96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0"
ops := UpdateOpts{
// Append a single Domain
Append{Value: Domain{Domain: "appended.mocksite4.com"}},
// Insert a single Domain
Insertion{
Index: 4,
Value: Domain{Domain: "inserted.mocksite4.com"},
},
// Bulk addition
Append{
Value: DomainList{
Domain{Domain: "bulkadded1.mocksite4.com"},
Domain{Domain: "bulkadded2.mocksite4.com"},
},
},
// Replace a single Origin
Replacement{
Index: 2,
Value: Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
},
// Bulk replace Origins
Replacement{
Index: 0, // Ignored
Value: OriginList{
Origin{Origin: "44.33.22.11", Port: 80, SSL: false},
Origin{Origin: "55.44.33.22", Port: 443, SSL: true},
},
},
// Remove a single CacheRule
Removal{
Index: 8,
Path: PathCaching,
},
// Bulk removal
Removal{
All: true,
Path: PathCaching,
},
// Service name replacement
NameReplacement{
NewName: "differentServiceName",
},
}
actual, err := Update(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0", ops).Extract()
th.AssertNoErr(t, err)
th.AssertEquals(t, expected, actual)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDeleteCDNServiceSuccessfully(t)
err := Delete(fake.ServiceClient(), "96737ae3-cfc1-4c72-be88-5d0e7cc9a3f0").ExtractErr()
th.AssertNoErr(t, err)
}
package services
import "github.com/rackspace/gophercloud"
func listURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL("services")
}
func createURL(c *gophercloud.ServiceClient) string {
return listURL(c)
}
func getURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL("services", id)
}
func updateURL(c *gophercloud.ServiceClient, id string) string {
return getURL(c, id)
}
func deleteURL(c *gophercloud.ServiceClient, id string) string {
return getURL(c, id)
}
......@@ -203,3 +203,14 @@ func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoi
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
// CDN service.
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
eo.ApplyDefaults("cdn")
url, err := client.EndpointLocator(eo)
if err != nil {
return nil, err
}
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
}
package defsecrules
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
const rootPath = "/os-security-group-default-rules"
func mockListRulesResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_default_rules": [
{
"from_port": 80,
"id": "{ruleID}",
"ip_protocol": "TCP",
"ip_range": {
"cidr": "10.10.10.0/24"
},
"to_port": 80
}
]
}
`)
})
}
func mockCreateRuleResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group_default_rule": {
"ip_protocol": "TCP",
"from_port": 80,
"to_port": 80,
"cidr": "10.10.12.0/24"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_default_rule": {
"from_port": 80,
"id": "{ruleID}",
"ip_protocol": "TCP",
"ip_range": {
"cidr": "10.10.12.0/24"
},
"to_port": 80
}
}
`)
})
}
func mockGetRuleResponse(t *testing.T, ruleID string) {
url := rootPath + "/" + ruleID
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_default_rule": {
"id": "{ruleID}",
"from_port": 80,
"to_port": 80,
"ip_protocol": "TCP",
"ip_range": {
"cidr": "10.10.12.0/24"
}
}
}
`)
})
}
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
url := rootPath + "/" + ruleID
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusNoContent)
})
}
package defsecrules
import (
"errors"
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/pagination"
)
// List will return a collection of default rules.
func List(client *gophercloud.ServiceClient) pagination.Pager {
createPage := func(r pagination.PageResult) pagination.Page {
return DefaultRulePage{pagination.SinglePageBase(r)}
}
return pagination.NewPager(client, rootURL(client), createPage)
}
// CreateOpts represents the configuration for adding a new default rule.
type CreateOpts struct {
// Required - the lower bound of the port range that will be opened.
FromPort int `json:"from_port"`
// Required - the upper bound of the port range that will be opened.
ToPort int `json:"to_port"`
// Required - the protocol type that will be allowed, e.g. TCP.
IPProtocol string `json:"ip_protocol"`
// ONLY required if FromGroupID is blank. This represents the IP range that
// will be the source of network traffic to your security group. Use
// 0.0.0.0/0 to allow all IP addresses.
CIDR string `json:"cidr,omitempty"`
}
// CreateOptsBuilder builds the create rule options into a serializable format.
type CreateOptsBuilder interface {
ToRuleCreateMap() (map[string]interface{}, error)
}
// ToRuleCreateMap builds the create rule options into a serializable format.
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
rule := make(map[string]interface{})
if opts.FromPort == 0 {
return rule, errors.New("A FromPort must be set")
}
if opts.ToPort == 0 {
return rule, errors.New("A ToPort must be set")
}
if opts.IPProtocol == "" {
return rule, errors.New("A IPProtocol must be set")
}
if opts.CIDR == "" {
return rule, errors.New("A CIDR must be set")
}
rule["from_port"] = opts.FromPort
rule["to_port"] = opts.ToPort
rule["ip_protocol"] = opts.IPProtocol
rule["cidr"] = opts.CIDR
return map[string]interface{}{"security_group_default_rule": rule}, nil
}
// Create is the operation responsible for creating a new default rule.
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var result CreateResult
reqBody, err := opts.ToRuleCreateMap()
if err != nil {
result.Err = err
return result
}
_, result.Err = perigee.Request("POST", rootURL(client), perigee.Options{
Results: &result.Body,
ReqBody: &reqBody,
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{200},
})
return result
}
// Get will return details for a particular default rule.
func Get(client *gophercloud.ServiceClient, id string) GetResult {
var result GetResult
_, result.Err = perigee.Request("GET", resourceURL(client, id), perigee.Options{
Results: &result.Body,
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{200},
})
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 = perigee.Request("DELETE", resourceURL(client, id), perigee.Options{
MoreHeaders: client.AuthenticatedHeaders(),
OkCodes: []int{204},
})
return result
}
package defsecrules
import (
"testing"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
"github.com/rackspace/gophercloud/testhelper/client"
)
const ruleID = "{ruleID}"
func TestList(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
mockListRulesResponse(t)
count := 0
err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
count++
actual, err := ExtractDefaultRules(page)
th.AssertNoErr(t, err)
expected := []DefaultRule{
DefaultRule{
FromPort: 80,
ID: ruleID,
IPProtocol: "TCP",
IPRange: secgroups.IPRange{CIDR: "10.10.10.0/24"},
ToPort: 80,
},
}
th.CheckDeepEquals(t, expected, actual)
return true, nil
})
th.AssertNoErr(t, err)
th.AssertEquals(t, 1, count)
}
func TestCreate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
mockCreateRuleResponse(t)
opts := CreateOpts{
IPProtocol: "TCP",
FromPort: 80,
ToPort: 80,
CIDR: "10.10.12.0/24",
}
group, err := Create(client.ServiceClient(), opts).Extract()
th.AssertNoErr(t, err)
expected := &DefaultRule{
ID: ruleID,
FromPort: 80,
ToPort: 80,
IPProtocol: "TCP",
IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"},
}
th.AssertDeepEquals(t, expected, group)
}
func TestGet(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
mockGetRuleResponse(t, ruleID)
group, err := Get(client.ServiceClient(), ruleID).Extract()
th.AssertNoErr(t, err)
expected := &DefaultRule{
ID: ruleID,
FromPort: 80,
ToPort: 80,
IPProtocol: "TCP",
IPRange: secgroups.IPRange{CIDR: "10.10.12.0/24"},
}
th.AssertDeepEquals(t, expected, group)
}
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
mockDeleteRuleResponse(t, ruleID)
err := Delete(client.ServiceClient(), ruleID).ExtractErr()
th.AssertNoErr(t, err)
}
package defsecrules
import (
"github.com/mitchellh/mapstructure"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/pagination"
)
// DefaultRule represents a default rule - which is identical to a
// normal security rule.
type DefaultRule secgroups.Rule
// DefaultRulePage is a single page of a DefaultRule collection.
type DefaultRulePage struct {
pagination.SinglePageBase
}
// IsEmpty determines whether or not a page of default rules contains any results.
func (page DefaultRulePage) IsEmpty() (bool, error) {
users, err := ExtractDefaultRules(page)
if err != nil {
return false, err
}
return len(users) == 0, nil
}
// ExtractDefaultRules returns a slice of DefaultRules contained in a single
// page of results.
func ExtractDefaultRules(page pagination.Page) ([]DefaultRule, error) {
casted := page.(DefaultRulePage).Body
var response struct {
Rules []DefaultRule `mapstructure:"security_group_default_rules"`
}
err := mapstructure.WeakDecode(casted, &response)
return response.Rules, err
}
type commonResult struct {
gophercloud.Result
}
// CreateResult represents the result of a create operation.
type CreateResult struct {
commonResult
}
// GetResult represents the result of a get operation.
type GetResult struct {
commonResult
}
// Extract will extract a DefaultRule struct from most responses.
func (r commonResult) Extract() (*DefaultRule, error) {
if r.Err != nil {
return nil, r.Err
}
var response struct {
Rule DefaultRule `mapstructure:"security_group_default_rule"`
}
err := mapstructure.WeakDecode(r.Body, &response)
return &response.Rule, err
}
package defsecrules
import "github.com/rackspace/gophercloud"
const rulepath = "os-security-group-default-rules"
func resourceURL(c *gophercloud.ServiceClient, id string) string {
return c.ServiceURL(rulepath, id)
}
func rootURL(c *gophercloud.ServiceClient) string {
return c.ServiceURL(rulepath)
}
......@@ -5,9 +5,34 @@ import (
"github.com/racker/perigee"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination"
)
// CreateOptsExt adds a KeyPair option to the base CreateOpts.
type CreateOptsExt struct {
servers.CreateOptsBuilder
KeyName string `json:"key_name,omitempty"`
}
// ToServerCreateMap adds the key_name and, optionally, key_data options to
// the base server creation options.
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
if err != nil {
return nil, err
}
if opts.KeyName == "" {
return base, nil
}
serverMap := base["server"].(map[string]interface{})
serverMap["key_name"] = opts.KeyName
return base, nil
}
// List returns a Pager that allows you to iterate over a collection of KeyPairs.
func List(client *gophercloud.ServiceClient) pagination.Pager {
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
......@@ -21,7 +46,7 @@ type CreateOptsBuilder interface {
ToKeyPairCreateMap() (map[string]interface{}, error)
}
// CreateOpts species keypair creation or import parameters.
// CreateOpts specifies keypair creation or import parameters.
type CreateOpts struct {
// Name [required] is a friendly name to refer to this KeyPair in other services.
Name string
......
package secgroups
import (
"fmt"
"net/http"
"testing"
th "github.com/rackspace/gophercloud/testhelper"
fake "github.com/rackspace/gophercloud/testhelper/client"
)
const rootPath = "/os-security-groups"
const listGroupsJSON = `
{
"security_groups": [
{
"description": "default",
"id": "{groupID}",
"name": "default",
"rules": [],
"tenant_id": "openstack"
}
]
}
`
func mockListGroupsResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, listGroupsJSON)
})
}
func mockListGroupsByServerResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("%s/servers/%s%s", rootPath, serverID, rootPath)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, listGroupsJSON)
})
}
func mockCreateGroupResponse(t *testing.T) {
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group": {
"name": "test",
"description": "something"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"description": "something",
"id": "{groupID}",
"name": "test",
"rules": [],
"tenant_id": "openstack"
}
}
`)
})
}
func mockUpdateGroupResponse(t *testing.T, groupID string) {
url := fmt.Sprintf("%s/%s", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "PUT")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group": {
"name": "new_name",
"description": "new_desc"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"description": "something",
"id": "{groupID}",
"name": "new_name",
"rules": [],
"tenant_id": "openstack"
}
}
`)
})
}
func mockGetGroupsResponse(t *testing.T, groupID string) {
url := fmt.Sprintf("%s/%s", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"description": "default",
"id": "{groupID}",
"name": "default",
"rules": [
{
"from_port": 80,
"group": {
"tenant_id": "openstack",
"name": "default"
},
"ip_protocol": "TCP",
"to_port": 85,
"parent_group_id": "{groupID}",
"ip_range": {
"cidr": "0.0.0.0"
},
"id": "{ruleID}"
}
],
"tenant_id": "openstack"
}
}
`)
})
}
func mockGetNumericIDGroupResponse(t *testing.T, groupID int) {
url := fmt.Sprintf("%s/%d", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group": {
"id": 12345
}
}
`)
})
}
func mockDeleteGroupResponse(t *testing.T, groupID string) {
url := fmt.Sprintf("%s/%s", rootPath, groupID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
func mockAddRuleResponse(t *testing.T) {
th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"security_group_rule": {
"from_port": 22,
"ip_protocol": "TCP",
"to_port": 22,
"parent_group_id": "{groupID}",
"cidr": "0.0.0.0/0"
}
} `)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `
{
"security_group_rule": {
"from_port": 22,
"group": {},
"ip_protocol": "TCP",
"to_port": 22,
"parent_group_id": "{groupID}",
"ip_range": {
"cidr": "0.0.0.0/0"
},
"id": "{ruleID}"
}
}`)
})
}
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "DELETE")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
func mockAddServerToGroupResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("/servers/%s/action", serverID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"addSecurityGroup": {
"name": "test"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
url := fmt.Sprintf("/servers/%s/action", serverID)
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestJSONRequest(t, r, `
{
"removeSecurityGroup": {
"name": "test"
}
}
`)
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted)
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment