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 @@ ...@@ -203,8 +203,8 @@
}, },
{ {
"ImportPath": "github.com/rackspace/gophercloud", "ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v1.0.0", "Comment": "v1.0.0-336-g2a6e319",
"Rev": "da56de6a59e53fdd61be1b5d9b87df34c47ac420" "Rev": "2a6e3190447abe5d000f951595ead1cf98df72d8"
}, },
{ {
"ImportPath": "github.com/skynetservices/skydns/msg", "ImportPath": "github.com/skynetservices/skydns/msg",
......
...@@ -4,6 +4,8 @@ install: ...@@ -4,6 +4,8 @@ install:
go: go:
- 1.1 - 1.1
- 1.2 - 1.2
- 1.3
- 1.4
- tip - tip
script: script/cibuild script: script/cibuild
after_success: after_success:
...@@ -12,3 +14,4 @@ after_success: ...@@ -12,3 +14,4 @@ after_success:
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
- export PATH=$PATH:$HOME/gopath/bin/ - export PATH=$PATH:$HOME/gopath/bin/
- goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8 - goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
sudo: false
...@@ -11,7 +11,7 @@ As a contributor you will need to setup your workspace in a slightly different ...@@ -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: way than just downloading it. Here are the basic installation instructions:
1. Configure your `$GOPATH` and run `go get` as described in the main 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: 2. Move into the directory that houses your local repository:
......
...@@ -238,7 +238,7 @@ err := pager.EachPage(func(page pagination.Page) (bool, error) { ...@@ -238,7 +238,7 @@ err := pager.EachPage(func(page pagination.Page) (bool, error) {
imageList, err := images.ExtractImages(page) imageList, err := images.ExtractImages(page)
for _, i := range imageList { 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) { ...@@ -284,7 +284,7 @@ err := pager.EachPage(func(page pagination.Page) (bool, error) {
imageList, err := images.ExtractImages(page) imageList, err := images.ExtractImages(page)
for _, i := range imageList { for _, i := range imageList {
// "i" will be a images.Image // "i" will be an images.Image
} }
}) })
``` ```
......
...@@ -13,9 +13,9 @@ import ( ...@@ -13,9 +13,9 @@ import (
func TestSnapshots(t *testing.T) { func TestSnapshots(t *testing.T) {
client, err := newClient() client, err := newClient(t)
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
v, err := volumes.Create(client, &volumes.CreateOpts{ v, err := volumes.Create(client, &volumes.CreateOpts{
Name: "gophercloud-test-volume", Name: "gophercloud-test-volume",
Size: 1, Size: 1,
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
th "github.com/rackspace/gophercloud/testhelper" th "github.com/rackspace/gophercloud/testhelper"
) )
func newClient() (*gophercloud.ServiceClient, error) { func newClient(t *testing.T) (*gophercloud.ServiceClient, error) {
ao, err := openstack.AuthOptionsFromEnv() ao, err := openstack.AuthOptionsFromEnv()
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
...@@ -26,7 +26,7 @@ func newClient() (*gophercloud.ServiceClient, error) { ...@@ -26,7 +26,7 @@ func newClient() (*gophercloud.ServiceClient, error) {
} }
func TestVolumes(t *testing.T) { func TestVolumes(t *testing.T) {
client, err := newClient() client, err := newClient(t)
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
cv, err := volumes.Create(client, &volumes.CreateOpts{ cv, err := volumes.Create(client, &volumes.CreateOpts{
......
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
) )
func TestVolumeTypes(t *testing.T) { func TestVolumeTypes(t *testing.T) {
client, err := newClient() client, err := newClient(t)
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{ vt, err := volumetypes.Create(client, &volumetypes.CreateOpts{
......
...@@ -5,10 +5,10 @@ package v2 ...@@ -5,10 +5,10 @@ package v2
import ( import (
"testing" "testing"
"github.com/rackspace/gophercloud/acceptance/tools"
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers" "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper" th "github.com/rackspace/gophercloud/testhelper"
"github.com/smashwilson/gophercloud/acceptance/tools"
) )
func TestBootFromVolume(t *testing.T) { func TestBootFromVolume(t *testing.T) {
...@@ -37,14 +37,19 @@ func TestBootFromVolume(t *testing.T) { ...@@ -37,14 +37,19 @@ func TestBootFromVolume(t *testing.T) {
serverCreateOpts := servers.CreateOpts{ serverCreateOpts := servers.CreateOpts{
Name: name, Name: name,
FlavorRef: "3", FlavorRef: choices.FlavorID,
ImageRef: choices.ImageID,
} }
server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{ server, err := bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
serverCreateOpts, serverCreateOpts,
bd, bd,
}).Extract() }).Extract()
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
t.Fatal(err)
}
t.Logf("Created server: %+v\n", server) t.Logf("Created server: %+v\n", server)
//defer deleteServer(t, client, server) defer servers.Delete(client, server.ID)
t.Logf("Deleting server [%s]...", name) 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 ( ...@@ -12,6 +12,7 @@ import (
"github.com/rackspace/gophercloud/openstack/compute/v2/servers" "github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks" "github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/pagination" "github.com/rackspace/gophercloud/pagination"
th "github.com/rackspace/gophercloud/testhelper"
) )
func TestListServers(t *testing.T) { func TestListServers(t *testing.T) {
...@@ -94,6 +95,8 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp ...@@ -94,6 +95,8 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp
name := tools.RandomString("ACPTTEST", 16) name := tools.RandomString("ACPTTEST", 16)
t.Logf("Attempting to create server: %s\n", name) t.Logf("Attempting to create server: %s\n", name)
pwd := tools.MakeNewPassword("")
server, err := servers.Create(client, servers.CreateOpts{ server, err := servers.Create(client, servers.CreateOpts{
Name: name, Name: name,
FlavorRef: choices.FlavorID, FlavorRef: choices.FlavorID,
...@@ -101,11 +104,14 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp ...@@ -101,11 +104,14 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp
Networks: []servers.Network{ Networks: []servers.Network{
servers.Network{UUID: network.ID}, servers.Network{UUID: network.ID},
}, },
AdminPass: pwd,
}).Extract() }).Extract()
if err != nil { if err != nil {
t.Fatalf("Unable to create server: %v", err) t.Fatalf("Unable to create server: %v", err)
} }
th.AssertEquals(t, pwd, server.AdminPass)
return server, err return server, err
} }
...@@ -391,3 +397,54 @@ func TestActionResizeRevert(t *testing.T) { ...@@ -391,3 +397,54 @@ func TestActionResizeRevert(t *testing.T) {
t.Fatal(err) 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) { ...@@ -82,7 +82,7 @@ func listPorts(t *testing.T) {
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
for _, p := range portList { 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) p.ID, p.Name, p.Status, p.MACAddress, p.FixedIPs, p.SecurityGroups)
} }
......
...@@ -17,7 +17,10 @@ func TestAccounts(t *testing.T) { ...@@ -17,7 +17,10 @@ func TestAccounts(t *testing.T) {
// Update an account's metadata. // Update an account's metadata.
updateres := accounts.Update(client, accounts.UpdateOpts{Metadata: 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 the deletion of the metadata set above.
defer func() { defer func() {
...@@ -29,11 +32,14 @@ func TestAccounts(t *testing.T) { ...@@ -29,11 +32,14 @@ func TestAccounts(t *testing.T) {
th.AssertNoErr(t, updateres.Err) 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. // 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) th.AssertNoErr(t, err)
for k := range metadata { for k := range metadata {
if am[k] != metadata[strings.Title(k)] { 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 ...@@ -5,11 +5,11 @@ package v2
import ( import (
"testing" "testing"
"github.com/rackspace/gophercloud/acceptance/tools"
osBFV "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume" 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/bootfromvolume"
"github.com/rackspace/gophercloud/rackspace/compute/v2/servers" "github.com/rackspace/gophercloud/rackspace/compute/v2/servers"
th "github.com/rackspace/gophercloud/testhelper" th "github.com/rackspace/gophercloud/testhelper"
"github.com/smashwilson/gophercloud/acceptance/tools"
) )
func TestBootFromVolume(t *testing.T) { func TestBootFromVolume(t *testing.T) {
...@@ -41,6 +41,9 @@ func TestBootFromVolume(t *testing.T) { ...@@ -41,6 +41,9 @@ func TestBootFromVolume(t *testing.T) {
}).Extract() }).Extract()
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
t.Logf("Created server: %+v\n", server) t.Logf("Created server: %+v\n", server)
//defer deleteServer(t, client, server) defer deleteServer(t, client, server)
t.Logf("Deleting server [%s]...", name)
getServer(t, client, server)
listServers(t, client)
} }
...@@ -39,11 +39,14 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName strin ...@@ -39,11 +39,14 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName strin
name := tools.RandomString("Gophercloud-", 8) name := tools.RandomString("Gophercloud-", 8)
pwd := tools.MakeNewPassword("")
opts := &servers.CreateOpts{ opts := &servers.CreateOpts{
Name: name, Name: name,
ImageRef: options.imageID, ImageRef: options.imageID,
FlavorRef: options.flavorID, FlavorRef: options.flavorID,
DiskConfig: diskconfig.Manual, DiskConfig: diskconfig.Manual,
AdminPass: pwd,
} }
if keyName != "" { if keyName != "" {
...@@ -59,6 +62,8 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName strin ...@@ -59,6 +62,8 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, keyName strin
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
t.Logf("Server created successfully.") t.Logf("Server created successfully.")
th.CheckEquals(t, pwd, s.AdminPass)
return s return s
} }
...@@ -95,6 +100,18 @@ func getServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Serve ...@@ -95,6 +100,18 @@ func getServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Serve
logServer(t, details, -1) 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) { func listServers(t *testing.T, client *gophercloud.ServiceClient) {
t.Logf("> servers.List") t.Logf("> servers.List")
...@@ -120,7 +137,7 @@ func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server ...@@ -120,7 +137,7 @@ func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server
original := server.AdminPass original := server.AdminPass
t.Logf("Changing server password.") 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) th.AssertNoErr(t, err)
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300) err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
...@@ -131,7 +148,7 @@ func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server ...@@ -131,7 +148,7 @@ func changeAdminPassword(t *testing.T, client *gophercloud.ServiceClient, server
func rebootServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) { func rebootServer(t *testing.T, client *gophercloud.ServiceClient, server *os.Server) {
t.Logf("> servers.Reboot") 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) th.AssertNoErr(t, err)
err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300) err = servers.WaitForStatus(client, server.ID, "ACTIVE", 300)
...@@ -192,6 +209,7 @@ func TestServerOperations(t *testing.T) { ...@@ -192,6 +209,7 @@ func TestServerOperations(t *testing.T) {
defer deleteServer(t, client, server) defer deleteServer(t, client, server)
getServer(t, client, server) getServer(t, client, server)
updateServer(t, client, server)
listServers(t, client) listServers(t, client)
changeAdminPassword(t, client, server) changeAdminPassword(t, client, server)
rebootServer(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) { ...@@ -13,11 +13,11 @@ func TestAccounts(t *testing.T) {
c, err := createClient(t, false) c, err := createClient(t, false)
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
updateres := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}) updateHeaders, err := raxAccounts.Update(c, raxAccounts.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}).Extract()
th.AssertNoErr(t, updateres.Err) th.AssertNoErr(t, err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header) t.Logf("Update Account Response Headers: %+v\n", updateHeaders)
defer func() { 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) th.AssertNoErr(t, updateres.Err)
metadata, err := raxAccounts.Get(c).ExtractMetadata() metadata, err := raxAccounts.Get(c).ExtractMetadata()
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
...@@ -25,8 +25,13 @@ func TestAccounts(t *testing.T) { ...@@ -25,8 +25,13 @@ func TestAccounts(t *testing.T) {
th.CheckEquals(t, metadata["White"], "") th.CheckEquals(t, metadata["White"], "")
}() }()
metadata, err := raxAccounts.Get(c).ExtractMetadata() getResp := raxAccounts.Get(c)
th.AssertNoErr(t, err) 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) t.Logf("Metadata from Get Account request (after update): %+v\n", metadata)
th.CheckEquals(t, metadata["White"], "mountains") th.CheckEquals(t, metadata["White"], "mountains")
......
...@@ -26,10 +26,11 @@ func TestCDNContainers(t *testing.T) { ...@@ -26,10 +26,11 @@ func TestCDNContainers(t *testing.T) {
raxCDNClient, err := createClient(t, true) raxCDNClient, err := createClient(t, true)
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
enableRes := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900})
r := raxCDNContainers.Enable(raxCDNClient, "gophercloud-test", raxCDNContainers.EnableOpts{CDNEnabled: true, TTL: 900}) t.Logf("Header map from Enable CDN Container request: %+v\n", enableRes.Header)
th.AssertNoErr(t, r.Err) enableHeader, err := enableRes.Extract()
t.Logf("Headers from Enable CDN Container request: %+v\n", r.Header) 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:") t.Logf("Container Names available to the currently issued token:")
count := 0 count := 0
...@@ -51,11 +52,15 @@ func TestCDNContainers(t *testing.T) { ...@@ -51,11 +52,15 @@ func TestCDNContainers(t *testing.T) {
t.Errorf("No CDN containers listed for your current token.") t.Errorf("No CDN containers listed for your current token.")
} }
updateres := raxCDNContainers.Update(raxCDNClient, "gophercloud-test", raxCDNContainers.UpdateOpts{CDNEnabled: false}) updateOpts := raxCDNContainers.UpdateOpts{XCDNEnabled: raxCDNContainers.Disabled, XLogRetention: raxCDNContainers.Enabled}
th.AssertNoErr(t, updateres.Err) updateHeader, err := raxCDNContainers.Update(raxCDNClient, "gophercloud-test", updateOpts).Extract()
t.Logf("Headers from Update CDN Container request: %+v\n", updateres.Header) 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) 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) { ...@@ -57,29 +57,34 @@ func TestContainers(t *testing.T) {
t.Errorf("No containers listed for your current token.") t.Errorf("No containers listed for your current token.")
} }
createres := raxContainers.Create(c, "gophercloud-test", nil) createHeader, err := raxContainers.Create(c, "gophercloud-test", nil).Extract()
th.AssertNoErr(t, createres.Err) th.AssertNoErr(t, err)
t.Logf("Headers from Create Container request: %+v\n", createHeader)
defer func() { defer func() {
res := raxContainers.Delete(c, "gophercloud-test") deleteres := raxContainers.Delete(c, "gophercloud-test")
th.AssertNoErr(t, res.Err) 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"}}) updateHeader, err := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": "mountains"}}).Extract()
th.AssertNoErr(t, updateres.Err) th.AssertNoErr(t, err)
t.Logf("Headers from Update Account request: %+v\n", updateres.Header) t.Logf("Headers from Update Container request: %+v\n", updateHeader)
defer func() { defer func() {
res := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": ""}}) res := raxContainers.Update(c, "gophercloud-test", raxContainers.UpdateOpts{Metadata: map[string]string{"white": ""}})
th.AssertNoErr(t, res.Err) th.AssertNoErr(t, res.Err)
metadata, err := raxContainers.Get(c, "gophercloud-test").ExtractMetadata() metadata, err := raxContainers.Get(c, "gophercloud-test").ExtractMetadata()
th.AssertNoErr(t, err) 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"], "") th.CheckEquals(t, metadata["White"], "")
}() }()
getres := raxContainers.Get(c, "gophercloud-test") getres := raxContainers.Get(c, "gophercloud-test")
t.Logf("Headers from Get Account request (after update): %+v\n", getres.Header) getHeader, err := getres.Extract()
metadata, err := getres.ExtractMetadata()
th.AssertNoErr(t, err) 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") th.CheckEquals(t, metadata["White"], "mountains")
} }
...@@ -21,6 +21,7 @@ func TestObjects(t *testing.T) { ...@@ -21,6 +21,7 @@ func TestObjects(t *testing.T) {
th.AssertNoErr(t, res.Err) th.AssertNoErr(t, res.Err)
defer func() { defer func() {
t.Logf("Deleting container...")
res := raxContainers.Delete(c, "gophercloud-test") res := raxContainers.Delete(c, "gophercloud-test")
th.AssertNoErr(t, res.Err) th.AssertNoErr(t, res.Err)
}() }()
...@@ -29,7 +30,9 @@ func TestObjects(t *testing.T) { ...@@ -29,7 +30,9 @@ func TestObjects(t *testing.T) {
options := &osObjects.CreateOpts{ContentType: "text/plain"} options := &osObjects.CreateOpts{ContentType: "text/plain"}
createres := raxObjects.Create(c, "gophercloud-test", "o1", content, options) createres := raxObjects.Create(c, "gophercloud-test", "o1", content, options)
th.AssertNoErr(t, createres.Err) th.AssertNoErr(t, createres.Err)
defer func() { defer func() {
t.Logf("Deleting object o1...")
res := raxObjects.Delete(c, "gophercloud-test", "o1", nil) res := raxObjects.Delete(c, "gophercloud-test", "o1", nil)
th.AssertNoErr(t, res.Err) th.AssertNoErr(t, res.Err)
}() }()
...@@ -80,6 +83,7 @@ func TestObjects(t *testing.T) { ...@@ -80,6 +83,7 @@ func TestObjects(t *testing.T) {
copyres := raxObjects.Copy(c, "gophercloud-test", "o1", &raxObjects.CopyOpts{Destination: "gophercloud-test/o2"}) copyres := raxObjects.Copy(c, "gophercloud-test", "o1", &raxObjects.CopyOpts{Destination: "gophercloud-test/o2"})
th.AssertNoErr(t, copyres.Err) th.AssertNoErr(t, copyres.Err)
defer func() { defer func() {
t.Logf("Deleting object o2...")
res := raxObjects.Delete(c, "gophercloud-test", "o2", nil) res := raxObjects.Delete(c, "gophercloud-test", "o2", nil)
th.AssertNoErr(t, res.Err) th.AssertNoErr(t, res.Err)
}() }()
...@@ -99,7 +103,7 @@ func TestObjects(t *testing.T) { ...@@ -99,7 +103,7 @@ func TestObjects(t *testing.T) {
metadata, err := raxObjects.Get(c, "gophercloud-test", "o2", nil).ExtractMetadata() metadata, err := raxObjects.Get(c, "gophercloud-test", "o2", nil).ExtractMetadata()
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update reverted): %+v\n", metadata) 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) getres := raxObjects.Get(c, "gophercloud-test", "o2", nil)
...@@ -108,5 +112,13 @@ func TestObjects(t *testing.T) { ...@@ -108,5 +112,13 @@ func TestObjects(t *testing.T) {
metadata, err := getres.ExtractMetadata() metadata, err := getres.ExtractMetadata()
th.AssertNoErr(t, err) th.AssertNoErr(t, err)
t.Logf("Metadata from Get Account request (after update): %+v\n", metadata) 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 package tools
import ( import (
"crypto/rand" "crypto/rand"
"errors" "errors"
mrand "math/rand"
"os" "os"
"time" "time"
...@@ -72,6 +73,12 @@ func RandomString(prefix string, n int) string { ...@@ -72,6 +73,12 @@ func RandomString(prefix string, n int) string {
return prefix + string(bytes) 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 // Elide returns the first bit of its input string with a suffix of "..." if it's longer than
// a comfortable 40 characters. // a comfortable 40 characters.
func Elide(value string) string { func Elide(value string) string {
......
package gophercloud package gophercloud
// AuthOptions allows anyone calling Authenticate to supply the required access /*
// credentials. Its fields are the union of those recognized by each identity AuthOptions stores information needed to authenticate to an OpenStack cluster.
// implementation and provider. 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 { type AuthOptions struct {
// IdentityEndpoint specifies the HTTP endpoint that is required to work with // IdentityEndpoint specifies the HTTP endpoint that is required to work with
// the Identity API of the appropriate version. Required by the identity // the Identity API of the appropriate version. While it's ultimately needed by
// services, but often populated by a provider Client. // all of the identity services, it will often be populated by a provider-level
// function.
IdentityEndpoint string IdentityEndpoint string
// Username is required if using Identity V2 API. Consult with your provider's // 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 // 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 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 // APIs. Consult with your provider's control panel to discover your account's
// preferred method of authentication. // preferred method of authentication.
Password, APIKey string Password, APIKey string
...@@ -25,7 +33,7 @@ type AuthOptions struct { ...@@ -25,7 +33,7 @@ type AuthOptions struct {
// The TenantID and TenantName fields are optional for the Identity V2 API. // 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 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. // how these fields influence authentication.
TenantID, TenantName string TenantID, TenantName string
...@@ -34,5 +42,7 @@ type AuthOptions struct { ...@@ -34,5 +42,7 @@ type AuthOptions struct {
// re-authenticate automatically if/when your token expires. If you set it to // 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 // false, it will not cache these settings, but re-authentication will not be
// possible. This setting defaults to false. // possible. This setting defaults to false.
//
// This setting is speculative and is currently not respected!
AllowReauth bool AllowReauth bool
} }
...@@ -2,10 +2,9 @@ package gophercloud ...@@ -2,10 +2,9 @@ package gophercloud
import "time" import "time"
// AuthResults encapsulates the raw results from an authentication request. As OpenStack allows // AuthResults [deprecated] is a leftover type from the v0.x days. It was
// extensions to influence the structure returned in ways that Gophercloud cannot predict at // intended to describe common functionality among identity service results, but
// compile-time, you should use type-safe accessors to work with the data represented by this type, // is not actually used anywhere.
// such as ServiceCatalog and TokenID.
type AuthResults interface { type AuthResults interface {
// TokenID returns the token's ID value from the authentication response. // TokenID returns the token's ID value from the authentication response.
TokenID() (string, error) TokenID() (string, error)
......
...@@ -30,9 +30,38 @@ to work with and represent the state of API resources: ...@@ -30,9 +30,38 @@ to work with and represent the state of API resources:
server, err := servers.Get(client, "{serverId}").Extract() server, err := servers.Get(client, "{serverId}").Extract()
Another convention is to return Result structs for API operations, which allow Intermediate Result structs are returned for API operations, which allow
you to access the HTTP headers, response body, and associated errors with the generic access to the HTTP headers, response body, and any errors associated
network transaction. To get a resource struct, you then call the Extract with the network transaction. To turn a result into a usable resource struct,
method which is chained to the response. 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 package gophercloud
...@@ -3,58 +3,85 @@ package gophercloud ...@@ -3,58 +3,85 @@ package gophercloud
import "errors" import "errors"
var ( 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.") 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.") ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.")
) )
// Availability indicates whether a specific service endpoint is accessible. // Availability indicates to whom a specific service endpoint is accessible:
// Identity v2 lists these as different kinds of URLs ("adminURL", // the internet at large, internal networks only, or only to administrators.
// "internalURL", and "publicURL"), while v3 lists them as "Interfaces". // 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 type Availability string
const ( const (
// AvailabilityAdmin makes an endpoint only available to administrators. // AvailabilityAdmin indicates that an endpoint is only available to
// administrators.
AvailabilityAdmin Availability = "admin" 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" 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" 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 EndpointOpts struct {
// Type is the service type for the client (e.g., "compute", "object-store"). // Type [required] is the service type for the client (e.g., "compute",
// Required. // "object-store"). Generally, this will be supplied by the service client
// function, but a user-given value will be honored if provided.
Type string Type string
// Name is the service name for the client (e.g., "nova") as it appears in // Name [optional] is the service name for the client (e.g., "nova") as it
// the service catalog. Services can have the same Type but a different Name, // appears in the service catalog. Services can have the same Type but a
// which is why both Type and Name are sometimes needed. Optional. // different Name, which is why both Type and Name are sometimes needed.
Name string Name string
// Region is the geographic region in which the service resides. Required only // Region [required] is the geographic region in which the endpoint resides,
// for services that span multiple regions. // generally specifying which datacenter should house your resources.
// Required only for services that span multiple regions.
Region string Region string
// Availability is the visibility of the endpoint to be returned. Valid types // Availability [optional] is the visibility of the endpoint to be returned.
// are: AvailabilityPublic, AvailabilityInternal, or AvailabilityAdmin. // Valid types include the constants AvailabilityPublic, AvailabilityInternal,
// Availability is not required, and defaults to AvailabilityPublic. // or AvailabilityAdmin from this package.
// Not all providers or services offer all Availability options. //
// Availability is not required, and defaults to AvailabilityPublic. Not all
// providers or services offer all Availability options.
Availability Availability 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 EndpointLocator is an internal function to be used by provider implementations.
// during ProviderClient authentication and used to discover related ServiceClients.
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) type EndpointLocator func(EndpointOpts) (string, error)
// ApplyDefaults sets EndpointOpts fields if not already set. Currently, // ApplyDefaults is an internal method to be used by provider implementations.
// EndpointOpts.Availability defaults to the public endpoint. //
// 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) { func (eo *EndpointOpts) ApplyDefaults(t string) {
if eo.Type == "" { if eo.Type == "" {
eo.Type = t 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 { ...@@ -28,7 +28,7 @@ type Volume struct {
CreatedAt string `mapstructure:"created_at"` CreatedAt string `mapstructure:"created_at"`
// Human-readable description for the volume. // 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. // The type of volume to create, either SATA or SSD.
VolumeType string `mapstructure:"volume_type"` 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 ...@@ -203,3 +203,14 @@ func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoi
} }
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil 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 ( ...@@ -5,9 +5,34 @@ import (
"github.com/racker/perigee" "github.com/racker/perigee"
"github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/pagination" "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. // List returns a Pager that allows you to iterate over a collection of KeyPairs.
func List(client *gophercloud.ServiceClient) pagination.Pager { func List(client *gophercloud.ServiceClient) pagination.Pager {
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
...@@ -21,7 +46,7 @@ type CreateOptsBuilder interface { ...@@ -21,7 +46,7 @@ type CreateOptsBuilder interface {
ToKeyPairCreateMap() (map[string]interface{}, error) ToKeyPairCreateMap() (map[string]interface{}, error)
} }
// CreateOpts species keypair creation or import parameters. // CreateOpts specifies keypair creation or import parameters.
type CreateOpts struct { type CreateOpts struct {
// Name [required] is a friendly name to refer to this KeyPair in other services. // Name [required] is a friendly name to refer to this KeyPair in other services.
Name string 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