Merge pull request #23131 from AlbertoPeon/up_gophercloud
Auto commit by PR queue bot
This commit is contained in:
commit
050980b472
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -908,8 +908,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rackspace/gophercloud",
|
"ImportPath": "github.com/rackspace/gophercloud",
|
||||||
"Comment": "v1.0.0-665-gf928634",
|
"Comment": "v1.0.0-842-g8992d74",
|
||||||
"Rev": "f92863476c034f851073599c09d90cd61ee95b3d"
|
"Rev": "8992d7483a06748dea706e4716d042a4a9e73918"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/russross/blackfriday",
|
"ImportPath": "github.com/russross/blackfriday",
|
||||||
|
21
Godeps/_workspace/src/github.com/rackspace/gophercloud/.travis.yml
generated
vendored
21
Godeps/_workspace/src/github.com/rackspace/gophercloud/.travis.yml
generated
vendored
@ -1,17 +1,20 @@
|
|||||||
language: go
|
language: go
|
||||||
|
sudo: false
|
||||||
install:
|
install:
|
||||||
|
- go get golang.org/x/crypto/ssh
|
||||||
- go get -v -tags 'fixtures acceptance' ./...
|
- go get -v -tags 'fixtures acceptance' ./...
|
||||||
go:
|
go:
|
||||||
- 1.1
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
- 1.4
|
||||||
|
- 1.5
|
||||||
- tip
|
- tip
|
||||||
script: script/cibuild
|
env:
|
||||||
after_success:
|
- COVERALLS_TOKEN=2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
|
||||||
- go get golang.org/x/tools/cmd/cover
|
before_install:
|
||||||
- go get github.com/axw/gocov/gocov
|
- go get github.com/axw/gocov/gocov
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
- export PATH=$PATH:$HOME/gopath/bin/
|
- go get github.com/pierrre/gotestcover
|
||||||
- goveralls 2k7PTU3xa474Hymwgdj6XjqenNfGTNkO8
|
- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||||
sudo: false
|
script:
|
||||||
|
- $HOME/gopath/bin/gotestcover -v -tags=fixtures -coverprofile=cover.out ./...
|
||||||
|
after_success:
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out
|
||||||
|
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md
generated
vendored
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/CONTRIBUTING.md
generated
vendored
@ -11,7 +11,8 @@ As a contributor you will need to setup your workspace in a slightly different
|
|||||||
way than just downloading it. Here are the basic installation instructions:
|
way than just downloading it. Here are the basic installation instructions:
|
||||||
|
|
||||||
1. Configure your `$GOPATH` and run `go get` as described in the main
|
1. Configure your `$GOPATH` and run `go get` as described in the main
|
||||||
[README](/README.md#how-to-install).
|
[README](/README.md#how-to-install) but add `-tags "fixtures acceptance"` to
|
||||||
|
get dependencies for unit and acceptance tests.
|
||||||
|
|
||||||
2. Move into the directory that houses your local repository:
|
2. Move into the directory that houses your local repository:
|
||||||
|
|
||||||
@ -158,25 +159,25 @@ deleted after the test suite finishes.
|
|||||||
To run all tests:
|
To run all tests:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test ./...
|
go test -tags fixtures ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
To run all tests with verbose output:
|
To run all tests with verbose output:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test -v ./...
|
go test -v -tags fixtures ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
To run tests that match certain [build tags]():
|
To run tests that match certain [build tags]():
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test -tags "foo bar" ./...
|
go test -tags "fixtures foo bar" ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
To run tests for a particular sub-package:
|
To run tests for a particular sub-package:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ./path/to/package && go test .
|
cd ./path/to/package && go test -tags fixtures .
|
||||||
```
|
```
|
||||||
|
|
||||||
## Basic style guide
|
## Basic style guide
|
||||||
|
4
Godeps/_workspace/src/github.com/rackspace/gophercloud/README.md
generated
vendored
4
Godeps/_workspace/src/github.com/rackspace/gophercloud/README.md
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
# Gophercloud: the OpenStack SDK for Go
|
# Gophercloud: an OpenStack SDK for Go
|
||||||
[](https://travis-ci.org/rackspace/gophercloud)
|
[](https://travis-ci.org/rackspace/gophercloud) [](https://coveralls.io/r/rackspace/gophercloud)
|
||||||
|
|
||||||
Gophercloud is a flexible SDK that allows you to consume and work with OpenStack
|
Gophercloud is a flexible SDK that allows you to consume and work with OpenStack
|
||||||
clouds in a simple and idiomatic way using golang. Many services are supported,
|
clouds in a simple and idiomatic way using golang. Many services are supported,
|
||||||
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/pkg.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/blockstorage/v1/pkg.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// The v1 package contains acceptance tests for the Openstack Cinder V1 service.
|
||||||
|
|
||||||
|
package v1
|
70
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/db/v1/common.go
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/db/v1/common.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// +build acceptance db
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/instances"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newClient(t *testing.T) *gophercloud.ServiceClient {
|
||||||
|
ao, err := openstack.AuthOptionsFromEnv()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
|
client, err := openstack.AuthenticatedClient(ao)
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
|
c, err := openstack.NewDBV1(client, gophercloud.EndpointOpts{
|
||||||
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
|
})
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type context struct {
|
||||||
|
test *testing.T
|
||||||
|
client *gophercloud.ServiceClient
|
||||||
|
instanceID string
|
||||||
|
DBIDs []string
|
||||||
|
users []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext(t *testing.T) context {
|
||||||
|
return context{
|
||||||
|
test: t,
|
||||||
|
client: newClient(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c context) Logf(msg string, args ...interface{}) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
c.test.Logf(msg, args...)
|
||||||
|
} else {
|
||||||
|
c.test.Log(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c context) AssertNoErr(err error) {
|
||||||
|
th.AssertNoErr(c.test, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c context) WaitUntilActive(id string) {
|
||||||
|
err := gophercloud.WaitFor(60, func() (bool, error) {
|
||||||
|
inst, err := instances.Get(c.client, id).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if inst.Status == "ACTIVE" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
c.AssertNoErr(err)
|
||||||
|
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/db/v1/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/openstack/db/v1/pkg.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package v1
|
73
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/db/v1/common.go
generated
vendored
Normal file
73
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/db/v1/common.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// +build acceptance db rackspace
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/acceptance/tools"
|
||||||
|
"github.com/rackspace/gophercloud/rackspace"
|
||||||
|
"github.com/rackspace/gophercloud/rackspace/db/v1/instances"
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newClient(t *testing.T) *gophercloud.ServiceClient {
|
||||||
|
opts, err := rackspace.AuthOptionsFromEnv()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
opts = tools.OnlyRS(opts)
|
||||||
|
|
||||||
|
client, err := rackspace.AuthenticatedClient(opts)
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
|
c, err := rackspace.NewDBV1(client, gophercloud.EndpointOpts{
|
||||||
|
Region: "IAD",
|
||||||
|
})
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type context struct {
|
||||||
|
test *testing.T
|
||||||
|
client *gophercloud.ServiceClient
|
||||||
|
instanceID string
|
||||||
|
DBIDs []string
|
||||||
|
replicaID string
|
||||||
|
backupID string
|
||||||
|
configGroupID string
|
||||||
|
users []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext(t *testing.T) context {
|
||||||
|
return context{
|
||||||
|
test: t,
|
||||||
|
client: newClient(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c context) Logf(msg string, args ...interface{}) {
|
||||||
|
if len(args) > 0 {
|
||||||
|
c.test.Logf(msg, args...)
|
||||||
|
} else {
|
||||||
|
c.test.Log(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c context) AssertNoErr(err error) {
|
||||||
|
th.AssertNoErr(c.test, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c context) WaitUntilActive(id string) {
|
||||||
|
err := gophercloud.WaitFor(60, func() (bool, error) {
|
||||||
|
inst, err := instances.Get(c.client, id).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if inst.Status == "ACTIVE" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
c.AssertNoErr(err)
|
||||||
|
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/db/v1/pkg.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/acceptance/rackspace/db/v1/pkg.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package v1
|
@ -2,6 +2,7 @@ package apiversions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
)
|
)
|
||||||
@ -11,5 +12,7 @@ func getURL(c *gophercloud.ServiceClient, version string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func listURL(c *gophercloud.ServiceClient) string {
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
return c.ServiceURL("")
|
u, _ := url.Parse(c.ServiceURL(""))
|
||||||
|
u.Path = "/"
|
||||||
|
return u.String()
|
||||||
}
|
}
|
||||||
|
54
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/client.go
generated
vendored
54
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/client.go
generated
vendored
@ -3,6 +3,7 @@ package openstack
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
|
tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
|
||||||
@ -64,8 +65,8 @@ func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.Provider
|
|||||||
// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
|
// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
|
||||||
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
|
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
|
||||||
versions := []*utils.Version{
|
versions := []*utils.Version{
|
||||||
&utils.Version{ID: v20, Priority: 20, Suffix: "/v2.0/"},
|
{ID: v20, Priority: 20, Suffix: "/v2.0/"},
|
||||||
&utils.Version{ID: v30, Priority: 30, Suffix: "/v3/"},
|
{ID: v30, Priority: 30, Suffix: "/v3/"},
|
||||||
}
|
}
|
||||||
|
|
||||||
chosen, endpoint, err := utils.ChooseVersion(client, versions)
|
chosen, endpoint, err := utils.ChooseVersion(client, versions)
|
||||||
@ -110,7 +111,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
|||||||
if options.AllowReauth {
|
if options.AllowReauth {
|
||||||
client.ReauthFunc = func() error {
|
client.ReauthFunc = func() error {
|
||||||
client.TokenID = ""
|
client.TokenID = ""
|
||||||
return AuthenticateV2(client, options)
|
return v2auth(client, endpoint, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.TokenID = token.ID
|
client.TokenID = token.ID
|
||||||
@ -167,7 +168,8 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
|
|||||||
|
|
||||||
if options.AllowReauth {
|
if options.AllowReauth {
|
||||||
client.ReauthFunc = func() error {
|
client.ReauthFunc = func() error {
|
||||||
return AuthenticateV3(client, options)
|
client.TokenID = ""
|
||||||
|
return v3auth(client, endpoint, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||||
@ -197,6 +199,40 @@ func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClien
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("identity")
|
||||||
|
eo.Availability = gophercloud.AvailabilityAdmin
|
||||||
|
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force using v2 API
|
||||||
|
if strings.Contains(url, "/v3") {
|
||||||
|
url = strings.Replace(url, "/v3", "/v2.0", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("identity")
|
||||||
|
eo.Availability = gophercloud.AvailabilityAdmin
|
||||||
|
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force using v3 API
|
||||||
|
if strings.Contains(url, "/v2.0") {
|
||||||
|
url = strings.Replace(url, "/v2.0", "/v3", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
|
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
|
||||||
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("object-store")
|
eo.ApplyDefaults("object-store")
|
||||||
@ -261,3 +297,13 @@ func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.Endpo
|
|||||||
}
|
}
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
|
||||||
|
func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("database")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ const (
|
|||||||
Volume SourceType = "volume"
|
Volume SourceType = "volume"
|
||||||
Snapshot SourceType = "snapshot"
|
Snapshot SourceType = "snapshot"
|
||||||
Image SourceType = "image"
|
Image SourceType = "image"
|
||||||
|
Blank SourceType = "blank"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockDevice is a structure with options for booting a server instance
|
// BlockDevice is a structure with options for booting a server instance
|
||||||
@ -32,6 +33,9 @@ type BlockDevice struct {
|
|||||||
// and "local".
|
// and "local".
|
||||||
DestinationType string `json:"destination_type"`
|
DestinationType string `json:"destination_type"`
|
||||||
|
|
||||||
|
// GuestFormat [optional] specifies the format of the block device.
|
||||||
|
GuestFormat string `json:"guest_format"`
|
||||||
|
|
||||||
// SourceType [required] must be one of: "volume", "snapshot", "image".
|
// SourceType [required] must be one of: "volume", "snapshot", "image".
|
||||||
SourceType SourceType `json:"source_type"`
|
SourceType SourceType `json:"source_type"`
|
||||||
|
|
||||||
@ -82,6 +86,9 @@ func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
|||||||
if bd.DestinationType != "" {
|
if bd.DestinationType != "" {
|
||||||
blockDevice[i]["destination_type"] = bd.DestinationType
|
blockDevice[i]["destination_type"] = bd.DestinationType
|
||||||
}
|
}
|
||||||
|
if bd.GuestFormat != "" {
|
||||||
|
blockDevice[i]["guest_format"] = bd.GuestFormat
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
serverMap["block_device_mapping_v2"] = blockDevice
|
serverMap["block_device_mapping_v2"] = blockDevice
|
||||||
@ -99,6 +106,11 @@ func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) s
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete imageName and flavorName that come from ToServerCreateMap().
|
||||||
|
// As of Liberty, Boot From Volume is failing if they are passed.
|
||||||
|
delete(reqBody["server"].(map[string]interface{}), "imageName")
|
||||||
|
delete(reqBody["server"].(map[string]interface{}), "flavorName")
|
||||||
|
|
||||||
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
_, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||||
OkCodes: []int{200, 202},
|
OkCodes: []int{200, 202},
|
||||||
})
|
})
|
||||||
|
@ -72,6 +72,41 @@ func mockCreateRuleResponse(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mockCreateRuleResponseICMPZero(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "POST")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
|
||||||
|
th.TestJSONRequest(t, r, `
|
||||||
|
{
|
||||||
|
"security_group_default_rule": {
|
||||||
|
"ip_protocol": "ICMP",
|
||||||
|
"from_port": 0,
|
||||||
|
"to_port": 0,
|
||||||
|
"cidr": "10.10.12.0/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"security_group_default_rule": {
|
||||||
|
"from_port": 0,
|
||||||
|
"id": "{ruleID}",
|
||||||
|
"ip_protocol": "ICMP",
|
||||||
|
"ip_range": {
|
||||||
|
"cidr": "10.10.12.0/24"
|
||||||
|
},
|
||||||
|
"to_port": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func mockGetRuleResponse(t *testing.T, ruleID string) {
|
func mockGetRuleResponse(t *testing.T, ruleID string) {
|
||||||
url := rootPath + "/" + ruleID
|
url := rootPath + "/" + ruleID
|
||||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -2,6 +2,7 @@ package defsecrules
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
@ -42,10 +43,10 @@ type CreateOptsBuilder interface {
|
|||||||
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
func (opts CreateOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
||||||
rule := make(map[string]interface{})
|
rule := make(map[string]interface{})
|
||||||
|
|
||||||
if opts.FromPort == 0 {
|
if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||||
return rule, errors.New("A FromPort must be set")
|
return rule, errors.New("A FromPort must be set")
|
||||||
}
|
}
|
||||||
if opts.ToPort == 0 {
|
if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||||
return rule, errors.New("A ToPort must be set")
|
return rule, errors.New("A ToPort must be set")
|
||||||
}
|
}
|
||||||
if opts.IPProtocol == "" {
|
if opts.IPProtocol == "" {
|
||||||
|
@ -155,6 +155,25 @@ func HandleAssociateSuccessfully(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleFixedAssociateSucessfully configures the test server to respond to a Post request
|
||||||
|
// to associate an allocated floating IP with a specific fixed IP address
|
||||||
|
func HandleAssociateFixedSuccessfully(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "POST")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||||
|
th.TestJSONRequest(t, r, `
|
||||||
|
{
|
||||||
|
"addFloatingIp": {
|
||||||
|
"address": "10.10.10.2",
|
||||||
|
"fixed_address": "166.78.185.201"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// HandleDisassociateSuccessfully configures the test server to respond to a Post request
|
// HandleDisassociateSuccessfully configures the test server to respond to a Post request
|
||||||
// to disassociate an allocated floating IP
|
// to disassociate an allocated floating IP
|
||||||
func HandleDisassociateSuccessfully(t *testing.T) {
|
func HandleDisassociateSuccessfully(t *testing.T) {
|
||||||
|
@ -26,6 +26,18 @@ type CreateOpts struct {
|
|||||||
Pool string
|
Pool string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance
|
||||||
|
type AssociateOpts struct {
|
||||||
|
// ServerID is the UUID of the server
|
||||||
|
ServerID string
|
||||||
|
|
||||||
|
// FixedIP is an optional fixed IP address of the server
|
||||||
|
FixedIP string
|
||||||
|
|
||||||
|
// FloatingIP is the floating IP to associate with an instance
|
||||||
|
FloatingIP string
|
||||||
|
}
|
||||||
|
|
||||||
// ToFloatingIPCreateMap constructs a request body from CreateOpts.
|
// ToFloatingIPCreateMap constructs a request body from CreateOpts.
|
||||||
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
||||||
if opts.Pool == "" {
|
if opts.Pool == "" {
|
||||||
@ -35,6 +47,26 @@ func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
|||||||
return map[string]interface{}{"pool": opts.Pool}, nil
|
return map[string]interface{}{"pool": opts.Pool}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToAssociateMap constructs a request body from AssociateOpts.
|
||||||
|
func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
|
||||||
|
if opts.ServerID == "" {
|
||||||
|
return nil, errors.New("Required field missing for floating IP association: ServerID")
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.FloatingIP == "" {
|
||||||
|
return nil, errors.New("Required field missing for floating IP association: FloatingIP")
|
||||||
|
}
|
||||||
|
|
||||||
|
associateInfo := map[string]interface{}{
|
||||||
|
"serverId": opts.ServerID,
|
||||||
|
"floatingIp": opts.FloatingIP,
|
||||||
|
"fixedIp": opts.FixedIP,
|
||||||
|
}
|
||||||
|
|
||||||
|
return associateInfo, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Create requests the creation of a new floating IP
|
// Create requests the creation of a new floating IP
|
||||||
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||||
var res CreateResult
|
var res CreateResult
|
||||||
@ -68,6 +100,7 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
|||||||
// association / disassociation
|
// association / disassociation
|
||||||
|
|
||||||
// Associate pairs an allocated floating IP with an instance
|
// Associate pairs an allocated floating IP with an instance
|
||||||
|
// Deprecated. Use AssociateInstance.
|
||||||
func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
|
func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
|
||||||
var res AssociateResult
|
var res AssociateResult
|
||||||
|
|
||||||
@ -79,7 +112,33 @@ func Associate(client *gophercloud.ServiceClient, serverId, fip string) Associat
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AssociateInstance pairs an allocated floating IP with an instance.
|
||||||
|
func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult {
|
||||||
|
var res AssociateResult
|
||||||
|
|
||||||
|
associateInfo, err := opts.ToAssociateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
addFloatingIp := make(map[string]interface{})
|
||||||
|
addFloatingIp["address"] = associateInfo["floatingIp"].(string)
|
||||||
|
|
||||||
|
// fixedIp is not required
|
||||||
|
if associateInfo["fixedIp"] != "" {
|
||||||
|
addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverId := associateInfo["serverId"].(string)
|
||||||
|
|
||||||
|
reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
|
||||||
|
_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// Disassociate decouples an allocated floating IP from an instance
|
// Disassociate decouples an allocated floating IP from an instance
|
||||||
|
// Deprecated. Use DisassociateInstance.
|
||||||
func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
|
func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
|
||||||
var res DisassociateResult
|
var res DisassociateResult
|
||||||
|
|
||||||
@ -90,3 +149,23 @@ func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) Disas
|
|||||||
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
|
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DisassociateInstance decouples an allocated floating IP from an instance
|
||||||
|
func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult {
|
||||||
|
var res DisassociateResult
|
||||||
|
|
||||||
|
associateInfo, err := opts.ToAssociateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
removeFloatingIp := make(map[string]interface{})
|
||||||
|
removeFloatingIp["address"] = associateInfo["floatingIp"].(string)
|
||||||
|
reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}
|
||||||
|
|
||||||
|
serverId := associateInfo["serverId"].(string)
|
||||||
|
|
||||||
|
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package quotasets provides information and interaction with QuotaSet
|
||||||
|
// extension for the OpenStack Compute service.
|
||||||
|
package quotasets
|
59
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/fixtures.go
generated
vendored
Normal file
59
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// +build fixtures
|
||||||
|
|
||||||
|
package quotasets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetOutput is a sample response to a Get call.
|
||||||
|
const GetOutput = `
|
||||||
|
{
|
||||||
|
"quota_set" : {
|
||||||
|
"instances" : 25,
|
||||||
|
"security_groups" : 10,
|
||||||
|
"security_group_rules" : 20,
|
||||||
|
"cores" : 200,
|
||||||
|
"injected_file_content_bytes" : 10240,
|
||||||
|
"injected_files" : 5,
|
||||||
|
"metadata_items" : 128,
|
||||||
|
"ram" : 200000,
|
||||||
|
"keypairs" : 10,
|
||||||
|
"injected_file_path_bytes" : 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const FirstTenantID = "555544443333222211110000ffffeeee"
|
||||||
|
|
||||||
|
// FirstQuotaSet is the first result in ListOutput.
|
||||||
|
var FirstQuotaSet = QuotaSet{
|
||||||
|
FixedIps: 0,
|
||||||
|
FloatingIps: 0,
|
||||||
|
InjectedFileContentBytes: 10240,
|
||||||
|
InjectedFilePathBytes: 255,
|
||||||
|
InjectedFiles: 5,
|
||||||
|
KeyPairs: 10,
|
||||||
|
MetadataItems: 128,
|
||||||
|
Ram: 200000,
|
||||||
|
SecurityGroupRules: 20,
|
||||||
|
SecurityGroups: 10,
|
||||||
|
Cores: 200,
|
||||||
|
Instances: 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleGetSuccessfully configures the test server to respond to a Get request for sample tenant
|
||||||
|
func HandleGetSuccessfully(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/os-quota-sets/"+FirstTenantID, 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)
|
||||||
|
})
|
||||||
|
}
|
12
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/requests.go
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/requests.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package quotasets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get returns public data about a previously created QuotaSet.
|
||||||
|
func Get(client *gophercloud.ServiceClient, tenantID string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
_, res.Err = client.Get(getURL(client, tenantID), &res.Body, nil)
|
||||||
|
return res
|
||||||
|
}
|
86
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/results.go
generated
vendored
Normal file
86
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/results.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package quotasets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QuotaSet is a set of operational limits that allow for control of compute usage.
|
||||||
|
type QuotaSet struct {
|
||||||
|
//ID is tenant associated with this quota_set
|
||||||
|
ID string `mapstructure:"id"`
|
||||||
|
//FixedIps is number of fixed ips alloted this quota_set
|
||||||
|
FixedIps int `mapstructure:"fixed_ips"`
|
||||||
|
// FloatingIps is number of floating ips alloted this quota_set
|
||||||
|
FloatingIps int `mapstructure:"floating_ips"`
|
||||||
|
// InjectedFileContentBytes is content bytes allowed for each injected file
|
||||||
|
InjectedFileContentBytes int `mapstructure:"injected_file_content_bytes"`
|
||||||
|
// InjectedFilePathBytes is allowed bytes for each injected file path
|
||||||
|
InjectedFilePathBytes int `mapstructure:"injected_file_path_bytes"`
|
||||||
|
// InjectedFiles is injected files allowed for each project
|
||||||
|
InjectedFiles int `mapstructure:"injected_files"`
|
||||||
|
// KeyPairs is number of ssh keypairs
|
||||||
|
KeyPairs int `mapstructure:"keypairs"`
|
||||||
|
// MetadataItems is number of metadata items allowed for each instance
|
||||||
|
MetadataItems int `mapstructure:"metadata_items"`
|
||||||
|
// Ram is megabytes allowed for each instance
|
||||||
|
Ram int `mapstructure:"ram"`
|
||||||
|
// SecurityGroupRules is rules allowed for each security group
|
||||||
|
SecurityGroupRules int `mapstructure:"security_group_rules"`
|
||||||
|
// SecurityGroups security groups allowed for each project
|
||||||
|
SecurityGroups int `mapstructure:"security_groups"`
|
||||||
|
// Cores is number of instance cores allowed for each project
|
||||||
|
Cores int `mapstructure:"cores"`
|
||||||
|
// Instances is number of instances allowed for each project
|
||||||
|
Instances int `mapstructure:"instances"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuotaSetPage stores a single, only page of QuotaSet results from a List call.
|
||||||
|
type QuotaSetPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a QuotaSetsetPage is empty.
|
||||||
|
func (page QuotaSetPage) IsEmpty() (bool, error) {
|
||||||
|
ks, err := ExtractQuotaSets(page)
|
||||||
|
return len(ks) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractQuotaSets interprets a page of results as a slice of QuotaSets.
|
||||||
|
func ExtractQuotaSets(page pagination.Page) ([]QuotaSet, error) {
|
||||||
|
var resp struct {
|
||||||
|
QuotaSets []QuotaSet `mapstructure:"quotas"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(page.(QuotaSetPage).Body, &resp)
|
||||||
|
results := make([]QuotaSet, len(resp.QuotaSets))
|
||||||
|
for i, q := range resp.QuotaSets {
|
||||||
|
results[i] = q
|
||||||
|
}
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type quotaResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract is a method that attempts to interpret any QuotaSet resource response as a QuotaSet struct.
|
||||||
|
func (r quotaResult) Extract() (*QuotaSet, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res struct {
|
||||||
|
QuotaSet *QuotaSet `json:"quota_set" mapstructure:"quota_set"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &res)
|
||||||
|
return res.QuotaSet, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||||
|
// as a QuotaSet.
|
||||||
|
type GetResult struct {
|
||||||
|
quotaResult
|
||||||
|
}
|
13
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/urls.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/quotasets/urls.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package quotasets
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
const resourcePath = "os-quota-sets"
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL(resourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return c.ServiceURL(resourcePath, tenantID)
|
||||||
|
}
|
@ -216,6 +216,42 @@ func mockAddRuleResponse(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mockAddRuleResponseICMPZero(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "POST")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
|
||||||
|
th.TestJSONRequest(t, r, `
|
||||||
|
{
|
||||||
|
"security_group_rule": {
|
||||||
|
"from_port": 0,
|
||||||
|
"ip_protocol": "ICMP",
|
||||||
|
"to_port": 0,
|
||||||
|
"parent_group_id": "{groupID}",
|
||||||
|
"cidr": "0.0.0.0/0"
|
||||||
|
}
|
||||||
|
} `)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"security_group_rule": {
|
||||||
|
"from_port": 0,
|
||||||
|
"group": {},
|
||||||
|
"ip_protocol": "ICMP",
|
||||||
|
"to_port": 0,
|
||||||
|
"parent_group_id": "{groupID}",
|
||||||
|
"ip_range": {
|
||||||
|
"cidr": "0.0.0.0/0"
|
||||||
|
},
|
||||||
|
"id": "{ruleID}"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
|
func mockDeleteRuleResponse(t *testing.T, ruleID string) {
|
||||||
url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
|
url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
|
||||||
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -2,6 +2,7 @@ package secgroups
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
@ -181,10 +182,10 @@ func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
|||||||
if opts.ParentGroupID == "" {
|
if opts.ParentGroupID == "" {
|
||||||
return rule, errors.New("A ParentGroupID must be set")
|
return rule, errors.New("A ParentGroupID must be set")
|
||||||
}
|
}
|
||||||
if opts.FromPort == 0 {
|
if opts.FromPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||||
return rule, errors.New("A FromPort must be set")
|
return rule, errors.New("A FromPort must be set")
|
||||||
}
|
}
|
||||||
if opts.ToPort == 0 {
|
if opts.ToPort == 0 && strings.ToUpper(opts.IPProtocol) != "ICMP" {
|
||||||
return rule, errors.New("A ToPort must be set")
|
return rule, errors.New("A ToPort must be set")
|
||||||
}
|
}
|
||||||
if opts.IPProtocol == "" {
|
if opts.IPProtocol == "" {
|
||||||
|
@ -235,6 +235,12 @@ const SingleServerBody = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ServerPasswordBody = `
|
||||||
|
{
|
||||||
|
"password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg=="
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ServerHerp is a Server struct that should correspond to the first result in ServerListBody.
|
// ServerHerp is a Server struct that should correspond to the first result in ServerListBody.
|
||||||
ServerHerp = Server{
|
ServerHerp = Server{
|
||||||
@ -399,6 +405,18 @@ func HandleServerDeletionSuccessfully(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion
|
||||||
|
// request.
|
||||||
|
func HandleServerForceDeletionSuccessfully(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "POST")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||||
|
th.TestJSONRequest(t, r, `{ "forceDelete": "" }`)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// HandleServerGetSuccessfully sets up the test server to respond to a server Get request.
|
// HandleServerGetSuccessfully sets up the test server to respond to a server Get request.
|
||||||
func HandleServerGetSuccessfully(t *testing.T) {
|
func HandleServerGetSuccessfully(t *testing.T) {
|
||||||
th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -662,3 +680,13 @@ func HandleCreateServerImageSuccessfully(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request.
|
||||||
|
func HandlePasswordGetSuccessfully(t *testing.T) {
|
||||||
|
th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
|
||||||
|
th.TestHeader(t, r, "Accept", "application/json")
|
||||||
|
|
||||||
|
fmt.Fprintf(w, ServerPasswordBody)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -303,6 +303,17 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ForceDelete(client *gophercloud.ServiceClient, id string) ActionResult {
|
||||||
|
var req struct {
|
||||||
|
ForceDelete string `json:"forceDelete"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var res ActionResult
|
||||||
|
_, res.Err = client.Post(actionURL(client, id), req, nil, nil)
|
||||||
|
return res
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Get requests details on a single server, by ID.
|
// Get requests details on a single server, by ID.
|
||||||
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
var result GetResult
|
var result GetResult
|
||||||
@ -850,3 +861,12 @@ func IDFromName(client *gophercloud.ServiceClient, name string) (string, error)
|
|||||||
return "", fmt.Errorf("Found %d servers matching %s", serverCount, name)
|
return "", fmt.Errorf("Found %d servers matching %s", serverCount, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPassword makes a request against the nova API to get the encrypted administrative password.
|
||||||
|
func GetPassword(client *gophercloud.ServiceClient, serverId string) GetPasswordResult {
|
||||||
|
var res GetPasswordResult
|
||||||
|
_, res.Err = client.Request("GET", passwordURL(client, serverId), gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package servers
|
package servers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"crypto/rsa"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
@ -82,6 +84,47 @@ type CreateImageResult struct {
|
|||||||
gophercloud.Result
|
gophercloud.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPasswordResult represent the result of a get os-server-password operation.
|
||||||
|
type GetPasswordResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractPassword gets the encrypted password.
|
||||||
|
// If privateKey != nil the password is decrypted with the private key.
|
||||||
|
// If privateKey == nil the encrypted password is returned and can be decrypted with:
|
||||||
|
// echo '<pwd>' | base64 -D | openssl rsautl -decrypt -inkey <private_key>
|
||||||
|
func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) {
|
||||||
|
|
||||||
|
if r.Err != nil {
|
||||||
|
return "", r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
if err == nil && privateKey != nil && response.Password != "" {
|
||||||
|
return decryptPassword(response.Password, privateKey)
|
||||||
|
}
|
||||||
|
return response.Password, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) {
|
||||||
|
b64EncryptedPassword := make([]byte, base64.StdEncoding.DecodedLen(len(encryptedPassword)))
|
||||||
|
|
||||||
|
n, err := base64.StdEncoding.Decode(b64EncryptedPassword, []byte(encryptedPassword))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to base64 decode encrypted password: %s", err)
|
||||||
|
}
|
||||||
|
password, err := rsa.DecryptPKCS1v15(nil, privateKey, b64EncryptedPassword[0:n])
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to decrypt password: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(password), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractImageID gets the ID of the newly created server image from the header
|
// ExtractImageID gets the ID of the newly created server image from the header
|
||||||
func (res CreateImageResult) ExtractImageID() (string, error) {
|
func (res CreateImageResult) ExtractImageID() (string, error) {
|
||||||
if res.Err != nil {
|
if res.Err != nil {
|
||||||
|
@ -45,3 +45,7 @@ func listAddressesURL(client *gophercloud.ServiceClient, id string) string {
|
|||||||
func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string {
|
func listAddressesByNetworkURL(client *gophercloud.ServiceClient, id, network string) string {
|
||||||
return client.ServiceURL("servers", id, "ips", network)
|
return client.ServiceURL("servers", id, "ips", network)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func passwordURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("servers", id, "os-server-password")
|
||||||
|
}
|
||||||
|
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/doc.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/doc.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Package configurations provides information and interaction with the
|
||||||
|
// configuration API resource in the Rackspace Database service.
|
||||||
|
//
|
||||||
|
// A configuration group is a collection of key/value pairs which define how a
|
||||||
|
// particular database operates. These key/value pairs are specific to each
|
||||||
|
// datastore type and serve like settings. Some directives are capable of being
|
||||||
|
// applied dynamically, while other directives require a server restart to take
|
||||||
|
// effect. The configuration group can be applied to an instance at creation or
|
||||||
|
// applied to an existing instance to modify the behavior of the running
|
||||||
|
// datastore on the instance.
|
||||||
|
package configurations
|
157
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/fixtures.go
generated
vendored
Normal file
157
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package configurations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timestamp = "2015-11-12T14:22:42Z"
|
||||||
|
timeVal, _ = time.Parse(time.RFC3339, timestamp)
|
||||||
|
)
|
||||||
|
|
||||||
|
var singleConfigJSON = `
|
||||||
|
{
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"datastore_name": "mysql",
|
||||||
|
"datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"datastore_version_name": "5.6",
|
||||||
|
"description": "example_description",
|
||||||
|
"id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
"name": "example-configuration-name",
|
||||||
|
"updated": "` + timestamp + `"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var singleConfigWithValuesJSON = `
|
||||||
|
{
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"datastore_name": "mysql",
|
||||||
|
"datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"datastore_version_name": "5.6",
|
||||||
|
"description": "example description",
|
||||||
|
"id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
"instance_count": 0,
|
||||||
|
"name": "example-configuration-name",
|
||||||
|
"updated": "` + timestamp + `",
|
||||||
|
"values": {
|
||||||
|
"collation_server": "latin1_swedish_ci",
|
||||||
|
"connect_timeout": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
ListConfigsJSON = fmt.Sprintf(`{"configurations": [%s]}`, singleConfigJSON)
|
||||||
|
GetConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigJSON)
|
||||||
|
CreateConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigWithValuesJSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
var CreateReq = `
|
||||||
|
{
|
||||||
|
"configuration": {
|
||||||
|
"datastore": {
|
||||||
|
"type": "a00000a0-00a0-0a00-00a0-000a000000aa",
|
||||||
|
"version": "b00000b0-00b0-0b00-00b0-000b000000bb"
|
||||||
|
},
|
||||||
|
"description": "example description",
|
||||||
|
"name": "example-configuration-name",
|
||||||
|
"values": {
|
||||||
|
"collation_server": "latin1_swedish_ci",
|
||||||
|
"connect_timeout": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var UpdateReq = `
|
||||||
|
{
|
||||||
|
"configuration": {
|
||||||
|
"values": {
|
||||||
|
"connect_timeout": 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var ListInstancesJSON = `
|
||||||
|
{
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
|
||||||
|
"name": "json_rack_instance"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var ListParamsJSON = `
|
||||||
|
{
|
||||||
|
"configuration-parameters": [
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"name": "innodb_file_per_table",
|
||||||
|
"restart_required": true,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4294967296,
|
||||||
|
"min": 0,
|
||||||
|
"name": "key_buffer_size",
|
||||||
|
"restart_required": false,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 65535,
|
||||||
|
"min": 2,
|
||||||
|
"name": "connect_timeout",
|
||||||
|
"restart_required": false,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4294967296,
|
||||||
|
"min": 0,
|
||||||
|
"name": "join_buffer_size",
|
||||||
|
"restart_required": false,
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var GetParamJSON = `
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"name": "innodb_file_per_table",
|
||||||
|
"restart_required": true,
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var ExampleConfig = Config{
|
||||||
|
Created: timeVal,
|
||||||
|
DatastoreName: "mysql",
|
||||||
|
DatastoreVersionID: "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
DatastoreVersionName: "5.6",
|
||||||
|
Description: "example_description",
|
||||||
|
ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
Name: "example-configuration-name",
|
||||||
|
Updated: timeVal,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ExampleConfigWithValues = Config{
|
||||||
|
Created: timeVal,
|
||||||
|
DatastoreName: "mysql",
|
||||||
|
DatastoreVersionID: "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
DatastoreVersionName: "5.6",
|
||||||
|
Description: "example description",
|
||||||
|
ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
Name: "example-configuration-name",
|
||||||
|
Updated: timeVal,
|
||||||
|
Values: map[string]interface{}{
|
||||||
|
"collation_server": "latin1_swedish_ci",
|
||||||
|
"connect_timeout": 120,
|
||||||
|
},
|
||||||
|
}
|
287
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/requests.go
generated
vendored
Normal file
287
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/requests.go
generated
vendored
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
package configurations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/instances"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List will list all of the available configurations.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return ConfigPage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, baseURL(client), pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder is a top-level interface which renders a JSON map.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToConfigCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatastoreOpts is the primary options struct for creating and modifying
|
||||||
|
// how configuration resources are associated with datastores.
|
||||||
|
type DatastoreOpts struct {
|
||||||
|
// [OPTIONAL] The type of datastore. Defaults to "MySQL".
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// [OPTIONAL] The specific version of a datastore. Defaults to "5.6".
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMap renders a JSON map for a datastore setting.
|
||||||
|
func (opts DatastoreOpts) ToMap() (map[string]string, error) {
|
||||||
|
datastore := map[string]string{}
|
||||||
|
|
||||||
|
if opts.Type != "" {
|
||||||
|
datastore["type"] = opts.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Version != "" {
|
||||||
|
datastore["version"] = opts.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
return datastore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts is the struct responsible for configuring new configurations.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// [REQUIRED] The configuration group name
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// [REQUIRED] A map of user-defined configuration settings that will define
|
||||||
|
// how each associated datastore works. Each key/value pair is specific to a
|
||||||
|
// datastore type.
|
||||||
|
Values map[string]interface{}
|
||||||
|
|
||||||
|
// [OPTIONAL] Associates the configuration group with a particular datastore.
|
||||||
|
Datastore *DatastoreOpts
|
||||||
|
|
||||||
|
// [OPTIONAL] A human-readable explanation for the group.
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToConfigCreateMap casts a CreateOpts struct into a JSON map.
|
||||||
|
func (opts CreateOpts) ToConfigCreateMap() (map[string]interface{}, error) {
|
||||||
|
if opts.Name == "" {
|
||||||
|
return nil, errors.New("Name is a required field")
|
||||||
|
}
|
||||||
|
if len(opts.Values) == 0 {
|
||||||
|
return nil, errors.New("Values must be a populated map")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"name": opts.Name,
|
||||||
|
"values": opts.Values,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Datastore != nil {
|
||||||
|
ds, err := opts.Datastore.ToMap()
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
config["datastore"] = ds
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Description != "" {
|
||||||
|
config["description"] = opts.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"configuration": config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new configuration group.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||||
|
var res CreateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToConfigCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will retrieve the details for a specified configuration group.
|
||||||
|
func Get(client *gophercloud.ServiceClient, configID string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", resourceURL(client, configID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder is the top-level interface for casting update options into
|
||||||
|
// JSON maps.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToConfigUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts is the struct responsible for modifying existing configurations.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
// [OPTIONAL] The configuration group name
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// [OPTIONAL] A map of user-defined configuration settings that will define
|
||||||
|
// how each associated datastore works. Each key/value pair is specific to a
|
||||||
|
// datastore type.
|
||||||
|
Values map[string]interface{}
|
||||||
|
|
||||||
|
// [OPTIONAL] Associates the configuration group with a particular datastore.
|
||||||
|
Datastore *DatastoreOpts
|
||||||
|
|
||||||
|
// [OPTIONAL] A human-readable explanation for the group.
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToConfigUpdateMap will cast an UpdateOpts struct into a JSON map.
|
||||||
|
func (opts UpdateOpts) ToConfigUpdateMap() (map[string]interface{}, error) {
|
||||||
|
config := map[string]interface{}{}
|
||||||
|
|
||||||
|
if opts.Name != "" {
|
||||||
|
config["name"] = opts.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Description != "" {
|
||||||
|
config["description"] = opts.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Datastore != nil {
|
||||||
|
ds, err := opts.Datastore.ToMap()
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
config["datastore"] = ds
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Values) > 0 {
|
||||||
|
config["values"] = opts.Values
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"configuration": config}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will modify an existing configuration group by performing a merge
|
||||||
|
// between new and existing values. If the key already exists, the new value
|
||||||
|
// will overwrite. All other keys will remain unaffected.
|
||||||
|
func Update(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) UpdateResult {
|
||||||
|
var res UpdateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToConfigUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("PATCH", resourceURL(client, configID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace will modify an existing configuration group by overwriting the
|
||||||
|
// entire parameter group with the new values provided. Any existing keys not
|
||||||
|
// included in UpdateOptsBuilder will be deleted.
|
||||||
|
func Replace(client *gophercloud.ServiceClient, configID string, opts UpdateOptsBuilder) ReplaceResult {
|
||||||
|
var res ReplaceResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToConfigUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("PUT", resourceURL(client, configID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will permanently delete a configuration group. Please note that
|
||||||
|
// config groups cannot be deleted whilst still attached to running instances -
|
||||||
|
// you must detach and then delete them.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, configID string) DeleteResult {
|
||||||
|
var res DeleteResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("DELETE", resourceURL(client, configID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListInstances will list all the instances associated with a particular
|
||||||
|
// configuration group.
|
||||||
|
func ListInstances(client *gophercloud.ServiceClient, configID string) pagination.Pager {
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return instances.InstancePage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, instancesURL(client, configID), pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDatastoreParams will list all the available and supported parameters
|
||||||
|
// that can be used for a particular datastore ID and a particular version.
|
||||||
|
// For example, if you are wondering how you can configure a MySQL 5.6 instance,
|
||||||
|
// you can use this operation (you will need to retrieve the MySQL datastore ID
|
||||||
|
// by using the datastores API).
|
||||||
|
func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, versionID string) pagination.Pager {
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return ParamPage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, listDSParamsURL(client, datastoreID, versionID), pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDatastoreParam will retrieve information about a specific configuration
|
||||||
|
// parameter. For example, you can use this operation to understand more about
|
||||||
|
// "innodb_file_per_table" configuration param for MySQL datastores. You will
|
||||||
|
// need the param's ID first, which can be attained by using the ListDatastoreParams
|
||||||
|
// operation.
|
||||||
|
func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) ParamResult {
|
||||||
|
var res ParamResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", getDSParamURL(client, datastoreID, versionID, paramID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGlobalParams is similar to ListDatastoreParams but does not require a
|
||||||
|
// DatastoreID.
|
||||||
|
func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagination.Pager {
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return ParamPage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, listGlobalParamsURL(client, versionID), pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalParam is similar to GetDatastoreParam but does not require a
|
||||||
|
// DatastoreID.
|
||||||
|
func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) ParamResult {
|
||||||
|
var res ParamResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", getGlobalParamURL(client, versionID, paramID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
197
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/results.go
generated
vendored
Normal file
197
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/results.go
generated
vendored
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package configurations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config represents a configuration group API resource.
|
||||||
|
type Config struct {
|
||||||
|
Created time.Time `mapstructure:"-"`
|
||||||
|
Updated time.Time `mapstructure:"-"`
|
||||||
|
DatastoreName string `mapstructure:"datastore_name"`
|
||||||
|
DatastoreVersionID string `mapstructure:"datastore_version_id"`
|
||||||
|
DatastoreVersionName string `mapstructure:"datastore_version_name"`
|
||||||
|
Description string
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Values map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigPage contains a page of Config resources in a paginated collection.
|
||||||
|
type ConfigPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty indicates whether a ConfigPage is empty.
|
||||||
|
func (r ConfigPage) IsEmpty() (bool, error) {
|
||||||
|
is, err := ExtractConfigs(r)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(is) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractConfigs will retrieve a slice of Config structs from a page.
|
||||||
|
func ExtractConfigs(page pagination.Page) ([]Config, error) {
|
||||||
|
casted := page.(ConfigPage).Body
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Configs []Config `mapstructure:"configurations" json:"configurations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mapstructure.Decode(casted, &resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vals []interface{}
|
||||||
|
switch casted.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
vals = casted.(map[string]interface{})["configurations"].([]interface{})
|
||||||
|
case map[string][]interface{}:
|
||||||
|
vals = casted.(map[string][]interface{})["configurations"]
|
||||||
|
default:
|
||||||
|
return resp.Configs, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range vals {
|
||||||
|
val := v.(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := val["created"].(string); ok && t != "" {
|
||||||
|
creationTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return resp.Configs, err
|
||||||
|
}
|
||||||
|
resp.Configs[i].Created = creationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := val["updated"].(string); ok && t != "" {
|
||||||
|
updatedTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return resp.Configs, err
|
||||||
|
}
|
||||||
|
resp.Configs[i].Updated = updatedTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Configs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will retrieve a Config resource from an operation result.
|
||||||
|
func (r commonResult) Extract() (*Config, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Config Config `mapstructure:"configuration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
val := r.Body.(map[string]interface{})["configuration"].(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := val["created"].(string); ok && t != "" {
|
||||||
|
creationTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return &response.Config, err
|
||||||
|
}
|
||||||
|
response.Config.Created = creationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := val["updated"].(string); ok && t != "" {
|
||||||
|
updatedTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return &response.Config, err
|
||||||
|
}
|
||||||
|
response.Config.Updated = updatedTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.Config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult represents the result of a Get operation.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult represents the result of a Create operation.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult represents the result of an Update operation.
|
||||||
|
type UpdateResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceResult represents the result of a Replace operation.
|
||||||
|
type ReplaceResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult represents the result of a Delete operation.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Param represents a configuration parameter API resource.
|
||||||
|
type Param struct {
|
||||||
|
Max int
|
||||||
|
Min int
|
||||||
|
Name string
|
||||||
|
RestartRequired bool `mapstructure:"restart_required" json:"restart_required"`
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamPage contains a page of Param resources in a paginated collection.
|
||||||
|
type ParamPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty indicates whether a ParamPage is empty.
|
||||||
|
func (r ParamPage) IsEmpty() (bool, error) {
|
||||||
|
is, err := ExtractParams(r)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(is) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractParams will retrieve a slice of Param structs from a page.
|
||||||
|
func ExtractParams(page pagination.Page) ([]Param, error) {
|
||||||
|
casted := page.(ParamPage).Body
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Params []Param `mapstructure:"configuration-parameters" json:"configuration-parameters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(casted, &resp)
|
||||||
|
return resp.Params, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamResult represents the result of an operation which retrieves details
|
||||||
|
// about a particular configuration param.
|
||||||
|
type ParamResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will retrieve a param from an operation result.
|
||||||
|
func (r ParamResult) Extract() (*Param, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var param Param
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, ¶m)
|
||||||
|
return ¶m, err
|
||||||
|
}
|
31
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/urls.go
generated
vendored
Normal file
31
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/configurations/urls.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package configurations
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func baseURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("configurations")
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient, configID string) string {
|
||||||
|
return c.ServiceURL("configurations", configID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func instancesURL(c *gophercloud.ServiceClient, configID string) string {
|
||||||
|
return c.ServiceURL("configurations", configID, "instances")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listDSParamsURL(c *gophercloud.ServiceClient, datastoreID, versionID string) string {
|
||||||
|
return c.ServiceURL("datastores", datastoreID, "versions", versionID, "parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDSParamURL(c *gophercloud.ServiceClient, datastoreID, versionID, paramID string) string {
|
||||||
|
return c.ServiceURL("datastores", datastoreID, "versions", versionID, "parameters", paramID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listGlobalParamsURL(c *gophercloud.ServiceClient, versionID string) string {
|
||||||
|
return c.ServiceURL("datastores", "versions", versionID, "parameters")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGlobalParamURL(c *gophercloud.ServiceClient, versionID, paramID string) string {
|
||||||
|
return c.ServiceURL("datastores", "versions", versionID, "parameters", paramID)
|
||||||
|
}
|
6
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/doc.go
generated
vendored
Normal file
6
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Package flavors provides information and interaction with the database API
|
||||||
|
// resource in the OpenStack Database service.
|
||||||
|
//
|
||||||
|
// A database, when referred to here, refers to the database engine running on
|
||||||
|
// an instance.
|
||||||
|
package databases
|
61
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/fixtures.go
generated
vendored
Normal file
61
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package databases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/fixture"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
instanceID = "{instanceID}"
|
||||||
|
resURL = "/instances/" + instanceID + "/databases"
|
||||||
|
)
|
||||||
|
|
||||||
|
var createDBsReq = `
|
||||||
|
{
|
||||||
|
"databases": [
|
||||||
|
{
|
||||||
|
"character_set": "utf8",
|
||||||
|
"collate": "utf8_general_ci",
|
||||||
|
"name": "testingdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sampledb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var listDBsResp = `
|
||||||
|
{
|
||||||
|
"databases": [
|
||||||
|
{
|
||||||
|
"name": "anotherexampledb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "exampledb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nextround"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sampledb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "testingdb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
func HandleCreate(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, resURL, "POST", createDBsReq, "", 202)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleList(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, resURL, "GET", "", listDBsResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleDelete(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, resURL+"/{dbName}", "DELETE", "", "", 202)
|
||||||
|
}
|
115
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/requests.go
generated
vendored
Normal file
115
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/requests.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package databases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder builds create options
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToDBCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatabaseOpts is the struct responsible for configuring a database; often in
|
||||||
|
// the context of an instance.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// [REQUIRED] Specifies the name of the database. Valid names can be composed
|
||||||
|
// of the following characters: letters (either case); numbers; these
|
||||||
|
// characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is
|
||||||
|
// permitted anywhere. Prohibited characters that are forbidden include:
|
||||||
|
// single quotes, double quotes, back quotes, semicolons, commas, backslashes,
|
||||||
|
// and forward slashes.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// [OPTIONAL] Set of symbols and encodings. The default character set is
|
||||||
|
// "utf8". See http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html for
|
||||||
|
// supported character sets.
|
||||||
|
CharSet string
|
||||||
|
|
||||||
|
// [OPTIONAL] Set of rules for comparing characters in a character set. The
|
||||||
|
// default value for collate is "utf8_general_ci". See
|
||||||
|
// http://dev.mysql.com/doc/refman/5.1/en/charset-mysql.html for supported
|
||||||
|
// collations.
|
||||||
|
Collate string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMap is a helper function to convert individual DB create opt structures
|
||||||
|
// into sub-maps.
|
||||||
|
func (opts CreateOpts) ToMap() (map[string]string, error) {
|
||||||
|
if opts.Name == "" {
|
||||||
|
return nil, fmt.Errorf("Name is a required field")
|
||||||
|
}
|
||||||
|
if len(opts.Name) > 64 {
|
||||||
|
return nil, fmt.Errorf("Name must be less than 64 chars long")
|
||||||
|
}
|
||||||
|
|
||||||
|
db := map[string]string{"name": opts.Name}
|
||||||
|
|
||||||
|
if opts.CharSet != "" {
|
||||||
|
db["character_set"] = opts.CharSet
|
||||||
|
}
|
||||||
|
if opts.Collate != "" {
|
||||||
|
db["collate"] = opts.Collate
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchCreateOpts allows for multiple databases to created and modified.
|
||||||
|
type BatchCreateOpts []CreateOpts
|
||||||
|
|
||||||
|
// ToDBCreateMap renders a JSON map for creating DBs.
|
||||||
|
func (opts BatchCreateOpts) ToDBCreateMap() (map[string]interface{}, error) {
|
||||||
|
dbs := make([]map[string]string, len(opts))
|
||||||
|
for i, db := range opts {
|
||||||
|
dbMap, err := db.ToMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbs[i] = dbMap
|
||||||
|
}
|
||||||
|
return map[string]interface{}{"databases": dbs}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new database within the specified instance. If the
|
||||||
|
// specified instance does not exist, a 404 error will be returned.
|
||||||
|
func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) CreateResult {
|
||||||
|
var res CreateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToDBCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", baseURL(client, instanceID), gophercloud.RequestOpts{
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// List will list all of the databases for a specified instance. Note: this
|
||||||
|
// operation will only return user-defined databases; it will exclude system
|
||||||
|
// databases like "mysql", "information_schema", "lost+found" etc.
|
||||||
|
func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
|
||||||
|
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return DBPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, baseURL(client, instanceID), createPageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will permanently delete the database within a specified instance.
|
||||||
|
// All contained data inside the database will also be permanently deleted.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) DeleteResult {
|
||||||
|
var res DeleteResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("DELETE", dbURL(client, instanceID, dbName), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
72
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/results.go
generated
vendored
Normal file
72
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/results.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package databases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Database represents a Database API resource.
|
||||||
|
type Database struct {
|
||||||
|
// Specifies the name of the MySQL database.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Set of symbols and encodings. The default character set is utf8.
|
||||||
|
CharSet string
|
||||||
|
|
||||||
|
// Set of rules for comparing characters in a character set. The default
|
||||||
|
// value for collate is utf8_general_ci.
|
||||||
|
Collate string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult represents the result of a Create operation.
|
||||||
|
type CreateResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult represents the result of a Delete operation.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBPage represents a single page of a paginated DB collection.
|
||||||
|
type DBPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks to see whether the collection is empty.
|
||||||
|
func (page DBPage) IsEmpty() (bool, error) {
|
||||||
|
dbs, err := ExtractDBs(page)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(dbs) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL will retrieve the next page URL.
|
||||||
|
func (page DBPage) NextPageURL() (string, error) {
|
||||||
|
type resp struct {
|
||||||
|
Links []gophercloud.Link `mapstructure:"databases_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var r resp
|
||||||
|
err := mapstructure.Decode(page.Body, &r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gophercloud.ExtractNextURL(r.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractDBs will convert a generic pagination struct into a more
|
||||||
|
// relevant slice of DB structs.
|
||||||
|
func ExtractDBs(page pagination.Page) ([]Database, error) {
|
||||||
|
casted := page.(DBPage).Body
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Databases []Database `mapstructure:"databases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(casted, &response)
|
||||||
|
return response.Databases, err
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/urls.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/databases/urls.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package databases
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func baseURL(c *gophercloud.ServiceClient, instanceID string) string {
|
||||||
|
return c.ServiceURL("instances", instanceID, "databases")
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbURL(c *gophercloud.ServiceClient, instanceID, dbName string) string {
|
||||||
|
return c.ServiceURL("instances", instanceID, "databases", dbName)
|
||||||
|
}
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package datastores provides information and interaction with the datastore
|
||||||
|
// API resource in the Rackspace Database service.
|
||||||
|
package datastores
|
100
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/fixtures.go
generated
vendored
Normal file
100
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package datastores
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
const version1JSON = `
|
||||||
|
{
|
||||||
|
"id": "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "5.1"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const version2JSON = `
|
||||||
|
{
|
||||||
|
"id": "c00000b0-00c0-0c00-00c0-000b000000cc",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "5.2"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var versionsJSON = fmt.Sprintf(`"versions": [%s, %s]`, version1JSON, version2JSON)
|
||||||
|
|
||||||
|
var singleDSJSON = fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"default_version": "c00000b0-00c0-0c00-00c0-000b000000cc",
|
||||||
|
"id": "10000000-0000-0000-0000-000000000001",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "mysql",
|
||||||
|
%s
|
||||||
|
}
|
||||||
|
`, versionsJSON)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ListDSResp = fmt.Sprintf(`{"datastores":[%s]}`, singleDSJSON)
|
||||||
|
GetDSResp = fmt.Sprintf(`{"datastore":%s}`, singleDSJSON)
|
||||||
|
ListVersionsResp = fmt.Sprintf(`{%s}`, versionsJSON)
|
||||||
|
GetVersionResp = fmt.Sprintf(`{"version":%s}`, version1JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
var ExampleVersion1 = Version{
|
||||||
|
ID: "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
Links: []gophercloud.Link{
|
||||||
|
gophercloud.Link{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"},
|
||||||
|
gophercloud.Link{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/b00000b0-00b0-0b00-00b0-000b000000bb"},
|
||||||
|
},
|
||||||
|
Name: "5.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
var exampleVersion2 = Version{
|
||||||
|
ID: "c00000b0-00c0-0c00-00c0-000b000000cc",
|
||||||
|
Links: []gophercloud.Link{
|
||||||
|
gophercloud.Link{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"},
|
||||||
|
gophercloud.Link{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/versions/c00000b0-00c0-0c00-00c0-000b000000cc"},
|
||||||
|
},
|
||||||
|
Name: "5.2",
|
||||||
|
}
|
||||||
|
|
||||||
|
var ExampleVersions = []Version{ExampleVersion1, exampleVersion2}
|
||||||
|
|
||||||
|
var ExampleDatastore = Datastore{
|
||||||
|
DefaultVersion: "c00000b0-00c0-0c00-00c0-000b000000cc",
|
||||||
|
ID: "10000000-0000-0000-0000-000000000001",
|
||||||
|
Links: []gophercloud.Link{
|
||||||
|
gophercloud.Link{Rel: "self", Href: "https://10.240.28.38:8779/v1.0/1234/datastores/10000000-0000-0000-0000-000000000001"},
|
||||||
|
gophercloud.Link{Rel: "bookmark", Href: "https://10.240.28.38:8779/datastores/10000000-0000-0000-0000-000000000001"},
|
||||||
|
},
|
||||||
|
Name: "mysql",
|
||||||
|
Versions: ExampleVersions,
|
||||||
|
}
|
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/requests.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/requests.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package datastores
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List will list all available datastore types that instances can use.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return DatastorePage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, baseURL(client), pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will retrieve the details of a specified datastore type.
|
||||||
|
func Get(client *gophercloud.ServiceClient, datastoreID string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", resourceURL(client, datastoreID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVersions will list all of the available versions for a specified
|
||||||
|
// datastore type.
|
||||||
|
func ListVersions(client *gophercloud.ServiceClient, datastoreID string) pagination.Pager {
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return VersionPage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, versionsURL(client, datastoreID), pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion will retrieve the details of a specified datastore version.
|
||||||
|
func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) GetVersionResult {
|
||||||
|
var res GetVersionResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", versionURL(client, datastoreID, versionID), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
123
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/results.go
generated
vendored
Normal file
123
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/results.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package datastores
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version represents a version API resource. Multiple versions belong to a Datastore.
|
||||||
|
type Version struct {
|
||||||
|
ID string
|
||||||
|
Links []gophercloud.Link
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Datastore represents a Datastore API resource.
|
||||||
|
type Datastore struct {
|
||||||
|
DefaultVersion string `json:"default_version" mapstructure:"default_version"`
|
||||||
|
ID string
|
||||||
|
Links []gophercloud.Link
|
||||||
|
Name string
|
||||||
|
Versions []Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatastorePartial is a meta structure which is used in various API responses.
|
||||||
|
// It is a lightweight and truncated version of a full Datastore resource,
|
||||||
|
// offering details of the Version, Type and VersionID only.
|
||||||
|
type DatastorePartial struct {
|
||||||
|
Version string
|
||||||
|
Type string
|
||||||
|
VersionID string `json:"version_id" mapstructure:"version_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult represents the result of a Get operation.
|
||||||
|
type GetResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersionResult represents the result of getting a version.
|
||||||
|
type GetVersionResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatastorePage represents a page of datastore resources.
|
||||||
|
type DatastorePage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty indicates whether a Datastore collection is empty.
|
||||||
|
func (r DatastorePage) IsEmpty() (bool, error) {
|
||||||
|
is, err := ExtractDatastores(r)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(is) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractDatastores retrieves a slice of datastore structs from a paginated
|
||||||
|
// collection.
|
||||||
|
func ExtractDatastores(page pagination.Page) ([]Datastore, error) {
|
||||||
|
casted := page.(DatastorePage).Body
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Datastores []Datastore `mapstructure:"datastores" json:"datastores"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(casted, &resp)
|
||||||
|
return resp.Datastores, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract retrieves a single Datastore struct from an operation result.
|
||||||
|
func (r GetResult) Extract() (*Datastore, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Datastore Datastore `mapstructure:"datastore"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
return &response.Datastore, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatastorePage represents a page of version resources.
|
||||||
|
type VersionPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty indicates whether a collection of version resources is empty.
|
||||||
|
func (r VersionPage) IsEmpty() (bool, error) {
|
||||||
|
is, err := ExtractVersions(r)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(is) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractVersions retrieves a slice of versions from a paginated collection.
|
||||||
|
func ExtractVersions(page pagination.Page) ([]Version, error) {
|
||||||
|
casted := page.(VersionPage).Body
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Versions []Version `mapstructure:"versions" json:"versions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(casted, &resp)
|
||||||
|
return resp.Versions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract retrieves a single Version struct from an operation result.
|
||||||
|
func (r GetVersionResult) Extract() (*Version, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Version Version `mapstructure:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
return &response.Version, err
|
||||||
|
}
|
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/urls.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/datastores/urls.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package datastores
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func baseURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("datastores")
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient, dsID string) string {
|
||||||
|
return c.ServiceURL("datastores", dsID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func versionsURL(c *gophercloud.ServiceClient, dsID string) string {
|
||||||
|
return c.ServiceURL("datastores", dsID, "versions")
|
||||||
|
}
|
||||||
|
|
||||||
|
func versionURL(c *gophercloud.ServiceClient, dsID, versionID string) string {
|
||||||
|
return c.ServiceURL("datastores", dsID, "versions", versionID)
|
||||||
|
}
|
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/doc.go
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/doc.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Package flavors provides information and interaction with the flavor API
|
||||||
|
// resource in the OpenStack Database service.
|
||||||
|
//
|
||||||
|
// A flavor is an available hardware configuration for a database instance.
|
||||||
|
// Each flavor has a unique combination of disk space, memory capacity and
|
||||||
|
// priority for CPU time.
|
||||||
|
package flavors
|
50
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/fixtures.go
generated
vendored
Normal file
50
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/fixture"
|
||||||
|
)
|
||||||
|
|
||||||
|
const flavor = `
|
||||||
|
{
|
||||||
|
"id": %d,
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://openstack.example.com/v1.0/1234/flavors/%d",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://openstack.example.com/flavors/%d",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "%s",
|
||||||
|
"ram": %d
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
flavorID = "{flavorID}"
|
||||||
|
_baseURL = "/flavors"
|
||||||
|
resURL = "/flavors/" + flavorID
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
flavor1 = fmt.Sprintf(flavor, 1, 1, 1, "m1.tiny", 512)
|
||||||
|
flavor2 = fmt.Sprintf(flavor, 2, 2, 2, "m1.small", 1024)
|
||||||
|
flavor3 = fmt.Sprintf(flavor, 3, 3, 3, "m1.medium", 2048)
|
||||||
|
flavor4 = fmt.Sprintf(flavor, 4, 4, 4, "m1.large", 4096)
|
||||||
|
|
||||||
|
listFlavorsResp = fmt.Sprintf(`{"flavors":[%s, %s, %s, %s]}`, flavor1, flavor2, flavor3, flavor4)
|
||||||
|
getFlavorResp = fmt.Sprintf(`{"flavor": %s}`, flavor1)
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleList(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, _baseURL, "GET", "", listFlavorsResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleGet(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, resURL, "GET", "", getFlavorResp, 200)
|
||||||
|
}
|
29
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/requests.go
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/requests.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List will list all available hardware flavors that an instance can use. The
|
||||||
|
// operation is identical to the one supported by the Nova API, but without the
|
||||||
|
// "disk" property.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
createPage := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return FlavorPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, listURL(client), createPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will retrieve information for a specified hardware flavor.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
|
var gr GetResult
|
||||||
|
|
||||||
|
_, gr.Err = client.Request("GET", getURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &gr.Body,
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
|
||||||
|
return gr
|
||||||
|
}
|
92
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/results.go
generated
vendored
Normal file
92
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/results.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||||
|
WeaklyTypedInput: true,
|
||||||
|
Result: &result,
|
||||||
|
})
|
||||||
|
|
||||||
|
err = decoder.Decode(gr.Body)
|
||||||
|
return &result.Flavor, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flavor records represent (virtual) hardware configurations for server resources in a region.
|
||||||
|
type Flavor struct {
|
||||||
|
// The flavor's unique identifier.
|
||||||
|
ID string `mapstructure:"id"`
|
||||||
|
|
||||||
|
// The RAM capacity for the flavor.
|
||||||
|
RAM int `mapstructure:"ram"`
|
||||||
|
|
||||||
|
// The Name field provides a human-readable moniker for the flavor.
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
|
||||||
|
// Links to access the flavor.
|
||||||
|
Links []gophercloud.Link
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlavorPage contains a single page of the response from a List call.
|
||||||
|
type FlavorPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines if a page contains any results.
|
||||||
|
func (p FlavorPage) IsEmpty() (bool, error) {
|
||||||
|
flavors, err := ExtractFlavors(p)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(flavors) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
||||||
|
func (p FlavorPage) NextPageURL() (string, error) {
|
||||||
|
type resp struct {
|
||||||
|
Links []gophercloud.Link `mapstructure:"flavors_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var r resp
|
||||||
|
err := mapstructure.Decode(p.Body, &r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gophercloud.ExtractNextURL(r.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.(FlavorPage).Body
|
||||||
|
var container struct {
|
||||||
|
Flavors []Flavor `mapstructure:"flavors"`
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||||
|
WeaklyTypedInput: true,
|
||||||
|
Result: &container,
|
||||||
|
})
|
||||||
|
|
||||||
|
err = decoder.Decode(casted)
|
||||||
|
|
||||||
|
return container.Flavors, err
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/urls.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/flavors/urls.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
|
return client.ServiceURL("flavors", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("flavors")
|
||||||
|
}
|
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/doc.go
generated
vendored
Normal file
7
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/doc.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Package instances provides information and interaction with the instance API
|
||||||
|
// resource in the OpenStack Database service.
|
||||||
|
//
|
||||||
|
// A database instance is an isolated database environment with compute and
|
||||||
|
// storage resources in a single tenant environment on a shared physical host
|
||||||
|
// machine.
|
||||||
|
package instances
|
169
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/fixtures.go
generated
vendored
Normal file
169
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
package instances
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/fixture"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timestamp = "2015-11-12T14:22:42Z"
|
||||||
|
timeVal, _ = time.Parse(time.RFC3339, timestamp)
|
||||||
|
)
|
||||||
|
|
||||||
|
var instance = `
|
||||||
|
{
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"datastore": {
|
||||||
|
"type": "mysql",
|
||||||
|
"version": "5.6"
|
||||||
|
},
|
||||||
|
"flavor": {
|
||||||
|
"id": "1",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://my-openstack.com/v1.0/1234/flavors/1",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "https://my-openstack.com/v1.0/1234/flavors/1",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://my-openstack.com/v1.0/1234/instances/1",
|
||||||
|
"rel": "self"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hostname": "e09ad9a3f73309469cf1f43d11e79549caf9acf2.my-openstack.com",
|
||||||
|
"id": "{instanceID}",
|
||||||
|
"name": "json_rack_instance",
|
||||||
|
"status": "BUILD",
|
||||||
|
"updated": "` + timestamp + `",
|
||||||
|
"volume": {
|
||||||
|
"size": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var createReq = `
|
||||||
|
{
|
||||||
|
"instance": {
|
||||||
|
"databases": [
|
||||||
|
{
|
||||||
|
"character_set": "utf8",
|
||||||
|
"collate": "utf8_general_ci",
|
||||||
|
"name": "sampledb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nextround"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flavorRef": "1",
|
||||||
|
"name": "json_rack_instance",
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"databases": [
|
||||||
|
{
|
||||||
|
"name": "sampledb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "demouser",
|
||||||
|
"password": "demopassword"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"volume": {
|
||||||
|
"size": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
instanceID = "{instanceID}"
|
||||||
|
rootURL = "/instances"
|
||||||
|
resURL = rootURL + "/" + instanceID
|
||||||
|
uRootURL = resURL + "/root"
|
||||||
|
aURL = resURL + "/action"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
restartReq = `{"restart": {}}`
|
||||||
|
resizeReq = `{"resize": {"flavorRef": "2"}}`
|
||||||
|
resizeVolReq = `{"resize": {"volume": {"size": 4}}}`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
createResp = fmt.Sprintf(`{"instance": %s}`, instance)
|
||||||
|
listInstancesResp = fmt.Sprintf(`{"instances":[%s]}`, instance)
|
||||||
|
getInstanceResp = createResp
|
||||||
|
enableUserResp = `{"user":{"name":"root","password":"secretsecret"}}`
|
||||||
|
isUserEnabledResp = `{"rootEnabled":true}`
|
||||||
|
)
|
||||||
|
|
||||||
|
var expectedInstance = Instance{
|
||||||
|
Created: timeVal,
|
||||||
|
Updated: timeVal,
|
||||||
|
Flavor: flavors.Flavor{
|
||||||
|
ID: "1",
|
||||||
|
Links: []gophercloud.Link{
|
||||||
|
gophercloud.Link{Href: "https://my-openstack.com/v1.0/1234/flavors/1", Rel: "self"},
|
||||||
|
gophercloud.Link{Href: "https://my-openstack.com/v1.0/1234/flavors/1", Rel: "bookmark"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Hostname: "e09ad9a3f73309469cf1f43d11e79549caf9acf2.my-openstack.com",
|
||||||
|
ID: instanceID,
|
||||||
|
Links: []gophercloud.Link{
|
||||||
|
gophercloud.Link{Href: "https://my-openstack.com/v1.0/1234/instances/1", Rel: "self"},
|
||||||
|
},
|
||||||
|
Name: "json_rack_instance",
|
||||||
|
Status: "BUILD",
|
||||||
|
Volume: Volume{Size: 2},
|
||||||
|
Datastore: datastores.DatastorePartial{
|
||||||
|
Type: "mysql",
|
||||||
|
Version: "5.6",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleCreate(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, rootURL, "POST", createReq, createResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleList(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, rootURL, "GET", "", listInstancesResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleGet(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, resURL, "GET", "", getInstanceResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleDelete(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, resURL, "DELETE", "", "", 202)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleEnableRoot(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, uRootURL, "POST", "", enableUserResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleIsRootEnabled(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, uRootURL, "GET", "", isUserEnabledResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleRestart(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, aURL, "POST", restartReq, "", 202)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleResize(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, aURL, "POST", resizeReq, "", 202)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleResizeVol(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, aURL, "POST", resizeVolReq, "", 202)
|
||||||
|
}
|
238
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/requests.go
generated
vendored
Normal file
238
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/requests.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
package instances
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/users"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder is the top-level interface for create options.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToInstanceCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DatastoreOpts represents the configuration for how an instance stores data.
|
||||||
|
type DatastoreOpts struct {
|
||||||
|
Version string
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts DatastoreOpts) ToMap() (map[string]string, error) {
|
||||||
|
return map[string]string{
|
||||||
|
"version": opts.Version,
|
||||||
|
"type": opts.Type,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts is the struct responsible for configuring a new database instance.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Either the integer UUID (in string form) of the flavor, or its URI
|
||||||
|
// reference as specified in the response from the List() call. Required.
|
||||||
|
FlavorRef string
|
||||||
|
|
||||||
|
// Specifies the volume size in gigabytes (GB). The value must be between 1
|
||||||
|
// and 300. Required.
|
||||||
|
Size int
|
||||||
|
|
||||||
|
// Name of the instance to create. The length of the name is limited to
|
||||||
|
// 255 characters and any characters are permitted. Optional.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// A slice of database information options.
|
||||||
|
Databases db.CreateOptsBuilder
|
||||||
|
|
||||||
|
// A slice of user information options.
|
||||||
|
Users users.CreateOptsBuilder
|
||||||
|
|
||||||
|
// Options to configure the type of datastore the instance will use. This is
|
||||||
|
// optional, and if excluded will default to MySQL.
|
||||||
|
Datastore *DatastoreOpts
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInstanceCreateMap will render a JSON map.
|
||||||
|
func (opts CreateOpts) ToInstanceCreateMap() (map[string]interface{}, error) {
|
||||||
|
if opts.Size > 300 || opts.Size < 1 {
|
||||||
|
return nil, fmt.Errorf("Size (GB) must be between 1-300")
|
||||||
|
}
|
||||||
|
if opts.FlavorRef == "" {
|
||||||
|
return nil, fmt.Errorf("FlavorRef is a required field")
|
||||||
|
}
|
||||||
|
|
||||||
|
instance := map[string]interface{}{
|
||||||
|
"volume": map[string]int{"size": opts.Size},
|
||||||
|
"flavorRef": opts.FlavorRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Name != "" {
|
||||||
|
instance["name"] = opts.Name
|
||||||
|
}
|
||||||
|
if opts.Databases != nil {
|
||||||
|
dbs, err := opts.Databases.ToDBCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
instance["databases"] = dbs["databases"]
|
||||||
|
}
|
||||||
|
if opts.Users != nil {
|
||||||
|
users, err := opts.Users.ToUserCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
instance["users"] = users["users"]
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"instance": instance}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create asynchronously provisions a new database instance. It requires the
|
||||||
|
// user to specify a flavor and a volume size. The API service then provisions
|
||||||
|
// the instance with the requested flavor and sets up a volume of the specified
|
||||||
|
// size, which is the storage for the database instance.
|
||||||
|
//
|
||||||
|
// Although this call only allows the creation of 1 instance per request, you
|
||||||
|
// can create an instance with multiple databases and users. The default
|
||||||
|
// binding for a MySQL instance is port 3306.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||||
|
var res CreateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToInstanceCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// List retrieves the status and information for all database instances.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return InstancePage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, baseURL(client), createPageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the status and information for a specified database instance.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete permanently destroys the database instance.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||||
|
var res DeleteResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableRootUser enables the login from any host for the root user and
|
||||||
|
// provides the user with a generated root password.
|
||||||
|
func EnableRootUser(client *gophercloud.ServiceClient, id string) UserRootResult {
|
||||||
|
var res UserRootResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", userRootURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRootEnabled checks an instance to see if root access is enabled. It returns
|
||||||
|
// True if root user is enabled for the specified database instance or False
|
||||||
|
// otherwise.
|
||||||
|
func IsRootEnabled(client *gophercloud.ServiceClient, id string) (bool, error) {
|
||||||
|
var res gophercloud.Result
|
||||||
|
|
||||||
|
_, err := client.Request("GET", userRootURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res.Body.(map[string]interface{})["rootEnabled"] == true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart will restart only the MySQL Instance. Restarting MySQL will
|
||||||
|
// erase any dynamic configuration settings that you have made within MySQL.
|
||||||
|
// The MySQL service will be unavailable until the instance restarts.
|
||||||
|
func Restart(client *gophercloud.ServiceClient, id string) ActionResult {
|
||||||
|
var res ActionResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONBody: map[string]interface{}{"restart": struct{}{}},
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize changes the memory size of the instance, assuming a valid
|
||||||
|
// flavorRef is provided. It will also restart the MySQL service.
|
||||||
|
func Resize(client *gophercloud.ServiceClient, id, flavorRef string) ActionResult {
|
||||||
|
var res ActionResult
|
||||||
|
|
||||||
|
type resize struct {
|
||||||
|
FlavorRef string `json:"flavorRef"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type req struct {
|
||||||
|
Resize resize `json:"resize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBody := req{Resize: resize{FlavorRef: flavorRef}}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONBody: reqBody,
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeVolume will resize the attached volume for an instance. It supports
|
||||||
|
// only increasing the volume size and does not support decreasing the size.
|
||||||
|
// The volume size is in gigabytes (GB) and must be an integer.
|
||||||
|
func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) ActionResult {
|
||||||
|
var res ActionResult
|
||||||
|
|
||||||
|
type volume struct {
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type resize struct {
|
||||||
|
Volume volume `json:"volume"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type req struct {
|
||||||
|
Resize resize `json:"resize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBody := req{Resize: resize{Volume: volume{Size: size}}}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", actionURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONBody: reqBody,
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
213
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/results.go
generated
vendored
Normal file
213
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/results.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
package instances
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/flavors"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/users"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Volume represents information about an attached volume for a database instance.
|
||||||
|
type Volume struct {
|
||||||
|
// The size in GB of the volume
|
||||||
|
Size int
|
||||||
|
|
||||||
|
Used float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance represents a remote MySQL instance.
|
||||||
|
type Instance struct {
|
||||||
|
// Indicates the datetime that the instance was created
|
||||||
|
Created time.Time `mapstructure:"-"`
|
||||||
|
|
||||||
|
// Indicates the most recent datetime that the instance was updated.
|
||||||
|
Updated time.Time `mapstructure:"-"`
|
||||||
|
|
||||||
|
// Indicates the hardware flavor the instance uses.
|
||||||
|
Flavor flavors.Flavor
|
||||||
|
|
||||||
|
// A DNS-resolvable hostname associated with the database instance (rather
|
||||||
|
// than an IPv4 address). Since the hostname always resolves to the correct
|
||||||
|
// IP address of the database instance, this relieves the user from the task
|
||||||
|
// of maintaining the mapping. Note that although the IP address may likely
|
||||||
|
// change on resizing, migrating, and so forth, the hostname always resolves
|
||||||
|
// to the correct database instance.
|
||||||
|
Hostname string
|
||||||
|
|
||||||
|
// Indicates the unique identifier for the instance resource.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Exposes various links that reference the instance resource.
|
||||||
|
Links []gophercloud.Link
|
||||||
|
|
||||||
|
// The human-readable name of the instance.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// The build status of the instance.
|
||||||
|
Status string
|
||||||
|
|
||||||
|
// Information about the attached volume of the instance.
|
||||||
|
Volume Volume
|
||||||
|
|
||||||
|
// Indicates how the instance stores data.
|
||||||
|
Datastore datastores.DatastorePartial
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult represents the result of a Create operation.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult represents the result of a Get operation.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult represents the result of a Delete operation.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will extract an Instance from various result structs.
|
||||||
|
func (r commonResult) Extract() (*Instance, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Instance Instance `mapstructure:"instance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
val := r.Body.(map[string]interface{})["instance"].(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := val["created"].(string); ok && t != "" {
|
||||||
|
creationTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return &response.Instance, err
|
||||||
|
}
|
||||||
|
response.Instance.Created = creationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := val["updated"].(string); ok && t != "" {
|
||||||
|
updatedTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return &response.Instance, err
|
||||||
|
}
|
||||||
|
response.Instance.Updated = updatedTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.Instance, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstancePage represents a single page of a paginated instance collection.
|
||||||
|
type InstancePage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks to see whether the collection is empty.
|
||||||
|
func (page InstancePage) IsEmpty() (bool, error) {
|
||||||
|
instances, err := ExtractInstances(page)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(instances) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL will retrieve the next page URL.
|
||||||
|
func (page InstancePage) NextPageURL() (string, error) {
|
||||||
|
type resp struct {
|
||||||
|
Links []gophercloud.Link `mapstructure:"instances_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var r resp
|
||||||
|
err := mapstructure.Decode(page.Body, &r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gophercloud.ExtractNextURL(r.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractInstances will convert a generic pagination struct into a more
|
||||||
|
// relevant slice of Instance structs.
|
||||||
|
func ExtractInstances(page pagination.Page) ([]Instance, error) {
|
||||||
|
casted := page.(InstancePage).Body
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Instances []Instance `mapstructure:"instances"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mapstructure.Decode(casted, &resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vals []interface{}
|
||||||
|
switch casted.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
vals = casted.(map[string]interface{})["instances"].([]interface{})
|
||||||
|
case map[string][]interface{}:
|
||||||
|
vals = casted.(map[string][]interface{})["instances"]
|
||||||
|
default:
|
||||||
|
return resp.Instances, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range vals {
|
||||||
|
val := v.(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := val["created"].(string); ok && t != "" {
|
||||||
|
creationTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return resp.Instances, err
|
||||||
|
}
|
||||||
|
resp.Instances[i].Created = creationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := val["updated"].(string); ok && t != "" {
|
||||||
|
updatedTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return resp.Instances, err
|
||||||
|
}
|
||||||
|
resp.Instances[i].Updated = updatedTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Instances, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRootResult represents the result of an operation to enable the root user.
|
||||||
|
type UserRootResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will extract root user information from a UserRootResult.
|
||||||
|
func (r UserRootResult) Extract() (*users.User, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
User users.User `mapstructure:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
|
||||||
|
return &response.User, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionResult represents the result of action requests, such as: restarting
|
||||||
|
// an instance service, resizing its memory allocation, and resizing its
|
||||||
|
// attached volume size.
|
||||||
|
type ActionResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/urls.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/instances/urls.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package instances
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func baseURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("instances")
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL("instances", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func userRootURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL("instances", id, "root")
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL("instances", id, "action")
|
||||||
|
}
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package users provides information and interaction with the user API
|
||||||
|
// resource in the OpenStack Database service.
|
||||||
|
package users
|
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/fixtures.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud/testhelper/fixture"
|
||||||
|
)
|
||||||
|
|
||||||
|
const user1 = `
|
||||||
|
{"databases": [{"name": "databaseA"}],"name": "dbuser3"%s}
|
||||||
|
`
|
||||||
|
|
||||||
|
const user2 = `
|
||||||
|
{"databases": [{"name": "databaseB"},{"name": "databaseC"}],"name": "dbuser4"%s}
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
instanceID = "{instanceID}"
|
||||||
|
_rootURL = "/instances/" + instanceID + "/users"
|
||||||
|
pUser1 = fmt.Sprintf(user1, `,"password":"secretsecret"`)
|
||||||
|
pUser2 = fmt.Sprintf(user2, `,"password":"secretsecret"`)
|
||||||
|
createReq = fmt.Sprintf(`{"users":[%s, %s]}`, pUser1, pUser2)
|
||||||
|
listResp = fmt.Sprintf(`{"users":[%s, %s]}`, fmt.Sprintf(user1, ""), fmt.Sprintf(user2, ""))
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleCreate(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, _rootURL, "POST", createReq, "", 202)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleList(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, _rootURL, "GET", "", listResp, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleDelete(t *testing.T) {
|
||||||
|
fixture.SetupHandler(t, _rootURL+"/{userName}", "DELETE", "", "", 202)
|
||||||
|
}
|
132
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/requests.go
generated
vendored
Normal file
132
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/requests.go
generated
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder is the top-level interface for creating JSON maps.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToUserCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts is the struct responsible for configuring a new user; often in the
|
||||||
|
// context of an instance.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// [REQUIRED] Specifies a name for the user. Valid names can be composed
|
||||||
|
// of the following characters: letters (either case); numbers; these
|
||||||
|
// characters '@', '?', '#', ' ' but NEVER beginning a name string; '_' is
|
||||||
|
// permitted anywhere. Prohibited characters that are forbidden include:
|
||||||
|
// single quotes, double quotes, back quotes, semicolons, commas, backslashes,
|
||||||
|
// and forward slashes. Spaces at the front or end of a user name are also
|
||||||
|
// not permitted.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// [REQUIRED] Specifies a password for the user.
|
||||||
|
Password string
|
||||||
|
|
||||||
|
// [OPTIONAL] An array of databases that this user will connect to. The
|
||||||
|
// "name" field is the only requirement for each option.
|
||||||
|
Databases db.BatchCreateOpts
|
||||||
|
|
||||||
|
// [OPTIONAL] Specifies the host from which a user is allowed to connect to
|
||||||
|
// the database. Possible values are a string containing an IPv4 address or
|
||||||
|
// "%" to allow connecting from any host. Optional; the default is "%".
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMap is a convenience function for creating sub-maps for individual users.
|
||||||
|
func (opts CreateOpts) ToMap() (map[string]interface{}, error) {
|
||||||
|
|
||||||
|
if opts.Name == "root" {
|
||||||
|
return nil, errors.New("root is a reserved user name and cannot be used")
|
||||||
|
}
|
||||||
|
if opts.Name == "" {
|
||||||
|
return nil, errors.New("Name is a required field")
|
||||||
|
}
|
||||||
|
if opts.Password == "" {
|
||||||
|
return nil, errors.New("Password is a required field")
|
||||||
|
}
|
||||||
|
|
||||||
|
user := map[string]interface{}{
|
||||||
|
"name": opts.Name,
|
||||||
|
"password": opts.Password,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Host != "" {
|
||||||
|
user["host"] = opts.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
dbs := make([]map[string]string, len(opts.Databases))
|
||||||
|
for i, db := range opts.Databases {
|
||||||
|
dbs[i] = map[string]string{"name": db.Name}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dbs) > 0 {
|
||||||
|
user["databases"] = dbs
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchCreateOpts allows multiple users to be created at once.
|
||||||
|
type BatchCreateOpts []CreateOpts
|
||||||
|
|
||||||
|
// ToUserCreateMap will generate a JSON map.
|
||||||
|
func (opts BatchCreateOpts) ToUserCreateMap() (map[string]interface{}, error) {
|
||||||
|
users := make([]map[string]interface{}, len(opts))
|
||||||
|
for i, opt := range opts {
|
||||||
|
user, err := opt.ToMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
users[i] = user
|
||||||
|
}
|
||||||
|
return map[string]interface{}{"users": users}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create asynchronously provisions a new user for the specified database
|
||||||
|
// instance based on the configuration defined in CreateOpts. If databases are
|
||||||
|
// assigned for a particular user, the user will be granted all privileges
|
||||||
|
// for those specified databases. "root" is a reserved name and cannot be used.
|
||||||
|
func Create(client *gophercloud.ServiceClient, instanceID string, opts CreateOptsBuilder) CreateResult {
|
||||||
|
var res CreateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToUserCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", baseURL(client, instanceID), gophercloud.RequestOpts{
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// List will list all the users associated with a specified database instance,
|
||||||
|
// along with their associated databases. This operation will not return any
|
||||||
|
// system users or administrators for a database.
|
||||||
|
func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
|
||||||
|
createPageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return UserPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, baseURL(client, instanceID), createPageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will permanently delete a user from a specified database instance.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, instanceID, userName string) DeleteResult {
|
||||||
|
var res DeleteResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("DELETE", userURL(client, instanceID, userName), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
73
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/results.go
generated
vendored
Normal file
73
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/results.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
db "github.com/rackspace/gophercloud/openstack/db/v1/databases"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User represents a database user
|
||||||
|
type User struct {
|
||||||
|
// The user name
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// The user password
|
||||||
|
Password string
|
||||||
|
|
||||||
|
// The databases associated with this user
|
||||||
|
Databases []db.Database
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult represents the result of a create operation.
|
||||||
|
type CreateResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult represents the result of a delete operation.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserPage represents a single page of a paginated user collection.
|
||||||
|
type UserPage struct {
|
||||||
|
pagination.LinkedPageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks to see whether the collection is empty.
|
||||||
|
func (page UserPage) IsEmpty() (bool, error) {
|
||||||
|
users, err := ExtractUsers(page)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(users) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextPageURL will retrieve the next page URL.
|
||||||
|
func (page UserPage) NextPageURL() (string, error) {
|
||||||
|
type resp struct {
|
||||||
|
Links []gophercloud.Link `mapstructure:"users_links"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var r resp
|
||||||
|
err := mapstructure.Decode(page.Body, &r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gophercloud.ExtractNextURL(r.Links)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractUsers will convert a generic pagination struct into a more
|
||||||
|
// relevant slice of User structs.
|
||||||
|
func ExtractUsers(page pagination.Page) ([]User, error) {
|
||||||
|
casted := page.(UserPage).Body
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Users []User `mapstructure:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(casted, &response)
|
||||||
|
|
||||||
|
return response.Users, err
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/urls.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/db/v1/users/urls.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package users
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func baseURL(c *gophercloud.ServiceClient, instanceID string) string {
|
||||||
|
return c.ServiceURL("instances", instanceID, "users")
|
||||||
|
}
|
||||||
|
|
||||||
|
func userURL(c *gophercloud.ServiceClient, instanceID, userName string) string {
|
||||||
|
return c.ServiceURL("instances", instanceID, "users", userName)
|
||||||
|
}
|
@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
|
"github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
|
||||||
th "github.com/rackspace/gophercloud/testhelper"
|
th "github.com/rackspace/gophercloud/testhelper"
|
||||||
|
thclient "github.com/rackspace/gophercloud/testhelper/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExpectedToken is the token that should be parsed from TokenCreationResponse.
|
// ExpectedToken is the token that should be parsed from TokenCreationResponse.
|
||||||
@ -54,6 +55,14 @@ var ExpectedServiceCatalog = &ServiceCatalog{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExpectedUser is the token that should be parsed from TokenGetResponse.
|
||||||
|
var ExpectedUser = &User{
|
||||||
|
ID: "a530fefc3d594c4ba2693a4ecd6be74e",
|
||||||
|
Name: "apiserver",
|
||||||
|
Roles: []Role{{"member"}, {"service"}},
|
||||||
|
UserName: "apiserver",
|
||||||
|
}
|
||||||
|
|
||||||
// TokenCreationResponse is a JSON response that contains ExpectedToken and ExpectedServiceCatalog.
|
// TokenCreationResponse is a JSON response that contains ExpectedToken and ExpectedServiceCatalog.
|
||||||
const TokenCreationResponse = `
|
const TokenCreationResponse = `
|
||||||
{
|
{
|
||||||
@ -99,6 +108,39 @@ const TokenCreationResponse = `
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// TokenGetResponse is a JSON response that contains ExpectedToken and ExpectedUser.
|
||||||
|
const TokenGetResponse = `
|
||||||
|
{
|
||||||
|
"access": {
|
||||||
|
"token": {
|
||||||
|
"issued_at": "2014-01-30T15:30:58.000000Z",
|
||||||
|
"expires": "2014-01-31T15:30:58Z",
|
||||||
|
"id": "aaaabbbbccccdddd",
|
||||||
|
"tenant": {
|
||||||
|
"description": "There are many tenants. This one is yours.",
|
||||||
|
"enabled": true,
|
||||||
|
"id": "fc394f2ab2df4114bde39905f800dc57",
|
||||||
|
"name": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"serviceCatalog": [],
|
||||||
|
"user": {
|
||||||
|
"id": "a530fefc3d594c4ba2693a4ecd6be74e",
|
||||||
|
"name": "apiserver",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "service"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"roles_links": [],
|
||||||
|
"username": "apiserver"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
// HandleTokenPost expects a POST against a /tokens handler, ensures that the request body has been
|
// HandleTokenPost expects a POST against a /tokens handler, ensures that the request body has been
|
||||||
// constructed properly given certain auth options, and returns the result.
|
// constructed properly given certain auth options, and returns the result.
|
||||||
func HandleTokenPost(t *testing.T, requestJSON string) {
|
func HandleTokenPost(t *testing.T, requestJSON string) {
|
||||||
@ -115,6 +157,19 @@ func HandleTokenPost(t *testing.T, requestJSON string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleTokenGet expects a Get against a /tokens handler, ensures that the request body has been
|
||||||
|
// constructed properly given certain auth options, and returns the result.
|
||||||
|
func HandleTokenGet(t *testing.T, token string) {
|
||||||
|
th.Mux.HandleFunc("/tokens/"+token, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
th.TestHeader(t, r, "Accept", "application/json")
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", thclient.TokenID)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintf(w, TokenGetResponse)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// IsSuccessful ensures that a CreateResult was successful and contains the correct token and
|
// IsSuccessful ensures that a CreateResult was successful and contains the correct token and
|
||||||
// service catalog.
|
// service catalog.
|
||||||
func IsSuccessful(t *testing.T, result CreateResult) {
|
func IsSuccessful(t *testing.T, result CreateResult) {
|
||||||
@ -126,3 +181,15 @@ func IsSuccessful(t *testing.T, result CreateResult) {
|
|||||||
th.AssertNoErr(t, err)
|
th.AssertNoErr(t, err)
|
||||||
th.CheckDeepEquals(t, ExpectedServiceCatalog, serviceCatalog)
|
th.CheckDeepEquals(t, ExpectedServiceCatalog, serviceCatalog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetIsSuccessful ensures that a GetResult was successful and contains the correct token and
|
||||||
|
// User Info.
|
||||||
|
func GetIsSuccessful(t *testing.T, result GetResult) {
|
||||||
|
token, err := result.ExtractToken()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckDeepEquals(t, ExpectedToken, token)
|
||||||
|
|
||||||
|
user, err := result.ExtractUser()
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.CheckDeepEquals(t, ExpectedUser, user)
|
||||||
|
}
|
||||||
|
@ -88,3 +88,12 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) CreateRe
|
|||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validates and retrieves information for user's token.
|
||||||
|
func Get(client *gophercloud.ServiceClient, token string) GetResult {
|
||||||
|
var result GetResult
|
||||||
|
_, result.Err = client.Get(GetURL(client, token), &result.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 203},
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
@ -25,6 +25,17 @@ type Token struct {
|
|||||||
Tenant tenants.Tenant
|
Tenant tenants.Tenant
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Authorization need user info which can get from token authentication's response
|
||||||
|
type Role struct {
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
}
|
||||||
|
type User struct {
|
||||||
|
ID string `mapstructure:"id"`
|
||||||
|
Name string `mapstructure:"name"`
|
||||||
|
UserName string `mapstructure:"username"`
|
||||||
|
Roles []Role `mapstructure:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
// Endpoint represents a single API endpoint offered by a service.
|
// Endpoint represents a single API endpoint offered by a service.
|
||||||
// It provides the public and internal URLs, if supported, along with a region specifier, again if provided.
|
// It provides the public and internal URLs, if supported, along with a region specifier, again if provided.
|
||||||
// The significance of the Region field will depend upon your provider.
|
// The significance of the Region field will depend upon your provider.
|
||||||
@ -74,6 +85,12 @@ type CreateResult struct {
|
|||||||
gophercloud.Result
|
gophercloud.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetResult is the deferred response from a Get call, which is the same with a Created token.
|
||||||
|
// Use ExtractUser() to interpret it as a User.
|
||||||
|
type GetResult struct {
|
||||||
|
CreateResult
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractToken returns the just-created Token from a CreateResult.
|
// ExtractToken returns the just-created Token from a CreateResult.
|
||||||
func (result CreateResult) ExtractToken() (*Token, error) {
|
func (result CreateResult) ExtractToken() (*Token, error) {
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
@ -131,3 +148,23 @@ func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
|||||||
func createErr(err error) CreateResult {
|
func createErr(err error) CreateResult {
|
||||||
return CreateResult{gophercloud.Result{Err: err}}
|
return CreateResult{gophercloud.Result{Err: err}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractUser returns the User from a GetResult.
|
||||||
|
func (result GetResult) ExtractUser() (*User, error) {
|
||||||
|
if result.Err != nil {
|
||||||
|
return nil, result.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Access struct {
|
||||||
|
User User `mapstructure:"user"`
|
||||||
|
} `mapstructure:"access"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(result.Body, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.Access.User, nil
|
||||||
|
}
|
||||||
|
@ -6,3 +6,8 @@ import "github.com/rackspace/gophercloud"
|
|||||||
func CreateURL(client *gophercloud.ServiceClient) string {
|
func CreateURL(client *gophercloud.ServiceClient) string {
|
||||||
return client.ServiceURL("tokens")
|
return client.ServiceURL("tokens")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetURL generates the URL used to Validate Tokens.
|
||||||
|
func GetURL(client *gophercloud.ServiceClient, token string) string {
|
||||||
|
return client.ServiceURL("tokens", token)
|
||||||
|
}
|
||||||
|
@ -15,9 +15,9 @@ type Scope struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
|
func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
|
||||||
h := c.AuthenticatedHeaders()
|
return map[string]string{
|
||||||
h["X-Subject-Token"] = subjectToken
|
"X-Subject-Token": subjectToken,
|
||||||
return h
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create authenticates and either generates a new token, or changes the Scope of an existing token.
|
// Create authenticates and either generates a new token, or changes the Scope of an existing token.
|
||||||
|
@ -102,6 +102,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
// Populate request body
|
// Populate request body
|
||||||
reqBody := request{FloatingIP: floatingIP{
|
reqBody := request{FloatingIP: floatingIP{
|
||||||
FloatingNetworkID: opts.FloatingNetworkID,
|
FloatingNetworkID: opts.FloatingNetworkID,
|
||||||
|
FloatingIP: opts.FloatingIP,
|
||||||
PortID: opts.PortID,
|
PortID: opts.PortID,
|
||||||
FixedIP: opts.FixedIP,
|
FixedIP: opts.FixedIP,
|
||||||
TenantID: opts.TenantID,
|
TenantID: opts.TenantID,
|
||||||
|
@ -16,6 +16,7 @@ type ListOpts struct {
|
|||||||
ID string `q:"id"`
|
ID string `q:"id"`
|
||||||
Name string `q:"name"`
|
Name string `q:"name"`
|
||||||
AdminStateUp *bool `q:"admin_state_up"`
|
AdminStateUp *bool `q:"admin_state_up"`
|
||||||
|
Distributed *bool `q:"distributed"`
|
||||||
Status string `q:"status"`
|
Status string `q:"status"`
|
||||||
TenantID string `q:"tenant_id"`
|
TenantID string `q:"tenant_id"`
|
||||||
Limit int `q:"limit"`
|
Limit int `q:"limit"`
|
||||||
@ -46,6 +47,7 @@ func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
|
|||||||
type CreateOpts struct {
|
type CreateOpts struct {
|
||||||
Name string
|
Name string
|
||||||
AdminStateUp *bool
|
AdminStateUp *bool
|
||||||
|
Distributed *bool
|
||||||
TenantID string
|
TenantID string
|
||||||
GatewayInfo *GatewayInfo
|
GatewayInfo *GatewayInfo
|
||||||
}
|
}
|
||||||
@ -62,6 +64,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
type router struct {
|
type router struct {
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||||
|
Distributed *bool `json:"distributed,omitempty"`
|
||||||
TenantID *string `json:"tenant_id,omitempty"`
|
TenantID *string `json:"tenant_id,omitempty"`
|
||||||
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
|
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
|
||||||
}
|
}
|
||||||
@ -73,6 +76,7 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
reqBody := request{Router: router{
|
reqBody := request{Router: router{
|
||||||
Name: gophercloud.MaybeString(opts.Name),
|
Name: gophercloud.MaybeString(opts.Name),
|
||||||
AdminStateUp: opts.AdminStateUp,
|
AdminStateUp: opts.AdminStateUp,
|
||||||
|
Distributed: opts.Distributed,
|
||||||
TenantID: gophercloud.MaybeString(opts.TenantID),
|
TenantID: gophercloud.MaybeString(opts.TenantID),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@ -96,7 +100,9 @@ func Get(c *gophercloud.ServiceClient, id string) GetResult {
|
|||||||
type UpdateOpts struct {
|
type UpdateOpts struct {
|
||||||
Name string
|
Name string
|
||||||
AdminStateUp *bool
|
AdminStateUp *bool
|
||||||
|
Distributed *bool
|
||||||
GatewayInfo *GatewayInfo
|
GatewayInfo *GatewayInfo
|
||||||
|
Routes []Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update allows routers to be updated. You can update the name, administrative
|
// Update allows routers to be updated. You can update the name, administrative
|
||||||
@ -108,7 +114,9 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
|
|||||||
type router struct {
|
type router struct {
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
AdminStateUp *bool `json:"admin_state_up,omitempty"`
|
||||||
|
Distributed *bool `json:"distributed,omitempty"`
|
||||||
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
|
GatewayInfo *GatewayInfo `json:"external_gateway_info,omitempty"`
|
||||||
|
Routes []Route `json:"routes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type request struct {
|
type request struct {
|
||||||
@ -118,12 +126,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResu
|
|||||||
reqBody := request{Router: router{
|
reqBody := request{Router: router{
|
||||||
Name: gophercloud.MaybeString(opts.Name),
|
Name: gophercloud.MaybeString(opts.Name),
|
||||||
AdminStateUp: opts.AdminStateUp,
|
AdminStateUp: opts.AdminStateUp,
|
||||||
|
Distributed: opts.Distributed,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
if opts.GatewayInfo != nil {
|
if opts.GatewayInfo != nil {
|
||||||
reqBody.Router.GatewayInfo = opts.GatewayInfo
|
reqBody.Router.GatewayInfo = opts.GatewayInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Routes != nil {
|
||||||
|
reqBody.Router.Routes = opts.Routes
|
||||||
|
}
|
||||||
|
|
||||||
// Send request to API
|
// Send request to API
|
||||||
var res UpdateResult
|
var res UpdateResult
|
||||||
_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
|
_, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
|
||||||
|
@ -12,6 +12,11 @@ type GatewayInfo struct {
|
|||||||
NetworkID string `json:"network_id" mapstructure:"network_id"`
|
NetworkID string `json:"network_id" mapstructure:"network_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
NextHop string `mapstructure:"nexthop" json:"nexthop"`
|
||||||
|
DestinationCIDR string `mapstructure:"destination" json:"destination"`
|
||||||
|
}
|
||||||
|
|
||||||
// Router represents a Neutron router. A router is a logical entity that
|
// Router represents a Neutron router. A router is a logical entity that
|
||||||
// forwards packets across internal subnets and NATs (network address
|
// forwards packets across internal subnets and NATs (network address
|
||||||
// translation) them on external networks through an appropriate gateway.
|
// translation) them on external networks through an appropriate gateway.
|
||||||
@ -30,6 +35,9 @@ type Router struct {
|
|||||||
// Administrative state of the router.
|
// Administrative state of the router.
|
||||||
AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
|
AdminStateUp bool `json:"admin_state_up" mapstructure:"admin_state_up"`
|
||||||
|
|
||||||
|
// Whether router is disitrubted or not..
|
||||||
|
Distributed bool `json:"distributed" mapstructure:"distributed"`
|
||||||
|
|
||||||
// Human readable name for the router. Does not have to be unique.
|
// Human readable name for the router. Does not have to be unique.
|
||||||
Name string `json:"name" mapstructure:"name"`
|
Name string `json:"name" mapstructure:"name"`
|
||||||
|
|
||||||
@ -39,6 +47,8 @@ type Router struct {
|
|||||||
// Owner of the router. Only admin users can specify a tenant identifier
|
// Owner of the router. Only admin users can specify a tenant identifier
|
||||||
// other than its own.
|
// other than its own.
|
||||||
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
|
TenantID string `json:"tenant_id" mapstructure:"tenant_id"`
|
||||||
|
|
||||||
|
Routes []Route `json:"routes" mapstructure:"routes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouterPage is the page returned by a pager when traversing over a
|
// RouterPage is the page returned by a pager when traversing over a
|
||||||
|
@ -104,8 +104,8 @@ type CreateOpts struct {
|
|||||||
TenantID string
|
TenantID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create is an operation which provisions a new security group with default
|
// Create is an operation which adds a new security group rule and associates it
|
||||||
// security group rules for the IPv4 and IPv6 ether types.
|
// with an existing security group (whose ID is specified in CreateOpts).
|
||||||
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
||||||
var res CreateResult
|
var res CreateResult
|
||||||
|
|
||||||
@ -159,14 +159,14 @@ func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves a particular security group based on its unique ID.
|
// Get retrieves a particular security group rule based on its unique ID.
|
||||||
func Get(c *gophercloud.ServiceClient, id string) GetResult {
|
func Get(c *gophercloud.ServiceClient, id string) GetResult {
|
||||||
var res GetResult
|
var res GetResult
|
||||||
_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
|
_, res.Err = c.Get(resourceURL(c, id), &res.Body, nil)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete will permanently delete a particular security group based on its unique ID.
|
// Delete will permanently delete a particular security group rule based on its unique ID.
|
||||||
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
|
func Delete(c *gophercloud.ServiceClient, id string) DeleteResult {
|
||||||
var res DeleteResult
|
var res DeleteResult
|
||||||
_, res.Err = c.Delete(resourceURL(c, id), nil)
|
_, res.Err = c.Delete(resourceURL(c, id), nil)
|
||||||
|
@ -95,15 +95,16 @@ type CreateOptsBuilder interface {
|
|||||||
|
|
||||||
// CreateOpts represents the attributes used when creating a new port.
|
// CreateOpts represents the attributes used when creating a new port.
|
||||||
type CreateOpts struct {
|
type CreateOpts struct {
|
||||||
NetworkID string
|
NetworkID string
|
||||||
Name string
|
Name string
|
||||||
AdminStateUp *bool
|
AdminStateUp *bool
|
||||||
MACAddress string
|
MACAddress string
|
||||||
FixedIPs interface{}
|
FixedIPs interface{}
|
||||||
DeviceID string
|
DeviceID string
|
||||||
DeviceOwner string
|
DeviceOwner string
|
||||||
TenantID string
|
TenantID string
|
||||||
SecurityGroups []string
|
SecurityGroups []string
|
||||||
|
AllowedAddressPairs []AddressPair
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToPortCreateMap casts a CreateOpts struct to a map.
|
// ToPortCreateMap casts a CreateOpts struct to a map.
|
||||||
@ -139,6 +140,9 @@ func (opts CreateOpts) ToPortCreateMap() (map[string]interface{}, error) {
|
|||||||
if opts.MACAddress != "" {
|
if opts.MACAddress != "" {
|
||||||
p["mac_address"] = opts.MACAddress
|
p["mac_address"] = opts.MACAddress
|
||||||
}
|
}
|
||||||
|
if opts.AllowedAddressPairs != nil {
|
||||||
|
p["allowed_address_pairs"] = opts.AllowedAddressPairs
|
||||||
|
}
|
||||||
|
|
||||||
return map[string]interface{}{"port": p}, nil
|
return map[string]interface{}{"port": p}, nil
|
||||||
}
|
}
|
||||||
@ -168,12 +172,13 @@ type UpdateOptsBuilder interface {
|
|||||||
|
|
||||||
// UpdateOpts represents the attributes used when updating an existing port.
|
// UpdateOpts represents the attributes used when updating an existing port.
|
||||||
type UpdateOpts struct {
|
type UpdateOpts struct {
|
||||||
Name string
|
Name string
|
||||||
AdminStateUp *bool
|
AdminStateUp *bool
|
||||||
FixedIPs interface{}
|
FixedIPs interface{}
|
||||||
DeviceID string
|
DeviceID string
|
||||||
DeviceOwner string
|
DeviceOwner string
|
||||||
SecurityGroups []string
|
SecurityGroups []string
|
||||||
|
AllowedAddressPairs []AddressPair
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToPortUpdateMap casts an UpdateOpts struct to a map.
|
// ToPortUpdateMap casts an UpdateOpts struct to a map.
|
||||||
@ -198,6 +203,9 @@ func (opts UpdateOpts) ToPortUpdateMap() (map[string]interface{}, error) {
|
|||||||
if opts.Name != "" {
|
if opts.Name != "" {
|
||||||
p["name"] = opts.Name
|
p["name"] = opts.Name
|
||||||
}
|
}
|
||||||
|
if opts.AllowedAddressPairs != nil {
|
||||||
|
p["allowed_address_pairs"] = opts.AllowedAddressPairs
|
||||||
|
}
|
||||||
|
|
||||||
return map[string]interface{}{"port": p}, nil
|
return map[string]interface{}{"port": p}, nil
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ func (r commonResult) Extract() (*Port, error) {
|
|||||||
var res struct {
|
var res struct {
|
||||||
Port *Port `json:"port"`
|
Port *Port `json:"port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := mapstructure.Decode(r.Body, &res)
|
err := mapstructure.Decode(r.Body, &res)
|
||||||
|
|
||||||
return res.Port, err
|
return res.Port, err
|
||||||
@ -51,6 +50,11 @@ type IP struct {
|
|||||||
IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
|
IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AddressPair struct {
|
||||||
|
IPAddress string `mapstructure:"ip_address" json:"ip_address,omitempty"`
|
||||||
|
MACAddress string `mapstructure:"mac_address" json:"mac_address,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Port represents a Neutron port. See package documentation for a top-level
|
// Port represents a Neutron port. See package documentation for a top-level
|
||||||
// description of what this is.
|
// description of what this is.
|
||||||
type Port struct {
|
type Port struct {
|
||||||
@ -78,6 +82,8 @@ type Port struct {
|
|||||||
SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
|
SecurityGroups []string `mapstructure:"security_groups" json:"security_groups"`
|
||||||
// Identifies the device (e.g., virtual server) using this port.
|
// Identifies the device (e.g., virtual server) using this port.
|
||||||
DeviceID string `mapstructure:"device_id" json:"device_id"`
|
DeviceID string `mapstructure:"device_id" json:"device_id"`
|
||||||
|
// Identifies the list of IP addresses the port will recognize/accept
|
||||||
|
AllowedAddressPairs []AddressPair `mapstructure:"allowed_address_pairs" json:"allowed_address_pairs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PortPage is the page returned by a pager when traversing over a collection
|
// PortPage is the page returned by a pager when traversing over a collection
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bufio"
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -167,7 +168,7 @@ type CreateOpts struct {
|
|||||||
ObjectManifest string `h:"X-Object-Manifest"`
|
ObjectManifest string `h:"X-Object-Manifest"`
|
||||||
TransferEncoding string `h:"Transfer-Encoding"`
|
TransferEncoding string `h:"Transfer-Encoding"`
|
||||||
Expires string `q:"expires"`
|
Expires string `q:"expires"`
|
||||||
MultipartManifest string `q:"multiple-manifest"`
|
MultipartManifest string `q:"multipart-manifest"`
|
||||||
Signature string `q:"signature"`
|
Signature string `q:"signature"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,19 +214,20 @@ func Create(c *gophercloud.ServiceClient, containerName, objectName string, cont
|
|||||||
}
|
}
|
||||||
|
|
||||||
hash := md5.New()
|
hash := md5.New()
|
||||||
|
bufioReader := bufio.NewReader(io.TeeReader(content, hash))
|
||||||
|
io.Copy(ioutil.Discard, bufioReader)
|
||||||
|
localChecksum := hash.Sum(nil)
|
||||||
|
|
||||||
contentBuffer := bytes.NewBuffer([]byte{})
|
h["ETag"] = fmt.Sprintf("%x", localChecksum)
|
||||||
_, err := io.Copy(contentBuffer, io.TeeReader(content, hash))
|
|
||||||
|
_, err := content.Seek(0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Err = err
|
res.Err = err
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
localChecksum := hash.Sum(nil)
|
|
||||||
h["ETag"] = fmt.Sprintf("%x", localChecksum)
|
|
||||||
|
|
||||||
ropts := gophercloud.RequestOpts{
|
ropts := gophercloud.RequestOpts{
|
||||||
RawBody: strings.NewReader(contentBuffer.String()),
|
RawBody: content,
|
||||||
MoreHeaders: h,
|
MoreHeaders: h,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +163,8 @@ type ListResourceEventsOpts struct {
|
|||||||
SortDir SortDir `q:"sort_dir"`
|
SortDir SortDir `q:"sort_dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToResourceEventsListQuery formats a ListOpts into a query string.
|
// ToResourceEventListQuery formats a ListResourceEventsOpts into a query string.
|
||||||
func (opts ListOpts) ToResourceEventsListQuery() (string, error) {
|
func (opts ListResourceEventsOpts) ToResourceEventListQuery() (string, error) {
|
||||||
q, err := gophercloud.BuildQueryString(opts)
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -28,10 +28,13 @@ var FindExpected = []Resource{
|
|||||||
LogicalID: "hello_world",
|
LogicalID: "hello_world",
|
||||||
StatusReason: "state changed",
|
StatusReason: "state changed",
|
||||||
UpdatedTime: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
|
UpdatedTime: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
|
||||||
|
CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC),
|
||||||
RequiredBy: []interface{}{},
|
RequiredBy: []interface{}{},
|
||||||
Status: "CREATE_IN_PROGRESS",
|
Status: "CREATE_IN_PROGRESS",
|
||||||
PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf",
|
PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf",
|
||||||
Type: "OS::Nova::Server",
|
Type: "OS::Nova::Server",
|
||||||
|
Attributes: map[string]interface{}{"SXSW": "atx"},
|
||||||
|
Description: "Some resource",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +43,8 @@ const FindOutput = `
|
|||||||
{
|
{
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
|
"description": "Some resource",
|
||||||
|
"attributes": {"SXSW": "atx"},
|
||||||
"resource_name": "hello_world",
|
"resource_name": "hello_world",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
@ -54,6 +59,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:11",
|
"updated_time": "2015-02-05T21:33:11",
|
||||||
|
"creation_time": "2015-02-05T21:33:10",
|
||||||
"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",
|
||||||
@ -93,10 +99,13 @@ var ListExpected = []Resource{
|
|||||||
LogicalID: "hello_world",
|
LogicalID: "hello_world",
|
||||||
StatusReason: "state changed",
|
StatusReason: "state changed",
|
||||||
UpdatedTime: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
|
UpdatedTime: time.Date(2015, 2, 5, 21, 33, 11, 0, time.UTC),
|
||||||
|
CreationTime: time.Date(2015, 2, 5, 21, 33, 10, 0, time.UTC),
|
||||||
RequiredBy: []interface{}{},
|
RequiredBy: []interface{}{},
|
||||||
Status: "CREATE_IN_PROGRESS",
|
Status: "CREATE_IN_PROGRESS",
|
||||||
PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf",
|
PhysicalID: "49181cd6-169a-4130-9455-31185bbfc5bf",
|
||||||
Type: "OS::Nova::Server",
|
Type: "OS::Nova::Server",
|
||||||
|
Attributes: map[string]interface{}{"SXSW": "atx"},
|
||||||
|
Description: "Some resource",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +130,10 @@ const ListOutput = `{
|
|||||||
"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",
|
||||||
"resource_type": "OS::Nova::Server"
|
"creation_time": "2015-02-05T21:33:10",
|
||||||
|
"resource_type": "OS::Nova::Server",
|
||||||
|
"attributes": {"SXSW": "atx"},
|
||||||
|
"description": "Some resource"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
@ -162,6 +174,7 @@ var GetExpected = &Resource{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
LogicalID: "wordpress_instance",
|
LogicalID: "wordpress_instance",
|
||||||
|
Attributes: map[string]interface{}{"SXSW": "atx"},
|
||||||
StatusReason: "state changed",
|
StatusReason: "state changed",
|
||||||
UpdatedTime: time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC),
|
UpdatedTime: time.Date(2014, 12, 10, 18, 34, 35, 0, time.UTC),
|
||||||
RequiredBy: []interface{}{},
|
RequiredBy: []interface{}{},
|
||||||
@ -174,6 +187,8 @@ var GetExpected = &Resource{
|
|||||||
const GetOutput = `
|
const GetOutput = `
|
||||||
{
|
{
|
||||||
"resource": {
|
"resource": {
|
||||||
|
"description": "Some resource",
|
||||||
|
"attributes": {"SXSW": "atx"},
|
||||||
"resource_name": "wordpress_instance",
|
"resource_name": "wordpress_instance",
|
||||||
"description": "",
|
"description": "",
|
||||||
"links": [
|
"links": [
|
||||||
@ -240,7 +255,7 @@ func HandleMetadataSuccessfully(t *testing.T, output string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListTypesExpected represents the expected object from a ListTypes request.
|
// ListTypesExpected represents the expected object from a ListTypes request.
|
||||||
var ListTypesExpected = []string{
|
var ListTypesExpected = ResourceTypes{
|
||||||
"OS::Nova::Server",
|
"OS::Nova::Server",
|
||||||
"OS::Heat::RandomString",
|
"OS::Heat::RandomString",
|
||||||
"OS::Swift::Container",
|
"OS::Swift::Container",
|
||||||
@ -251,6 +266,18 @@ var ListTypesExpected = []string{
|
|||||||
"OS::Nova::KeyPair",
|
"OS::Nova::KeyPair",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// same as above, but sorted
|
||||||
|
var SortedListTypesExpected = ResourceTypes{
|
||||||
|
"OS::Cinder::VolumeAttachment",
|
||||||
|
"OS::Heat::RandomString",
|
||||||
|
"OS::Nova::FloatingIP",
|
||||||
|
"OS::Nova::FloatingIPAssociation",
|
||||||
|
"OS::Nova::KeyPair",
|
||||||
|
"OS::Nova::Server",
|
||||||
|
"OS::Swift::Container",
|
||||||
|
"OS::Trove::Instance",
|
||||||
|
}
|
||||||
|
|
||||||
// ListTypesOutput represents the response body from a ListTypes request.
|
// ListTypesOutput represents the response body from a ListTypes request.
|
||||||
const ListTypesOutput = `
|
const ListTypesOutput = `
|
||||||
{
|
{
|
||||||
@ -296,6 +323,11 @@ var GetSchemaExpected = &TypeSchema{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ResourceType: "OS::Heat::AResourceName",
|
ResourceType: "OS::Heat::AResourceName",
|
||||||
|
SupportStatus: map[string]interface{}{
|
||||||
|
"message": "A status message",
|
||||||
|
"status": "SUPPORTED",
|
||||||
|
"version": "2014.1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSchemaOutput represents the response body from a Schema request.
|
// GetSchemaOutput represents the response body from a Schema request.
|
||||||
@ -314,7 +346,12 @@ const GetSchemaOutput = `
|
|||||||
"description": "A resource description."
|
"description": "A resource description."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resource_type": "OS::Heat::AResourceName"
|
"resource_type": "OS::Heat::AResourceName",
|
||||||
|
"support_status": {
|
||||||
|
"message": "A status message",
|
||||||
|
"status": "SUPPORTED",
|
||||||
|
"version": "2014.1"
|
||||||
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
// HandleGetSchemaSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName`
|
// HandleGetSchemaSuccessfully creates an HTTP handler at `/resource_types/OS::Heat::AResourceName`
|
||||||
@ -332,56 +369,7 @@ func HandleGetSchemaSuccessfully(t *testing.T, output string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTemplateExpected represents the expected object from a Template request.
|
// GetTemplateExpected represents the expected object from a Template request.
|
||||||
var GetTemplateExpected = &TypeTemplate{
|
var GetTemplateExpected = "{\n \"HeatTemplateFormatVersion\": \"2012-12-12\",\n \"Outputs\": {\n \"private_key\": {\n \"Description\": \"The private key if it has been saved.\",\n \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"private_key\\\"]}\"\n },\n \"public_key\": {\n \"Description\": \"The public key.\",\n \"Value\": \"{\\\"Fn::GetAtt\\\": [\\\"KeyPair\\\", \\\"public_key\\\"]}\"\n }\n },\n \"Parameters\": {\n \"name\": {\n \"Description\": \"The name of the key pair.\",\n \"Type\": \"String\"\n },\n \"public_key\": {\n \"Description\": \"The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.\",\n \"Type\": \"String\"\n },\n \"save_private_key\": {\n \"AllowedValues\": [\n \"True\",\n \"true\",\n \"False\",\n \"false\"\n ],\n \"Default\": false,\n \"Description\": \"True if the system should remember a generated private key; False otherwise.\",\n \"Type\": \"String\"\n }\n },\n \"Resources\": {\n \"KeyPair\": {\n \"Properties\": {\n \"name\": {\n \"Ref\": \"name\"\n },\n \"public_key\": {\n \"Ref\": \"public_key\"\n },\n \"save_private_key\": {\n \"Ref\": \"save_private_key\"\n }\n },\n \"Type\": \"OS::Nova::KeyPair\"\n }\n }\n}"
|
||||||
HeatTemplateFormatVersion: "2012-12-12",
|
|
||||||
Outputs: map[string]interface{}{
|
|
||||||
"private_key": map[string]interface{}{
|
|
||||||
"Description": "The private key if it has been saved.",
|
|
||||||
"Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"private_key\"]}",
|
|
||||||
},
|
|
||||||
"public_key": map[string]interface{}{
|
|
||||||
"Description": "The public key.",
|
|
||||||
"Value": "{\"Fn::GetAtt\": [\"KeyPair\", \"public_key\"]}",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Parameters: map[string]interface{}{
|
|
||||||
"name": map[string]interface{}{
|
|
||||||
"Description": "The name of the key pair.",
|
|
||||||
"Type": "String",
|
|
||||||
},
|
|
||||||
"public_key": map[string]interface{}{
|
|
||||||
"Description": "The optional public key. This allows users to supply the public key from a pre-existing key pair. If not supplied, a new key pair will be generated.",
|
|
||||||
"Type": "String",
|
|
||||||
},
|
|
||||||
"save_private_key": map[string]interface{}{
|
|
||||||
"AllowedValues": []string{
|
|
||||||
"True",
|
|
||||||
"true",
|
|
||||||
"False",
|
|
||||||
"false",
|
|
||||||
},
|
|
||||||
"Default": false,
|
|
||||||
"Description": "True if the system should remember a generated private key; False otherwise.",
|
|
||||||
"Type": "String",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Resources: map[string]interface{}{
|
|
||||||
"KeyPair": map[string]interface{}{
|
|
||||||
"Properties": map[string]interface{}{
|
|
||||||
"name": map[string]interface{}{
|
|
||||||
"Ref": "name",
|
|
||||||
},
|
|
||||||
"public_key": map[string]interface{}{
|
|
||||||
"Ref": "public_key",
|
|
||||||
},
|
|
||||||
"save_private_key": map[string]interface{}{
|
|
||||||
"Ref": "save_private_key",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"Type": "OS::Nova::KeyPair",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTemplateOutput represents the response body from a Template request.
|
// GetTemplateOutput represents the response body from a Template request.
|
||||||
const GetTemplateOutput = `
|
const GetTemplateOutput = `
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package stackresources
|
package stackresources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
@ -12,15 +13,18 @@ import (
|
|||||||
|
|
||||||
// Resource represents a stack resource.
|
// Resource represents a stack resource.
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
Links []gophercloud.Link `mapstructure:"links"`
|
Attributes map[string]interface{} `mapstructure:"attributes"`
|
||||||
LogicalID string `mapstructure:"logical_resource_id"`
|
CreationTime time.Time `mapstructure:"-"`
|
||||||
Name string `mapstructure:"resource_name"`
|
Description string `mapstructure:"description"`
|
||||||
PhysicalID string `mapstructure:"physical_resource_id"`
|
Links []gophercloud.Link `mapstructure:"links"`
|
||||||
RequiredBy []interface{} `mapstructure:"required_by"`
|
LogicalID string `mapstructure:"logical_resource_id"`
|
||||||
Status string `mapstructure:"resource_status"`
|
Name string `mapstructure:"resource_name"`
|
||||||
StatusReason string `mapstructure:"resource_status_reason"`
|
PhysicalID string `mapstructure:"physical_resource_id"`
|
||||||
Type string `mapstructure:"resource_type"`
|
RequiredBy []interface{} `mapstructure:"required_by"`
|
||||||
UpdatedTime time.Time `mapstructure:"-"`
|
Status string `mapstructure:"resource_status"`
|
||||||
|
StatusReason string `mapstructure:"resource_status_reason"`
|
||||||
|
Type string `mapstructure:"resource_type"`
|
||||||
|
UpdatedTime time.Time `mapstructure:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindResult represents the result of a Find operation.
|
// FindResult represents the result of a Find operation.
|
||||||
@ -54,6 +58,13 @@ func (r FindResult) Extract() ([]Resource, error) {
|
|||||||
}
|
}
|
||||||
res.Res[i].UpdatedTime = t
|
res.Res[i].UpdatedTime = t
|
||||||
}
|
}
|
||||||
|
if date, ok := resource["creation_time"]; ok && date != nil {
|
||||||
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Res[i].CreationTime = t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Res, nil
|
return res.Res, nil
|
||||||
@ -75,18 +86,6 @@ func (r ResourcePage) IsEmpty() (bool, error) {
|
|||||||
return len(resources) == 0, nil
|
return len(resources) == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastMarker returns the last container name in a ListResult.
|
|
||||||
func (r ResourcePage) LastMarker() (string, error) {
|
|
||||||
resources, err := ExtractResources(r)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if len(resources) == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return resources[len(resources)-1].PhysicalID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractResources interprets the results of a single page from a List() call, producing a slice of Resource entities.
|
// ExtractResources interprets the results of a single page from a List() call, producing a slice of Resource entities.
|
||||||
func ExtractResources(page pagination.Page) ([]Resource, error) {
|
func ExtractResources(page pagination.Page) ([]Resource, error) {
|
||||||
casted := page.(ResourcePage).Body
|
casted := page.(ResourcePage).Body
|
||||||
@ -94,8 +93,9 @@ func ExtractResources(page pagination.Page) ([]Resource, error) {
|
|||||||
var response struct {
|
var response struct {
|
||||||
Resources []Resource `mapstructure:"resources"`
|
Resources []Resource `mapstructure:"resources"`
|
||||||
}
|
}
|
||||||
err := mapstructure.Decode(casted, &response)
|
if err := mapstructure.Decode(casted, &response); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
var resources []interface{}
|
var resources []interface{}
|
||||||
switch casted.(type) {
|
switch casted.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
@ -115,9 +115,16 @@ func ExtractResources(page pagination.Page) ([]Resource, error) {
|
|||||||
}
|
}
|
||||||
response.Resources[i].UpdatedTime = t
|
response.Resources[i].UpdatedTime = t
|
||||||
}
|
}
|
||||||
|
if date, ok := resource["creation_time"]; ok && date != nil {
|
||||||
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
response.Resources[i].CreationTime = t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Resources, err
|
return response.Resources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResult represents the result of a Get operation.
|
// GetResult represents the result of a Get operation.
|
||||||
@ -149,6 +156,13 @@ func (r GetResult) Extract() (*Resource, error) {
|
|||||||
}
|
}
|
||||||
res.Res.UpdatedTime = t
|
res.Res.UpdatedTime = t
|
||||||
}
|
}
|
||||||
|
if date, ok := resource["creation_time"]; ok && date != nil {
|
||||||
|
t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Res.CreationTime = t
|
||||||
|
}
|
||||||
|
|
||||||
return res.Res, nil
|
return res.Res, nil
|
||||||
}
|
}
|
||||||
@ -192,21 +206,42 @@ func (r ResourceTypePage) IsEmpty() (bool, error) {
|
|||||||
return len(rts) == 0, nil
|
return len(rts) == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceTypes represents the type that holds the result of ExtractResourceTypes.
|
||||||
|
// We define methods on this type to sort it before output
|
||||||
|
type ResourceTypes []string
|
||||||
|
|
||||||
|
func (r ResourceTypes) Len() int {
|
||||||
|
return len(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r ResourceTypes) Swap(i, j int) {
|
||||||
|
r[i], r[j] = r[j], r[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r ResourceTypes) Less(i, j int) bool {
|
||||||
|
return r[i] < r[j]
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractResourceTypes extracts and returns resource types.
|
// ExtractResourceTypes extracts and returns resource types.
|
||||||
func ExtractResourceTypes(page pagination.Page) ([]string, error) {
|
func ExtractResourceTypes(page pagination.Page) (ResourceTypes, error) {
|
||||||
|
casted := page.(ResourceTypePage).Body
|
||||||
|
|
||||||
var response struct {
|
var response struct {
|
||||||
ResourceTypes []string `mapstructure:"resource_types"`
|
ResourceTypes ResourceTypes `mapstructure:"resource_types"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := mapstructure.Decode(page.(ResourceTypePage).Body, &response)
|
if err := mapstructure.Decode(casted, &response); err != nil {
|
||||||
return response.ResourceTypes, err
|
return nil, err
|
||||||
|
}
|
||||||
|
return response.ResourceTypes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeSchema represents a stack resource schema.
|
// TypeSchema represents a stack resource schema.
|
||||||
type TypeSchema struct {
|
type TypeSchema struct {
|
||||||
Attributes map[string]interface{} `mapstructure:"attributes"`
|
Attributes map[string]interface{} `mapstructure:"attributes"`
|
||||||
Properties map[string]interface{} `mapstrucutre:"properties"`
|
Properties map[string]interface{} `mapstrucutre:"properties"`
|
||||||
ResourceType string `mapstructure:"resource_type"`
|
ResourceType string `mapstructure:"resource_type"`
|
||||||
|
SupportStatus map[string]interface{} `mapstructure:"support_status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SchemaResult represents the result of a Schema operation.
|
// SchemaResult represents the result of a Schema operation.
|
||||||
@ -230,31 +265,20 @@ func (r SchemaResult) Extract() (*TypeSchema, error) {
|
|||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeTemplate represents a stack resource template.
|
|
||||||
type TypeTemplate struct {
|
|
||||||
HeatTemplateFormatVersion string
|
|
||||||
Outputs map[string]interface{}
|
|
||||||
Parameters map[string]interface{}
|
|
||||||
Resources map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TemplateResult represents the result of a Template operation.
|
// TemplateResult represents the result of a Template operation.
|
||||||
type TemplateResult struct {
|
type TemplateResult struct {
|
||||||
gophercloud.Result
|
gophercloud.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract returns a pointer to a TypeTemplate object and is called after a
|
// Extract returns the template and is called after a
|
||||||
// Template operation.
|
// Template operation.
|
||||||
func (r TemplateResult) Extract() (*TypeTemplate, error) {
|
func (r TemplateResult) Extract() ([]byte, error) {
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return nil, r.Err
|
return nil, r.Err
|
||||||
}
|
}
|
||||||
|
template, err := json.MarshalIndent(r.Body, "", " ")
|
||||||
var res TypeTemplate
|
if err != nil {
|
||||||
|
|
||||||
if err := mapstructure.Decode(r.Body, &res); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return template, nil
|
||||||
return &res, nil
|
|
||||||
}
|
}
|
||||||
|
137
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/environment.go
generated
vendored
Normal file
137
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/environment.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package stacks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Environment is a structure that represents stack environments
|
||||||
|
type Environment struct {
|
||||||
|
TE
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnvironmentSections is a map containing allowed sections in a stack environment file
|
||||||
|
var EnvironmentSections = map[string]bool{
|
||||||
|
"parameters": true,
|
||||||
|
"parameter_defaults": true,
|
||||||
|
"resource_registry": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the contents of the Environment
|
||||||
|
func (e *Environment) Validate() error {
|
||||||
|
if e.Parsed == nil {
|
||||||
|
if err := e.Parse(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key := range e.Parsed {
|
||||||
|
if _, ok := EnvironmentSections[key]; !ok {
|
||||||
|
return fmt.Errorf("Environment has wrong section: %s", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse environment file to resolve the URL's of the resources. This is done by
|
||||||
|
// reading from the `Resource Registry` section, which is why the function is
|
||||||
|
// named GetRRFileContents.
|
||||||
|
func (e *Environment) getRRFileContents(ignoreIf igFunc) error {
|
||||||
|
// initialize environment if empty
|
||||||
|
if e.Files == nil {
|
||||||
|
e.Files = make(map[string]string)
|
||||||
|
}
|
||||||
|
if e.fileMaps == nil {
|
||||||
|
e.fileMaps = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the resource registry
|
||||||
|
rr := e.Parsed["resource_registry"]
|
||||||
|
|
||||||
|
// search the resource registry for URLs
|
||||||
|
switch rr.(type) {
|
||||||
|
// process further only if the resource registry is a map
|
||||||
|
case map[string]interface{}, map[interface{}]interface{}:
|
||||||
|
rrMap, err := toStringKeys(rr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// the resource registry might contain a base URL for the resource. If
|
||||||
|
// such a field is present, use it. Otherwise, use the default base URL.
|
||||||
|
var baseURL string
|
||||||
|
if val, ok := rrMap["base_url"]; ok {
|
||||||
|
baseURL = val.(string)
|
||||||
|
} else {
|
||||||
|
baseURL = e.baseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// The contents of the resource may be located in a remote file, which
|
||||||
|
// will be a template. Instantiate a temporary template to manage the
|
||||||
|
// contents.
|
||||||
|
tempTemplate := new(Template)
|
||||||
|
tempTemplate.baseURL = baseURL
|
||||||
|
tempTemplate.client = e.client
|
||||||
|
|
||||||
|
// Fetch the contents of remote resource URL's
|
||||||
|
if err = tempTemplate.getFileContents(rr, ignoreIf, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// check the `resources` section (if it exists) for more URL's. Note that
|
||||||
|
// the previous call to GetFileContents was (deliberately) not recursive
|
||||||
|
// as we want more control over where to look for URL's
|
||||||
|
if val, ok := rrMap["resources"]; ok {
|
||||||
|
switch val.(type) {
|
||||||
|
// process further only if the contents are a map
|
||||||
|
case map[string]interface{}, map[interface{}]interface{}:
|
||||||
|
resourcesMap, err := toStringKeys(val)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range resourcesMap {
|
||||||
|
switch v.(type) {
|
||||||
|
case map[string]interface{}, map[interface{}]interface{}:
|
||||||
|
resourceMap, err := toStringKeys(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var resourceBaseURL string
|
||||||
|
// if base_url for the resource type is defined, use it
|
||||||
|
if val, ok := resourceMap["base_url"]; ok {
|
||||||
|
resourceBaseURL = val.(string)
|
||||||
|
} else {
|
||||||
|
resourceBaseURL = baseURL
|
||||||
|
}
|
||||||
|
tempTemplate.baseURL = resourceBaseURL
|
||||||
|
if err := tempTemplate.getFileContents(v, ignoreIf, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the resource registry contained any URL's, store them. This can
|
||||||
|
// then be passed as parameter to api calls to Heat api.
|
||||||
|
e.Files = tempTemplate.Files
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to choose keys whose values are other environment files
|
||||||
|
func ignoreIfEnvironment(key string, value interface{}) bool {
|
||||||
|
// base_url and hooks refer to components which cannot have urls
|
||||||
|
if key == "base_url" || key == "hooks" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// if value is not string, it cannot be a URL
|
||||||
|
valueString, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// if value contains `::`, it must be a reference to another resource type
|
||||||
|
// e.g. OS::Nova::Server : Rackspace::Cloud::Server
|
||||||
|
if strings.Contains(valueString, "::") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -63,6 +63,7 @@ var ListExpected = []ListedStack{
|
|||||||
CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
|
CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
|
||||||
Status: "CREATE_COMPLETE",
|
Status: "CREATE_COMPLETE",
|
||||||
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
||||||
|
Tags: []string{"rackspace", "atx"},
|
||||||
},
|
},
|
||||||
ListedStack{
|
ListedStack{
|
||||||
Description: "Simple template to test heat commands",
|
Description: "Simple template to test heat commands",
|
||||||
@ -78,6 +79,7 @@ var ListExpected = []ListedStack{
|
|||||||
UpdatedTime: time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC),
|
UpdatedTime: time.Date(2014, 12, 11, 17, 40, 37, 0, time.UTC),
|
||||||
Status: "UPDATE_COMPLETE",
|
Status: "UPDATE_COMPLETE",
|
||||||
ID: "db6977b2-27aa-4775-9ae7-6213212d4ada",
|
ID: "db6977b2-27aa-4775-9ae7-6213212d4ada",
|
||||||
|
Tags: []string{"sfo", "satx"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +100,8 @@ const FullListOutput = `
|
|||||||
"creation_time": "2015-02-03T20:07:39",
|
"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",
|
||||||
|
"tags": ["rackspace", "atx"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Simple template to test heat commands",
|
"description": "Simple template to test heat commands",
|
||||||
@ -113,7 +116,8 @@ const FullListOutput = `
|
|||||||
"creation_time": "2014-12-11T17:39:16",
|
"creation_time": "2014-12-11T17:39:16",
|
||||||
"updated_time": "2014-12-11T17:40:37",
|
"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",
|
||||||
|
"tags": ["sfo", "satx"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -165,6 +169,7 @@ var GetExpected = &RetrievedStack{
|
|||||||
Status: "CREATE_COMPLETE",
|
Status: "CREATE_COMPLETE",
|
||||||
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
||||||
TemplateDescription: "Simple template to test heat commands",
|
TemplateDescription: "Simple template to test heat commands",
|
||||||
|
Tags: []string{"rackspace", "atx"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOutput represents the response body from a Get request.
|
// GetOutput represents the response body from a Get request.
|
||||||
@ -194,7 +199,8 @@ const GetOutput = `
|
|||||||
"stack_status": "CREATE_COMPLETE",
|
"stack_status": "CREATE_COMPLETE",
|
||||||
"updated_time": null,
|
"updated_time": null,
|
||||||
"id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
"id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
||||||
"template_description": "Simple template to test heat commands"
|
"template_description": "Simple template to test heat commands",
|
||||||
|
"tags": ["rackspace", "atx"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -248,7 +254,6 @@ var PreviewExpected = &PreviewedStack{
|
|||||||
"OS::stack_name": "postman_stack",
|
"OS::stack_name": "postman_stack",
|
||||||
"OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
"OS::stack_id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
||||||
},
|
},
|
||||||
StatusReason: "Stack CREATE completed successfully",
|
|
||||||
Name: "postman_stack",
|
Name: "postman_stack",
|
||||||
CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
|
CreationTime: time.Date(2015, 2, 3, 20, 7, 39, 0, time.UTC),
|
||||||
Links: []gophercloud.Link{
|
Links: []gophercloud.Link{
|
||||||
@ -259,7 +264,6 @@ var PreviewExpected = &PreviewedStack{
|
|||||||
},
|
},
|
||||||
Capabilities: []interface{}{},
|
Capabilities: []interface{}{},
|
||||||
NotificationTopics: []interface{}{},
|
NotificationTopics: []interface{}{},
|
||||||
Status: "CREATE_COMPLETE",
|
|
||||||
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
ID: "16ef0584-4458-41eb-87c8-0dc8d5f66c87",
|
||||||
TemplateDescription: "Simple template to test heat commands",
|
TemplateDescription: "Simple template to test heat commands",
|
||||||
}
|
}
|
||||||
@ -316,6 +320,20 @@ var AbandonExpected = &AbandonedStack{
|
|||||||
"type": "OS::Nova::Server",
|
"type": "OS::Nova::Server",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Files: map[string]string{
|
||||||
|
"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n flavor:\n type: string\n description: Flavor for the server to be created\n default: 4353\n hidden: true\nresources:\n test_server:\n type: \"OS::Nova::Server\"\n properties:\n name: test-server\n flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n",
|
||||||
|
},
|
||||||
|
StackUserProjectID: "897686",
|
||||||
|
ProjectID: "897686",
|
||||||
|
Environment: map[string]interface{}{
|
||||||
|
"encrypted_param_names": make([]map[string]interface{}, 0),
|
||||||
|
"parameter_defaults": make(map[string]interface{}),
|
||||||
|
"parameters": make(map[string]interface{}),
|
||||||
|
"resource_registry": map[string]interface{}{
|
||||||
|
"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
|
||||||
|
"resources": make(map[string]interface{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbandonOutput represents the response body from an Abandon request.
|
// AbandonOutput represents the response body from an Abandon request.
|
||||||
@ -354,21 +372,233 @@ const AbandonOutput = `
|
|||||||
"name": "hello_world",
|
"name": "hello_world",
|
||||||
"resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
|
"resource_id": "8a310d36-46fc-436f-8be4-37a696b8ac63",
|
||||||
"action": "CREATE",
|
"action": "CREATE",
|
||||||
"type": "OS::Nova::Server",
|
"type": "OS::Nova::Server"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"files": {
|
||||||
|
"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "heat_template_version: 2014-10-16\nparameters:\n flavor:\n type: string\n description: Flavor for the server to be created\n default: 4353\n hidden: true\nresources:\n test_server:\n type: \"OS::Nova::Server\"\n properties:\n name: test-server\n flavor: 2 GB General Purpose v1\n image: Debian 7 (Wheezy) (PVHVM)\n"
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"encrypted_param_names": [],
|
||||||
|
"parameter_defaults": {},
|
||||||
|
"parameters": {},
|
||||||
|
"resource_registry": {
|
||||||
|
"file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml": "file:///Users/prat8228/go/src/github.com/rackspace/rack/my_nova.yaml",
|
||||||
|
"resources": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stack_user_project_id": "897686",
|
||||||
|
"project_id": "897686"
|
||||||
}`
|
}`
|
||||||
|
|
||||||
// HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon`
|
// HandleAbandonSuccessfully creates an HTTP handler at `/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon`
|
||||||
// on the test handler mux that responds with an `Abandon` response.
|
// on the test handler mux that responds with an `Abandon` response.
|
||||||
func HandleAbandonSuccessfully(t *testing.T) {
|
func HandleAbandonSuccessfully(t *testing.T, output string) {
|
||||||
th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87/abandon", func(w http.ResponseWriter, r *http.Request) {
|
th.Mux.HandleFunc("/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c8/abandon", func(w http.ResponseWriter, r *http.Request) {
|
||||||
th.TestMethod(t, r, "DELETE")
|
th.TestMethod(t, r, "DELETE")
|
||||||
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
th.TestHeader(t, r, "Accept", "application/json")
|
th.TestHeader(t, r, "Accept", "application/json")
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
fmt.Fprintf(w, AbandonOutput)
|
fmt.Fprintf(w, output)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidJSONTemplate is a valid OpenStack Heat template in JSON format
|
||||||
|
const ValidJSONTemplate = `
|
||||||
|
{
|
||||||
|
"heat_template_version": "2014-10-16",
|
||||||
|
"parameters": {
|
||||||
|
"flavor": {
|
||||||
|
"default": 4353,
|
||||||
|
"description": "Flavor for the server to be created",
|
||||||
|
"hidden": true,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"test_server": {
|
||||||
|
"properties": {
|
||||||
|
"flavor": "2 GB General Purpose v1",
|
||||||
|
"image": "Debian 7 (Wheezy) (PVHVM)",
|
||||||
|
"name": "test-server"
|
||||||
|
},
|
||||||
|
"type": "OS::Nova::Server"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// ValidJSONTemplateParsed is the expected parsed version of ValidJSONTemplate
|
||||||
|
var ValidJSONTemplateParsed = map[string]interface{}{
|
||||||
|
"heat_template_version": "2014-10-16",
|
||||||
|
"parameters": map[string]interface{}{
|
||||||
|
"flavor": map[string]interface{}{
|
||||||
|
"default": 4353,
|
||||||
|
"description": "Flavor for the server to be created",
|
||||||
|
"hidden": true,
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"resources": map[string]interface{}{
|
||||||
|
"test_server": map[string]interface{}{
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"flavor": "2 GB General Purpose v1",
|
||||||
|
"image": "Debian 7 (Wheezy) (PVHVM)",
|
||||||
|
"name": "test-server",
|
||||||
|
},
|
||||||
|
"type": "OS::Nova::Server",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidYAMLTemplate is a valid OpenStack Heat template in YAML format
|
||||||
|
const ValidYAMLTemplate = `
|
||||||
|
heat_template_version: 2014-10-16
|
||||||
|
parameters:
|
||||||
|
flavor:
|
||||||
|
type: string
|
||||||
|
description: Flavor for the server to be created
|
||||||
|
default: 4353
|
||||||
|
hidden: true
|
||||||
|
resources:
|
||||||
|
test_server:
|
||||||
|
type: "OS::Nova::Server"
|
||||||
|
properties:
|
||||||
|
name: test-server
|
||||||
|
flavor: 2 GB General Purpose v1
|
||||||
|
image: Debian 7 (Wheezy) (PVHVM)
|
||||||
|
`
|
||||||
|
|
||||||
|
// InvalidTemplateNoVersion is an invalid template as it has no `version` section
|
||||||
|
const InvalidTemplateNoVersion = `
|
||||||
|
parameters:
|
||||||
|
flavor:
|
||||||
|
type: string
|
||||||
|
description: Flavor for the server to be created
|
||||||
|
default: 4353
|
||||||
|
hidden: true
|
||||||
|
resources:
|
||||||
|
test_server:
|
||||||
|
type: "OS::Nova::Server"
|
||||||
|
properties:
|
||||||
|
name: test-server
|
||||||
|
flavor: 2 GB General Purpose v1
|
||||||
|
image: Debian 7 (Wheezy) (PVHVM)
|
||||||
|
`
|
||||||
|
|
||||||
|
// ValidJSONEnvironment is a valid environment for a stack in JSON format
|
||||||
|
const ValidJSONEnvironment = `
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"user_key": "userkey"
|
||||||
|
},
|
||||||
|
"resource_registry": {
|
||||||
|
"My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml",
|
||||||
|
"OS::Quantum*": "OS::Neutron*",
|
||||||
|
"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml",
|
||||||
|
"OS::Metering::Alarm": "OS::Ceilometer::Alarm",
|
||||||
|
"AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml",
|
||||||
|
"resources": {
|
||||||
|
"my_db_server": {
|
||||||
|
"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml"
|
||||||
|
},
|
||||||
|
"my_server": {
|
||||||
|
"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
|
||||||
|
"hooks": "pre-create"
|
||||||
|
},
|
||||||
|
"nested_stack": {
|
||||||
|
"nested_resource": {
|
||||||
|
"hooks": "pre-update"
|
||||||
|
},
|
||||||
|
"another_resource": {
|
||||||
|
"hooks": [
|
||||||
|
"pre-create",
|
||||||
|
"pre-update"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// ValidJSONEnvironmentParsed is the expected parsed version of ValidJSONEnvironment
|
||||||
|
var ValidJSONEnvironmentParsed = map[string]interface{}{
|
||||||
|
"parameters": map[string]interface{}{
|
||||||
|
"user_key": "userkey",
|
||||||
|
},
|
||||||
|
"resource_registry": map[string]interface{}{
|
||||||
|
"My::WP::Server": "file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml",
|
||||||
|
"OS::Quantum*": "OS::Neutron*",
|
||||||
|
"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml",
|
||||||
|
"OS::Metering::Alarm": "OS::Ceilometer::Alarm",
|
||||||
|
"AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml",
|
||||||
|
"resources": map[string]interface{}{
|
||||||
|
"my_db_server": map[string]interface{}{
|
||||||
|
"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
|
||||||
|
},
|
||||||
|
"my_server": map[string]interface{}{
|
||||||
|
"OS::DBInstance": "file:///home/mine/all_my_cool_templates/db.yaml",
|
||||||
|
"hooks": "pre-create",
|
||||||
|
},
|
||||||
|
"nested_stack": map[string]interface{}{
|
||||||
|
"nested_resource": map[string]interface{}{
|
||||||
|
"hooks": "pre-update",
|
||||||
|
},
|
||||||
|
"another_resource": map[string]interface{}{
|
||||||
|
"hooks": []interface{}{
|
||||||
|
"pre-create",
|
||||||
|
"pre-update",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidYAMLEnvironment is a valid environment for a stack in YAML format
|
||||||
|
const ValidYAMLEnvironment = `
|
||||||
|
parameters:
|
||||||
|
user_key: userkey
|
||||||
|
resource_registry:
|
||||||
|
My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml
|
||||||
|
# allow older templates with Quantum in them.
|
||||||
|
"OS::Quantum*": "OS::Neutron*"
|
||||||
|
# Choose your implementation of AWS::CloudWatch::Alarm
|
||||||
|
"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
|
||||||
|
#"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm"
|
||||||
|
"OS::Metering::Alarm": "OS::Ceilometer::Alarm"
|
||||||
|
"AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml"
|
||||||
|
resources:
|
||||||
|
my_db_server:
|
||||||
|
"OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml
|
||||||
|
my_server:
|
||||||
|
"OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml
|
||||||
|
hooks: pre-create
|
||||||
|
nested_stack:
|
||||||
|
nested_resource:
|
||||||
|
hooks: pre-update
|
||||||
|
another_resource:
|
||||||
|
hooks: [pre-create, pre-update]
|
||||||
|
`
|
||||||
|
|
||||||
|
// InvalidEnvironment is an invalid environment as it has an extra section called `resources`
|
||||||
|
const InvalidEnvironment = `
|
||||||
|
parameters:
|
||||||
|
flavor:
|
||||||
|
type: string
|
||||||
|
description: Flavor for the server to be created
|
||||||
|
default: 4353
|
||||||
|
hidden: true
|
||||||
|
resources:
|
||||||
|
test_server:
|
||||||
|
type: "OS::Nova::Server"
|
||||||
|
properties:
|
||||||
|
name: test-server
|
||||||
|
flavor: 2 GB General Purpose v1
|
||||||
|
image: Debian 7 (Wheezy) (PVHVM)
|
||||||
|
parameter_defaults:
|
||||||
|
KeyName: heat_key
|
||||||
|
`
|
||||||
|
@ -2,6 +2,7 @@ package stacks
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
@ -32,9 +33,16 @@ type CreateOptsBuilder interface {
|
|||||||
type CreateOpts struct {
|
type CreateOpts struct {
|
||||||
// (REQUIRED) The name of the stack. It must start with an alphabetic character.
|
// (REQUIRED) The name of the stack. It must start with an alphabetic character.
|
||||||
Name string
|
Name string
|
||||||
|
// (REQUIRED) A structure that contains either the template file or url. Call the
|
||||||
|
// associated methods to extract the information relevant to send in a create request.
|
||||||
|
TemplateOpts *Template
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, TemplateURL will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
||||||
// This value is ignored if Template is supplied inline.
|
// This value is ignored if Template is supplied inline.
|
||||||
TemplateURL string
|
TemplateURL string
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, Template will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
||||||
// is a stringified version of the JSON/YAML template. Since the template will likely
|
// is a stringified version of the JSON/YAML template. Since the template will likely
|
||||||
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
||||||
@ -50,8 +58,14 @@ type CreateOpts struct {
|
|||||||
// creation fails. Default is true, meaning all resources are not deleted when
|
// creation fails. Default is true, meaning all resources are not deleted when
|
||||||
// stack creation fails.
|
// stack creation fails.
|
||||||
DisableRollback Rollback
|
DisableRollback Rollback
|
||||||
|
// (OPTIONAL) A structure that contains details for the environment of the stack.
|
||||||
|
EnvironmentOpts *Environment
|
||||||
|
// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
|
||||||
// (OPTIONAL) A stringified JSON environment for the stack.
|
// (OPTIONAL) A stringified JSON environment for the stack.
|
||||||
Environment string
|
Environment string
|
||||||
|
// (DEPRECATED): Files is automatically determined
|
||||||
|
// by parsing the template and environment passed as TemplateOpts and
|
||||||
|
// EnvironmentOpts respectively.
|
||||||
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
||||||
// to pass provider template contents. Example:
|
// to pass provider template contents. Example:
|
||||||
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
||||||
@ -60,6 +74,8 @@ type CreateOpts struct {
|
|||||||
Parameters map[string]string
|
Parameters map[string]string
|
||||||
// (OPTIONAL) The timeout for stack creation in minutes.
|
// (OPTIONAL) The timeout for stack creation in minutes.
|
||||||
Timeout int
|
Timeout int
|
||||||
|
// (OPTIONAL) A list of tags to assosciate with the Stack
|
||||||
|
Tags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackCreateMap casts a CreateOpts struct to a map.
|
// ToStackCreateMap casts a CreateOpts struct to a map.
|
||||||
@ -70,25 +86,60 @@ func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) {
|
|||||||
return s, errors.New("Required field 'Name' not provided.")
|
return s, errors.New("Required field 'Name' not provided.")
|
||||||
}
|
}
|
||||||
s["stack_name"] = opts.Name
|
s["stack_name"] = opts.Name
|
||||||
|
Files := make(map[string]string)
|
||||||
if opts.Template != "" {
|
if opts.TemplateOpts == nil {
|
||||||
s["template"] = opts.Template
|
if opts.Template != "" {
|
||||||
} else if opts.TemplateURL != "" {
|
s["template"] = opts.Template
|
||||||
s["template_url"] = opts.TemplateURL
|
} else if opts.TemplateURL != "" {
|
||||||
|
s["template_url"] = opts.TemplateURL
|
||||||
|
} else {
|
||||||
|
return s, errors.New("Either Template or TemplateURL must be provided.")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return s, errors.New("Either Template or TemplateURL must be provided.")
|
if err := opts.TemplateOpts.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.TemplateOpts.fixFileRefs()
|
||||||
|
s["template"] = string(opts.TemplateOpts.Bin)
|
||||||
|
|
||||||
|
for k, v := range opts.TemplateOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.DisableRollback != nil {
|
||||||
|
s["disable_rollback"] = &opts.DisableRollback
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.EnvironmentOpts != nil {
|
||||||
|
if err := opts.EnvironmentOpts.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.EnvironmentOpts.fixFileRefs()
|
||||||
|
for k, v := range opts.EnvironmentOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
|
s["environment"] = string(opts.EnvironmentOpts.Bin)
|
||||||
|
} else if opts.Environment != "" {
|
||||||
|
s["environment"] = opts.Environment
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Files != nil {
|
||||||
|
s["files"] = opts.Files
|
||||||
|
} else {
|
||||||
|
s["files"] = Files
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.DisableRollback != nil {
|
if opts.DisableRollback != nil {
|
||||||
s["disable_rollback"] = &opts.DisableRollback
|
s["disable_rollback"] = &opts.DisableRollback
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Environment != "" {
|
|
||||||
s["environment"] = opts.Environment
|
|
||||||
}
|
|
||||||
if opts.Files != nil {
|
|
||||||
s["files"] = opts.Files
|
|
||||||
}
|
|
||||||
if opts.Parameters != nil {
|
if opts.Parameters != nil {
|
||||||
s["parameters"] = opts.Parameters
|
s["parameters"] = opts.Parameters
|
||||||
}
|
}
|
||||||
@ -97,6 +148,9 @@ func (opts CreateOpts) ToStackCreateMap() (map[string]interface{}, error) {
|
|||||||
s["timeout_mins"] = opts.Timeout
|
s["timeout_mins"] = opts.Timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Tags != nil {
|
||||||
|
s["tags"] = strings.Join(opts.Tags, ",")
|
||||||
|
}
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,9 +187,16 @@ type AdoptOpts struct {
|
|||||||
Name string
|
Name string
|
||||||
// (REQUIRED) The timeout for stack creation in minutes.
|
// (REQUIRED) The timeout for stack creation in minutes.
|
||||||
Timeout int
|
Timeout int
|
||||||
|
// (REQUIRED) A structure that contains either the template file or url. Call the
|
||||||
|
// associated methods to extract the information relevant to send in a create request.
|
||||||
|
TemplateOpts *Template
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, TemplateURL will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
||||||
// This value is ignored if Template is supplied inline.
|
// This value is ignored if Template is supplied inline.
|
||||||
TemplateURL string
|
TemplateURL string
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, Template will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
||||||
// is a stringified version of the JSON/YAML template. Since the template will likely
|
// is a stringified version of the JSON/YAML template. Since the template will likely
|
||||||
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
||||||
@ -151,8 +212,14 @@ type AdoptOpts struct {
|
|||||||
// creation fails. Default is true, meaning all resources are not deleted when
|
// creation fails. Default is true, meaning all resources are not deleted when
|
||||||
// stack creation fails.
|
// stack creation fails.
|
||||||
DisableRollback Rollback
|
DisableRollback Rollback
|
||||||
|
// (OPTIONAL) A structure that contains details for the environment of the stack.
|
||||||
|
EnvironmentOpts *Environment
|
||||||
|
// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
|
||||||
// (OPTIONAL) A stringified JSON environment for the stack.
|
// (OPTIONAL) A stringified JSON environment for the stack.
|
||||||
Environment string
|
Environment string
|
||||||
|
// (DEPRECATED): Files is automatically determined
|
||||||
|
// by parsing the template and environment passed as TemplateOpts and
|
||||||
|
// EnvironmentOpts respectively.
|
||||||
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
||||||
// to pass provider template contents. Example:
|
// to pass provider template contents. Example:
|
||||||
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
||||||
@ -169,40 +236,69 @@ func (opts AdoptOpts) ToStackAdoptMap() (map[string]interface{}, error) {
|
|||||||
return s, errors.New("Required field 'Name' not provided.")
|
return s, errors.New("Required field 'Name' not provided.")
|
||||||
}
|
}
|
||||||
s["stack_name"] = opts.Name
|
s["stack_name"] = opts.Name
|
||||||
|
Files := make(map[string]string)
|
||||||
if opts.Template != "" {
|
if opts.AdoptStackData != "" {
|
||||||
s["template"] = opts.Template
|
s["adopt_stack_data"] = opts.AdoptStackData
|
||||||
} else if opts.TemplateURL != "" {
|
} else if opts.TemplateOpts == nil {
|
||||||
s["template_url"] = opts.TemplateURL
|
if opts.Template != "" {
|
||||||
|
s["template"] = opts.Template
|
||||||
|
} else if opts.TemplateURL != "" {
|
||||||
|
s["template_url"] = opts.TemplateURL
|
||||||
|
} else {
|
||||||
|
return s, errors.New("One of AdoptStackData, Template, TemplateURL or TemplateOpts must be provided.")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return s, errors.New("Either Template or TemplateURL must be provided.")
|
if err := opts.TemplateOpts.Parse(); err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if opts.AdoptStackData == "" {
|
if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
|
||||||
return s, errors.New("Required field 'AdoptStackData' not provided.")
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.TemplateOpts.fixFileRefs()
|
||||||
|
s["template"] = string(opts.TemplateOpts.Bin)
|
||||||
|
|
||||||
|
for k, v := range opts.TemplateOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s["adopt_stack_data"] = opts.AdoptStackData
|
|
||||||
|
|
||||||
if opts.DisableRollback != nil {
|
if opts.DisableRollback != nil {
|
||||||
s["disable_rollback"] = &opts.DisableRollback
|
s["disable_rollback"] = &opts.DisableRollback
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Environment != "" {
|
if opts.EnvironmentOpts != nil {
|
||||||
|
if err := opts.EnvironmentOpts.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.EnvironmentOpts.fixFileRefs()
|
||||||
|
for k, v := range opts.EnvironmentOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
|
s["environment"] = string(opts.EnvironmentOpts.Bin)
|
||||||
|
} else if opts.Environment != "" {
|
||||||
s["environment"] = opts.Environment
|
s["environment"] = opts.Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Files != nil {
|
if opts.Files != nil {
|
||||||
s["files"] = opts.Files
|
s["files"] = opts.Files
|
||||||
|
} else {
|
||||||
|
s["files"] = Files
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Parameters != nil {
|
if opts.Parameters != nil {
|
||||||
s["parameters"] = opts.Parameters
|
s["parameters"] = opts.Parameters
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Timeout == 0 {
|
if opts.Timeout != 0 {
|
||||||
return nil, errors.New("Required field 'Timeout' not provided.")
|
s["timeout"] = opts.Timeout
|
||||||
}
|
}
|
||||||
s["timeout_mins"] = opts.Timeout
|
s["timeout_mins"] = opts.Timeout
|
||||||
|
|
||||||
return map[string]interface{}{"stack": s}, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adopt accepts an AdoptOpts struct and creates a new stack using the resources
|
// Adopt accepts an AdoptOpts struct and creates a new stack using the resources
|
||||||
@ -305,9 +401,16 @@ type UpdateOptsBuilder interface {
|
|||||||
// UpdateOpts contains the common options struct used in this package's Update
|
// UpdateOpts contains the common options struct used in this package's Update
|
||||||
// operation.
|
// operation.
|
||||||
type UpdateOpts struct {
|
type UpdateOpts struct {
|
||||||
|
// (REQUIRED) A structure that contains either the template file or url. Call the
|
||||||
|
// associated methods to extract the information relevant to send in a create request.
|
||||||
|
TemplateOpts *Template
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, TemplateURL will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
||||||
// This value is ignored if Template is supplied inline.
|
// This value is ignored if Template is supplied inline.
|
||||||
TemplateURL string
|
TemplateURL string
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, Template will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
||||||
// is a stringified version of the JSON/YAML template. Since the template will likely
|
// is a stringified version of the JSON/YAML template. Since the template will likely
|
||||||
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
||||||
@ -319,8 +422,14 @@ type UpdateOpts struct {
|
|||||||
// }
|
// }
|
||||||
// opts.Template = string(b)
|
// opts.Template = string(b)
|
||||||
Template string
|
Template string
|
||||||
|
// (OPTIONAL) A structure that contains details for the environment of the stack.
|
||||||
|
EnvironmentOpts *Environment
|
||||||
|
// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
|
||||||
// (OPTIONAL) A stringified JSON environment for the stack.
|
// (OPTIONAL) A stringified JSON environment for the stack.
|
||||||
Environment string
|
Environment string
|
||||||
|
// (DEPRECATED): Files is automatically determined
|
||||||
|
// by parsing the template and environment passed as TemplateOpts and
|
||||||
|
// EnvironmentOpts respectively.
|
||||||
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
||||||
// to pass provider template contents. Example:
|
// to pass provider template contents. Example:
|
||||||
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
||||||
@ -329,26 +438,58 @@ type UpdateOpts struct {
|
|||||||
Parameters map[string]string
|
Parameters map[string]string
|
||||||
// (OPTIONAL) The timeout for stack creation in minutes.
|
// (OPTIONAL) The timeout for stack creation in minutes.
|
||||||
Timeout int
|
Timeout int
|
||||||
|
// (OPTIONAL) A list of tags to assosciate with the Stack
|
||||||
|
Tags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackUpdateMap casts a CreateOpts struct to a map.
|
// ToStackUpdateMap casts a CreateOpts struct to a map.
|
||||||
func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
|
func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
|
||||||
s := make(map[string]interface{})
|
s := make(map[string]interface{})
|
||||||
|
Files := make(map[string]string)
|
||||||
if opts.Template != "" {
|
if opts.TemplateOpts == nil {
|
||||||
s["template"] = opts.Template
|
if opts.Template != "" {
|
||||||
} else if opts.TemplateURL != "" {
|
s["template"] = opts.Template
|
||||||
s["template_url"] = opts.TemplateURL
|
} else if opts.TemplateURL != "" {
|
||||||
|
s["template_url"] = opts.TemplateURL
|
||||||
|
} else {
|
||||||
|
return s, errors.New("Either Template or TemplateURL must be provided.")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return s, errors.New("Either Template or TemplateURL must be provided.")
|
if err := opts.TemplateOpts.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.TemplateOpts.fixFileRefs()
|
||||||
|
s["template"] = string(opts.TemplateOpts.Bin)
|
||||||
|
|
||||||
|
for k, v := range opts.TemplateOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Environment != "" {
|
if opts.EnvironmentOpts != nil {
|
||||||
|
if err := opts.EnvironmentOpts.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.EnvironmentOpts.fixFileRefs()
|
||||||
|
for k, v := range opts.EnvironmentOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
|
s["environment"] = string(opts.EnvironmentOpts.Bin)
|
||||||
|
} else if opts.Environment != "" {
|
||||||
s["environment"] = opts.Environment
|
s["environment"] = opts.Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Files != nil {
|
if opts.Files != nil {
|
||||||
s["files"] = opts.Files
|
s["files"] = opts.Files
|
||||||
|
} else {
|
||||||
|
s["files"] = Files
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Parameters != nil {
|
if opts.Parameters != nil {
|
||||||
@ -359,6 +500,10 @@ func (opts UpdateOpts) ToStackUpdateMap() (map[string]interface{}, error) {
|
|||||||
s["timeout_mins"] = opts.Timeout
|
s["timeout_mins"] = opts.Timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Tags != nil {
|
||||||
|
s["tags"] = strings.Join(opts.Tags, ",")
|
||||||
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,9 +542,16 @@ type PreviewOpts struct {
|
|||||||
Name string
|
Name string
|
||||||
// (REQUIRED) The timeout for stack creation in minutes.
|
// (REQUIRED) The timeout for stack creation in minutes.
|
||||||
Timeout int
|
Timeout int
|
||||||
|
// (REQUIRED) A structure that contains either the template file or url. Call the
|
||||||
|
// associated methods to extract the information relevant to send in a create request.
|
||||||
|
TemplateOpts *Template
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, TemplateURL will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
// (OPTIONAL; REQUIRED IF Template IS EMPTY) The URL of the template to instantiate.
|
||||||
// This value is ignored if Template is supplied inline.
|
// This value is ignored if Template is supplied inline.
|
||||||
TemplateURL string
|
TemplateURL string
|
||||||
|
// (DEPRECATED): Please use TemplateOpts for providing the template. If
|
||||||
|
// TemplateOpts is provided, Template will be ignored
|
||||||
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
// (OPTIONAL; REQUIRED IF TemplateURL IS EMPTY) A template to instantiate. The value
|
||||||
// is a stringified version of the JSON/YAML template. Since the template will likely
|
// is a stringified version of the JSON/YAML template. Since the template will likely
|
||||||
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
// be located in a file, one way to set this variable is by using ioutil.ReadFile:
|
||||||
@ -415,8 +567,14 @@ type PreviewOpts struct {
|
|||||||
// creation fails. Default is true, meaning all resources are not deleted when
|
// creation fails. Default is true, meaning all resources are not deleted when
|
||||||
// stack creation fails.
|
// stack creation fails.
|
||||||
DisableRollback Rollback
|
DisableRollback Rollback
|
||||||
|
// (OPTIONAL) A structure that contains details for the environment of the stack.
|
||||||
|
EnvironmentOpts *Environment
|
||||||
|
// (DEPRECATED): Please use EnvironmentOpts to provide Environment data
|
||||||
// (OPTIONAL) A stringified JSON environment for the stack.
|
// (OPTIONAL) A stringified JSON environment for the stack.
|
||||||
Environment string
|
Environment string
|
||||||
|
// (DEPRECATED): Files is automatically determined
|
||||||
|
// by parsing the template and environment passed as TemplateOpts and
|
||||||
|
// EnvironmentOpts respectively.
|
||||||
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
// (OPTIONAL) A map that maps file names to file contents. It can also be used
|
||||||
// to pass provider template contents. Example:
|
// to pass provider template contents. Example:
|
||||||
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
// Files: `{"myfile": "#!/bin/bash\necho 'Hello world' > /root/testfile.txt"}`
|
||||||
@ -433,25 +591,56 @@ func (opts PreviewOpts) ToStackPreviewMap() (map[string]interface{}, error) {
|
|||||||
return s, errors.New("Required field 'Name' not provided.")
|
return s, errors.New("Required field 'Name' not provided.")
|
||||||
}
|
}
|
||||||
s["stack_name"] = opts.Name
|
s["stack_name"] = opts.Name
|
||||||
|
Files := make(map[string]string)
|
||||||
if opts.Template != "" {
|
if opts.TemplateOpts == nil {
|
||||||
s["template"] = opts.Template
|
if opts.Template != "" {
|
||||||
} else if opts.TemplateURL != "" {
|
s["template"] = opts.Template
|
||||||
s["template_url"] = opts.TemplateURL
|
} else if opts.TemplateURL != "" {
|
||||||
|
s["template_url"] = opts.TemplateURL
|
||||||
|
} else {
|
||||||
|
return s, errors.New("Either Template or TemplateURL must be provided.")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return s, errors.New("Either Template or TemplateURL must be provided.")
|
if err := opts.TemplateOpts.Parse(); err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := opts.TemplateOpts.getFileContents(opts.TemplateOpts.Parsed, ignoreIfTemplate, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.TemplateOpts.fixFileRefs()
|
||||||
|
s["template"] = string(opts.TemplateOpts.Bin)
|
||||||
|
|
||||||
|
for k, v := range opts.TemplateOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
if opts.DisableRollback != nil {
|
if opts.DisableRollback != nil {
|
||||||
s["disable_rollback"] = &opts.DisableRollback
|
s["disable_rollback"] = &opts.DisableRollback
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Environment != "" {
|
if opts.EnvironmentOpts != nil {
|
||||||
|
if err := opts.EnvironmentOpts.Parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := opts.EnvironmentOpts.getRRFileContents(ignoreIfEnvironment); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.EnvironmentOpts.fixFileRefs()
|
||||||
|
for k, v := range opts.EnvironmentOpts.Files {
|
||||||
|
Files[k] = v
|
||||||
|
}
|
||||||
|
s["environment"] = string(opts.EnvironmentOpts.Bin)
|
||||||
|
} else if opts.Environment != "" {
|
||||||
s["environment"] = opts.Environment
|
s["environment"] = opts.Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Files != nil {
|
if opts.Files != nil {
|
||||||
s["files"] = opts.Files
|
s["files"] = opts.Files
|
||||||
|
} else {
|
||||||
|
s["files"] = Files
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Parameters != nil {
|
if opts.Parameters != nil {
|
||||||
s["parameters"] = opts.Parameters
|
s["parameters"] = opts.Parameters
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ type ListedStack struct {
|
|||||||
Name string `mapstructure:"stack_name"`
|
Name string `mapstructure:"stack_name"`
|
||||||
Status string `mapstructure:"stack_status"`
|
Status string `mapstructure:"stack_status"`
|
||||||
StatusReason string `mapstructure:"stack_status_reason"`
|
StatusReason string `mapstructure:"stack_status_reason"`
|
||||||
|
Tags []string `mapstructure:"tags"`
|
||||||
UpdatedTime time.Time `mapstructure:"-"`
|
UpdatedTime time.Time `mapstructure:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ func ExtractStacks(page pagination.Page) ([]ListedStack, error) {
|
|||||||
Stacks []ListedStack `mapstructure:"stacks"`
|
Stacks []ListedStack `mapstructure:"stacks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err := mapstructure.Decode(page.(StackPage).Body, &res)
|
err := mapstructure.Decode(casted, &res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -133,6 +134,7 @@ type RetrievedStack struct {
|
|||||||
Name string `mapstructure:"stack_name"`
|
Name string `mapstructure:"stack_name"`
|
||||||
Status string `mapstructure:"stack_status"`
|
Status string `mapstructure:"stack_status"`
|
||||||
StatusReason string `mapstructure:"stack_status_reason"`
|
StatusReason string `mapstructure:"stack_status_reason"`
|
||||||
|
Tags []string `mapstructure:"tags"`
|
||||||
TemplateDescription string `mapstructure:"template_description"`
|
TemplateDescription string `mapstructure:"template_description"`
|
||||||
Timeout int `mapstructure:"timeout_mins"`
|
Timeout int `mapstructure:"timeout_mins"`
|
||||||
UpdatedTime time.Time `mapstructure:"-"`
|
UpdatedTime time.Time `mapstructure:"-"`
|
||||||
@ -200,21 +202,19 @@ type DeleteResult struct {
|
|||||||
|
|
||||||
// PreviewedStack represents the result of a Preview operation.
|
// PreviewedStack represents the result of a Preview operation.
|
||||||
type PreviewedStack struct {
|
type PreviewedStack struct {
|
||||||
Capabilities []interface{} `mapstructure:"capabilities"`
|
Capabilities []interface{} `mapstructure:"capabilities"`
|
||||||
CreationTime time.Time `mapstructure:"-"`
|
CreationTime time.Time `mapstructure:"-"`
|
||||||
Description string `mapstructure:"description"`
|
Description string `mapstructure:"description"`
|
||||||
DisableRollback bool `mapstructure:"disable_rollback"`
|
DisableRollback bool `mapstructure:"disable_rollback"`
|
||||||
ID string `mapstructure:"id"`
|
ID string `mapstructure:"id"`
|
||||||
Links []gophercloud.Link `mapstructure:"links"`
|
Links []gophercloud.Link `mapstructure:"links"`
|
||||||
Name string `mapstructure:"stack_name"`
|
Name string `mapstructure:"stack_name"`
|
||||||
NotificationTopics []interface{} `mapstructure:"notification_topics"`
|
NotificationTopics []interface{} `mapstructure:"notification_topics"`
|
||||||
Parameters map[string]string `mapstructure:"parameters"`
|
Parameters map[string]string `mapstructure:"parameters"`
|
||||||
Resources []map[string]interface{} `mapstructure:"resources"`
|
Resources []interface{} `mapstructure:"resources"`
|
||||||
Status string `mapstructure:"stack_status"`
|
TemplateDescription string `mapstructure:"template_description"`
|
||||||
StatusReason string `mapstructure:"stack_status_reason"`
|
Timeout int `mapstructure:"timeout_mins"`
|
||||||
TemplateDescription string `mapstructure:"template_description"`
|
UpdatedTime time.Time `mapstructure:"-"`
|
||||||
Timeout int `mapstructure:"timeout_mins"`
|
|
||||||
UpdatedTime time.Time `mapstructure:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreviewResult represents the result of a Preview operation.
|
// PreviewResult represents the result of a Preview operation.
|
||||||
@ -269,12 +269,16 @@ func (r PreviewResult) Extract() (*PreviewedStack, error) {
|
|||||||
|
|
||||||
// AbandonedStack represents the result of an Abandon operation.
|
// AbandonedStack represents the result of an Abandon operation.
|
||||||
type AbandonedStack struct {
|
type AbandonedStack struct {
|
||||||
Status string `mapstructure:"status"`
|
Status string `mapstructure:"status"`
|
||||||
Name string `mapstructure:"name"`
|
Name string `mapstructure:"name"`
|
||||||
Template map[string]interface{} `mapstructure:"template"`
|
Template map[string]interface{} `mapstructure:"template"`
|
||||||
Action string `mapstructure:"action"`
|
Action string `mapstructure:"action"`
|
||||||
ID string `mapstructure:"id"`
|
ID string `mapstructure:"id"`
|
||||||
Resources map[string]interface{} `mapstructure:"resources"`
|
Resources map[string]interface{} `mapstructure:"resources"`
|
||||||
|
Files map[string]string `mapstructure:"files"`
|
||||||
|
StackUserProjectID string `mapstructure:"stack_user_project_id"`
|
||||||
|
ProjectID string `mapstructure:"project_id"`
|
||||||
|
Environment map[string]interface{} `mapstructure:"environment"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbandonResult represents the result of an Abandon operation.
|
// AbandonResult represents the result of an Abandon operation.
|
||||||
|
139
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/template.go
generated
vendored
Normal file
139
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/template.go
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package stacks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Template is a structure that represents OpenStack Heat templates
|
||||||
|
type Template struct {
|
||||||
|
TE
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemplateFormatVersions is a map containing allowed variations of the template format version
|
||||||
|
// Note that this contains the permitted variations of the _keys_ not the values.
|
||||||
|
var TemplateFormatVersions = map[string]bool{
|
||||||
|
"HeatTemplateFormatVersion": true,
|
||||||
|
"heat_template_version": true,
|
||||||
|
"AWSTemplateFormatVersion": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the contents of the Template
|
||||||
|
func (t *Template) Validate() error {
|
||||||
|
if t.Parsed == nil {
|
||||||
|
if err := t.Parse(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key := range t.Parsed {
|
||||||
|
if _, ok := TemplateFormatVersions[key]; ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Template format version not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileContents recursively parses a template to search for urls. These urls
|
||||||
|
// are assumed to point to other templates (known in OpenStack Heat as child
|
||||||
|
// templates). The contents of these urls are fetched and stored in the `Files`
|
||||||
|
// parameter of the template structure. This is the only way that a user can
|
||||||
|
// use child templates that are located in their filesystem; urls located on the
|
||||||
|
// web (e.g. on github or swift) can be fetched directly by Heat engine.
|
||||||
|
func (t *Template) getFileContents(te interface{}, ignoreIf igFunc, recurse bool) error {
|
||||||
|
// initialize template if empty
|
||||||
|
if t.Files == nil {
|
||||||
|
t.Files = make(map[string]string)
|
||||||
|
}
|
||||||
|
if t.fileMaps == nil {
|
||||||
|
t.fileMaps = make(map[string]string)
|
||||||
|
}
|
||||||
|
switch te.(type) {
|
||||||
|
// if te is a map
|
||||||
|
case map[string]interface{}, map[interface{}]interface{}:
|
||||||
|
teMap, err := toStringKeys(te)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range teMap {
|
||||||
|
value, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
// if the value is not a string, recursively parse that value
|
||||||
|
if err := t.getFileContents(v, ignoreIf, recurse); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if !ignoreIf(k, value) {
|
||||||
|
// at this point, the k, v pair has a reference to an external template.
|
||||||
|
// The assumption of heatclient is that value v is a reference
|
||||||
|
// to a file in the users environment
|
||||||
|
|
||||||
|
// create a new child template
|
||||||
|
childTemplate := new(Template)
|
||||||
|
|
||||||
|
// initialize child template
|
||||||
|
|
||||||
|
// get the base location of the child template
|
||||||
|
baseURL, err := gophercloud.NormalizePathURL(t.baseURL, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
childTemplate.baseURL = baseURL
|
||||||
|
childTemplate.client = t.client
|
||||||
|
|
||||||
|
// fetch the contents of the child template
|
||||||
|
if err := childTemplate.Parse(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// process child template recursively if required. This is
|
||||||
|
// required if the child template itself contains references to
|
||||||
|
// other templates
|
||||||
|
if recurse {
|
||||||
|
if err := childTemplate.getFileContents(childTemplate.Parsed, ignoreIf, recurse); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update parent template with current child templates' content.
|
||||||
|
// At this point, the child template has been parsed recursively.
|
||||||
|
t.fileMaps[value] = childTemplate.URL
|
||||||
|
t.Files[childTemplate.URL] = string(childTemplate.Bin)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
// if te is a slice, call the function on each element of the slice.
|
||||||
|
case []interface{}:
|
||||||
|
teSlice := te.([]interface{})
|
||||||
|
for i := range teSlice {
|
||||||
|
if err := t.getFileContents(teSlice[i], ignoreIf, recurse); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if te is anything else, return
|
||||||
|
case string, bool, float64, nil, int:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%v: Unrecognized type", reflect.TypeOf(te))
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to choose keys whose values are other template files
|
||||||
|
func ignoreIfTemplate(key string, value interface{}) bool {
|
||||||
|
// key must be either `get_file` or `type` for value to be a URL
|
||||||
|
if key != "get_file" && key != "type" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// value must be a string
|
||||||
|
valueString, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// `.template` and `.yaml` are allowed suffixes for template URLs when referred to by `type`
|
||||||
|
if key == "type" && !(strings.HasSuffix(valueString, ".template") || strings.HasSuffix(valueString, ".yaml")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
161
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/utils.go
generated
vendored
Normal file
161
Godeps/_workspace/src/github.com/rackspace/gophercloud/openstack/orchestration/v1/stacks/utils.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
package stacks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is an interface that expects a Get method similar to http.Get. This
|
||||||
|
// is needed for unit testing, since we can mock an http client. Thus, the
|
||||||
|
// client will usually be an http.Client EXCEPT in unit tests.
|
||||||
|
type Client interface {
|
||||||
|
Get(string) (*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TE is a base structure for both Template and Environment
|
||||||
|
type TE struct {
|
||||||
|
// Bin stores the contents of the template or environment.
|
||||||
|
Bin []byte
|
||||||
|
// URL stores the URL of the template. This is allowed to be a 'file://'
|
||||||
|
// for local files.
|
||||||
|
URL string
|
||||||
|
// Parsed contains a parsed version of Bin. Since there are 2 different
|
||||||
|
// fields referring to the same value, you must be careful when accessing
|
||||||
|
// this filed.
|
||||||
|
Parsed map[string]interface{}
|
||||||
|
// Files contains a mapping between the urls in templates to their contents.
|
||||||
|
Files map[string]string
|
||||||
|
// fileMaps is a map used internally when determining Files.
|
||||||
|
fileMaps map[string]string
|
||||||
|
// baseURL represents the location of the template or environment file.
|
||||||
|
baseURL string
|
||||||
|
// client is an interface which allows TE to fetch contents from URLS
|
||||||
|
client Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch fetches the contents of a TE from its URL. Once a TE structure has a
|
||||||
|
// URL, call the fetch method to fetch the contents.
|
||||||
|
func (t *TE) Fetch() error {
|
||||||
|
// if the baseURL is not provided, use the current directors as the base URL
|
||||||
|
if t.baseURL == "" {
|
||||||
|
u, err := getBasePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.baseURL = u
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the contents are already present, do nothing.
|
||||||
|
if t.Bin != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a fqdn from the URL using the baseURL of the TE. For local files,
|
||||||
|
// the URL's will have the `file` scheme.
|
||||||
|
u, err := gophercloud.NormalizePathURL(t.baseURL, t.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.URL = u
|
||||||
|
|
||||||
|
// get an HTTP client if none present
|
||||||
|
if t.client == nil {
|
||||||
|
t.client = getHTTPClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the client to fetch the contents of the TE
|
||||||
|
resp, err := t.client.Get(t.URL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Bin = body
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the basepath of the TE
|
||||||
|
func getBasePath() (string, error) {
|
||||||
|
basePath, err := filepath.Abs(".")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
u, err := gophercloud.NormalizePathURL("", basePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a an HTTP client to retrieve URL's. This client allows the use of `file`
|
||||||
|
// scheme since we may need to fetch files from users filesystem
|
||||||
|
func getHTTPClient() Client {
|
||||||
|
transport := &http.Transport{}
|
||||||
|
transport.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
|
||||||
|
return &http.Client{Transport: transport}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse will parse the contents and then validate. The contents MUST be either JSON or YAML.
|
||||||
|
func (t *TE) Parse() error {
|
||||||
|
if err := t.Fetch(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if jerr := json.Unmarshal(t.Bin, &t.Parsed); jerr != nil {
|
||||||
|
if yerr := yaml.Unmarshal(t.Bin, &t.Parsed); yerr != nil {
|
||||||
|
return fmt.Errorf("Data in neither json nor yaml format.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.Validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the contents of TE
|
||||||
|
func (t *TE) Validate() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// igfunc is a parameter used by GetFileContents and GetRRFileContents to check
|
||||||
|
// for valid URL's.
|
||||||
|
type igFunc func(string, interface{}) bool
|
||||||
|
|
||||||
|
// convert map[interface{}]interface{} to map[string]interface{}
|
||||||
|
func toStringKeys(m interface{}) (map[string]interface{}, error) {
|
||||||
|
switch m.(type) {
|
||||||
|
case map[string]interface{}, map[interface{}]interface{}:
|
||||||
|
typedMap := make(map[string]interface{})
|
||||||
|
if _, ok := m.(map[interface{}]interface{}); ok {
|
||||||
|
for k, v := range m.(map[interface{}]interface{}) {
|
||||||
|
typedMap[k.(string)] = v
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
typedMap = m.(map[string]interface{})
|
||||||
|
}
|
||||||
|
return typedMap, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Expected a map of type map[string]interface{} or map[interface{}]interface{}, actual type: %v", reflect.TypeOf(m))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix the reference to files by replacing relative URL's by absolute
|
||||||
|
// URL's
|
||||||
|
func (t *TE) fixFileRefs() {
|
||||||
|
tStr := string(t.Bin)
|
||||||
|
if t.fileMaps == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for k, v := range t.fileMaps {
|
||||||
|
tStr = strings.Replace(tStr, k, v, -1)
|
||||||
|
}
|
||||||
|
t.Bin = []byte(tStr)
|
||||||
|
}
|
@ -10,29 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// GetExpected represents the expected object from a Get request.
|
// GetExpected represents the expected object from a Get request.
|
||||||
var GetExpected = &Template{
|
var GetExpected = "{\n \"description\": \"Simple template to test heat commands\",\n \"heat_template_version\": \"2013-05-23\",\n \"parameters\": {\n \"flavor\": {\n \"default\": \"m1.tiny\",\n \"type\": \"string\"\n }\n },\n \"resources\": {\n \"hello_world\": {\n \"properties\": {\n \"flavor\": {\n \"get_param\": \"flavor\"\n },\n \"image\": \"ad091b52-742f-469e-8f3c-fd81cadf0743\",\n \"key_name\": \"heat_key\"\n },\n \"type\": \"OS::Nova::Server\"\n }\n }\n}"
|
||||||
Description: "Simple template to test heat commands",
|
|
||||||
HeatTemplateVersion: "2013-05-23",
|
|
||||||
Parameters: map[string]interface{}{
|
|
||||||
"flavor": map[string]interface{}{
|
|
||||||
"default": "m1.tiny",
|
|
||||||
"type": "string",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Resources: map[string]interface{}{
|
|
||||||
"hello_world": map[string]interface{}{
|
|
||||||
"type": "OS::Nova::Server",
|
|
||||||
"properties": map[string]interface{}{
|
|
||||||
"key_name": "heat_key",
|
|
||||||
"flavor": map[string]interface{}{
|
|
||||||
"get_param": "flavor",
|
|
||||||
},
|
|
||||||
"image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
|
|
||||||
"user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOutput represents the response body from a Get request.
|
// GetOutput represents the response body from a Get request.
|
||||||
const GetOutput = `
|
const GetOutput = `
|
||||||
@ -53,8 +31,7 @@ const GetOutput = `
|
|||||||
"flavor": {
|
"flavor": {
|
||||||
"get_param": "flavor"
|
"get_param": "flavor"
|
||||||
},
|
},
|
||||||
"image": "ad091b52-742f-469e-8f3c-fd81cadf0743",
|
"image": "ad091b52-742f-469e-8f3c-fd81cadf0743"
|
||||||
"user_data": "#!/bin/bash -xv\necho \"hello world\" > /root/hello-world.txt\n"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,14 @@ type ValidateOptsBuilder interface {
|
|||||||
|
|
||||||
// ValidateOpts specifies the template validation parameters.
|
// ValidateOpts specifies the template validation parameters.
|
||||||
type ValidateOpts struct {
|
type ValidateOpts struct {
|
||||||
Template map[string]interface{}
|
Template string
|
||||||
TemplateURL string
|
TemplateURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToStackTemplateValidateMap assembles a request body based on the contents of a ValidateOpts.
|
// ToStackTemplateValidateMap assembles a request body based on the contents of a ValidateOpts.
|
||||||
func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, error) {
|
func (opts ValidateOpts) ToStackTemplateValidateMap() (map[string]interface{}, error) {
|
||||||
vo := make(map[string]interface{})
|
vo := make(map[string]interface{})
|
||||||
if opts.Template != nil {
|
if opts.Template != "" {
|
||||||
vo["template"] = opts.Template
|
vo["template"] = opts.Template
|
||||||
return vo, nil
|
return vo, nil
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,33 @@
|
|||||||
package stacktemplates
|
package stacktemplates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/rackspace/gophercloud"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Template represents a stack template.
|
|
||||||
type Template struct {
|
|
||||||
Description string `mapstructure:"description"`
|
|
||||||
HeatTemplateVersion string `mapstructure:"heat_template_version"`
|
|
||||||
Parameters map[string]interface{} `mapstructure:"parameters"`
|
|
||||||
Resources map[string]interface{} `mapstructure:"resources"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult represents the result of a Get operation.
|
// GetResult represents the result of a Get operation.
|
||||||
type GetResult struct {
|
type GetResult struct {
|
||||||
gophercloud.Result
|
gophercloud.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract returns a pointer to a Template object and is called after a
|
// Extract returns the JSON template and is called after a Get operation.
|
||||||
// Get operation.
|
func (r GetResult) Extract() ([]byte, error) {
|
||||||
func (r GetResult) Extract() (*Template, error) {
|
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
return nil, r.Err
|
return nil, r.Err
|
||||||
}
|
}
|
||||||
|
template, err := json.MarshalIndent(r.Body, "", " ")
|
||||||
var res Template
|
if err != nil {
|
||||||
if err := mapstructure.Decode(r.Body, &res); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return template, nil
|
||||||
return &res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatedTemplate represents the parsed object returned from a Validate request.
|
// ValidatedTemplate represents the parsed object returned from a Validate request.
|
||||||
type ValidatedTemplate struct {
|
type ValidatedTemplate struct {
|
||||||
Description string
|
Description string `mapstructure:"Description"`
|
||||||
Parameters map[string]interface{}
|
Parameters map[string]interface{} `mapstructure:"Parameters"`
|
||||||
|
ParameterGroups map[string]interface{} `mapstructure:"ParameterGroups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateResult represents the result of a Validate operation.
|
// ValidateResult represents the result of a Validate operation.
|
||||||
|
26
Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go
generated
vendored
26
Godeps/_workspace/src/github.com/rackspace/gophercloud/provider_client.go
generated
vendored
@ -177,6 +177,9 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set connection parameter to close the connection immediately when we've got the response
|
||||||
|
req.Close = true
|
||||||
|
|
||||||
// Issue the request.
|
// Issue the request.
|
||||||
resp, err := client.HTTPClient.Do(req)
|
resp, err := client.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -192,10 +195,13 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) (
|
|||||||
if options.RawBody != nil {
|
if options.RawBody != nil {
|
||||||
options.RawBody.Seek(0, 0)
|
options.RawBody.Seek(0, 0)
|
||||||
}
|
}
|
||||||
|
resp.Body.Close()
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,6 +249,8 @@ func defaultOkCodes(method string) []int {
|
|||||||
return []int{201, 202}
|
return []int{201, 202}
|
||||||
case method == "PUT":
|
case method == "PUT":
|
||||||
return []int{201, 202}
|
return []int{201, 202}
|
||||||
|
case method == "PATCH":
|
||||||
|
return []int{200, 204}
|
||||||
case method == "DELETE":
|
case method == "DELETE":
|
||||||
return []int{202, 204}
|
return []int{202, 204}
|
||||||
}
|
}
|
||||||
@ -296,6 +304,24 @@ func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse
|
|||||||
return client.Request("PUT", url, *opts)
|
return client.Request("PUT", url, *opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (client *ProviderClient) Patch(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &RequestOpts{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := (JSONBody).(io.ReadSeeker); ok {
|
||||||
|
opts.RawBody = v
|
||||||
|
} else if JSONBody != nil {
|
||||||
|
opts.JSONBody = JSONBody
|
||||||
|
}
|
||||||
|
|
||||||
|
if JSONResponse != nil {
|
||||||
|
opts.JSONResponse = JSONResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.Request("PATCH", url, *opts)
|
||||||
|
}
|
||||||
|
|
||||||
func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
|
func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &RequestOpts{}
|
opts = &RequestOpts{}
|
||||||
|
10
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/client.go
generated
vendored
10
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/client.go
generated
vendored
@ -212,3 +212,13 @@ func NewRackConnectV3(client *gophercloud.ProviderClient, eo gophercloud.Endpoin
|
|||||||
}
|
}
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
|
||||||
|
func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("rax:database")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
6
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/doc.go
generated
vendored
Normal file
6
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Package backups provides information and interaction with the backup API
|
||||||
|
// resource in the Rackspace Database service.
|
||||||
|
//
|
||||||
|
// A backup is a copy of a database instance that can be used to restore it to
|
||||||
|
// some defined point in history.
|
||||||
|
package backups
|
66
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/fixtures.go
generated
vendored
Normal file
66
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package backups
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
var (
|
||||||
|
timestamp = "2015-11-12T14:22:42Z"
|
||||||
|
timeVal, _ = time.Parse(time.RFC3339, timestamp)
|
||||||
|
)
|
||||||
|
|
||||||
|
var getResp = `
|
||||||
|
{
|
||||||
|
"backup": {
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"description": "My Backup",
|
||||||
|
"id": "61f12fef-edb1-4561-8122-e7c00ef26a82",
|
||||||
|
"instance_id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
|
||||||
|
"locationRef": null,
|
||||||
|
"name": "snapshot",
|
||||||
|
"parent_id": null,
|
||||||
|
"size": 100,
|
||||||
|
"status": "NEW",
|
||||||
|
"datastore": {
|
||||||
|
"version": "5.1",
|
||||||
|
"type": "MySQL",
|
||||||
|
"version_id": "20000000-0000-0000-0000-000000000002"
|
||||||
|
},
|
||||||
|
"updated": "` + timestamp + `"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var createReq = `
|
||||||
|
{
|
||||||
|
"backup": {
|
||||||
|
"description": "My Backup",
|
||||||
|
"instance": "d4603f69-ec7e-4e9b-803f-600b9205576f",
|
||||||
|
"name": "snapshot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var createResp = getResp
|
||||||
|
|
||||||
|
var listResp = `
|
||||||
|
{
|
||||||
|
"backups": [
|
||||||
|
{
|
||||||
|
"status": "COMPLETED",
|
||||||
|
"updated": "` + timestamp + `",
|
||||||
|
"description": "Backup from Restored Instance",
|
||||||
|
"datastore": {
|
||||||
|
"version": "5.1",
|
||||||
|
"type": "MySQL",
|
||||||
|
"version_id": "20000000-0000-0000-0000-000000000002"
|
||||||
|
},
|
||||||
|
"id": "87972694-4be2-40f5-83f8-501656e0032a",
|
||||||
|
"size": 0.141026,
|
||||||
|
"name": "restored_backup",
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"instance_id": "29af2cd9-0674-48ab-b87a-b160f00208e6",
|
||||||
|
"parent_id": null,
|
||||||
|
"locationRef": "http://localhost/path/to/backup"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
138
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/requests.go
generated
vendored
Normal file
138
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/requests.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package backups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder is the top-level interface for creating JSON maps.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToBackupCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts is responsible for configuring newly provisioned backups.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// [REQUIRED] The name of the backup. The only restriction is the name must
|
||||||
|
// be less than 64 characters long.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// [REQUIRED] The ID of the instance being backed up.
|
||||||
|
InstanceID string
|
||||||
|
|
||||||
|
// [OPTIONAL] A human-readable explanation of the backup.
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBackupCreateMap will create a JSON map for the Create operation.
|
||||||
|
func (opts CreateOpts) ToBackupCreateMap() (map[string]interface{}, error) {
|
||||||
|
if opts.Name == "" {
|
||||||
|
return nil, errors.New("Name is a required field")
|
||||||
|
}
|
||||||
|
if opts.InstanceID == "" {
|
||||||
|
return nil, errors.New("InstanceID is a required field")
|
||||||
|
}
|
||||||
|
|
||||||
|
backup := map[string]interface{}{
|
||||||
|
"name": opts.Name,
|
||||||
|
"instance": opts.InstanceID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Description != "" {
|
||||||
|
backup["description"] = opts.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"backup": backup}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create asynchronously creates a new backup for a specified database instance.
|
||||||
|
// During the backup process, write access on MyISAM databases will be
|
||||||
|
// temporarily disabled; innoDB databases will be unaffected. During this time,
|
||||||
|
// you will not be able to add or delete databases or users; nor delete, stop
|
||||||
|
// or reboot the instance itself. Only one backup is permitted at once.
|
||||||
|
//
|
||||||
|
// Backups are not deleted when database instances are deleted; you must
|
||||||
|
// manually delete any backups created using Delete(). Backups are saved to your
|
||||||
|
// Cloud Files account in a new container called z_CLOUDDB_BACKUPS. It is
|
||||||
|
// strongly recommended you do not alter this container or its contents; usual
|
||||||
|
// storage costs apply.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
|
||||||
|
var res CreateResult
|
||||||
|
|
||||||
|
reqBody, err := opts.ToBackupCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
res.Err = err
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
_, res.Err = client.Request("POST", baseURL(client), gophercloud.RequestOpts{
|
||||||
|
JSONBody: &reqBody,
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOptsBuilder is the top-level interface for creating query strings.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToBackupListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOpts allows you to refine a list search by certain parameters.
|
||||||
|
type ListOpts struct {
|
||||||
|
// The type of datastore by which to filter.
|
||||||
|
Datastore string `q:"datastore"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBackupListQuery converts a ListOpts struct into a query string.
|
||||||
|
func (opts ListOpts) ToBackupListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return q.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List will list all the saved backups for all database instances.
|
||||||
|
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := baseURL(client)
|
||||||
|
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToBackupListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
|
||||||
|
pageFn := func(r pagination.PageResult) pagination.Page {
|
||||||
|
return BackupPage{pagination.SinglePageBase(r)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, url, pageFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will retrieve details for a particular backup based on its unique ID.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
|
var res GetResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("GET", resourceURL(client, id), gophercloud.RequestOpts{
|
||||||
|
JSONResponse: &res.Body,
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will permanently delete a backup.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
|
||||||
|
var res DeleteResult
|
||||||
|
|
||||||
|
_, res.Err = client.Request("DELETE", resourceURL(client, id), gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
149
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/results.go
generated
vendored
Normal file
149
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/results.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package backups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
"github.com/rackspace/gophercloud/openstack/db/v1/datastores"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Status represents the various states a Backup can be in.
|
||||||
|
type Status string
|
||||||
|
|
||||||
|
// Enum types for the status.
|
||||||
|
const (
|
||||||
|
StatusNew Status = "NEW"
|
||||||
|
StatusBuilding Status = "BUILDING"
|
||||||
|
StatusCompleted Status = "COMPLETED"
|
||||||
|
StatusFailed Status = "FAILED"
|
||||||
|
StatusDeleteFailed Status = "DELETE_FAILED"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Backup represents a Backup API resource.
|
||||||
|
type Backup struct {
|
||||||
|
Description string
|
||||||
|
ID string
|
||||||
|
InstanceID string `json:"instance_id" mapstructure:"instance_id"`
|
||||||
|
LocationRef string
|
||||||
|
Name string
|
||||||
|
ParentID string `json:"parent_id" mapstructure:"parent_id"`
|
||||||
|
Size float64
|
||||||
|
Status Status
|
||||||
|
Created time.Time `mapstructure:"-"`
|
||||||
|
Updated time.Time `mapstructure:"-"`
|
||||||
|
Datastore datastores.DatastorePartial
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult represents the result of a create operation.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult represents the result of a get operation.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult represents the result of a delete operation.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will retrieve a Backup struct from an operation's result.
|
||||||
|
func (r commonResult) Extract() (*Backup, error) {
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Backup Backup `mapstructure:"backup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := mapstructure.Decode(r.Body, &response)
|
||||||
|
val := r.Body.(map[string]interface{})["backup"].(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := val["created"].(string); ok && t != "" {
|
||||||
|
creationTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return &response.Backup, err
|
||||||
|
}
|
||||||
|
response.Backup.Created = creationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := val["updated"].(string); ok && t != "" {
|
||||||
|
updatedTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return &response.Backup, err
|
||||||
|
}
|
||||||
|
response.Backup.Updated = updatedTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.Backup, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupPage represents a page of backups.
|
||||||
|
type BackupPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty checks whether an BackupPage struct is empty.
|
||||||
|
func (r BackupPage) IsEmpty() (bool, error) {
|
||||||
|
is, err := ExtractBackups(r)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return len(is) == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractBackups will retrieve a slice of Backup structs from a paginated collection.
|
||||||
|
func ExtractBackups(page pagination.Page) ([]Backup, error) {
|
||||||
|
casted := page.(BackupPage).Body
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
Backups []Backup `mapstructure:"backups" json:"backups"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mapstructure.Decode(casted, &resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vals []interface{}
|
||||||
|
switch casted.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
vals = casted.(map[string]interface{})["backups"].([]interface{})
|
||||||
|
case map[string][]interface{}:
|
||||||
|
vals = casted.(map[string][]interface{})["backups"]
|
||||||
|
default:
|
||||||
|
return resp.Backups, fmt.Errorf("Unknown type: %v", reflect.TypeOf(casted))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range vals {
|
||||||
|
val := v.(map[string]interface{})
|
||||||
|
|
||||||
|
if t, ok := val["created"].(string); ok && t != "" {
|
||||||
|
creationTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return resp.Backups, err
|
||||||
|
}
|
||||||
|
resp.Backups[i].Created = creationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := val["updated"].(string); ok && t != "" {
|
||||||
|
updatedTime, err := time.Parse(time.RFC3339, t)
|
||||||
|
if err != nil {
|
||||||
|
return resp.Backups, err
|
||||||
|
}
|
||||||
|
resp.Backups[i].Updated = updatedTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Backups, nil
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/urls.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/backups/urls.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package backups
|
||||||
|
|
||||||
|
import "github.com/rackspace/gophercloud"
|
||||||
|
|
||||||
|
func baseURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("backups")
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient, backupID string) string {
|
||||||
|
return c.ServiceURL("backups", backupID)
|
||||||
|
}
|
79
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/configurations/delegate.go
generated
vendored
Normal file
79
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/configurations/delegate.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package configurations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/db/v1/configurations"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List will list all of the available configurations.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return os.List(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new configuration group.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts os.CreateOptsBuilder) os.CreateResult {
|
||||||
|
return os.Create(client, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will retrieve the details for a specified configuration group.
|
||||||
|
func Get(client *gophercloud.ServiceClient, configID string) os.GetResult {
|
||||||
|
return os.Get(client, configID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will modify an existing configuration group by performing a merge
|
||||||
|
// between new and existing values. If the key already exists, the new value
|
||||||
|
// will overwrite. All other keys will remain unaffected.
|
||||||
|
func Update(client *gophercloud.ServiceClient, configID string, opts os.UpdateOptsBuilder) os.UpdateResult {
|
||||||
|
return os.Update(client, configID, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace will modify an existing configuration group by overwriting the
|
||||||
|
// entire parameter group with the new values provided. Any existing keys not
|
||||||
|
// included in UpdateOptsBuilder will be deleted.
|
||||||
|
func Replace(client *gophercloud.ServiceClient, configID string, opts os.UpdateOptsBuilder) os.ReplaceResult {
|
||||||
|
return os.Replace(client, configID, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will permanently delete a configuration group. Please note that
|
||||||
|
// config groups cannot be deleted whilst still attached to running instances -
|
||||||
|
// you must detach and then delete them.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, configID string) os.DeleteResult {
|
||||||
|
return os.Delete(client, configID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListInstances will list all the instances associated with a particular
|
||||||
|
// configuration group.
|
||||||
|
func ListInstances(client *gophercloud.ServiceClient, configID string) pagination.Pager {
|
||||||
|
return os.ListInstances(client, configID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListDatastoreParams will list all the available and supported parameters
|
||||||
|
// that can be used for a particular datastore ID and a particular version.
|
||||||
|
// For example, if you are wondering how you can configure a MySQL 5.6 instance,
|
||||||
|
// you can use this operation (you will need to retrieve the MySQL datastore ID
|
||||||
|
// by using the datastores API).
|
||||||
|
func ListDatastoreParams(client *gophercloud.ServiceClient, datastoreID, versionID string) pagination.Pager {
|
||||||
|
return os.ListDatastoreParams(client, datastoreID, versionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDatastoreParam will retrieve information about a specific configuration
|
||||||
|
// parameter. For example, you can use this operation to understand more about
|
||||||
|
// "innodb_file_per_table" configuration param for MySQL datastores. You will
|
||||||
|
// need the param's ID first, which can be attained by using the ListDatastoreParams
|
||||||
|
// operation.
|
||||||
|
func GetDatastoreParam(client *gophercloud.ServiceClient, datastoreID, versionID, paramID string) os.ParamResult {
|
||||||
|
return os.GetDatastoreParam(client, datastoreID, versionID, paramID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListGlobalParams is similar to ListDatastoreParams but does not require a
|
||||||
|
// DatastoreID.
|
||||||
|
func ListGlobalParams(client *gophercloud.ServiceClient, versionID string) pagination.Pager {
|
||||||
|
return os.ListGlobalParams(client, versionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGlobalParam is similar to GetDatastoreParam but does not require a
|
||||||
|
// DatastoreID.
|
||||||
|
func GetGlobalParam(client *gophercloud.ServiceClient, versionID, paramID string) os.ParamResult {
|
||||||
|
return os.GetGlobalParam(client, versionID, paramID)
|
||||||
|
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/configurations/doc.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/configurations/doc.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package configurations
|
159
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/configurations/fixtures.go
generated
vendored
Normal file
159
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/configurations/fixtures.go
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package configurations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/db/v1/configurations"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
timestamp = "2015-11-12T14:22:42Z"
|
||||||
|
timeVal, _ = time.Parse(time.RFC3339, timestamp)
|
||||||
|
)
|
||||||
|
|
||||||
|
var singleConfigJSON = `
|
||||||
|
{
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"datastore_name": "mysql",
|
||||||
|
"datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"datastore_version_name": "5.6",
|
||||||
|
"description": "example_description",
|
||||||
|
"id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
"name": "example-configuration-name",
|
||||||
|
"updated": "` + timestamp + `"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var singleConfigWithValuesJSON = `
|
||||||
|
{
|
||||||
|
"created": "` + timestamp + `",
|
||||||
|
"datastore_name": "mysql",
|
||||||
|
"datastore_version_id": "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
"datastore_version_name": "5.6",
|
||||||
|
"description": "example description",
|
||||||
|
"id": "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
"instance_count": 0,
|
||||||
|
"name": "example-configuration-name",
|
||||||
|
"updated": "` + timestamp + `",
|
||||||
|
"values": {
|
||||||
|
"collation_server": "latin1_swedish_ci",
|
||||||
|
"connect_timeout": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
listConfigsJSON = fmt.Sprintf(`{"configurations": [%s]}`, singleConfigJSON)
|
||||||
|
getConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigJSON)
|
||||||
|
createConfigJSON = fmt.Sprintf(`{"configuration": %s}`, singleConfigWithValuesJSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
var createReq = `
|
||||||
|
{
|
||||||
|
"configuration": {
|
||||||
|
"datastore": {
|
||||||
|
"type": "a00000a0-00a0-0a00-00a0-000a000000aa",
|
||||||
|
"version": "b00000b0-00b0-0b00-00b0-000b000000bb"
|
||||||
|
},
|
||||||
|
"description": "example description",
|
||||||
|
"name": "example-configuration-name",
|
||||||
|
"values": {
|
||||||
|
"collation_server": "latin1_swedish_ci",
|
||||||
|
"connect_timeout": 120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var updateReq = `
|
||||||
|
{
|
||||||
|
"configuration": {
|
||||||
|
"values": {
|
||||||
|
"connect_timeout": 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var listInstancesJSON = `
|
||||||
|
{
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"id": "d4603f69-ec7e-4e9b-803f-600b9205576f",
|
||||||
|
"name": "json_rack_instance"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var listParamsJSON = `
|
||||||
|
{
|
||||||
|
"configuration-parameters": [
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"name": "innodb_file_per_table",
|
||||||
|
"restart_required": true,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4294967296,
|
||||||
|
"min": 0,
|
||||||
|
"name": "key_buffer_size",
|
||||||
|
"restart_required": false,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 65535,
|
||||||
|
"min": 2,
|
||||||
|
"name": "connect_timeout",
|
||||||
|
"restart_required": false,
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max": 4294967296,
|
||||||
|
"min": 0,
|
||||||
|
"name": "join_buffer_size",
|
||||||
|
"restart_required": false,
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var getParamJSON = `
|
||||||
|
{
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"name": "innodb_file_per_table",
|
||||||
|
"restart_required": true,
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
var exampleConfig = os.Config{
|
||||||
|
Created: timeVal,
|
||||||
|
DatastoreName: "mysql",
|
||||||
|
DatastoreVersionID: "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
DatastoreVersionName: "5.6",
|
||||||
|
Description: "example_description",
|
||||||
|
ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
Name: "example-configuration-name",
|
||||||
|
Updated: timeVal,
|
||||||
|
}
|
||||||
|
|
||||||
|
var exampleConfigWithValues = os.Config{
|
||||||
|
Created: timeVal,
|
||||||
|
DatastoreName: "mysql",
|
||||||
|
DatastoreVersionID: "b00000b0-00b0-0b00-00b0-000b000000bb",
|
||||||
|
DatastoreVersionName: "5.6",
|
||||||
|
Description: "example description",
|
||||||
|
ID: "005a8bb7-a8df-40ee-b0b7-fc144641abc2",
|
||||||
|
Name: "example-configuration-name",
|
||||||
|
Updated: timeVal,
|
||||||
|
Values: map[string]interface{}{
|
||||||
|
"collation_server": "latin1_swedish_ci",
|
||||||
|
"connect_timeout": 120,
|
||||||
|
},
|
||||||
|
}
|
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/databases/delegate.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/databases/delegate.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package databases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/db/v1/databases"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Create(client *gophercloud.ServiceClient, instanceID string, opts os.CreateOptsBuilder) os.CreateResult {
|
||||||
|
return os.Create(client, instanceID, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func List(client *gophercloud.ServiceClient, instanceID string) pagination.Pager {
|
||||||
|
return os.List(client, instanceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Delete(client *gophercloud.ServiceClient, instanceID, dbName string) os.DeleteResult {
|
||||||
|
return os.Delete(client, instanceID, dbName)
|
||||||
|
}
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/databases/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/databases/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package databases provides information and interaction with the database API
|
||||||
|
// resource in the Rackspace Database service.
|
||||||
|
package databases
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/databases/urls.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/databases/urls.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package databases
|
28
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/datastores/delegate.go
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/datastores/delegate.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package datastores
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/db/v1/datastores"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List will list all available flavors.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return os.List(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the details for a particular flavor.
|
||||||
|
func Get(client *gophercloud.ServiceClient, flavorID string) os.GetResult {
|
||||||
|
return os.Get(client, flavorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVersions will list all of the available versions for a specified
|
||||||
|
// datastore type.
|
||||||
|
func ListVersions(client *gophercloud.ServiceClient, datastoreID string) pagination.Pager {
|
||||||
|
return os.ListVersions(client, datastoreID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion will retrieve the details of a specified datastore version.
|
||||||
|
func GetVersion(client *gophercloud.ServiceClient, datastoreID, versionID string) os.GetVersionResult {
|
||||||
|
return os.GetVersion(client, datastoreID, versionID)
|
||||||
|
}
|
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/datastores/doc.go
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/datastores/doc.go
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package datastores
|
17
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/flavors/delegate.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/flavors/delegate.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package flavors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/db/v1/flavors"
|
||||||
|
"github.com/rackspace/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List will list all available flavors.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return os.List(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the details for a particular flavor.
|
||||||
|
func Get(client *gophercloud.ServiceClient, flavorID string) os.GetResult {
|
||||||
|
return os.Get(client, flavorID)
|
||||||
|
}
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/flavors/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/flavors/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package flavors provides information and interaction with the flavor API
|
||||||
|
// resource in the Rackspace Database service.
|
||||||
|
package flavors
|
49
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/instances/delegate.go
generated
vendored
Normal file
49
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/instances/delegate.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package instances
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rackspace/gophercloud"
|
||||||
|
os "github.com/rackspace/gophercloud/openstack/db/v1/instances"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get retrieves the status and information for a specified database instance.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) GetResult {
|
||||||
|
return GetResult{os.Get(client, id)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete permanently destroys the database instance.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) os.DeleteResult {
|
||||||
|
return os.Delete(client, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableRootUser enables the login from any host for the root user and
|
||||||
|
// provides the user with a generated root password.
|
||||||
|
func EnableRootUser(client *gophercloud.ServiceClient, id string) os.UserRootResult {
|
||||||
|
return os.EnableRootUser(client, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRootEnabled checks an instance to see if root access is enabled. It returns
|
||||||
|
// True if root user is enabled for the specified database instance or False
|
||||||
|
// otherwise.
|
||||||
|
func IsRootEnabled(client *gophercloud.ServiceClient, id string) (bool, error) {
|
||||||
|
return os.IsRootEnabled(client, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart will restart only the MySQL Instance. Restarting MySQL will
|
||||||
|
// erase any dynamic configuration settings that you have made within MySQL.
|
||||||
|
// The MySQL service will be unavailable until the instance restarts.
|
||||||
|
func Restart(client *gophercloud.ServiceClient, id string) os.ActionResult {
|
||||||
|
return os.Restart(client, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize changes the memory size of the instance, assuming a valid
|
||||||
|
// flavorRef is provided. It will also restart the MySQL service.
|
||||||
|
func Resize(client *gophercloud.ServiceClient, id, flavorRef string) os.ActionResult {
|
||||||
|
return os.Resize(client, id, flavorRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeVolume will resize the attached volume for an instance. It supports
|
||||||
|
// only increasing the volume size and does not support decreasing the size.
|
||||||
|
// The volume size is in gigabytes (GB) and must be an integer.
|
||||||
|
func ResizeVolume(client *gophercloud.ServiceClient, id string, size int) os.ActionResult {
|
||||||
|
return os.ResizeVolume(client, id, size)
|
||||||
|
}
|
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/instances/doc.go
generated
vendored
Normal file
3
Godeps/_workspace/src/github.com/rackspace/gophercloud/rackspace/db/v1/instances/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package instances provides information and interaction with the instance API
|
||||||
|
// resource in the Rackspace Database service.
|
||||||
|
package instances
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user