updated gophercloud
This commit is contained in:
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@@ -489,8 +489,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud",
|
"ImportPath": "github.com/rackspace/gophercloud",
|
||||||
"Comment": "v1.0.0-569-gf3ced00",
|
"Comment": "v1.0.0-665-gf928634",
|
||||||
"Rev": "f3ced00552c1c7d4a6184500af9062cfb4ff4463"
|
"Rev": "f92863476c034f851073599c09d90cd61ee95b3d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/russross/blackfriday",
|
"ImportPath": "github.com/russross/blackfriday",
|
||||||
|
|||||||
5
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md
generated
vendored
5
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md
generated
vendored
@@ -28,11 +28,10 @@ fork as `origin` instead:
|
|||||||
git remote add origin git@github.com/<my_username>/gophercloud
|
git remote add origin git@github.com/<my_username>/gophercloud
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Checkout the latest development branch ([click here](/branches) to see all
|
4. Checkout the latest development branch:
|
||||||
the branches):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git checkout release/v1.0.1
|
git checkout master
|
||||||
```
|
```
|
||||||
|
|
||||||
5. If you're working on something (discussed more in detail below), you will
|
5. If you're working on something (discussed more in detail below), you will
|
||||||
|
|||||||
78
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/network_test.go
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/compute/v2/network_test.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// +build acceptance compute servers
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getNetworkIDFromNetworkExtension(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
|
||||||
|
allPages, err := networks.List(client).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to list networks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkList, err := networks.ExtractNetworks(allPages)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to list networks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID := ""
|
||||||
|
for _, network := range networkList {
|
||||||
|
t.Logf("Network: %v", network)
|
||||||
|
if network.Label == networkName {
|
||||||
|
networkID = network.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Found network ID for %s: %s\n", networkName, networkID)
|
||||||
|
|
||||||
|
return networkID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworks(t *testing.T) {
|
||||||
|
networkName := os.Getenv("OS_NETWORK_NAME")
|
||||||
|
if networkName == "" {
|
||||||
|
t.Fatalf("OS_NETWORK_NAME must be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
choices, err := ComputeChoicesFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create a compute client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID, err := getNetworkIDFromNetworkExtension(t, client, networkName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to get network ID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createNetworkServer is defined in tenantnetworks_test.go
|
||||||
|
server, err := createNetworkServer(t, client, choices, networkID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create server: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
servers.Delete(client, server.ID)
|
||||||
|
t.Logf("Server deleted.")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = waitForStatus(client, server, "ACTIVE"); err != nil {
|
||||||
|
t.Fatalf("Unable to wait for server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allPages, err := networks.List(client).AllPages()
|
||||||
|
allNetworks, err := networks.ExtractNetworks(allPages)
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
t.Logf("Retrieved all %d networks: %+v", len(allNetworks), allNetworks)
|
||||||
|
}
|
||||||
@@ -3,10 +3,15 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups"
|
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createServerGroup(t *testing.T, computeClient *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) {
|
func createServerGroup(t *testing.T, computeClient *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) {
|
||||||
@@ -36,7 +41,53 @@ func getServerGroup(t *testing.T, computeClient *gophercloud.ServiceClient, sgID
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createServerInGroup(t *testing.T, computeClient *gophercloud.ServiceClient, choices *ComputeChoices, serverGroup *servergroups.ServerGroup) (*servers.Server, error) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping test that requires server creation in short mode.")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := tools.RandomString("ACPTTEST", 16)
|
||||||
|
t.Logf("Attempting to create server: %s\n", name)
|
||||||
|
|
||||||
|
pwd := tools.MakeNewPassword("")
|
||||||
|
|
||||||
|
serverCreateOpts := servers.CreateOpts{
|
||||||
|
Name: name,
|
||||||
|
FlavorRef: choices.FlavorID,
|
||||||
|
ImageRef: choices.ImageID,
|
||||||
|
AdminPass: pwd,
|
||||||
|
}
|
||||||
|
server, err := servers.Create(computeClient, schedulerhints.CreateOptsExt{
|
||||||
|
serverCreateOpts,
|
||||||
|
schedulerhints.SchedulerHints{
|
||||||
|
Group: serverGroup.ID,
|
||||||
|
},
|
||||||
|
}).Extract()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
th.AssertEquals(t, pwd, server.AdminPass)
|
||||||
|
|
||||||
|
return server, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifySchedulerWorked(t *testing.T, firstServer, secondServer *servers.Server) error {
|
||||||
|
t.Logf("First server hostID: %v", firstServer.HostID)
|
||||||
|
t.Logf("Second server hostID: %v", secondServer.HostID)
|
||||||
|
if firstServer.HostID == secondServer.HostID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("%s and %s were not scheduled on the same host.", firstServer.ID, secondServer.ID)
|
||||||
|
}
|
||||||
|
|
||||||
func TestServerGroups(t *testing.T) {
|
func TestServerGroups(t *testing.T) {
|
||||||
|
choices, err := ComputeChoicesFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
computeClient, err := newClient()
|
computeClient, err := newClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to create a compute client: %v", err)
|
t.Fatalf("Unable to create a compute client: %v", err)
|
||||||
@@ -48,11 +99,45 @@ func TestServerGroups(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
servergroups.Delete(computeClient, sg.ID)
|
servergroups.Delete(computeClient, sg.ID)
|
||||||
t.Logf("ServerGroup deleted.")
|
t.Logf("Server Group deleted.")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = getServerGroup(t, computeClient, sg.ID)
|
err = getServerGroup(t, computeClient, sg.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to get server group: %v", err)
|
t.Fatalf("Unable to get server group: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstServer, err := createServerInGroup(t, computeClient, choices, sg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create server: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
servers.Delete(computeClient, firstServer.ID)
|
||||||
|
t.Logf("Server deleted.")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = waitForStatus(computeClient, firstServer, "ACTIVE"); err != nil {
|
||||||
|
t.Fatalf("Unable to wait for server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
firstServer, err = servers.Get(computeClient, firstServer.ID).Extract()
|
||||||
|
|
||||||
|
secondServer, err := createServerInGroup(t, computeClient, choices, sg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create server: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
servers.Delete(computeClient, secondServer.ID)
|
||||||
|
t.Logf("Server deleted.")
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = waitForStatus(computeClient, secondServer, "ACTIVE"); err != nil {
|
||||||
|
t.Fatalf("Unable to wait for server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secondServer, err = servers.Get(computeClient, secondServer.ID).Extract()
|
||||||
|
|
||||||
|
if err = verifySchedulerWorked(t, firstServer, secondServer); err != nil {
|
||||||
|
t.Fatalf("Scheduling did not work: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,12 @@ func createServer(t *testing.T, client *gophercloud.ServiceClient, choices *Comp
|
|||||||
servers.Network{UUID: network.ID},
|
servers.Network{UUID: network.ID},
|
||||||
},
|
},
|
||||||
AdminPass: pwd,
|
AdminPass: pwd,
|
||||||
|
Personality: servers.Personality{
|
||||||
|
&servers.File{
|
||||||
|
Path: "/etc/test",
|
||||||
|
Contents: []byte("hello world"),
|
||||||
|
},
|
||||||
|
},
|
||||||
}).Extract()
|
}).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to create server: %v", err)
|
t.Fatalf("Unable to create server: %v", err)
|
||||||
|
|||||||
61
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/tokens_test.go
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/identity/v2/tokens_test.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// +build acceptance
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||||
|
"github.com/rackspace/gophercloud/rackspace"
|
||||||
|
"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
|
||||||
|
// Obtain credentials from the environment.
|
||||||
|
options, err := rackspace.AuthOptionsFromEnv()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
options = tools.OnlyRS(options)
|
||||||
|
|
||||||
|
if options.Username == "" {
|
||||||
|
t.Fatal("Please provide a Rackspace username as RS_USERNAME.")
|
||||||
|
}
|
||||||
|
if options.APIKey == "" {
|
||||||
|
t.Fatal("Please provide a Rackspace API key as RS_API_KEY.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
|
||||||
|
ao := rackspaceAuthOptions(t)
|
||||||
|
|
||||||
|
provider, err := rackspace.NewClient(ao.IdentityEndpoint)
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
|
if auth {
|
||||||
|
err = rackspace.Authenticate(provider, ao)
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rackspace.NewIdentityV2(provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTokenAuth(t *testing.T) {
|
||||||
|
authedClient := createClient(t, true)
|
||||||
|
token := authedClient.TokenID
|
||||||
|
|
||||||
|
tenantID := os.Getenv("RS_TENANT_ID")
|
||||||
|
if tenantID == "" {
|
||||||
|
t.Skip("You must set RS_TENANT_ID environment variable to run this test")
|
||||||
|
}
|
||||||
|
|
||||||
|
authOpts := tokens.AuthOptions{}
|
||||||
|
authOpts.TenantID = tenantID
|
||||||
|
authOpts.Token = token
|
||||||
|
|
||||||
|
_, err := tokens.Create(authedClient, authOpts).ExtractToken()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
}
|
||||||
4
Godeps/_workspace/src/github.com/rackspace/gophercloud/auth_options.go
generated
vendored
4
Godeps/_workspace/src/github.com/rackspace/gophercloud/auth_options.go
generated
vendored
@@ -43,4 +43,8 @@ type AuthOptions struct {
|
|||||||
// 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.
|
||||||
AllowReauth bool
|
AllowReauth bool
|
||||||
|
|
||||||
|
// TokenID allows users to authenticate (possibly as another user) with an
|
||||||
|
// authentication token ID.
|
||||||
|
TokenID string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,3 +171,36 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet
|
|||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a snapshot's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
snapshotCount := 0
|
||||||
|
snapshotID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A snapshot name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
snapshotList, err := ExtractSnapshots(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range snapshotList {
|
||||||
|
if s.Name == name {
|
||||||
|
snapshotCount++
|
||||||
|
snapshotID = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch snapshotCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find snapshot: %s", name)
|
||||||
|
case 1:
|
||||||
|
return snapshotID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d snapshots matching %s", snapshotCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -201,3 +201,36 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
|
|||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a server's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
volumeCount := 0
|
||||||
|
volumeID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A volume name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
volumeList, err := ExtractVolumes(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range volumeList {
|
||||||
|
if s.Name == name {
|
||||||
|
volumeCount++
|
||||||
|
volumeID = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch volumeCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find volume: %s", name)
|
||||||
|
case 1:
|
||||||
|
return volumeID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d volumes matching %s", volumeCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package volumes
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
fixtures "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
th "github.com/rackspace/gophercloud/testhelper"
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
"github.com/rackspace/gophercloud/testhelper/client"
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
@@ -12,7 +13,7 @@ func TestList(t *testing.T) {
|
|||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
MockListResponse(t)
|
fixtures.MockListResponse(t)
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ func TestListAll(t *testing.T) {
|
|||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
MockListResponse(t)
|
fixtures.MockListResponse(t)
|
||||||
|
|
||||||
allPages, err := List(client.ServiceClient(), &ListOpts{}).AllPages()
|
allPages, err := List(client.ServiceClient(), &ListOpts{}).AllPages()
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
@@ -75,7 +76,7 @@ func TestGet(t *testing.T) {
|
|||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
MockGetResponse(t)
|
fixtures.MockGetResponse(t)
|
||||||
|
|
||||||
v, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
|
v, err := Get(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
@@ -89,7 +90,7 @@ func TestCreate(t *testing.T) {
|
|||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
MockCreateResponse(t)
|
fixtures.MockCreateResponse(t)
|
||||||
|
|
||||||
options := &CreateOpts{Size: 75}
|
options := &CreateOpts{Size: 75}
|
||||||
n, err := Create(client.ServiceClient(), options).Extract()
|
n, err := Create(client.ServiceClient(), options).Extract()
|
||||||
@@ -103,7 +104,7 @@ func TestDelete(t *testing.T) {
|
|||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
MockDeleteResponse(t)
|
fixtures.MockDeleteResponse(t)
|
||||||
|
|
||||||
res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
res := Delete(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22")
|
||||||
th.AssertNoErr(t, res.Err)
|
th.AssertNoErr(t, res.Err)
|
||||||
@@ -113,7 +114,7 @@ func TestUpdate(t *testing.T) {
|
|||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
MockUpdateResponse(t)
|
fixtures.MockUpdateResponse(t)
|
||||||
|
|
||||||
options := UpdateOpts{Name: "vol-002"}
|
options := UpdateOpts{Name: "vol-002"}
|
||||||
v, err := Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
|
v, err := Update(client.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22", options).Extract()
|
||||||
|
|||||||
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing/doc.go
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
This is package created is to hold fixtures (which imports testing),
|
||||||
|
so that importing volumes package does not inadvertently import testing into production code
|
||||||
|
More information here:
|
||||||
|
https://github.com/rackspace/gophercloud/issues/473
|
||||||
|
*/
|
||||||
|
package testing
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package volumes
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -32,6 +32,8 @@ func TestCreateOpts(t *testing.T) {
|
|||||||
"name": "createdserver",
|
"name": "createdserver",
|
||||||
"imageRef": "asdfasdfasdf",
|
"imageRef": "asdfasdfasdf",
|
||||||
"flavorRef": "performance1-1",
|
"flavorRef": "performance1-1",
|
||||||
|
"flavorName": "",
|
||||||
|
"imageName": "",
|
||||||
"block_device_mapping_v2":[
|
"block_device_mapping_v2":[
|
||||||
{
|
{
|
||||||
"uuid":"123456",
|
"uuid":"123456",
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ func TestCreateOpts(t *testing.T) {
|
|||||||
"name": "createdserver",
|
"name": "createdserver",
|
||||||
"imageRef": "asdfasdfasdf",
|
"imageRef": "asdfasdfasdf",
|
||||||
"flavorRef": "performance1-1",
|
"flavorRef": "performance1-1",
|
||||||
|
"flavorName": "",
|
||||||
|
"imageName": "",
|
||||||
"OS-DCF:diskConfig": "MANUAL"
|
"OS-DCF:diskConfig": "MANUAL"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/doc.go
generated
vendored
Normal file
2
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package network provides the ability to manage nova-networks
|
||||||
|
package networks
|
||||||
209
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/fixtures.go
generated
vendored
Normal file
209
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/fixtures.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// +build fixtures
|
||||||
|
|
||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListOutput is a sample response to a List call.
|
||||||
|
const ListOutput = `
|
||||||
|
{
|
||||||
|
"networks": [
|
||||||
|
{
|
||||||
|
"bridge": "br100",
|
||||||
|
"bridge_interface": "eth0",
|
||||||
|
"broadcast": "10.0.0.7",
|
||||||
|
"cidr": "10.0.0.0/29",
|
||||||
|
"cidr_v6": null,
|
||||||
|
"created_at": "2011-08-15 06:19:19.387525",
|
||||||
|
"deleted": false,
|
||||||
|
"deleted_at": null,
|
||||||
|
"dhcp_start": "10.0.0.3",
|
||||||
|
"dns1": null,
|
||||||
|
"dns2": null,
|
||||||
|
"gateway": "10.0.0.1",
|
||||||
|
"gateway_v6": null,
|
||||||
|
"host": "nsokolov-desktop",
|
||||||
|
"id": "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||||
|
"injected": false,
|
||||||
|
"label": "mynet_0",
|
||||||
|
"multi_host": false,
|
||||||
|
"netmask": "255.255.255.248",
|
||||||
|
"netmask_v6": null,
|
||||||
|
"priority": null,
|
||||||
|
"project_id": "1234",
|
||||||
|
"rxtx_base": null,
|
||||||
|
"updated_at": "2011-08-16 09:26:13.048257",
|
||||||
|
"vlan": 100,
|
||||||
|
"vpn_private_address": "10.0.0.2",
|
||||||
|
"vpn_public_address": "127.0.0.1",
|
||||||
|
"vpn_public_port": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bridge": "br101",
|
||||||
|
"bridge_interface": "eth0",
|
||||||
|
"broadcast": "10.0.0.15",
|
||||||
|
"cidr": "10.0.0.10/29",
|
||||||
|
"cidr_v6": null,
|
||||||
|
"created_at": "2011-08-15 06:19:19.885495",
|
||||||
|
"deleted": false,
|
||||||
|
"deleted_at": null,
|
||||||
|
"dhcp_start": "10.0.0.11",
|
||||||
|
"dns1": null,
|
||||||
|
"dns2": null,
|
||||||
|
"gateway": "10.0.0.9",
|
||||||
|
"gateway_v6": null,
|
||||||
|
"host": null,
|
||||||
|
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||||
|
"injected": false,
|
||||||
|
"label": "mynet_1",
|
||||||
|
"multi_host": false,
|
||||||
|
"netmask": "255.255.255.248",
|
||||||
|
"netmask_v6": null,
|
||||||
|
"priority": null,
|
||||||
|
"project_id": null,
|
||||||
|
"rxtx_base": null,
|
||||||
|
"updated_at": null,
|
||||||
|
"vlan": 101,
|
||||||
|
"vpn_private_address": "10.0.0.10",
|
||||||
|
"vpn_public_address": null,
|
||||||
|
"vpn_public_port": 1001
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// GetOutput is a sample response to a Get call.
|
||||||
|
const GetOutput = `
|
||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"bridge": "br101",
|
||||||
|
"bridge_interface": "eth0",
|
||||||
|
"broadcast": "10.0.0.15",
|
||||||
|
"cidr": "10.0.0.10/29",
|
||||||
|
"cidr_v6": null,
|
||||||
|
"created_at": "2011-08-15 06:19:19.885495",
|
||||||
|
"deleted": false,
|
||||||
|
"deleted_at": null,
|
||||||
|
"dhcp_start": "10.0.0.11",
|
||||||
|
"dns1": null,
|
||||||
|
"dns2": null,
|
||||||
|
"gateway": "10.0.0.9",
|
||||||
|
"gateway_v6": null,
|
||||||
|
"host": null,
|
||||||
|
"id": "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||||
|
"injected": false,
|
||||||
|
"label": "mynet_1",
|
||||||
|
"multi_host": false,
|
||||||
|
"netmask": "255.255.255.248",
|
||||||
|
"netmask_v6": null,
|
||||||
|
"priority": null,
|
||||||
|
"project_id": null,
|
||||||
|
"rxtx_base": null,
|
||||||
|
"updated_at": null,
|
||||||
|
"vlan": 101,
|
||||||
|
"vpn_private_address": "10.0.0.10",
|
||||||
|
"vpn_public_address": null,
|
||||||
|
"vpn_public_port": 1001
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// FirstNetwork is the first result in ListOutput.
|
||||||
|
var nilTime time.Time
|
||||||
|
var FirstNetwork = Network{
|
||||||
|
Bridge: "br100",
|
||||||
|
BridgeInterface: "eth0",
|
||||||
|
Broadcast: "10.0.0.7",
|
||||||
|
CIDR: "10.0.0.0/29",
|
||||||
|
CIDRv6: "",
|
||||||
|
CreatedAt: time.Date(2011, 8, 15, 6, 19, 19, 387525000, time.UTC),
|
||||||
|
Deleted: false,
|
||||||
|
DeletedAt: nilTime,
|
||||||
|
DHCPStart: "10.0.0.3",
|
||||||
|
DNS1: "",
|
||||||
|
DNS2: "",
|
||||||
|
Gateway: "10.0.0.1",
|
||||||
|
Gatewayv6: "",
|
||||||
|
Host: "nsokolov-desktop",
|
||||||
|
ID: "20c8acc0-f747-4d71-a389-46d078ebf047",
|
||||||
|
Injected: false,
|
||||||
|
Label: "mynet_0",
|
||||||
|
MultiHost: false,
|
||||||
|
Netmask: "255.255.255.248",
|
||||||
|
Netmaskv6: "",
|
||||||
|
Priority: 0,
|
||||||
|
ProjectID: "1234",
|
||||||
|
RXTXBase: 0,
|
||||||
|
UpdatedAt: time.Date(2011, 8, 16, 9, 26, 13, 48257000, time.UTC),
|
||||||
|
VLAN: 100,
|
||||||
|
VPNPrivateAddress: "10.0.0.2",
|
||||||
|
VPNPublicAddress: "127.0.0.1",
|
||||||
|
VPNPublicPort: 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecondNetwork is the second result in ListOutput.
|
||||||
|
var SecondNetwork = Network{
|
||||||
|
Bridge: "br101",
|
||||||
|
BridgeInterface: "eth0",
|
||||||
|
Broadcast: "10.0.0.15",
|
||||||
|
CIDR: "10.0.0.10/29",
|
||||||
|
CIDRv6: "",
|
||||||
|
CreatedAt: time.Date(2011, 8, 15, 6, 19, 19, 885495000, time.UTC),
|
||||||
|
Deleted: false,
|
||||||
|
DeletedAt: nilTime,
|
||||||
|
DHCPStart: "10.0.0.11",
|
||||||
|
DNS1: "",
|
||||||
|
DNS2: "",
|
||||||
|
Gateway: "10.0.0.9",
|
||||||
|
Gatewayv6: "",
|
||||||
|
Host: "",
|
||||||
|
ID: "20c8acc0-f747-4d71-a389-46d078ebf000",
|
||||||
|
Injected: false,
|
||||||
|
Label: "mynet_1",
|
||||||
|
MultiHost: false,
|
||||||
|
Netmask: "255.255.255.248",
|
||||||
|
Netmaskv6: "",
|
||||||
|
Priority: 0,
|
||||||
|
ProjectID: "",
|
||||||
|
RXTXBase: 0,
|
||||||
|
UpdatedAt: nilTime,
|
||||||
|
VLAN: 101,
|
||||||
|
VPNPrivateAddress: "10.0.0.10",
|
||||||
|
VPNPublicAddress: "",
|
||||||
|
VPNPublicPort: 1001,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectedNetworkSlice is the slice of results that should be parsed
|
||||||
|
// from ListOutput, in the expected order.
|
||||||
|
var ExpectedNetworkSlice = []Network{FirstNetwork, SecondNetwork}
|
||||||
|
|
||||||
|
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||||
|
func HandleListSuccessfully(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/os-networks", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, ListOutput)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleGetSuccessfully configures the test server to respond to a Get request
|
||||||
|
// for an existing network.
|
||||||
|
func HandleGetSuccessfully(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/os-networks/20c8acc0-f747-4d71-a389-46d078ebf000", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, GetOutput)
|
||||||
|
})
|
||||||
|
}
|
||||||
22
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/requests.go
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/requests.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List returns a Pager that allows you to iterate over a collection of Network.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
url := listURL(client)
|
||||||
|
createPage := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return NetworkPage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, url, createPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns data about a previously created Network.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||||
|
return res
|
||||||
|
}
|
||||||
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/requests_test.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestList(t *testing.T) {
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
HandleListSuccessfully(t)
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
err := List(client.ServiceClient()).EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
count++
|
||||||
|
actual, err := ExtractNetworks(page)
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckDeepEquals(t, ExpectedNetworkSlice, actual)
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckEquals(t, 1, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGet(t *testing.T) {
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
HandleGetSuccessfully(t)
|
||||||
|
|
||||||
|
actual, err := Get(client.ServiceClient(), "20c8acc0-f747-4d71-a389-46d078ebf000").Extract()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckDeepEquals(t, &SecondNetwork, actual)
|
||||||
|
}
|
||||||
222
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/results.go
generated
vendored
Normal file
222
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/results.go
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Network represents a nova-network that an instance communicates on
|
||||||
|
type Network struct {
|
||||||
|
// The Bridge that VIFs on this network are connected to
|
||||||
|
Bridge string `mapstructure:"bridge"`
|
||||||
|
|
||||||
|
// BridgeInterface is what interface is connected to the Bridge
|
||||||
|
BridgeInterface string `mapstructure:"bridge_interface"`
|
||||||
|
|
||||||
|
// The Broadcast address of the network.
|
||||||
|
Broadcast string `mapstructure:"broadcast"`
|
||||||
|
|
||||||
|
// CIDR is the IPv4 subnet.
|
||||||
|
CIDR string `mapstructure:"cidr"`
|
||||||
|
|
||||||
|
// CIDRv6 is the IPv6 subnet.
|
||||||
|
CIDRv6 string `mapstructure:"cidr_v6"`
|
||||||
|
|
||||||
|
// CreatedAt is when the network was created..
|
||||||
|
CreatedAt time.Time `mapstructure:"-"`
|
||||||
|
|
||||||
|
// Deleted shows if the network has been deleted.
|
||||||
|
Deleted bool `mapstructure:"deleted"`
|
||||||
|
|
||||||
|
// DeletedAt is the time when the network was deleted.
|
||||||
|
DeletedAt time.Time `mapstructure:"-"`
|
||||||
|
|
||||||
|
// DHCPStart is the start of the DHCP address range.
|
||||||
|
DHCPStart string `mapstructure:"dhcp_start"`
|
||||||
|
|
||||||
|
// DNS1 is the first DNS server to use through DHCP.
|
||||||
|
DNS1 string `mapstructure:"dns_1"`
|
||||||
|
|
||||||
|
// DNS2 is the first DNS server to use through DHCP.
|
||||||
|
DNS2 string `mapstructure:"dns_2"`
|
||||||
|
|
||||||
|
// Gateway is the network gateway.
|
||||||
|
Gateway string `mapstructure:"gateway"`
|
||||||
|
|
||||||
|
// Gatewayv6 is the IPv6 network gateway.
|
||||||
|
Gatewayv6 string `mapstructure:"gateway_v6"`
|
||||||
|
|
||||||
|
// Host is the host that the network service is running on.
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
|
||||||
|
// ID is the UUID of the network.
|
||||||
|
ID string `mapstructure:"id"`
|
||||||
|
|
||||||
|
// Injected determines if network information is injected into the host.
|
||||||
|
Injected bool `mapstructure:"injected"`
|
||||||
|
|
||||||
|
// Label is the common name that the network has..
|
||||||
|
Label string `mapstructure:"label"`
|
||||||
|
|
||||||
|
// MultiHost is if multi-host networking is enablec..
|
||||||
|
MultiHost bool `mapstructure:"multi_host"`
|
||||||
|
|
||||||
|
// Netmask is the network netmask.
|
||||||
|
Netmask string `mapstructure:"netmask"`
|
||||||
|
|
||||||
|
// Netmaskv6 is the IPv6 netmask.
|
||||||
|
Netmaskv6 string `mapstructure:"netmask_v6"`
|
||||||
|
|
||||||
|
// Priority is the network interface priority.
|
||||||
|
Priority int `mapstructure:"priority"`
|
||||||
|
|
||||||
|
// ProjectID is the project associated with this network.
|
||||||
|
ProjectID string `mapstructure:"project_id"`
|
||||||
|
|
||||||
|
// RXTXBase configures bandwidth entitlement.
|
||||||
|
RXTXBase int `mapstructure:"rxtx_base"`
|
||||||
|
|
||||||
|
// UpdatedAt is the time when the network was last updated.
|
||||||
|
UpdatedAt time.Time `mapstructure:"-"`
|
||||||
|
|
||||||
|
// VLAN is the vlan this network runs on.
|
||||||
|
VLAN int `mapstructure:"vlan"`
|
||||||
|
|
||||||
|
// VPNPrivateAddress is the private address of the CloudPipe VPN.
|
||||||
|
VPNPrivateAddress string `mapstructure:"vpn_private_address"`
|
||||||
|
|
||||||
|
// VPNPublicAddress is the public address of the CloudPipe VPN.
|
||||||
|
VPNPublicAddress string `mapstructure:"vpn_public_address"`
|
||||||
|
|
||||||
|
// VPNPublicPort is the port of the CloudPipe VPN.
|
||||||
|
VPNPublicPort int `mapstructure:"vpn_public_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkPage stores a single, only page of Networks
|
||||||
|
// results from a List call.
|
||||||
|
type NetworkPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a NetworkPage is empty.
|
||||||
|
func (page NetworkPage) IsEmpty() (bool, error) {
|
||||||
|
va, err := ExtractNetworks(page)
|
||||||
|
return len(va) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractNetworks interprets a page of results as a slice of Networks
|
||||||
|
func ExtractNetworks(page pagination.Page) ([]Network, error) {
|
||||||
|
var res struct {
|
||||||
|
Networks []Network `mapstructure:"networks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(page.(NetworkPage).Body, &res)
|
||||||
|
|
||||||
|
var rawNetworks []interface{}
|
||||||
|
body := page.(NetworkPage).Body
|
||||||
|
switch body.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
rawNetworks = body.(map[string]interface{})["networks"].([]interface{})
|
||||||
|
case map[string][]interface{}:
|
||||||
|
rawNetworks = body.(map[string][]interface{})["networks"]
|
||||||
|
default:
|
||||||
|
return res.Networks, fmt.Errorf("Unknown type")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range rawNetworks {
|
||||||
|
thisNetwork := rawNetworks[i].(map[string]interface{})
|
||||||
|
if t, ok := thisNetwork["created_at"].(string); ok && t != "" {
|
||||||
|
createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||||
|
if err != nil {
|
||||||
|
return res.Networks, err
|
||||||
|
}
|
||||||
|
res.Networks[i].CreatedAt = createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := thisNetwork["updated_at"].(string); ok && t != "" {
|
||||||
|
updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||||
|
if err != nil {
|
||||||
|
return res.Networks, err
|
||||||
|
}
|
||||||
|
res.Networks[i].UpdatedAt = updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := thisNetwork["deleted_at"].(string); ok && t != "" {
|
||||||
|
deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||||
|
if err != nil {
|
||||||
|
return res.Networks, err
|
||||||
|
}
|
||||||
|
res.Networks[i].DeletedAt = deletedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Networks, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetworkResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract is a method that attempts to interpret any Network resource
|
||||||
|
// response as a Network struct.
|
||||||
|
func (r NetworkResult) Extract() (*Network, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res struct {
|
||||||
|
Network *Network `json:"network" mapstructure:"network"`
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &mapstructure.DecoderConfig{
|
||||||
|
Result: &res,
|
||||||
|
WeaklyTypedInput: true,
|
||||||
|
}
|
||||||
|
decoder, err := mapstructure.NewDecoder(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := decoder.Decode(r.Body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b := r.Body.(map[string]interface{})["network"].(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := b["created_at"].(string); ok && t != "" {
|
||||||
|
createdAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||||
|
if err != nil {
|
||||||
|
return res.Network, err
|
||||||
|
}
|
||||||
|
res.Network.CreatedAt = createdAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := b["updated_at"].(string); ok && t != "" {
|
||||||
|
updatedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||||
|
if err != nil {
|
||||||
|
return res.Network, err
|
||||||
|
}
|
||||||
|
res.Network.UpdatedAt = updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := b["deleted_at"].(string); ok && t != "" {
|
||||||
|
deletedAt, err := time.Parse("2006-01-02 15:04:05.000000", t)
|
||||||
|
if err != nil {
|
||||||
|
return res.Network, err
|
||||||
|
}
|
||||||
|
res.Network.DeletedAt = deletedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Network, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||||
|
// as a Network.
|
||||||
|
type GetResult struct {
|
||||||
|
NetworkResult
|
||||||
|
}
|
||||||
17
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/urls.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/urls.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
const resourcePath = "os-networks"
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL(resourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL(resourcePath, id)
|
||||||
|
}
|
||||||
25
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/urls_test.go
generated
vendored
Normal file
25
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/networks/urls_test.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package networks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListURL(t *testing.T) {
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
c := client.ServiceClient()
|
||||||
|
|
||||||
|
th.CheckEquals(t, c.Endpoint+"os-networks", listURL(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetURL(t *testing.T) {
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
c := client.ServiceClient()
|
||||||
|
id := "1"
|
||||||
|
|
||||||
|
th.CheckEquals(t, c.Endpoint+"os-networks/"+id, getURL(c, id))
|
||||||
|
}
|
||||||
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Package schedulerhints enables instances to provide the OpenStack scheduler
|
||||||
|
// hints about where they should be placed in the cloud.
|
||||||
|
package schedulerhints
|
||||||
134
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests.go
generated
vendored
Normal file
134
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package schedulerhints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SchedulerHints represents a set of scheduling hints that are passed to the
|
||||||
|
// OpenStack scheduler
|
||||||
|
type SchedulerHints struct {
|
||||||
|
// Group specifies a Server Group to place the instance in.
|
||||||
|
Group string
|
||||||
|
|
||||||
|
// DifferentHost will place the instance on a compute node that does not
|
||||||
|
// host the given instances.
|
||||||
|
DifferentHost []string
|
||||||
|
|
||||||
|
// SameHost will place the instance on a compute node that hosts the given
|
||||||
|
// instances.
|
||||||
|
SameHost []string
|
||||||
|
|
||||||
|
// Query is a conditional statement that results in compute nodes able to
|
||||||
|
// host the instance.
|
||||||
|
Query []interface{}
|
||||||
|
|
||||||
|
// TargetCell specifies a cell name where the instance will be placed.
|
||||||
|
TargetCell string
|
||||||
|
|
||||||
|
// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
|
||||||
|
BuildNearHostIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedulerHintsBuilder builds the scheduler hints into a serializable format.
|
||||||
|
type SchedulerHintsBuilder interface {
|
||||||
|
ToServerSchedulerHintsMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
|
||||||
|
func (opts SchedulerHints) ToServerSchedulerHintsMap() (map[string]interface{}, error) {
|
||||||
|
sh := make(map[string]interface{})
|
||||||
|
|
||||||
|
uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
|
||||||
|
|
||||||
|
if opts.Group != "" {
|
||||||
|
if !uuidRegex.MatchString(opts.Group) {
|
||||||
|
return nil, fmt.Errorf("Group must be a UUID")
|
||||||
|
}
|
||||||
|
sh["group"] = opts.Group
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.DifferentHost) > 0 {
|
||||||
|
for _, diffHost := range opts.DifferentHost {
|
||||||
|
if !uuidRegex.MatchString(diffHost) {
|
||||||
|
return nil, fmt.Errorf("The hosts in DifferentHost must be in UUID format.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sh["different_host"] = opts.DifferentHost
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.SameHost) > 0 {
|
||||||
|
for _, sameHost := range opts.SameHost {
|
||||||
|
if !uuidRegex.MatchString(sameHost) {
|
||||||
|
return nil, fmt.Errorf("The hosts in SameHost must be in UUID format.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sh["same_host"] = opts.SameHost
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query can be something simple like:
|
||||||
|
[">=", "$free_ram_mb", 1024]
|
||||||
|
|
||||||
|
Or more complex like:
|
||||||
|
['and',
|
||||||
|
['>=', '$free_ram_mb', 1024],
|
||||||
|
['>=', '$free_disk_mb', 200 * 1024]
|
||||||
|
]
|
||||||
|
|
||||||
|
Because of the possible complexity, just make sure the length is a minimum of 3.
|
||||||
|
*/
|
||||||
|
if len(opts.Query) > 0 {
|
||||||
|
if len(opts.Query) < 3 {
|
||||||
|
return nil, fmt.Errorf("Query must be a conditional statement in the format of [op,variable,value]")
|
||||||
|
}
|
||||||
|
sh["query"] = opts.Query
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.TargetCell != "" {
|
||||||
|
sh["target_cell"] = opts.TargetCell
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.BuildNearHostIP != "" {
|
||||||
|
if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
|
||||||
|
return nil, fmt.Errorf("BuildNearHostIP must be a valid subnet in the form 192.168.1.1/24")
|
||||||
|
}
|
||||||
|
ipParts := strings.Split(opts.BuildNearHostIP, "/")
|
||||||
|
sh["build_near_host_ip"] = ipParts[0]
|
||||||
|
sh["cidr"] = "/" + ipParts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return sh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
|
||||||
|
type CreateOptsExt struct {
|
||||||
|
servers.CreateOptsBuilder
|
||||||
|
|
||||||
|
// SchedulerHints provides a set of hints to the scheduler.
|
||||||
|
SchedulerHints SchedulerHintsBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateMap adds the SchedulerHints option to the base server creation options.
|
||||||
|
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(schedulerHints) == 0 {
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
base["os:scheduler_hints"] = schedulerHints
|
||||||
|
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
130
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests_test.go
generated
vendored
Normal file
130
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package schedulerhints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateOpts(t *testing.T) {
|
||||||
|
base := servers.CreateOpts{
|
||||||
|
Name: "createdserver",
|
||||||
|
ImageRef: "asdfasdfasdf",
|
||||||
|
FlavorRef: "performance1-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerHints := SchedulerHints{
|
||||||
|
Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
|
||||||
|
DifferentHost: []string{
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287",
|
||||||
|
},
|
||||||
|
SameHost: []string{
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287",
|
||||||
|
},
|
||||||
|
Query: []interface{}{">=", "$free_ram_mb", "1024"},
|
||||||
|
TargetCell: "foobar",
|
||||||
|
BuildNearHostIP: "192.168.1.1/24",
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := CreateOptsExt{
|
||||||
|
CreateOptsBuilder: base,
|
||||||
|
SchedulerHints: schedulerHints,
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `
|
||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"name": "createdserver",
|
||||||
|
"imageRef": "asdfasdfasdf",
|
||||||
|
"flavorRef": "performance1-1",
|
||||||
|
"flavorName": "",
|
||||||
|
"imageName": ""
|
||||||
|
},
|
||||||
|
"os:scheduler_hints": {
|
||||||
|
"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
|
||||||
|
"different_host": [
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287"
|
||||||
|
],
|
||||||
|
"same_host": [
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
">=", "$free_ram_mb", "1024"
|
||||||
|
],
|
||||||
|
"target_cell": "foobar",
|
||||||
|
"build_near_host_ip": "192.168.1.1",
|
||||||
|
"cidr": "/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
actual, err := ext.ToServerCreateMap()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckJSONEquals(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateOptsWithComplexQuery(t *testing.T) {
|
||||||
|
base := servers.CreateOpts{
|
||||||
|
Name: "createdserver",
|
||||||
|
ImageRef: "asdfasdfasdf",
|
||||||
|
FlavorRef: "performance1-1",
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerHints := SchedulerHints{
|
||||||
|
Group: "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
|
||||||
|
DifferentHost: []string{
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287",
|
||||||
|
},
|
||||||
|
SameHost: []string{
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287",
|
||||||
|
},
|
||||||
|
Query: []interface{}{"and", []string{">=", "$free_ram_mb", "1024"}, []string{">=", "$free_disk_mb", "204800"}},
|
||||||
|
TargetCell: "foobar",
|
||||||
|
BuildNearHostIP: "192.168.1.1/24",
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := CreateOptsExt{
|
||||||
|
CreateOptsBuilder: base,
|
||||||
|
SchedulerHints: schedulerHints,
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `
|
||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"name": "createdserver",
|
||||||
|
"imageRef": "asdfasdfasdf",
|
||||||
|
"flavorRef": "performance1-1",
|
||||||
|
"flavorName": "",
|
||||||
|
"imageName": ""
|
||||||
|
},
|
||||||
|
"os:scheduler_hints": {
|
||||||
|
"group": "101aed42-22d9-4a3e-9ba1-21103b0d1aba",
|
||||||
|
"different_host": [
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287"
|
||||||
|
],
|
||||||
|
"same_host": [
|
||||||
|
"a0cf03a5-d921-4877-bb5c-86d26cf818e1",
|
||||||
|
"8c19174f-4220-44f0-824a-cd1eeef10287"
|
||||||
|
],
|
||||||
|
"query": [
|
||||||
|
"and",
|
||||||
|
[">=", "$free_ram_mb", "1024"],
|
||||||
|
[">=", "$free_disk_mb", "204800"]
|
||||||
|
],
|
||||||
|
"target_cell": "foobar",
|
||||||
|
"build_near_host_ip": "192.168.1.1",
|
||||||
|
"cidr": "/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
actual, err := ext.ToServerCreateMap()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckJSONEquals(t, expected, actual)
|
||||||
|
}
|
||||||
@@ -242,6 +242,7 @@ func mockAddServerToGroupResponse(t *testing.T, serverID string) {
|
|||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,5 +262,6 @@ func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
|
|||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,44 @@ package volumeattach
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
fixtures "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
th "github.com/rackspace/gophercloud/testhelper"
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
"github.com/rackspace/gophercloud/testhelper/client"
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FirstVolumeAttachment is the first result in ListOutput.
|
||||||
|
var FirstVolumeAttachment = VolumeAttachment{
|
||||||
|
Device: "/dev/vdd",
|
||||||
|
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
||||||
|
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||||
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecondVolumeAttachment is the first result in ListOutput.
|
||||||
|
var SecondVolumeAttachment = VolumeAttachment{
|
||||||
|
Device: "/dev/vdc",
|
||||||
|
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||||
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
|
||||||
|
// from ListOutput, in the expected order.
|
||||||
|
var ExpectedVolumeAttachmentSlice = []VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
|
||||||
|
|
||||||
|
//CreatedVolumeAttachment is the parsed result from CreatedOutput.
|
||||||
|
var CreatedVolumeAttachment = VolumeAttachment{
|
||||||
|
Device: "/dev/vdc",
|
||||||
|
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||||
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
}
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
HandleListSuccessfully(t)
|
fixtures.HandleListSuccessfully(t)
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
@@ -30,7 +59,7 @@ func TestList(t *testing.T) {
|
|||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
HandleCreateSuccessfully(t)
|
fixtures.HandleCreateSuccessfully(t)
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
actual, err := Create(client.ServiceClient(), serverId, CreateOpts{
|
actual, err := Create(client.ServiceClient(), serverId, CreateOpts{
|
||||||
@@ -44,7 +73,7 @@ func TestCreate(t *testing.T) {
|
|||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
HandleGetSuccessfully(t)
|
fixtures.HandleGetSuccessfully(t)
|
||||||
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
@@ -56,7 +85,7 @@ func TestGet(t *testing.T) {
|
|||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
HandleDeleteSuccessfully(t)
|
fixtures.HandleDeleteSuccessfully(t)
|
||||||
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
|
|||||||
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing/doc.go
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing/doc.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
This is package created is to hold fixtures (which imports testing),
|
||||||
|
so that importing volumeattach package does not inadvertently import testing into production code
|
||||||
|
More information here:
|
||||||
|
https://github.com/rackspace/gophercloud/issues/473
|
||||||
|
*/
|
||||||
|
package testing
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// +build fixtures
|
// +build fixtures
|
||||||
|
|
||||||
package volumeattach
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -55,34 +55,6 @@ const CreateOutput = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// FirstVolumeAttachment is the first result in ListOutput.
|
|
||||||
var FirstVolumeAttachment = VolumeAttachment{
|
|
||||||
Device: "/dev/vdd",
|
|
||||||
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
|
||||||
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
|
||||||
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecondVolumeAttachment is the first result in ListOutput.
|
|
||||||
var SecondVolumeAttachment = VolumeAttachment{
|
|
||||||
Device: "/dev/vdc",
|
|
||||||
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
|
||||||
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
|
||||||
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
|
|
||||||
// from ListOutput, in the expected order.
|
|
||||||
var ExpectedVolumeAttachmentSlice = []VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
|
|
||||||
|
|
||||||
// CreatedVolumeAttachment is the parsed result from CreatedOutput.
|
|
||||||
var CreatedVolumeAttachment = VolumeAttachment{
|
|
||||||
Device: "/dev/vdc",
|
|
||||||
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
|
||||||
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
|
||||||
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
|
||||||
}
|
|
||||||
|
|
||||||
// HandleListSuccessfully configures the test server to respond to a List request.
|
// HandleListSuccessfully configures the test server to respond to a List request.
|
||||||
func HandleListSuccessfully(t *testing.T) {
|
func HandleListSuccessfully(t *testing.T) {
|
||||||
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/os-volume_attachments", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package flavors
|
package flavors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
@@ -66,3 +68,36 @@ func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
|||||||
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a flavor's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
flavorCount := 0
|
||||||
|
flavorID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A flavor name must be provided.")
|
||||||
|
}
|
||||||
|
pager := ListDetail(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
flavorList, err := ExtractFlavors(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range flavorList {
|
||||||
|
if f.Name == name {
|
||||||
|
flavorCount++
|
||||||
|
flavorID = f.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch flavorCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find flavor: %s", name)
|
||||||
|
case 1:
|
||||||
|
return flavorID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package images
|
package images
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
@@ -70,3 +72,38 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
|||||||
_, result.Err = client.Delete(deleteURL(client, id), nil)
|
_, result.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns an image's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
imageCount := 0
|
||||||
|
imageID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("An image name must be provided.")
|
||||||
|
}
|
||||||
|
pager := ListDetail(client, &ListOpts{
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
imageList, err := ExtractImages(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, i := range imageList {
|
||||||
|
if i.Name == name {
|
||||||
|
imageCount++
|
||||||
|
imageID = i.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch imageCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find image: %s", name)
|
||||||
|
case 1:
|
||||||
|
return imageID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d images matching %s", imageCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package servers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,6 +17,7 @@ import (
|
|||||||
type ListOptsBuilder interface {
|
type ListOptsBuilder interface {
|
||||||
ToServerListQuery() (string, error)
|
ToServerListQuery() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListOpts allows the filtering and sorting of paginated collections through
|
// ListOpts allows the filtering and sorting of paginated collections through
|
||||||
// the API. Filtering is achieved by passing in struct field values that map to
|
// the API. Filtering is achieved by passing in struct field values that map to
|
||||||
// the server attributes you want to see returned. Marker and Limit are used
|
// the server attributes you want to see returned. Marker and Limit are used
|
||||||
@@ -45,6 +49,9 @@ type ListOpts struct {
|
|||||||
|
|
||||||
// Integer value for the limit of values to return.
|
// Integer value for the limit of values to return.
|
||||||
Limit int `q:"limit"`
|
Limit int `q:"limit"`
|
||||||
|
|
||||||
|
// Bool to show all tenants
|
||||||
|
AllTenants bool `q:"all_tenants"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToServerListQuery formats a ListOpts into a query string.
|
// ToServerListQuery formats a ListOpts into a query string.
|
||||||
@@ -95,18 +102,54 @@ type Network struct {
|
|||||||
FixedIP string
|
FixedIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Personality is an array of files that are injected into the server at launch.
|
||||||
|
type Personality []*File
|
||||||
|
|
||||||
|
// File is used within CreateOpts and RebuildOpts to inject a file into the server at launch.
|
||||||
|
// File implements the json.Marshaler interface, so when a Create or Rebuild operation is requested,
|
||||||
|
// json.Marshal will call File's MarshalJSON method.
|
||||||
|
type File struct {
|
||||||
|
// Path of the file
|
||||||
|
Path string
|
||||||
|
// Contents of the file. Maximum content size is 255 bytes.
|
||||||
|
Contents []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON marshals the escaped file, base64 encoding the contents.
|
||||||
|
func (f *File) MarshalJSON() ([]byte, error) {
|
||||||
|
file := struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Contents string `json:"contents"`
|
||||||
|
}{
|
||||||
|
Path: f.Path,
|
||||||
|
Contents: base64.StdEncoding.EncodeToString(f.Contents),
|
||||||
|
}
|
||||||
|
return json.Marshal(file)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateOpts specifies server creation parameters.
|
// CreateOpts specifies server creation parameters.
|
||||||
type CreateOpts struct {
|
type CreateOpts struct {
|
||||||
// Name [required] is the name to assign to the newly launched server.
|
// Name [required] is the name to assign to the newly launched server.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// ImageRef [required] is the ID or full URL to the image that contains the server's OS and initial state.
|
// ImageRef [optional; required if ImageName is not provided] is the ID or full
|
||||||
// Optional if using the boot-from-volume extension.
|
// URL to the image that contains the server's OS and initial state.
|
||||||
|
// Also optional if using the boot-from-volume extension.
|
||||||
ImageRef string
|
ImageRef string
|
||||||
|
|
||||||
// FlavorRef [required] is the ID or full URL to the flavor that describes the server's specs.
|
// ImageName [optional; required if ImageRef is not provided] is the name of the
|
||||||
|
// image that contains the server's OS and initial state.
|
||||||
|
// Also optional if using the boot-from-volume extension.
|
||||||
|
ImageName string
|
||||||
|
|
||||||
|
// FlavorRef [optional; required if FlavorName is not provided] is the ID or
|
||||||
|
// full URL to the flavor that describes the server's specs.
|
||||||
FlavorRef string
|
FlavorRef string
|
||||||
|
|
||||||
|
// FlavorName [optional; required if FlavorRef is not provided] is the name of
|
||||||
|
// the flavor that describes the server's specs.
|
||||||
|
FlavorName string
|
||||||
|
|
||||||
// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
|
// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
|
||||||
SecurityGroups []string
|
SecurityGroups []string
|
||||||
|
|
||||||
@@ -124,9 +167,9 @@ type CreateOpts struct {
|
|||||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
|
|
||||||
// Personality [optional] includes the path and contents of a file to inject into the server at launch.
|
// Personality [optional] includes files to inject into the server at launch.
|
||||||
// The maximum size of the file is 255 bytes (decoded).
|
// Create will base64-encode file contents for you.
|
||||||
Personality []byte
|
Personality Personality
|
||||||
|
|
||||||
// ConfigDrive [optional] enables metadata injection through a configuration drive.
|
// ConfigDrive [optional] enables metadata injection through a configuration drive.
|
||||||
ConfigDrive bool
|
ConfigDrive bool
|
||||||
@@ -148,16 +191,14 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
|||||||
|
|
||||||
server["name"] = opts.Name
|
server["name"] = opts.Name
|
||||||
server["imageRef"] = opts.ImageRef
|
server["imageRef"] = opts.ImageRef
|
||||||
|
server["imageName"] = opts.ImageName
|
||||||
server["flavorRef"] = opts.FlavorRef
|
server["flavorRef"] = opts.FlavorRef
|
||||||
|
server["flavorName"] = opts.FlavorName
|
||||||
|
|
||||||
if opts.UserData != nil {
|
if opts.UserData != nil {
|
||||||
encoded := base64.StdEncoding.EncodeToString(opts.UserData)
|
encoded := base64.StdEncoding.EncodeToString(opts.UserData)
|
||||||
server["user_data"] = &encoded
|
server["user_data"] = &encoded
|
||||||
}
|
}
|
||||||
if opts.Personality != nil {
|
|
||||||
encoded := base64.StdEncoding.EncodeToString(opts.Personality)
|
|
||||||
server["personality"] = &encoded
|
|
||||||
}
|
|
||||||
if opts.ConfigDrive {
|
if opts.ConfigDrive {
|
||||||
server["config_drive"] = "true"
|
server["config_drive"] = "true"
|
||||||
}
|
}
|
||||||
@@ -202,6 +243,10 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
|||||||
server["networks"] = networks
|
server["networks"] = networks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(opts.Personality) > 0 {
|
||||||
|
server["personality"] = opts.Personality
|
||||||
|
}
|
||||||
|
|
||||||
return map[string]interface{}{"server": server}, nil
|
return map[string]interface{}{"server": server}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +260,38 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateRes
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If ImageRef isn't provided, use ImageName to ascertain the image ID.
|
||||||
|
if reqBody["server"].(map[string]interface{})["imageRef"].(string) == "" {
|
||||||
|
imageName := reqBody["server"].(map[string]interface{})["imageName"].(string)
|
||||||
|
if imageName == "" {
|
||||||
|
res.Err = errors.New("One and only one of ImageRef and ImageName must be provided.")
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
imageID, err := images.IDFromName(client, imageName)
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
reqBody["server"].(map[string]interface{})["imageRef"] = imageID
|
||||||
|
}
|
||||||
|
delete(reqBody["server"].(map[string]interface{}), "imageName")
|
||||||
|
|
||||||
|
// If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID.
|
||||||
|
if reqBody["server"].(map[string]interface{})["flavorRef"].(string) == "" {
|
||||||
|
flavorName := reqBody["server"].(map[string]interface{})["flavorName"].(string)
|
||||||
|
if flavorName == "" {
|
||||||
|
res.Err = errors.New("One and only one of FlavorRef and FlavorName must be provided.")
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
flavorID, err := flavors.IDFromName(client, flavorName)
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
reqBody["server"].(map[string]interface{})["flavorRef"] = flavorID
|
||||||
|
}
|
||||||
|
delete(reqBody["server"].(map[string]interface{}), "flavorName")
|
||||||
|
|
||||||
_, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil)
|
_, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -391,9 +468,9 @@ type RebuildOpts struct {
|
|||||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
|
|
||||||
// Personality [optional] includes the path and contents of a file to inject into the server at launch.
|
// Personality [optional] includes files to inject into the server at launch.
|
||||||
// The maximum size of the file is 255 bytes (decoded).
|
// Rebuild will base64-encode file contents for you.
|
||||||
Personality []byte
|
Personality Personality
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
|
// ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON
|
||||||
@@ -429,9 +506,8 @@ func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) {
|
|||||||
server["metadata"] = opts.Metadata
|
server["metadata"] = opts.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Personality != nil {
|
if len(opts.Personality) > 0 {
|
||||||
encoded := base64.StdEncoding.EncodeToString(opts.Personality)
|
server["personality"] = opts.Personality
|
||||||
server["personality"] = &encoded
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map[string]interface{}{"rebuild": server}, nil
|
return map[string]interface{}{"rebuild": server}, nil
|
||||||
@@ -678,9 +754,7 @@ func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumRe
|
|||||||
// DeleteMetadatum will delete the key-value pair with the given key for the given server ID.
|
// DeleteMetadatum will delete the key-value pair with the given key for the given server ID.
|
||||||
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult {
|
func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult {
|
||||||
var res DeleteMetadatumResult
|
var res DeleteMetadatumResult
|
||||||
_, res.Err = client.Delete(metadatumURL(client, id, key), &gophercloud.RequestOpts{
|
_, res.Err = client.Delete(metadatumURL(client, id, key), nil)
|
||||||
JSONResponse: &res.Body,
|
|
||||||
})
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,3 +817,36 @@ func CreateImage(client *gophercloud.ServiceClient, serverId string, opts Create
|
|||||||
res.Header = response.Header
|
res.Header = response.Header
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a server's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
serverCount := 0
|
||||||
|
serverID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A server name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
serverList, err := ExtractServers(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range serverList {
|
||||||
|
if s.Name == name {
|
||||||
|
serverCount++
|
||||||
|
serverID = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch serverCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find server: %s", name)
|
||||||
|
case 1:
|
||||||
|
return serverID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d servers matching %s", serverCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package servers
|
package servers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -334,3 +336,38 @@ func TestCreateServerImage(t *testing.T) {
|
|||||||
_, err := CreateImage(client.ServiceClient(), "serverimage", CreateImageOpts{Name: "test"}).ExtractImageID()
|
_, err := CreateImage(client.ServiceClient(), "serverimage", CreateImageOpts{Name: "test"}).ExtractImageID()
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshalPersonality(t *testing.T) {
|
||||||
|
name := "/etc/test"
|
||||||
|
contents := []byte("asdfasdf")
|
||||||
|
|
||||||
|
personality := Personality{
|
||||||
|
&File{
|
||||||
|
Path: name,
|
||||||
|
Contents: contents,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(personality)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual []map[string]string
|
||||||
|
err = json.Unmarshal(data, &actual)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actual) != 1 {
|
||||||
|
t.Fatal("expected personality length 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual[0]["path"] != name {
|
||||||
|
t.Fatal("file path incorrect")
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual[0]["contents"] != base64.StdEncoding.EncodeToString(contents) {
|
||||||
|
t.Fatal("file contents incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ var (
|
|||||||
// ErrDomainNameProvided is returned if you attempt to authenticate with a DomainName.
|
// ErrDomainNameProvided is returned if you attempt to authenticate with a DomainName.
|
||||||
ErrDomainNameProvided = unacceptedAttributeErr("DomainName")
|
ErrDomainNameProvided = unacceptedAttributeErr("DomainName")
|
||||||
|
|
||||||
// ErrUsernameRequired is returned if you attempt ot authenticate without a Username.
|
// ErrUsernameRequired is returned if you attempt to authenticate without a Username.
|
||||||
ErrUsernameRequired = errors.New("You must supply a Username in your AuthOptions.")
|
ErrUsernameRequired = errors.New("You must supply a Username in your AuthOptions.")
|
||||||
|
|
||||||
// ErrPasswordRequired is returned if you don't provide a password.
|
// ErrPasswordRequired is returned if you don't provide a password.
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package tokens
|
package tokens
|
||||||
|
|
||||||
import "github.com/rackspace/gophercloud"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
|
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
|
||||||
type AuthOptionsBuilder interface {
|
type AuthOptionsBuilder interface {
|
||||||
@@ -38,20 +42,24 @@ func (auth AuthOptions) ToTokenCreateMap() (map[string]interface{}, error) {
|
|||||||
return nil, ErrDomainNameProvided
|
return nil, ErrDomainNameProvided
|
||||||
}
|
}
|
||||||
|
|
||||||
// Username and Password are always required.
|
|
||||||
if auth.Username == "" {
|
|
||||||
return nil, ErrUsernameRequired
|
|
||||||
}
|
|
||||||
if auth.Password == "" {
|
|
||||||
return nil, ErrPasswordRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the request map.
|
// Populate the request map.
|
||||||
authMap := make(map[string]interface{})
|
authMap := make(map[string]interface{})
|
||||||
|
|
||||||
authMap["passwordCredentials"] = map[string]interface{}{
|
if auth.Username != "" {
|
||||||
"username": auth.Username,
|
if auth.Password != "" {
|
||||||
"password": auth.Password,
|
authMap["passwordCredentials"] = map[string]interface{}{
|
||||||
|
"username": auth.Username,
|
||||||
|
"password": auth.Password,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, ErrPasswordRequired
|
||||||
|
}
|
||||||
|
} else if auth.TokenID != "" {
|
||||||
|
authMap["token"] = map[string]interface{}{
|
||||||
|
"id": auth.TokenID,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("You must provide either username/password or tenantID/token values.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if auth.TenantID != "" {
|
if auth.TenantID != "" {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package tokens
|
package tokens
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
@@ -22,7 +23,7 @@ func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr err
|
|||||||
HandleTokenPost(t, "")
|
HandleTokenPost(t, "")
|
||||||
|
|
||||||
actualErr := Create(client.ServiceClient(), AuthOptions{options}).Err
|
actualErr := Create(client.ServiceClient(), AuthOptions{options}).Err
|
||||||
th.CheckEquals(t, expectedErr, actualErr)
|
th.CheckDeepEquals(t, expectedErr, actualErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateWithPassword(t *testing.T) {
|
func TestCreateWithPassword(t *testing.T) {
|
||||||
@@ -128,7 +129,7 @@ func TestRequireUsername(t *testing.T) {
|
|||||||
Password: "thing",
|
Password: "thing",
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenPostErr(t, options, ErrUsernameRequired)
|
tokenPostErr(t, options, fmt.Errorf("You must provide either username/password or tenantID/token values."))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequirePassword(t *testing.T) {
|
func TestRequirePassword(t *testing.T) {
|
||||||
|
|||||||
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/doc.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// Package roles provides information and interaction with the roles API
|
||||||
|
// resource for the OpenStack Identity service.
|
||||||
|
package roles
|
||||||
50
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/requests.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/requests.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package roles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListAssignmentsOptsBuilder allows extensions to add additional parameters to
|
||||||
|
// the ListAssignments request.
|
||||||
|
type ListAssignmentsOptsBuilder interface {
|
||||||
|
ToRolesListAssignmentsQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAssignmentsOpts allows you to query the ListAssignments method.
|
||||||
|
// Specify one of or a combination of GroupId, RoleId, ScopeDomainId, ScopeProjectId,
|
||||||
|
// and/or UserId to search for roles assigned to corresponding entities.
|
||||||
|
// Effective lists effective assignments at the user, project, and domain level,
|
||||||
|
// allowing for the effects of group membership.
|
||||||
|
type ListAssignmentsOpts struct {
|
||||||
|
GroupId string `q:"group.id"`
|
||||||
|
RoleId string `q:"role.id"`
|
||||||
|
ScopeDomainId string `q:"scope.domain.id"`
|
||||||
|
ScopeProjectId string `q:"scope.project.id"`
|
||||||
|
UserId string `q:"user.id"`
|
||||||
|
Effective bool `q:"effective"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRolesListAssignmentsQuery formats a ListAssignmentsOpts into a query string.
|
||||||
|
func (opts ListAssignmentsOpts) ToRolesListAssignmentsQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return q.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAssignments enumerates the roles assigned to a specified resource.
|
||||||
|
func ListAssignments(client *gophercloud.ServiceClient, opts ListAssignmentsOptsBuilder) pagination.Pager {
|
||||||
|
url := listAssignmentsURL(client)
|
||||||
|
query, err := opts.ToRolesListAssignmentsQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
createPage := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return RoleAssignmentsPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, url, createPage)
|
||||||
|
}
|
||||||
104
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/requests_test.go
generated
vendored
Normal file
104
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/requests_test.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package roles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListSinglePage(t *testing.T) {
|
||||||
|
testhelper.SetupHTTP()
|
||||||
|
defer testhelper.TeardownHTTP()
|
||||||
|
|
||||||
|
testhelper.Mux.HandleFunc("/role_assignments", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
testhelper.TestMethod(t, r, "GET")
|
||||||
|
testhelper.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"role_assignments": [
|
||||||
|
{
|
||||||
|
"links": {
|
||||||
|
"assignment": "http://identity:35357/v3/domains/161718/users/313233/roles/123456"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"id": "123456"
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"domain": {
|
||||||
|
"id": "161718"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"id": "313233"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"links": {
|
||||||
|
"assignment": "http://identity:35357/v3/projects/456789/groups/101112/roles/123456",
|
||||||
|
"membership": "http://identity:35357/v3/groups/101112/users/313233"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"id": "123456"
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"project": {
|
||||||
|
"id": "456789"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"id": "313233"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": {
|
||||||
|
"self": "http://identity:35357/v3/role_assignments?effective",
|
||||||
|
"previous": null,
|
||||||
|
"next": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
err := ListAssignments(client.ServiceClient(), ListAssignmentsOpts{}).EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
count++
|
||||||
|
actual, err := ExtractRoleAssignments(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := []RoleAssignment{
|
||||||
|
RoleAssignment{
|
||||||
|
Role: Role{ID: "123456"},
|
||||||
|
Scope: Scope{Domain: Domain{ID: "161718"}},
|
||||||
|
User: User{ID: "313233"},
|
||||||
|
Group: Group{},
|
||||||
|
},
|
||||||
|
RoleAssignment{
|
||||||
|
Role: Role{ID: "123456"},
|
||||||
|
Scope: Scope{Project: Project{ID: "456789"}},
|
||||||
|
User: User{ID: "313233"},
|
||||||
|
Group: Group{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected, actual) {
|
||||||
|
t.Errorf("Expected %#v, got %#v", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error while paging: %v", err)
|
||||||
|
}
|
||||||
|
if count != 1 {
|
||||||
|
t.Errorf("Expected 1 page, got %d", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
81
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/results.go
generated
vendored
Normal file
81
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/results.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package roles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoleAssignment is the result of a role assignments query.
|
||||||
|
type RoleAssignment struct {
|
||||||
|
Role Role `json:"role,omitempty"`
|
||||||
|
Scope Scope `json:"scope,omitempty"`
|
||||||
|
User User `json:"user,omitempty"`
|
||||||
|
Group Group `json:"group,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scope struct {
|
||||||
|
Domain Domain `json:"domain,omitempty"`
|
||||||
|
Project Project `json:"domain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Domain struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Project struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleAssignmentsPage is a single page of RoleAssignments results.
|
||||||
|
type RoleAssignmentsPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if the page contains no results.
|
||||||
|
func (p RoleAssignmentsPage) IsEmpty() (bool, error) {
|
||||||
|
roleAssignments, err := ExtractRoleAssignments(p)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(roleAssignments) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
||||||
|
func (page RoleAssignmentsPage) NextPageURL() (string, error) {
|
||||||
|
type resp struct {
|
||||||
|
Links struct {
|
||||||
|
Next string `mapstructure:"next"`
|
||||||
|
} `mapstructure:"links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var r resp
|
||||||
|
err := mapstructure.Decode(page.Body, &r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Links.Next, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractRoleAssignments extracts a slice of RoleAssignments from a Collection acquired from List.
|
||||||
|
func ExtractRoleAssignments(page pagination.Page) ([]RoleAssignment, error) {
|
||||||
|
var response struct {
|
||||||
|
RoleAssignments []RoleAssignment `mapstructure:"role_assignments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(page.(RoleAssignmentsPage).Body, &response)
|
||||||
|
return response.RoleAssignments, err
|
||||||
|
}
|
||||||
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/urls.go
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/urls.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package roles
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func listAssignmentsURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("role_assignments")
|
||||||
|
}
|
||||||
15
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/urls_test.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/identity/v3/roles/urls_test.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package roles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListAssignmentsURL(t *testing.T) {
|
||||||
|
client := gophercloud.ServiceClient{Endpoint: "http://localhost:5000/v3/"}
|
||||||
|
url := listAssignmentsURL(&client)
|
||||||
|
if url != "http://localhost:5000/v3/role_assignments" {
|
||||||
|
t.Errorf("Unexpected list URL generated: [%s]", url)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -212,9 +212,9 @@ func TestUpdate(t *testing.T) {
|
|||||||
"name": "fw",
|
"name": "fw",
|
||||||
"admin_state_up": false,
|
"admin_state_up": false,
|
||||||
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
"tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
|
||||||
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
|
"firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
|
||||||
"id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
|
"id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
|
||||||
"description": "OpenStack firewall",
|
"description": "OpenStack firewall"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ func TestAssociateHealthMonitor(t *testing.T) {
|
|||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err := AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract()
|
_, err := AssociateMonitor(fake.ServiceClient(), "332abe93-f488-41ba-870b-2ac66be7f853", "b624decf-d5d3-4c66-9a3d-f047e7786181").Extract()
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ type CreateOpts struct {
|
|||||||
// Required. Human-readable name for the VIP. Does not have to be unique.
|
// Required. Human-readable name for the VIP. Does not have to be unique.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
|
// Required for admins. Indicates the owner of the VIP.
|
||||||
|
TenantID string
|
||||||
|
|
||||||
// Optional. Describes the security group.
|
// Optional. Describes the security group.
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
@@ -62,6 +65,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
|
|
||||||
type secgroup struct {
|
type secgroup struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
TenantID string `json:"tenant_id,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +75,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
|
|
||||||
reqBody := request{SecGroup: secgroup{
|
reqBody := request{SecGroup: secgroup{
|
||||||
Name: opts.Name,
|
Name: opts.Name,
|
||||||
|
TenantID: opts.TenantID,
|
||||||
Description: opts.Description,
|
Description: opts.Description,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@@ -91,3 +96,36 @@ func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
|
|||||||
_, res.Err = c.Delete(resourceURL(c, id), nil)
|
_, res.Err = c.Delete(resourceURL(c, id), nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convenience function that returns a security group's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
securityGroupCount := 0
|
||||||
|
securityGroupID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A security group name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, ListOpts{})
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
securityGroupList, err := ExtractGroups(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range securityGroupList {
|
||||||
|
if s.Name == name {
|
||||||
|
securityGroupCount++
|
||||||
|
securityGroupID = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch securityGroupCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find security group: %s", name)
|
||||||
|
case 1:
|
||||||
|
return securityGroupID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d security groups matching %s", securityGroupCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ type CreateOpts struct {
|
|||||||
// attribute matches the specified IP prefix as the source IP address of the
|
// attribute matches the specified IP prefix as the source IP address of the
|
||||||
// IP packet.
|
// IP packet.
|
||||||
RemoteIPPrefix string
|
RemoteIPPrefix string
|
||||||
|
|
||||||
|
// Required for admins. Indicates the owner of the VIP.
|
||||||
|
TenantID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create is an operation which provisions a new security group with default
|
// Create is an operation which provisions a new security group with default
|
||||||
@@ -133,6 +136,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
Protocol string `json:"protocol,omitempty"`
|
Protocol string `json:"protocol,omitempty"`
|
||||||
RemoteGroupID string `json:"remote_group_id,omitempty"`
|
RemoteGroupID string `json:"remote_group_id,omitempty"`
|
||||||
RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
|
RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
|
||||||
|
TenantID string `json:"tenant_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type request struct {
|
type request struct {
|
||||||
@@ -148,6 +152,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
Protocol: opts.Protocol,
|
Protocol: opts.Protocol,
|
||||||
RemoteGroupID: opts.RemoteGroupID,
|
RemoteGroupID: opts.RemoteGroupID,
|
||||||
RemoteIPPrefix: opts.RemoteIPPrefix,
|
RemoteIPPrefix: opts.RemoteIPPrefix,
|
||||||
|
TenantID: opts.TenantID,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
|
_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package networks
|
package networks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
@@ -189,3 +191,36 @@ func Delete(c *gophercloud.ServiceClient, networkID string) DeleteResult {
|
|||||||
_, res.Err = c.Delete(deleteURL(c, networkID), nil)
|
_, res.Err = c.Delete(deleteURL(c, networkID), nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convenience function that returns a network's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
networkCount := 0
|
||||||
|
networkID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A network name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
networkList, err := ExtractNetworks(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range networkList {
|
||||||
|
if n.Name == name {
|
||||||
|
networkCount++
|
||||||
|
networkID = n.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch networkCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find network: %s", name)
|
||||||
|
case 1:
|
||||||
|
return networkID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d networks matching %s", networkCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ func TestCreateWithOptionalFields(t *testing.T) {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
iTrue := true
|
iTrue := true
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ports
|
package ports
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
@@ -223,3 +225,36 @@ func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
|
|||||||
_, res.Err = c.Delete(deleteURL(c, id), nil)
|
_, res.Err = c.Delete(deleteURL(c, id), nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convenience function that returns a port's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
portCount := 0
|
||||||
|
portID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A port name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
portList, err := ExtractPorts(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range portList {
|
||||||
|
if p.Name == name {
|
||||||
|
portCount++
|
||||||
|
portID = p.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch portCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find port: %s", name)
|
||||||
|
case 1:
|
||||||
|
return portID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d ports matching %s", portCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package subnets
|
package subnets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
@@ -200,10 +202,10 @@ func (opts UpdateOpts) ToSubnetUpdateMap() (map[string]interface{}, error) {
|
|||||||
if opts.GatewayIP != "" {
|
if opts.GatewayIP != "" {
|
||||||
s["gateway_ip"] = opts.GatewayIP
|
s["gateway_ip"] = opts.GatewayIP
|
||||||
}
|
}
|
||||||
if len(opts.DNSNameservers) != 0 {
|
if opts.DNSNameservers != nil {
|
||||||
s["dns_nameservers"] = opts.DNSNameservers
|
s["dns_nameservers"] = opts.DNSNameservers
|
||||||
}
|
}
|
||||||
if len(opts.HostRoutes) != 0 {
|
if opts.HostRoutes != nil {
|
||||||
s["host_routes"] = opts.HostRoutes
|
s["host_routes"] = opts.HostRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,3 +236,36 @@ func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
|
|||||||
_, res.Err = c.Delete(deleteURL(c, id), nil)
|
_, res.Err = c.Delete(deleteURL(c, id), nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convenience function that returns a subnet's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
subnetCount := 0
|
||||||
|
subnetID := ""
|
||||||
|
if name == "" {
|
||||||
|
return "", fmt.Errorf("A subnet name must be provided.")
|
||||||
|
}
|
||||||
|
pager := List(client, nil)
|
||||||
|
pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||||
|
subnetList, err := ExtractSubnets(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range subnetList {
|
||||||
|
if s.Name == name {
|
||||||
|
subnetCount++
|
||||||
|
subnetID = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
switch subnetCount {
|
||||||
|
case 0:
|
||||||
|
return "", fmt.Errorf("Unable to find subnet: %s", name)
|
||||||
|
case 1:
|
||||||
|
return subnetID, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("Found %d subnets matching %s", subnetCount, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ type AllocationPool struct {
|
|||||||
// HostRoute represents a route that should be used by devices with IPs from
|
// HostRoute represents a route that should be used by devices with IPs from
|
||||||
// a subnet (not including local subnet route).
|
// a subnet (not including local subnet route).
|
||||||
type HostRoute struct {
|
type HostRoute struct {
|
||||||
DestinationCIDR string `json:"destination"`
|
DestinationCIDR string `mapstructure:"destination" json:"destination"`
|
||||||
NextHop string `json:"nexthop"`
|
NextHop string `mapstructure:"nexthop" json:"nexthop"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subnet represents a subnet. See package documentation for a top-level
|
// Subnet represents a subnet. See package documentation for a top-level
|
||||||
|
|||||||
54
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/results_test.go
generated
vendored
Normal file
54
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/networking/v2/subnets/results_test.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package subnets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHostRoute(t *testing.T) {
|
||||||
|
sejson := []byte(`
|
||||||
|
{"subnet": {
|
||||||
|
"name": "test-subnet",
|
||||||
|
"enable_dhcp": false,
|
||||||
|
"network_id": "3e66c41e-cbbd-4019-9aab-740b7e4150a0",
|
||||||
|
"tenant_id": "f86e123198cf42d19c8854c5f80c2f06",
|
||||||
|
"dns_nameservers": [],
|
||||||
|
"gateway_ip": "172.16.0.1",
|
||||||
|
"ipv6_ra_mode": null,
|
||||||
|
"allocation_pools": [
|
||||||
|
{
|
||||||
|
"start": "172.16.0.2",
|
||||||
|
"end": "172.16.255.254"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"host_routes": [
|
||||||
|
{
|
||||||
|
"destination": "172.20.1.0/24",
|
||||||
|
"nexthop": "172.16.0.2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ip_version": 4,
|
||||||
|
"ipv6_address_mode": null,
|
||||||
|
"cidr": "172.16.0.0/16",
|
||||||
|
"id": "6dcaa873-7115-41af-9ef5-915f73636e43",
|
||||||
|
"subnetpool_id": null
|
||||||
|
}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var dejson interface{}
|
||||||
|
err := json.Unmarshal(sejson, &dejson)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := commonResult{gophercloud.Result{Body: dejson}}
|
||||||
|
subnet, err := resp.Extract()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
route := subnet.HostRoutes[0]
|
||||||
|
th.AssertEquals(t, route.NextHop, "172.16.0.2")
|
||||||
|
th.AssertEquals(t, route.DestinationCIDR, "172.20.1.0/24")
|
||||||
|
}
|
||||||
@@ -43,7 +43,9 @@ func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) GetResult {
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{204},
|
OkCodes: []int{204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -97,7 +99,9 @@ func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) UpdateResult {
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{201, 202, 204},
|
OkCodes: []int{201, 202, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,9 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{201, 202, 204},
|
OkCodes: []int{201, 202, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -180,7 +182,9 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{201, 202, 204},
|
OkCodes: []int{201, 202, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -193,7 +197,9 @@ func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
|
|||||||
resp, err := c.Request("HEAD", getURL(c, containerName), gophercloud.RequestOpts{
|
resp, err := c.Request("HEAD", getURL(c, containerName), gophercloud.RequestOpts{
|
||||||
OkCodes: []int{200, 204},
|
OkCodes: []int{200, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -107,12 +109,18 @@ func HandleListObjectNamesSuccessfully(t *testing.T) {
|
|||||||
|
|
||||||
// HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux
|
// HandleCreateTextObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux
|
||||||
// that responds with a `Create` response. A Content-Type of "text/plain" is expected.
|
// that responds with a `Create` response. A Content-Type of "text/plain" is expected.
|
||||||
func HandleCreateTextObjectSuccessfully(t *testing.T) {
|
func HandleCreateTextObjectSuccessfully(t *testing.T, content string) {
|
||||||
th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
|
||||||
th.TestMethod(t, r, "PUT")
|
th.TestMethod(t, r, "PUT")
|
||||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
th.TestHeader(t, r, "Content-Type", "text/plain")
|
th.TestHeader(t, r, "Content-Type", "text/plain")
|
||||||
th.TestHeader(t, r, "Accept", "application/json")
|
th.TestHeader(t, r, "Accept", "application/json")
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, content)
|
||||||
|
localChecksum := hash.Sum(nil)
|
||||||
|
|
||||||
|
w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -120,7 +128,7 @@ func HandleCreateTextObjectSuccessfully(t *testing.T) {
|
|||||||
// HandleCreateTypelessObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
|
// HandleCreateTypelessObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler
|
||||||
// mux that responds with a `Create` response. No Content-Type header may be present in the request, so that server-
|
// mux that responds with a `Create` response. No Content-Type header may be present in the request, so that server-
|
||||||
// side content-type detection will be triggered properly.
|
// side content-type detection will be triggered properly.
|
||||||
func HandleCreateTypelessObjectSuccessfully(t *testing.T) {
|
func HandleCreateTypelessObjectSuccessfully(t *testing.T, content string) {
|
||||||
th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
|
||||||
th.TestMethod(t, r, "PUT")
|
th.TestMethod(t, r, "PUT")
|
||||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
@@ -130,6 +138,11 @@ func HandleCreateTypelessObjectSuccessfully(t *testing.T) {
|
|||||||
t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType)
|
t.Errorf("Expected Content-Type header to be omitted, but was %#v", contentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, content)
|
||||||
|
localChecksum := hash.Sum(nil)
|
||||||
|
|
||||||
|
w.Header().Set("ETag", fmt.Sprintf("%x", localChecksum))
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
|
"crypto/md5"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -134,10 +136,11 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{200, 304},
|
OkCodes: []int{200, 304},
|
||||||
})
|
})
|
||||||
|
if resp != nil {
|
||||||
res.Body = resp.Body
|
res.Header = resp.Header
|
||||||
|
res.Body = resp.Body
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
res.Header = resp.Header
|
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -187,8 +190,9 @@ func (opts CreateOpts) ToObjectCreateParams() (map[string]string, string, error)
|
|||||||
return h, q.String(), nil
|
return h, q.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create is a function that creates a new object or replaces an existing object.
|
// Create is a function that creates a new object or replaces an existing object. If the returned response's ETag
|
||||||
func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.Reader, opts CreateOptsBuilder) CreateResult {
|
// header fails to match the local checksum, the failed request will automatically be retried up to a maximum of 3 times.
|
||||||
|
func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.ReadSeeker, opts CreateOptsBuilder) CreateResult {
|
||||||
var res CreateResult
|
var res CreateResult
|
||||||
|
|
||||||
url := createURL(c, containerName, objectName)
|
url := createURL(c, containerName, objectName)
|
||||||
@@ -208,14 +212,37 @@ func Create(c *gophercloud.ServiceClient, containerName, objectName string, cont
|
|||||||
url += query
|
url += query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
|
||||||
|
contentBuffer := bytes.NewBuffer([]byte{})
|
||||||
|
_, err := io.Copy(contentBuffer, io.TeeReader(content, hash))
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
localChecksum := hash.Sum(nil)
|
||||||
|
h["ETag"] = fmt.Sprintf("%x", localChecksum)
|
||||||
|
|
||||||
ropts := gophercloud.RequestOpts{
|
ropts := gophercloud.RequestOpts{
|
||||||
RawBody: content,
|
RawBody: strings.NewReader(contentBuffer.String()),
|
||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.Request("PUT", url, ropts)
|
resp, err := c.Request("PUT", url, ropts)
|
||||||
res.Header = resp.Header
|
if err != nil {
|
||||||
res.Err = err
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
if resp.Header.Get("ETag") == fmt.Sprintf("%x", localChecksum) {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
res.Err = fmt.Errorf("Local checksum does not match API ETag header")
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +297,9 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{201},
|
OkCodes: []int{201},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -310,7 +339,9 @@ func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts
|
|||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.Delete(url, nil)
|
resp, err := c.Delete(url, nil)
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -354,7 +385,9 @@ func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts Ge
|
|||||||
resp, err := c.Request("HEAD", url, gophercloud.RequestOpts{
|
resp, err := c.Request("HEAD", url, gophercloud.RequestOpts{
|
||||||
OkCodes: []int{200, 204},
|
OkCodes: []int{200, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -410,7 +443,9 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts
|
|||||||
resp, err := c.Request("POST", url, gophercloud.RequestOpts{
|
resp, err := c.Request("POST", url, gophercloud.RequestOpts{
|
||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package objects
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
@@ -83,24 +86,44 @@ func TestListObjectNames(t *testing.T) {
|
|||||||
func TestCreateObject(t *testing.T) {
|
func TestCreateObject(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
HandleCreateTextObjectSuccessfully(t)
|
|
||||||
|
|
||||||
content := bytes.NewBufferString("Did gyre and gimble in the wabe")
|
content := "Did gyre and gimble in the wabe"
|
||||||
|
|
||||||
|
HandleCreateTextObjectSuccessfully(t, content)
|
||||||
|
|
||||||
options := &CreateOpts{ContentType: "text/plain"}
|
options := &CreateOpts{ContentType: "text/plain"}
|
||||||
res := Create(fake.ServiceClient(), "testContainer", "testObject", content, options)
|
res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), options)
|
||||||
th.AssertNoErr(t, res.Err)
|
th.AssertNoErr(t, res.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateObjectWithoutContentType(t *testing.T) {
|
func TestCreateObjectWithoutContentType(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
HandleCreateTypelessObjectSuccessfully(t)
|
|
||||||
|
|
||||||
content := bytes.NewBufferString("The sky was the color of television, tuned to a dead channel.")
|
content := "The sky was the color of television, tuned to a dead channel."
|
||||||
res := Create(fake.ServiceClient(), "testContainer", "testObject", content, &CreateOpts{})
|
|
||||||
|
HandleCreateTypelessObjectSuccessfully(t, content)
|
||||||
|
|
||||||
|
res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), &CreateOpts{})
|
||||||
th.AssertNoErr(t, res.Err)
|
th.AssertNoErr(t, res.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestErrorIsRaisedForChecksumMismatch(t *testing.T) {
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
|
th.Mux.HandleFunc("/testContainer/testObject", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("ETag", "acbd18db4cc2f85cedef654fccc4a4d8")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
})
|
||||||
|
|
||||||
|
content := strings.NewReader("The sky was the color of television, tuned to a dead channel.")
|
||||||
|
res := Create(fake.ServiceClient(), "testContainer", "testObject", content, &CreateOpts{})
|
||||||
|
|
||||||
|
err := fmt.Errorf("Local checksum does not match API ETag header")
|
||||||
|
th.AssertDeepEquals(t, err, res.Err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCopyObject(t *testing.T) {
|
func TestCopyObject(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const FindOutput = `
|
|||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:11Z",
|
"event_time": "2015-02-05T21:33:11",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
|
||||||
@@ -90,7 +90,7 @@ const FindOutput = `
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:27Z",
|
"event_time": "2015-02-05T21:33:27",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
||||||
@@ -184,7 +184,7 @@ const ListOutput = `
|
|||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:11Z",
|
"event_time": "2015-02-05T21:33:11",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
|
||||||
@@ -207,7 +207,7 @@ const ListOutput = `
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:27Z",
|
"event_time": "2015-02-05T21:33:27",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
||||||
@@ -309,7 +309,7 @@ const ListResourceEventsOutput = `
|
|||||||
"events": [
|
"events": [
|
||||||
{
|
{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:11Z",
|
"event_time": "2015-02-05T21:33:11",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
|
||||||
@@ -332,7 +332,7 @@ const ListResourceEventsOutput = `
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:27Z",
|
"event_time": "2015-02-05T21:33:27",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
||||||
@@ -408,7 +408,7 @@ const GetOutput = `
|
|||||||
{
|
{
|
||||||
"event":{
|
"event":{
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"event_time": "2015-02-05T21:33:27Z",
|
"event_time": "2015-02-05T21:33:27",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
"href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func (r FindResult) Extract() ([]Event, error) {
|
|||||||
for i, eventRaw := range events {
|
for i, eventRaw := range events {
|
||||||
event := eventRaw.(map[string]interface{})
|
event := eventRaw.(map[string]interface{})
|
||||||
if date, ok := event["event_time"]; ok && date != nil {
|
if date, ok := event["event_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ func ExtractEvents(page pagination.Page) ([]Event, error) {
|
|||||||
for i, eventRaw := range events {
|
for i, eventRaw := range events {
|
||||||
event := eventRaw.(map[string]interface{})
|
event := eventRaw.(map[string]interface{})
|
||||||
if date, ok := event["event_time"]; ok && date != nil {
|
if date, ok := event["event_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ func (r GetResult) Extract() (*Event, error) {
|
|||||||
event := r.Body.(map[string]interface{})["event"].(map[string]interface{})
|
event := r.Body.(map[string]interface{})["event"].(map[string]interface{})
|
||||||
|
|
||||||
if date, ok := event["event_time"]; ok && date != nil {
|
if date, ok := event["event_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const FindOutput = `
|
|||||||
],
|
],
|
||||||
"logical_resource_id": "hello_world",
|
"logical_resource_id": "hello_world",
|
||||||
"resource_status_reason": "state changed",
|
"resource_status_reason": "state changed",
|
||||||
"updated_time": "2015-02-05T21:33:11Z",
|
"updated_time": "2015-02-05T21:33:11",
|
||||||
"required_by": [],
|
"required_by": [],
|
||||||
"resource_status": "CREATE_IN_PROGRESS",
|
"resource_status": "CREATE_IN_PROGRESS",
|
||||||
"physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
|
"physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
|
||||||
@@ -117,7 +117,7 @@ const ListOutput = `{
|
|||||||
],
|
],
|
||||||
"logical_resource_id": "hello_world",
|
"logical_resource_id": "hello_world",
|
||||||
"resource_status_reason": "state changed",
|
"resource_status_reason": "state changed",
|
||||||
"updated_time": "2015-02-05T21:33:11Z",
|
"updated_time": "2015-02-05T21:33:11",
|
||||||
"required_by": [],
|
"required_by": [],
|
||||||
"resource_status": "CREATE_IN_PROGRESS",
|
"resource_status": "CREATE_IN_PROGRESS",
|
||||||
"physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
|
"physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
|
||||||
@@ -188,7 +188,7 @@ const GetOutput = `
|
|||||||
],
|
],
|
||||||
"logical_resource_id": "wordpress_instance",
|
"logical_resource_id": "wordpress_instance",
|
||||||
"resource_status": "CREATE_COMPLETE",
|
"resource_status": "CREATE_COMPLETE",
|
||||||
"updated_time": "2014-12-10T18:34:35Z",
|
"updated_time": "2014-12-10T18:34:35",
|
||||||
"required_by": [],
|
"required_by": [],
|
||||||
"resource_status_reason": "state changed",
|
"resource_status_reason": "state changed",
|
||||||
"physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194",
|
"physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194",
|
||||||
|
|||||||
@@ -25,12 +25,6 @@ type ListOptsBuilder interface {
|
|||||||
// ListOpts allows the filtering and sorting of paginated collections through
|
// ListOpts allows the filtering and sorting of paginated collections through
|
||||||
// the API. Marker and Limit are used for pagination.
|
// the API. Marker and Limit are used for pagination.
|
||||||
type ListOpts struct {
|
type ListOpts struct {
|
||||||
// The stack resource ID with which to start the listing.
|
|
||||||
Marker string `q:"marker"`
|
|
||||||
|
|
||||||
// Integer value for the limit of values to return.
|
|
||||||
Limit int `q:"limit"`
|
|
||||||
|
|
||||||
// Include resources from nest stacks up to Depth levels of recursion.
|
// Include resources from nest stacks up to Depth levels of recursion.
|
||||||
Depth int `q:"nested_depth"`
|
Depth int `q:"nested_depth"`
|
||||||
}
|
}
|
||||||
@@ -57,9 +51,7 @@ func List(client *gophercloud.ServiceClient, stackName, stackID string, opts Lis
|
|||||||
}
|
}
|
||||||
|
|
||||||
createPageFn := func(r pagination.PageResult) pagination.Page {
|
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
p := ResourcePage{pagination.MarkerPageBase{PageResult: r}}
|
return ResourcePage{pagination.SinglePageBase(r)}
|
||||||
p.MarkerPageBase.Owner = p
|
|
||||||
return p
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pagination.NewPager(client, url, createPageFn)
|
return pagination.NewPager(client, url, createPageFn)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (r FindResult) Extract() ([]Resource, error) {
|
|||||||
for i, resourceRaw := range resources {
|
for i, resourceRaw := range resources {
|
||||||
resource := resourceRaw.(map[string]interface{})
|
resource := resourceRaw.(map[string]interface{})
|
||||||
if date, ok := resource["updated_time"]; ok && date != nil {
|
if date, ok := resource["updated_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ func (r FindResult) Extract() ([]Resource, error) {
|
|||||||
// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
|
// As OpenStack extensions may freely alter the response bodies of structures returned to the client, you may only safely access the
|
||||||
// data provided through the ExtractResources call.
|
// data provided through the ExtractResources call.
|
||||||
type ResourcePage struct {
|
type ResourcePage struct {
|
||||||
pagination.MarkerPageBase
|
pagination.SinglePageBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty returns true if a page contains no Server results.
|
// IsEmpty returns true if a page contains no Server results.
|
||||||
@@ -109,7 +109,7 @@ func ExtractResources(page pagination.Page) ([]Resource, error) {
|
|||||||
for i, resourceRaw := range resources {
|
for i, resourceRaw := range resources {
|
||||||
resource := resourceRaw.(map[string]interface{})
|
resource := resourceRaw.(map[string]interface{})
|
||||||
if date, ok := resource["updated_time"]; ok && date != nil {
|
if date, ok := resource["updated_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -143,7 +143,7 @@ func (r GetResult) Extract() (*Resource, error) {
|
|||||||
resource := r.Body.(map[string]interface{})["resource"].(map[string]interface{})
|
resource := r.Body.(map[string]interface{})["resource"].(map[string]interface{})
|
||||||
|
|
||||||
if date, ok := resource["updated_time"]; ok && date != nil {
|
if date, ok := resource["updated_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ const FullListOutput = `
|
|||||||
],
|
],
|
||||||
"stack_status_reason": "Stack CREATE completed successfully",
|
"stack_status_reason": "Stack CREATE completed successfully",
|
||||||
"stack_name": "postman_stack",
|
"stack_name": "postman_stack",
|
||||||
"creation_time": "2015-02-03T20:07:39Z",
|
"creation_time": "2015-02-03T20:07:39",
|
||||||
"updated_time": null,
|
"updated_time": null,
|
||||||
"stack_status": "CREATE_COMPLETE",
|
"stack_status": "CREATE_COMPLETE",
|
||||||
"id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
|
"id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
|
||||||
@@ -110,8 +110,8 @@ const FullListOutput = `
|
|||||||
],
|
],
|
||||||
"stack_status_reason": "Stack successfully updated",
|
"stack_status_reason": "Stack successfully updated",
|
||||||
"stack_name": "gophercloud-test-stack-2",
|
"stack_name": "gophercloud-test-stack-2",
|
||||||
"creation_time": "2014-12-11T17:39:16Z",
|
"creation_time": "2014-12-11T17:39:16",
|
||||||
"updated_time": "2014-12-11T17:40:37Z",
|
"updated_time": "2014-12-11T17:40:37",
|
||||||
"stack_status": "UPDATE_COMPLETE",
|
"stack_status": "UPDATE_COMPLETE",
|
||||||
"id": "db6977b2-27aa-4775-9ae7-6213212d4ada"
|
"id": "db6977b2-27aa-4775-9ae7-6213212d4ada"
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ const GetOutput = `
|
|||||||
"stack_status_reason": "Stack CREATE completed successfully",
|
"stack_status_reason": "Stack CREATE completed successfully",
|
||||||
"stack_name": "postman_stack",
|
"stack_name": "postman_stack",
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"creation_time": "2015-02-03T20:07:39Z",
|
"creation_time": "2015-02-03T20:07:39",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
"href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func ExtractStacks(page pagination.Page) ([]ListedStack, error) {
|
|||||||
thisStack := (rawStacks[i]).(map[string]interface{})
|
thisStack := (rawStacks[i]).(map[string]interface{})
|
||||||
|
|
||||||
if t, ok := thisStack["creation_time"].(string); ok && t != "" {
|
if t, ok := thisStack["creation_time"].(string); ok && t != "" {
|
||||||
creationTime, err := time.Parse(time.RFC3339, t)
|
creationTime, err := time.Parse(gophercloud.STACK_TIME_FMT, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res.Stacks, err
|
return res.Stacks, err
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ func ExtractStacks(page pagination.Page) ([]ListedStack, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if t, ok := thisStack["updated_time"].(string); ok && t != "" {
|
if t, ok := thisStack["updated_time"].(string); ok && t != "" {
|
||||||
updatedTime, err := time.Parse(time.RFC3339, t)
|
updatedTime, err := time.Parse(gophercloud.STACK_TIME_FMT, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res.Stacks, err
|
return res.Stacks, err
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ func (r GetResult) Extract() (*RetrievedStack, error) {
|
|||||||
b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
|
b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
|
||||||
|
|
||||||
if date, ok := b["creation_time"]; ok && date != nil {
|
if date, ok := b["creation_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ func (r GetResult) Extract() (*RetrievedStack, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if date, ok := b["updated_time"]; ok && date != nil {
|
if date, ok := b["updated_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -249,7 +249,7 @@ func (r PreviewResult) Extract() (*PreviewedStack, error) {
|
|||||||
b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
|
b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
|
||||||
|
|
||||||
if date, ok := b["creation_time"]; ok && date != nil {
|
if date, ok := b["creation_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -257,7 +257,7 @@ func (r PreviewResult) Extract() (*PreviewedStack, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if date, ok := b["updated_time"]; ok && date != nil {
|
if date, ok := b["updated_time"]; ok && date != nil {
|
||||||
t, err := time.Parse(time.RFC3339, date.(string))
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
10
Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/http.go
generated
vendored
10
Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/http.go
generated
vendored
@@ -36,13 +36,19 @@ func PageResultFrom(resp *http.Response) (PageResult, error) {
|
|||||||
parsedBody = rawBody
|
parsedBody = rawBody
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return PageResultFromParsed(resp, parsedBody), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PageResultFromParsed constructs a PageResult from an HTTP response that has already had its
|
||||||
|
// body parsed as JSON (and closed).
|
||||||
|
func PageResultFromParsed(resp *http.Response, body interface{}) PageResult {
|
||||||
return PageResult{
|
return PageResult{
|
||||||
Result: gophercloud.Result{
|
Result: gophercloud.Result{
|
||||||
Body: parsedBody,
|
Body: body,
|
||||||
Header: resp.Header,
|
Header: resp.Header,
|
||||||
},
|
},
|
||||||
URL: *resp.Request.URL,
|
URL: *resp.Request.URL,
|
||||||
}, err
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request performs an HTTP request and extracts the http.Response from the result.
|
// Request performs an HTTP request and extracts the http.Response from the result.
|
||||||
|
|||||||
6
Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/pager.go
generated
vendored
6
Godeps/_workspace/src/github.com/rackspace/gophercloud/pagination/pager.go
generated
vendored
@@ -174,8 +174,10 @@ func (p Pager) AllPages() (Page, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Remove the trailing comma.
|
if len(pagesSlice) > 0 {
|
||||||
pagesSlice = pagesSlice[:len(pagesSlice)-1]
|
// Remove the trailing comma.
|
||||||
|
pagesSlice = pagesSlice[:len(pagesSlice)-1]
|
||||||
|
}
|
||||||
var b []byte
|
var b []byte
|
||||||
// Combine the slice of slices in to a single slice.
|
// Combine the slice of slices in to a single slice.
|
||||||
for _, slice := range pagesSlice {
|
for _, slice := range pagesSlice {
|
||||||
|
|||||||
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go
generated
vendored
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go
generated
vendored
@@ -85,9 +85,9 @@ type RequestOpts struct {
|
|||||||
// content type of the request will default to "application/json" unless overridden by MoreHeaders.
|
// content type of the request will default to "application/json" unless overridden by MoreHeaders.
|
||||||
// It's an error to specify both a JSONBody and a RawBody.
|
// It's an error to specify both a JSONBody and a RawBody.
|
||||||
JSONBody interface{}
|
JSONBody interface{}
|
||||||
// RawBody contains an io.Reader that will be consumed by the request directly. No content-type
|
// RawBody contains an io.ReadSeeker that will be consumed by the request directly. No content-type
|
||||||
// will be set unless one is provided explicitly by MoreHeaders.
|
// will be set unless one is provided explicitly by MoreHeaders.
|
||||||
RawBody io.Reader
|
RawBody io.ReadSeeker
|
||||||
|
|
||||||
// JSONResponse, if provided, will be populated with the contents of the response body parsed as
|
// JSONResponse, if provided, will be populated with the contents of the response body parsed as
|
||||||
// JSON.
|
// JSON.
|
||||||
@@ -124,11 +124,11 @@ var applicationJSON = "application/json"
|
|||||||
// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication
|
// Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication
|
||||||
// header will automatically be provided.
|
// header will automatically be provided.
|
||||||
func (client *ProviderClient) Request(method, url string, options RequestOpts) (*http.Response, error) {
|
func (client *ProviderClient) Request(method, url string, options RequestOpts) (*http.Response, error) {
|
||||||
var body io.Reader
|
var body io.ReadSeeker
|
||||||
var contentType *string
|
var contentType *string
|
||||||
|
|
||||||
// Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided
|
// Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided
|
||||||
// io.Reader as-is. Default the content-type to application/json.
|
// io.ReadSeeker as-is. Default the content-type to application/json.
|
||||||
if options.JSONBody != nil {
|
if options.JSONBody != nil {
|
||||||
if options.RawBody != nil {
|
if options.RawBody != nil {
|
||||||
panic("Please provide only one of JSONBody or RawBody to gophercloud.Request().")
|
panic("Please provide only one of JSONBody or RawBody to gophercloud.Request().")
|
||||||
@@ -189,6 +189,9 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error trying to re-authenticate: %s", err)
|
return nil, fmt.Errorf("Error trying to re-authenticate: %s", err)
|
||||||
}
|
}
|
||||||
|
if options.RawBody != nil {
|
||||||
|
options.RawBody.Seek(0, 0)
|
||||||
|
}
|
||||||
resp, err = client.Request(method, url, options)
|
resp, err = client.Request(method, url, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Successfully re-authenticated, but got error executing request: %s", err)
|
return nil, fmt.Errorf("Successfully re-authenticated, but got error executing request: %s", err)
|
||||||
@@ -224,7 +227,9 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) (
|
|||||||
// Parse the response body as JSON, if requested to do so.
|
// Parse the response body as JSON, if requested to do so.
|
||||||
if options.JSONResponse != nil {
|
if options.JSONResponse != nil {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
json.NewDecoder(resp.Body).Decode(options.JSONResponse)
|
if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@@ -260,7 +265,7 @@ func (client *ProviderClient) Post(url string, JSONBody interface{}, JSONRespons
|
|||||||
opts = &RequestOpts{}
|
opts = &RequestOpts{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := (JSONBody).(io.Reader); ok {
|
if v, ok := (JSONBody).(io.ReadSeeker); ok {
|
||||||
opts.RawBody = v
|
opts.RawBody = v
|
||||||
} else if JSONBody != nil {
|
} else if JSONBody != nil {
|
||||||
opts.JSONBody = JSONBody
|
opts.JSONBody = JSONBody
|
||||||
@@ -278,7 +283,7 @@ func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse
|
|||||||
opts = &RequestOpts{}
|
opts = &RequestOpts{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, ok := (JSONBody).(io.Reader); ok {
|
if v, ok := (JSONBody).(io.ReadSeeker); ok {
|
||||||
opts.RawBody = v
|
opts.RawBody = v
|
||||||
} else if JSONBody != nil {
|
} else if JSONBody != nil {
|
||||||
opts.JSONBody = JSONBody
|
opts.JSONBody = JSONBody
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package volumes
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes/testing"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
th "github.com/rackspace/gophercloud/testhelper"
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
fake "github.com/rackspace/gophercloud/testhelper/client"
|
fake "github.com/rackspace/gophercloud/testhelper/client"
|
||||||
@@ -64,7 +65,7 @@ func TestCreate(t *testing.T) {
|
|||||||
|
|
||||||
os.MockCreateResponse(t)
|
os.MockCreateResponse(t)
|
||||||
|
|
||||||
n, err := Create(fake.ServiceClient(), CreateOpts{os.CreateOpts{Size: 75}}).Extract()
|
n, err := Create(fake.ServiceClient(), CreateOpts{volumes.CreateOpts{Size: 75}}).Extract()
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
th.AssertEquals(t, n.Size, 4)
|
th.AssertEquals(t, n.Size, 4)
|
||||||
@@ -72,12 +73,12 @@ func TestCreate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSizeRange(t *testing.T) {
|
func TestSizeRange(t *testing.T) {
|
||||||
_, err := Create(fake.ServiceClient(), CreateOpts{os.CreateOpts{Size: 1}}).Extract()
|
_, err := Create(fake.ServiceClient(), CreateOpts{volumes.CreateOpts{Size: 1}}).Extract()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected error, got none")
|
t.Fatalf("Expected error, got none")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = Create(fake.ServiceClient(), CreateOpts{os.CreateOpts{Size: 2000}}).Extract()
|
_, err = Create(fake.ServiceClient(), CreateOpts{volumes.CreateOpts{Size: 2000}}).Extract()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected error, got none")
|
t.Fatalf("Expected error, got none")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ func TestCreateOpts(t *testing.T) {
|
|||||||
"name": "createdserver",
|
"name": "createdserver",
|
||||||
"imageRef": "asdfasdfasdf",
|
"imageRef": "asdfasdfasdf",
|
||||||
"flavorRef": "performance1-1",
|
"flavorRef": "performance1-1",
|
||||||
|
"flavorName": "",
|
||||||
|
"imageName": "",
|
||||||
"block_device_mapping_v2":[
|
"block_device_mapping_v2":[
|
||||||
{
|
{
|
||||||
"uuid":"123456",
|
"uuid":"123456",
|
||||||
|
|||||||
@@ -36,11 +36,8 @@ func ListDetail(client *gophercloud.ServiceClient, opts os.ListOptsBuilder) pagi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get returns details about a single flavor, identity by ID.
|
// Get returns details about a single flavor, identity by ID.
|
||||||
func Get(client *gophercloud.ServiceClient, id string) os.GetResult {
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
return os.Get(client, id)
|
var res GetResult
|
||||||
}
|
_, res.Err = client.Get(getURL(client, id), &res.Body, nil)
|
||||||
|
return res
|
||||||
// ExtractFlavors interprets a page of List results as Flavors.
|
|
||||||
func ExtractFlavors(page pagination.Page) ([]os.Flavor, error) {
|
|
||||||
return os.ExtractFlavors(page)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
package flavors
|
package flavors
|
||||||
|
|
||||||
import (
|
|
||||||
os "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ListOutput is a sample response of a flavor List request.
|
// ListOutput is a sample response of a flavor List request.
|
||||||
const ListOutput = `
|
const ListOutput = `
|
||||||
{
|
{
|
||||||
@@ -103,7 +99,7 @@ const GetOutput = `
|
|||||||
|
|
||||||
// Performance1Flavor is the expected result of parsing GetOutput, or the first element of
|
// Performance1Flavor is the expected result of parsing GetOutput, or the first element of
|
||||||
// ListOutput.
|
// ListOutput.
|
||||||
var Performance1Flavor = os.Flavor{
|
var Performance1Flavor = Flavor{
|
||||||
ID: "performance1-1",
|
ID: "performance1-1",
|
||||||
Disk: 20,
|
Disk: 20,
|
||||||
RAM: 1024,
|
RAM: 1024,
|
||||||
@@ -111,10 +107,16 @@ var Performance1Flavor = os.Flavor{
|
|||||||
RxTxFactor: 200.0,
|
RxTxFactor: 200.0,
|
||||||
Swap: 0,
|
Swap: 0,
|
||||||
VCPUs: 1,
|
VCPUs: 1,
|
||||||
|
ExtraSpecs: ExtraSpecs{
|
||||||
|
NumDataDisks: 0,
|
||||||
|
Class: "performance1",
|
||||||
|
DiskIOIndex: 0,
|
||||||
|
PolicyClass: "performance_flavor",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performance2Flavor is the second result expected from parsing ListOutput.
|
// Performance2Flavor is the second result expected from parsing ListOutput.
|
||||||
var Performance2Flavor = os.Flavor{
|
var Performance2Flavor = Flavor{
|
||||||
ID: "performance1-2",
|
ID: "performance1-2",
|
||||||
Disk: 40,
|
Disk: 40,
|
||||||
RAM: 2048,
|
RAM: 2048,
|
||||||
@@ -122,8 +124,14 @@ var Performance2Flavor = os.Flavor{
|
|||||||
RxTxFactor: 400.0,
|
RxTxFactor: 400.0,
|
||||||
Swap: 0,
|
Swap: 0,
|
||||||
VCPUs: 2,
|
VCPUs: 2,
|
||||||
|
ExtraSpecs: ExtraSpecs{
|
||||||
|
NumDataDisks: 0,
|
||||||
|
Class: "performance1",
|
||||||
|
DiskIOIndex: 0,
|
||||||
|
PolicyClass: "performance_flavor",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpectedFlavorSlice is the slice of Flavor structs that are expected to be parsed from
|
// ExpectedFlavorSlice is the slice of Flavor structs that are expected to be parsed from
|
||||||
// ListOutput.
|
// ListOutput.
|
||||||
var ExpectedFlavorSlice = []os.Flavor{Performance1Flavor, Performance2Flavor}
|
var ExpectedFlavorSlice = []Flavor{Performance1Flavor, Performance2Flavor}
|
||||||
|
|||||||
104
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/flavors/results.go
generated
vendored
Normal file
104
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/flavors/results.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtraSpecs provide additional information about the flavor.
|
||||||
|
type ExtraSpecs struct {
|
||||||
|
// The number of data disks
|
||||||
|
NumDataDisks int `mapstructure:"number_of_data_disks"`
|
||||||
|
// The flavor class
|
||||||
|
Class string `mapstructure:"class"`
|
||||||
|
// Relative measure of disk I/O performance from 0-99, where higher is faster
|
||||||
|
DiskIOIndex int `mapstructure:"disk_io_index"`
|
||||||
|
PolicyClass string `mapstructure:"policy_class"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flavor records represent (virtual) hardware configurations for server resources in a region.
|
||||||
|
type Flavor struct {
|
||||||
|
// The Id field contains the flavor's unique identifier.
|
||||||
|
// For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance.
|
||||||
|
ID string `mapstructure:"id"`
|
||||||
|
|
||||||
|
// The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively.
|
||||||
|
Disk int `mapstructure:"disk"`
|
||||||
|
RAM int `mapstructure:"ram"`
|
||||||
|
|
||||||
|
// The Name field provides a human-readable moniker for the flavor.
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
|
||||||
|
RxTxFactor float64 `mapstructure:"rxtx_factor"`
|
||||||
|
|
||||||
|
// Swap indicates how much space is reserved for swap.
|
||||||
|
// If not provided, this field will be set to 0.
|
||||||
|
Swap int `mapstructure:"swap"`
|
||||||
|
|
||||||
|
// VCPUs indicates how many (virtual) CPUs are available for this flavor.
|
||||||
|
VCPUs int `mapstructure:"vcpus"`
|
||||||
|
|
||||||
|
// ExtraSpecs provides extra information about the flavor
|
||||||
|
ExtraSpecs ExtraSpecs `mapstructure:"OS-FLV-WITH-EXT-SPECS:extra_specs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult temporarily holds the response from a Get call.
|
||||||
|
type GetResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract provides access to the individual Flavor returned by the Get function.
|
||||||
|
func (gr GetResult) Extract() (*Flavor, error) {
|
||||||
|
if gr.Err != nil {
|
||||||
|
return nil, gr.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result struct {
|
||||||
|
Flavor Flavor `mapstructure:"flavor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &mapstructure.DecoderConfig{
|
||||||
|
DecodeHook: defaulter,
|
||||||
|
Result: &result,
|
||||||
|
}
|
||||||
|
decoder, err := mapstructure.NewDecoder(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = decoder.Decode(gr.Body)
|
||||||
|
return &result.Flavor, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaulter(from, to reflect.Kind, v interface{}) (interface{}, error) {
|
||||||
|
if (from == reflect.String) && (to == reflect.Int) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation.
|
||||||
|
func ExtractFlavors(page pagination.Page) ([]Flavor, error) {
|
||||||
|
casted := page.(os.FlavorPage).Body
|
||||||
|
var container struct {
|
||||||
|
Flavors []Flavor `mapstructure:"flavors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &mapstructure.DecoderConfig{
|
||||||
|
DecodeHook: defaulter,
|
||||||
|
Result: &container,
|
||||||
|
}
|
||||||
|
decoder, err := mapstructure.NewDecoder(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return container.Flavors, err
|
||||||
|
}
|
||||||
|
err = decoder.Decode(casted)
|
||||||
|
if err != nil {
|
||||||
|
return container.Flavors, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return container.Flavors, nil
|
||||||
|
}
|
||||||
9
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/flavors/urls.go
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/compute/v2/flavors/urls.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id)
|
||||||
|
}
|
||||||
@@ -12,13 +12,24 @@ type CreateOpts struct {
|
|||||||
// Name [required] is the name to assign to the newly launched server.
|
// Name [required] is the name to assign to the newly launched server.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// ImageRef [required] is the ID or full URL to the image that contains the server's OS and initial state.
|
// ImageRef [optional; required if ImageName is not provided] is the ID or full
|
||||||
// Optional if using the boot-from-volume extension.
|
// URL to the image that contains the server's OS and initial state.
|
||||||
|
// Also optional if using the boot-from-volume extension.
|
||||||
ImageRef string
|
ImageRef string
|
||||||
|
|
||||||
// FlavorRef [required] is the ID or full URL to the flavor that describes the server's specs.
|
// ImageName [optional; required if ImageRef is not provided] is the name of the
|
||||||
|
// image that contains the server's OS and initial state.
|
||||||
|
// Also optional if using the boot-from-volume extension.
|
||||||
|
ImageName string
|
||||||
|
|
||||||
|
// FlavorRef [optional; required if FlavorName is not provided] is the ID or
|
||||||
|
// full URL to the flavor that describes the server's specs.
|
||||||
FlavorRef string
|
FlavorRef string
|
||||||
|
|
||||||
|
// FlavorName [optional; required if FlavorRef is not provided] is the name of
|
||||||
|
// the flavor that describes the server's specs.
|
||||||
|
FlavorName string
|
||||||
|
|
||||||
// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
|
// SecurityGroups [optional] lists the names of the security groups to which this server should belong.
|
||||||
SecurityGroups []string
|
SecurityGroups []string
|
||||||
|
|
||||||
@@ -36,9 +47,9 @@ type CreateOpts struct {
|
|||||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
|
|
||||||
// Personality [optional] includes the path and contents of a file to inject into the server at launch.
|
// Personality [optional] includes files to inject into the server at launch.
|
||||||
// The maximum size of the file is 255 bytes (decoded).
|
// Create will base64-encode file contents for you.
|
||||||
Personality []byte
|
Personality os.Personality
|
||||||
|
|
||||||
// ConfigDrive [optional] enables metadata injection through a configuration drive.
|
// ConfigDrive [optional] enables metadata injection through a configuration drive.
|
||||||
ConfigDrive bool
|
ConfigDrive bool
|
||||||
@@ -58,7 +69,7 @@ type CreateOpts struct {
|
|||||||
DiskConfig diskconfig.DiskConfig
|
DiskConfig diskconfig.DiskConfig
|
||||||
|
|
||||||
// BlockDevice [optional] will create the server from a volume, which is created from an image,
|
// BlockDevice [optional] will create the server from a volume, which is created from an image,
|
||||||
// a snapshot, or an another volume.
|
// a snapshot, or another volume.
|
||||||
BlockDevice []bootfromvolume.BlockDevice
|
BlockDevice []bootfromvolume.BlockDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +79,9 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
|||||||
base := os.CreateOpts{
|
base := os.CreateOpts{
|
||||||
Name: opts.Name,
|
Name: opts.Name,
|
||||||
ImageRef: opts.ImageRef,
|
ImageRef: opts.ImageRef,
|
||||||
|
ImageName: opts.ImageName,
|
||||||
FlavorRef: opts.FlavorRef,
|
FlavorRef: opts.FlavorRef,
|
||||||
|
FlavorName: opts.FlavorName,
|
||||||
SecurityGroups: opts.SecurityGroups,
|
SecurityGroups: opts.SecurityGroups,
|
||||||
UserData: opts.UserData,
|
UserData: opts.UserData,
|
||||||
AvailabilityZone: opts.AvailabilityZone,
|
AvailabilityZone: opts.AvailabilityZone,
|
||||||
@@ -104,7 +117,9 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
|
|||||||
// key_name doesn't actually come from the extension (or at least isn't documented there) so
|
// key_name doesn't actually come from the extension (or at least isn't documented there) so
|
||||||
// we need to add it manually.
|
// we need to add it manually.
|
||||||
serverMap := res["server"].(map[string]interface{})
|
serverMap := res["server"].(map[string]interface{})
|
||||||
serverMap["key_name"] = opts.KeyPair
|
if opts.KeyPair != "" {
|
||||||
|
serverMap["key_name"] = opts.KeyPair
|
||||||
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@@ -130,9 +145,9 @@ type RebuildOpts struct {
|
|||||||
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
// Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server.
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
|
|
||||||
// Personality [optional] includes the path and contents of a file to inject into the server at launch.
|
// Personality [optional] includes files to inject into the server at launch.
|
||||||
// The maximum size of the file is 255 bytes (decoded).
|
// Rebuild will base64-encode file contents for you.
|
||||||
Personality []byte
|
Personality os.Personality
|
||||||
|
|
||||||
// Rackspace-specific stuff begins here.
|
// Rackspace-specific stuff begins here.
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ func TestCreateOpts(t *testing.T) {
|
|||||||
"name": "createdserver",
|
"name": "createdserver",
|
||||||
"imageRef": "image-id",
|
"imageRef": "image-id",
|
||||||
"flavorRef": "flavor-id",
|
"flavorRef": "flavor-id",
|
||||||
|
"flavorName": "",
|
||||||
|
"imageName": "",
|
||||||
"key_name": "mykey",
|
"key_name": "mykey",
|
||||||
"OS-DCF:diskConfig": "MANUAL"
|
"OS-DCF:diskConfig": "MANUAL"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,53 @@ package volumeattach
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
os "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||||
|
fixtures "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach/testing"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
th "github.com/rackspace/gophercloud/testhelper"
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
"github.com/rackspace/gophercloud/testhelper/client"
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FirstVolumeAttachment is the first result in ListOutput.
|
||||||
|
var FirstVolumeAttachment = volumeattach.VolumeAttachment{
|
||||||
|
Device: "/dev/vdd",
|
||||||
|
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
||||||
|
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||||
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f803",
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecondVolumeAttachment is the first result in ListOutput.
|
||||||
|
var SecondVolumeAttachment = volumeattach.VolumeAttachment{
|
||||||
|
Device: "/dev/vdc",
|
||||||
|
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||||
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectedVolumeAttachmentSlide is the slice of results that should be parsed
|
||||||
|
// from ListOutput, in the expected order.
|
||||||
|
var ExpectedVolumeAttachmentSlice = []volumeattach.VolumeAttachment{FirstVolumeAttachment, SecondVolumeAttachment}
|
||||||
|
|
||||||
|
//CreatedVolumeAttachment is the parsed result from CreatedOutput.
|
||||||
|
var CreatedVolumeAttachment = volumeattach.VolumeAttachment{
|
||||||
|
Device: "/dev/vdc",
|
||||||
|
ID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
|
||||||
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
|
}
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
os.HandleListSuccessfully(t)
|
fixtures.HandleListSuccessfully(t)
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
err := List(client.ServiceClient(), serverId).EachPage(func(page pagination.Page) (bool, error) {
|
err := List(client.ServiceClient(), serverId).EachPage(func(page pagination.Page) (bool, error) {
|
||||||
count++
|
count++
|
||||||
actual, err := os.ExtractVolumeAttachments(page)
|
actual, err := volumeattach.ExtractVolumeAttachments(page)
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
th.CheckDeepEquals(t, os.ExpectedVolumeAttachmentSlice, actual)
|
th.CheckDeepEquals(t, ExpectedVolumeAttachmentSlice, actual)
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
@@ -31,33 +60,33 @@ func TestList(t *testing.T) {
|
|||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
os.HandleCreateSuccessfully(t)
|
fixtures.HandleCreateSuccessfully(t)
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
actual, err := Create(client.ServiceClient(), serverId, os.CreateOpts{
|
actual, err := Create(client.ServiceClient(), serverId, volumeattach.CreateOpts{
|
||||||
Device: "/dev/vdc",
|
Device: "/dev/vdc",
|
||||||
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
VolumeID: "a26887c6-c47b-4654-abb5-dfadf7d3f804",
|
||||||
}).Extract()
|
}).Extract()
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
th.CheckDeepEquals(t, &os.CreatedVolumeAttachment, actual)
|
th.CheckDeepEquals(t, &CreatedVolumeAttachment, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
func TestGet(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
os.HandleGetSuccessfully(t)
|
fixtures.HandleGetSuccessfully(t)
|
||||||
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
actual, err := Get(client.ServiceClient(), serverId, aId).Extract()
|
actual, err := Get(client.ServiceClient(), serverId, aId).Extract()
|
||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
th.CheckDeepEquals(t, &os.SecondVolumeAttachment, actual)
|
th.CheckDeepEquals(t, &SecondVolumeAttachment, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDelete(t *testing.T) {
|
func TestDelete(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
os.HandleDeleteSuccessfully(t)
|
fixtures.HandleDeleteSuccessfully(t)
|
||||||
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
aId := "a26887c6-c47b-4654-abb5-dfadf7d3f804"
|
||||||
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
serverId := "4d8c3732-a248-40ed-bebc-539a6ffd25c0"
|
||||||
|
|
||||||
|
|||||||
36
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/fixtures.go
generated
vendored
36
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/lb/v1/nodes/fixtures.go
generated
vendored
@@ -107,6 +107,42 @@ func mockCreateResponse(t *testing.T, lbID int) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mockCreateErrResponse(t *testing.T, lbID int) {
|
||||||
|
th.Mux.HandleFunc(_rootURL(lbID), 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, `
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"address": "10.2.2.3",
|
||||||
|
"port": 80,
|
||||||
|
"condition": "ENABLED",
|
||||||
|
"type": "PRIMARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "10.2.2.4",
|
||||||
|
"port": 81,
|
||||||
|
"condition": "ENABLED",
|
||||||
|
"type": "SECONDARY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(422) // Unprocessable Entity
|
||||||
|
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"code": 422,
|
||||||
|
"message": "Load Balancer '%d' has a status of 'PENDING_UPDATE' and is considered immutable."
|
||||||
|
}
|
||||||
|
`, lbID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func mockBatchDeleteResponse(t *testing.T, lbID int, ids []int) {
|
func mockBatchDeleteResponse(t *testing.T, lbID int, ids []int) {
|
||||||
th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc(_rootURL(lbID), func(w http.ResponseWriter, r *http.Request) {
|
||||||
th.TestMethod(t, r, "DELETE")
|
th.TestMethod(t, r, "DELETE")
|
||||||
|
|||||||
@@ -119,12 +119,7 @@ func Create(client *gophercloud.ServiceClient, loadBalancerID int, opts CreateOp
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := pagination.PageResultFrom(resp)
|
pr := pagination.PageResultFromParsed(resp, res.Body)
|
||||||
if err != nil {
|
|
||||||
res.Err = err
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
return CreateResult{pagination.SinglePageBase(pr)}
|
return CreateResult{pagination.SinglePageBase(pr)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,38 @@ func TestCreate(t *testing.T) {
|
|||||||
th.CheckDeepEquals(t, expected, actual)
|
th.CheckDeepEquals(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateErr(t *testing.T) {
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
|
mockCreateErrResponse(t, lbID)
|
||||||
|
|
||||||
|
opts := CreateOpts{
|
||||||
|
CreateOpt{
|
||||||
|
Address: "10.2.2.3",
|
||||||
|
Port: 80,
|
||||||
|
Condition: ENABLED,
|
||||||
|
Type: PRIMARY,
|
||||||
|
},
|
||||||
|
CreateOpt{
|
||||||
|
Address: "10.2.2.4",
|
||||||
|
Port: 81,
|
||||||
|
Condition: ENABLED,
|
||||||
|
Type: SECONDARY,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
page := Create(client.ServiceClient(), lbID, opts)
|
||||||
|
|
||||||
|
actual, err := page.ExtractNodes()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Did not receive expected error from ExtractNodes")
|
||||||
|
}
|
||||||
|
if actual != nil {
|
||||||
|
t.Fatalf("Received non-nil result from failed ExtractNodes: %#v", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBulkDelete(t *testing.T) {
|
func TestBulkDelete(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
|
|||||||
@@ -126,6 +126,9 @@ type CreateResult struct {
|
|||||||
|
|
||||||
// ExtractNodes extracts a slice of Node structs from a CreateResult.
|
// ExtractNodes extracts a slice of Node structs from a CreateResult.
|
||||||
func (res CreateResult) ExtractNodes() ([]Node, error) {
|
func (res CreateResult) ExtractNodes() ([]Node, error) {
|
||||||
|
if res.Err != nil {
|
||||||
|
return nil, res.Err
|
||||||
|
}
|
||||||
return commonExtractNodes(res.Body)
|
return commonExtractNodes(res.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ func mockEnableResponse(t *testing.T, lbID int) {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ func mockUpdateResponse(t *testing.T, lbID int) {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ func mockCreateResponse(t *testing.T, lbID int) {
|
|||||||
`)
|
`)
|
||||||
|
|
||||||
w.WriteHeader(http.StatusAccepted)
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
fmt.Fprintf(w, `{}`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -203,8 +203,17 @@ func TestCreateWithOptionalFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"name": "sample_network",
|
||||||
|
"admin_state_up": true,
|
||||||
|
"shared": true,
|
||||||
|
"tenant_id": "12345"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
iTrue := true
|
iTrue := true
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ func Delete(c *gophercloud.ServiceClient, opts DeleteOptsBuilder) DeleteResult {
|
|||||||
JSONBody: reqBody,
|
JSONBody: reqBody,
|
||||||
JSONResponse: &res.Body,
|
JSONResponse: &res.Body,
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ func Enable(c *gophercloud.ServiceClient, containerName string, opts EnableOptsB
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{201, 202, 204},
|
OkCodes: []int{201, 202, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,9 @@ func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
|
|||||||
resp, err := c.Request("HEAD", getURL(c, containerName), gophercloud.RequestOpts{
|
resp, err := c.Request("HEAD", getURL(c, containerName), gophercloud.RequestOpts{
|
||||||
OkCodes: []int{200, 204},
|
OkCodes: []int{200, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@@ -149,7 +153,9 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB
|
|||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
OkCodes: []int{202, 204},
|
OkCodes: []int{202, 204},
|
||||||
})
|
})
|
||||||
res.Header = resp.Header
|
if resp != nil {
|
||||||
|
res.Header = resp.Header
|
||||||
|
}
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create is a function that creates a new object or replaces an existing object.
|
// Create is a function that creates a new object or replaces an existing object.
|
||||||
func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.Reader, opts os.CreateOptsBuilder) os.CreateResult {
|
func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.ReadSeeker, opts os.CreateOptsBuilder) os.CreateResult {
|
||||||
return os.Create(c, containerName, objectName, content, opts)
|
return os.Create(c, containerName, objectName, content, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
|
os "github.com/rackspace/gophercloud/openstack/objectstorage/v1/objects"
|
||||||
@@ -66,21 +66,23 @@ func TestListObjectNames(t *testing.T) {
|
|||||||
func TestCreateObject(t *testing.T) {
|
func TestCreateObject(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
os.HandleCreateTextObjectSuccessfully(t)
|
|
||||||
|
|
||||||
content := bytes.NewBufferString("Did gyre and gimble in the wabe")
|
content := "Did gyre and gimble in the wabe"
|
||||||
|
os.HandleCreateTextObjectSuccessfully(t, content)
|
||||||
|
|
||||||
options := &os.CreateOpts{ContentType: "text/plain"}
|
options := &os.CreateOpts{ContentType: "text/plain"}
|
||||||
res := Create(fake.ServiceClient(), "testContainer", "testObject", content, options)
|
res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), options)
|
||||||
th.AssertNoErr(t, res.Err)
|
th.AssertNoErr(t, res.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateObjectWithoutContentType(t *testing.T) {
|
func TestCreateObjectWithoutContentType(t *testing.T) {
|
||||||
th.SetupHTTP()
|
th.SetupHTTP()
|
||||||
defer th.TeardownHTTP()
|
defer th.TeardownHTTP()
|
||||||
os.HandleCreateTypelessObjectSuccessfully(t)
|
|
||||||
|
|
||||||
content := bytes.NewBufferString("The sky was the color of television, tuned to a dead channel.")
|
content := "The sky was the color of television, tuned to a dead channel."
|
||||||
res := Create(fake.ServiceClient(), "testContainer", "testObject", content, &os.CreateOpts{})
|
os.HandleCreateTypelessObjectSuccessfully(t, content)
|
||||||
|
|
||||||
|
res := Create(fake.ServiceClient(), "testContainer", "testObject", strings.NewReader(content), &os.CreateOpts{})
|
||||||
th.AssertNoErr(t, res.Err)
|
th.AssertNoErr(t, res.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/results.go
generated
vendored
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/results.go
generated
vendored
@@ -113,6 +113,9 @@ func DecodeHeader(from, to interface{}) error {
|
|||||||
// RFC3339Milli describes a common time format used by some API responses.
|
// RFC3339Milli describes a common time format used by some API responses.
|
||||||
const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
|
const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
|
||||||
|
|
||||||
|
// Time format used in cloud orchestration
|
||||||
|
const STACK_TIME_FMT = "2006-01-02T15:04:05"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Link is an internal type to be used in packages of collection resources that are
|
Link is an internal type to be used in packages of collection resources that are
|
||||||
paginated in a certain way.
|
paginated in a certain way.
|
||||||
|
|||||||
Reference in New Issue
Block a user