Update gophercloud version #5046
This commit is contained in:
parent
a23ab86226
commit
469f125933
|
@ -0,0 +1,148 @@
|
||||||
|
# Tips
|
||||||
|
|
||||||
|
## Implementing default logging and re-authentication attempts
|
||||||
|
|
||||||
|
You can implement custom logging and/or limit re-auth attempts by creating a custom HTTP client
|
||||||
|
like the following and setting it as the provider client's HTTP Client (via the
|
||||||
|
`gophercloud.ProviderClient.HTTPClient` field):
|
||||||
|
|
||||||
|
```go
|
||||||
|
//...
|
||||||
|
|
||||||
|
// LogRoundTripper satisfies the http.RoundTripper interface and is used to
|
||||||
|
// customize the default Gophercloud RoundTripper to allow for logging.
|
||||||
|
type LogRoundTripper struct {
|
||||||
|
rt http.RoundTripper
|
||||||
|
numReauthAttempts int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHTTPClient return a custom HTTP client that allows for logging relevant
|
||||||
|
// information before and after the HTTP request.
|
||||||
|
func newHTTPClient() http.Client {
|
||||||
|
return http.Client{
|
||||||
|
Transport: &LogRoundTripper{
|
||||||
|
rt: http.DefaultTransport,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
|
||||||
|
func (lrt *LogRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||||
|
glog.Infof("Request URL: %s\n", request.URL)
|
||||||
|
|
||||||
|
response, err := lrt.rt.RoundTrip(request)
|
||||||
|
if response == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode == http.StatusUnauthorized {
|
||||||
|
if lrt.numReauthAttempts == 3 {
|
||||||
|
return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.")
|
||||||
|
}
|
||||||
|
lrt.numReauthAttempts++
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.Debugf("Response Status: %s\n", response.Status)
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint := "https://127.0.0.1/auth"
|
||||||
|
pc := openstack.NewClient(endpoint)
|
||||||
|
pc.HTTPClient = newHTTPClient()
|
||||||
|
|
||||||
|
//...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Implementing custom objects
|
||||||
|
|
||||||
|
OpenStack request/response objects may differ among variable names or types.
|
||||||
|
|
||||||
|
### Custom request objects
|
||||||
|
|
||||||
|
To pass custom options to a request, implement the desired `<ACTION>OptsBuilder` interface. For
|
||||||
|
example, to pass in
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyCreateServerOpts struct {
|
||||||
|
Name string
|
||||||
|
Size int
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
to `servers.Create`, simply implement the `servers.CreateOptsBuilder` interface:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func (o MyCreateServeropts) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"name": o.Name,
|
||||||
|
"size": o.Size,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
create an instance of your custom options object, and pass it to `servers.Create`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
myOpts := MyCreateServerOpts{
|
||||||
|
Name: "s1",
|
||||||
|
Size: "100",
|
||||||
|
}
|
||||||
|
server, err := servers.Create(computeClient, myOpts).Extract()
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom response objects
|
||||||
|
|
||||||
|
Some OpenStack services have extensions. Extensions that are supported in Gophercloud can be
|
||||||
|
combined to create a custom object:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
type MyVolume struct {
|
||||||
|
volumes.Volume
|
||||||
|
tenantattr.VolumeExt
|
||||||
|
}
|
||||||
|
|
||||||
|
var v struct {
|
||||||
|
MyVolume `json:"volume"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := volumes.Get(client, volID).ExtractInto(&v)
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Overriding default `UnmarshalJSON` method
|
||||||
|
|
||||||
|
For some response objects, a field may be a custom type or may be allowed to take on
|
||||||
|
different types. In these cases, overriding the default `UnmarshalJSON` method may be
|
||||||
|
necessary. To do this, declare the JSON `struct` field tag as "-" and create an `UnmarshalJSON`
|
||||||
|
method on the type:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
type MyVolume struct {
|
||||||
|
ID string `json: "id"`
|
||||||
|
TimeCreated time.Time `json: "-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MyVolume) UnmarshalJSON(b []byte) error {
|
||||||
|
type tmp MyVolume
|
||||||
|
var s struct {
|
||||||
|
tmp
|
||||||
|
TimeCreated gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*r = Volume(s.tmp)
|
||||||
|
|
||||||
|
r.TimeCreated = time.Time(s.CreatedAt)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
```
|
|
@ -74,7 +74,7 @@ import (
|
||||||
|
|
||||||
// Option 1: Pass in the values yourself
|
// Option 1: Pass in the values yourself
|
||||||
opts := gophercloud.AuthOptions{
|
opts := gophercloud.AuthOptions{
|
||||||
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
|
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||||
Username: "{username}",
|
Username: "{username}",
|
||||||
Password: "{password}",
|
Password: "{password}",
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,10 @@ The above code sample creates a new server with the parameters, and embodies the
|
||||||
new resource in the `server` variable (a
|
new resource in the `server` variable (a
|
||||||
[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct).
|
[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct).
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
Have a look at the [FAQ](./FAQ.md) for some tips on customizing the way Gophercloud works.
|
||||||
|
|
||||||
## Backwards-Compatibility Guarantees
|
## Backwards-Compatibility Guarantees
|
||||||
|
|
||||||
None. Vendor it and write tests covering the parts you use.
|
None. Vendor it and write tests covering the parts you use.
|
||||||
|
|
|
@ -20,6 +20,12 @@
|
||||||
- A PR that is in-progress should have `[wip]` in front of the PR's title. When
|
- A PR that is in-progress should have `[wip]` in front of the PR's title. When
|
||||||
ready for review, remove the `[wip]` and ping a core contributor with an `@`.
|
ready for review, remove the `[wip]` and ping a core contributor with an `@`.
|
||||||
|
|
||||||
|
- Forcing PRs to be small can have the effect of users submitting PRs in a hierarchical chain, with
|
||||||
|
one depending on the next. If a PR depends on another one, it should have a [Pending #PRNUM]
|
||||||
|
prefix in the PR title. In addition, it will be the PR submitter's responsibility to remove the
|
||||||
|
[Pending #PRNUM] tag once the PR has been updated with the merged, dependent PR. That will
|
||||||
|
let reviewers know it is ready to review.
|
||||||
|
|
||||||
- A PR should be small. Even if you intend on implementing an entire
|
- A PR should be small. Even if you intend on implementing an entire
|
||||||
service, a PR should only be one route of that service
|
service, a PR should only be one route of that service
|
||||||
(e.g. create server or get server, but not both).
|
(e.g. create server or get server, but not both).
|
||||||
|
@ -51,7 +57,7 @@
|
||||||
|
|
||||||
### Naming
|
### Naming
|
||||||
|
|
||||||
- For methods on a type in `response.go`, the receiver should be named `r` and the
|
- For methods on a type in `results.go`, the receiver should be named `r` and the
|
||||||
variable into which it will be unmarshalled `s`.
|
variable into which it will be unmarshalled `s`.
|
||||||
|
|
||||||
- Functions in `requests.go`, with the exception of functions that return a
|
- Functions in `requests.go`, with the exception of functions that return a
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package gophercloud
|
package gophercloud
|
||||||
|
|
||||||
/*
|
/*
|
||||||
AuthOptions stores information needed to authenticate to an OpenStack cluster.
|
AuthOptions stores information needed to authenticate to an OpenStack Cloud.
|
||||||
You can populate one manually, or use a provider's AuthOptionsFromEnv() function
|
You can populate one manually, or use a provider's AuthOptionsFromEnv() function
|
||||||
to read relevant information from the standard environment variables. Pass one
|
to read relevant information from the standard environment variables. Pass one
|
||||||
to a provider's AuthenticatedClient function to authenticate and obtain a
|
to a provider's AuthenticatedClient function to authenticate and obtain a
|
||||||
|
@ -21,19 +21,26 @@ type AuthOptions struct {
|
||||||
// control panel to discover your account's username. In Identity V3, either
|
// control panel to discover your account's username. In Identity V3, either
|
||||||
// UserID or a combination of Username and DomainID or DomainName are needed.
|
// UserID or a combination of Username and DomainID or DomainName are needed.
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
UserID string `json:"id,omitempty"`
|
UserID string `json:"-"`
|
||||||
|
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
|
||||||
// At most one of DomainID and DomainName must be provided if using Username
|
// At most one of DomainID and DomainName must be provided if using Username
|
||||||
// with Identity V3. Otherwise, either are optional.
|
// with Identity V3. Otherwise, either are optional.
|
||||||
DomainID string `json:"id,omitempty"`
|
DomainID string `json:"-"`
|
||||||
DomainName string `json:"name,omitempty"`
|
DomainName string `json:"name,omitempty"`
|
||||||
|
|
||||||
// The TenantID and TenantName fields are optional for the Identity V2 API.
|
// The TenantID and TenantName fields are optional for the Identity V2 API.
|
||||||
|
// The same fields are known as project_id and project_name in the Identity
|
||||||
|
// V3 API, but are collected as TenantID and TenantName here in both cases.
|
||||||
// Some providers allow you to specify a TenantName instead of the TenantId.
|
// Some providers allow you to specify a TenantName instead of the TenantId.
|
||||||
// Some require both. Your provider's authentication policies will determine
|
// Some require both. Your provider's authentication policies will determine
|
||||||
// how these fields influence authentication.
|
// how these fields influence authentication.
|
||||||
|
// If DomainID or DomainName are provided, they will also apply to TenantName.
|
||||||
|
// It is not currently possible to authenticate with Username and a Domain
|
||||||
|
// and scope to a Project in a different Domain by using TenantName. To
|
||||||
|
// accomplish that, the ProjectID will need to be provided to the TenantID
|
||||||
|
// option.
|
||||||
TenantID string `json:"tenantId,omitempty"`
|
TenantID string `json:"tenantId,omitempty"`
|
||||||
TenantName string `json:"tenantName,omitempty"`
|
TenantName string `json:"tenantName,omitempty"`
|
||||||
|
|
||||||
|
@ -132,14 +139,6 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
|
||||||
// if insufficient or incompatible information is present.
|
// if insufficient or incompatible information is present.
|
||||||
var req request
|
var req request
|
||||||
|
|
||||||
// Test first for unrecognized arguments.
|
|
||||||
if opts.TenantID != "" {
|
|
||||||
return nil, ErrTenantIDProvided{}
|
|
||||||
}
|
|
||||||
if opts.TenantName != "" {
|
|
||||||
return nil, ErrTenantNameProvided{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Password == "" {
|
if opts.Password == "" {
|
||||||
if opts.TokenID != "" {
|
if opts.TokenID != "" {
|
||||||
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
|
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
|
||||||
|
@ -252,15 +251,12 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
|
|
||||||
if opts.TenantID != "" {
|
if opts.TenantID != "" {
|
||||||
scope.ProjectID = opts.TenantID
|
scope.ProjectID = opts.TenantID
|
||||||
opts.TenantID = ""
|
|
||||||
opts.TenantName = ""
|
|
||||||
} else {
|
} else {
|
||||||
if opts.TenantName != "" {
|
if opts.TenantName != "" {
|
||||||
scope.ProjectName = opts.TenantName
|
scope.ProjectName = opts.TenantName
|
||||||
scope.DomainID = opts.DomainID
|
scope.DomainID = opts.DomainID
|
||||||
scope.DomainName = opts.DomainName
|
scope.DomainName = opts.DomainName
|
||||||
}
|
}
|
||||||
opts.TenantName = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if scope.ProjectName != "" {
|
if scope.ProjectName != "" {
|
||||||
|
|
|
@ -4,11 +4,13 @@ clouds. The library has a three-level hierarchy: providers, services, and
|
||||||
resources.
|
resources.
|
||||||
|
|
||||||
Provider structs represent the service providers that offer and manage a
|
Provider structs represent the service providers that offer and manage a
|
||||||
collection of services. Examples of providers include: OpenStack, Rackspace,
|
collection of services. The IdentityEndpoint is typically refered to as
|
||||||
HP. These are defined like so:
|
"auth_url" in information provided by the cloud operator. Additionally,
|
||||||
|
the cloud may refer to TenantID or TenantName as project_id and project_name.
|
||||||
|
These are defined like so:
|
||||||
|
|
||||||
opts := gophercloud.AuthOptions{
|
opts := gophercloud.AuthOptions{
|
||||||
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
|
IdentityEndpoint: "https://openstack.example.com:5000/v2.0",
|
||||||
Username: "{username}",
|
Username: "{username}",
|
||||||
Password: "{password}",
|
Password: "{password}",
|
||||||
TenantID: "{tenant_id}",
|
TenantID: "{tenant_id}",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package internal
|
|
@ -0,0 +1,34 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemainingKeys will inspect a struct and compare it to a map. Any struct
|
||||||
|
// field that does not have a JSON tag that matches a key in the map or
|
||||||
|
// a matching lower-case field in the map will be returned as an extra.
|
||||||
|
//
|
||||||
|
// This is useful for determining the extra fields returned in response bodies
|
||||||
|
// for resources that can contain an arbitrary or dynamic number of fields.
|
||||||
|
func RemainingKeys(s interface{}, m map[string]interface{}) (extras map[string]interface{}) {
|
||||||
|
extras = make(map[string]interface{})
|
||||||
|
for k, v := range m {
|
||||||
|
extras[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
valueOf := reflect.ValueOf(s)
|
||||||
|
typeOf := reflect.TypeOf(s)
|
||||||
|
for i := 0; i < valueOf.NumField(); i++ {
|
||||||
|
field := typeOf.Field(i)
|
||||||
|
|
||||||
|
lowerField := strings.ToLower(field.Name)
|
||||||
|
delete(extras, lowerField)
|
||||||
|
|
||||||
|
if tagValue := field.Tag.Get("json"); tagValue != "" && tagValue != "-" {
|
||||||
|
delete(extras, tagValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -182,9 +182,10 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
|
||||||
// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
|
// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
|
||||||
func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
endpoint := client.IdentityBase + "v2.0/"
|
endpoint := client.IdentityBase + "v2.0/"
|
||||||
|
clientType := "identity"
|
||||||
var err error
|
var err error
|
||||||
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||||
eo.ApplyDefaults("identity")
|
eo.ApplyDefaults(clientType)
|
||||||
endpoint, err = client.EndpointLocator(eo)
|
endpoint, err = client.EndpointLocator(eo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -194,15 +195,17 @@ func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp
|
||||||
return &gophercloud.ServiceClient{
|
return &gophercloud.ServiceClient{
|
||||||
ProviderClient: client,
|
ProviderClient: client,
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
|
Type: clientType,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
|
// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
|
||||||
func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
endpoint := client.IdentityBase + "v3/"
|
endpoint := client.IdentityBase + "v3/"
|
||||||
|
clientType := "identity"
|
||||||
var err error
|
var err error
|
||||||
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||||
eo.ApplyDefaults("identity")
|
eo.ApplyDefaults(clientType)
|
||||||
endpoint, err = client.EndpointLocator(eo)
|
endpoint, err = client.EndpointLocator(eo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -212,112 +215,81 @@ func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOp
|
||||||
return &gophercloud.ServiceClient{
|
return &gophercloud.ServiceClient{
|
||||||
ProviderClient: client,
|
ProviderClient: client,
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
|
Type: clientType,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) {
|
||||||
|
sc := new(gophercloud.ServiceClient)
|
||||||
|
eo.ApplyDefaults(clientType)
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
sc.ProviderClient = client
|
||||||
|
sc.Endpoint = url
|
||||||
|
sc.Type = clientType
|
||||||
|
return sc, 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")
|
return initClientOpts(client, eo, "object-store")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
|
// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
|
||||||
func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("compute")
|
return initClientOpts(client, eo, "compute")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
|
// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
|
||||||
func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("network")
|
sc, err := initClientOpts(client, eo, "network")
|
||||||
url, err := client.EndpointLocator(eo)
|
sc.ResourceBase = sc.Endpoint + "v2.0/"
|
||||||
if err != nil {
|
return sc, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{
|
|
||||||
ProviderClient: client,
|
|
||||||
Endpoint: url,
|
|
||||||
ResourceBase: url + "v2.0/",
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
|
// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
|
||||||
func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("volume")
|
return initClientOpts(client, eo, "volume")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
|
// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
|
||||||
func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("volumev2")
|
return initClientOpts(client, eo, "volumev2")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
|
// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
|
||||||
func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("sharev2")
|
return initClientOpts(client, eo, "sharev2")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
|
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
|
||||||
// CDN service.
|
// CDN service.
|
||||||
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("cdn")
|
return initClientOpts(client, eo, "cdn")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
|
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
|
||||||
func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("orchestration")
|
return initClientOpts(client, eo, "orchestration")
|
||||||
url, err := client.EndpointLocator(eo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
|
// 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) {
|
func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("database")
|
return initClientOpts(client, eo, "database")
|
||||||
url, err := client.EndpointLocator(eo)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS service.
|
||||||
}
|
func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
sc, err := initClientOpts(client, eo, "dns")
|
||||||
|
sc.ResourceBase = sc.Endpoint + "v2/"
|
||||||
|
return sc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 image service.
|
// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 image service.
|
||||||
func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
eo.ApplyDefaults("image")
|
sc, err := initClientOpts(client, eo, "image")
|
||||||
url, err := client.EndpointLocator(eo)
|
sc.ResourceBase = sc.Endpoint + "v2/"
|
||||||
if err != nil {
|
return sc, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &gophercloud.ServiceClient{ProviderClient: client,
|
|
||||||
Endpoint: url,
|
|
||||||
ResourceBase: url + "v2/"}, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package floatingips
|
package floatingips
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
@ -8,7 +11,7 @@ import (
|
||||||
// A FloatingIP is an IP that can be associated with an instance
|
// A FloatingIP is an IP that can be associated with an instance
|
||||||
type FloatingIP struct {
|
type FloatingIP struct {
|
||||||
// ID is a unique ID of the Floating IP
|
// ID is a unique ID of the Floating IP
|
||||||
ID string `json:"id"`
|
ID string `json:"-"`
|
||||||
|
|
||||||
// FixedIP is the IP of the instance related to the Floating IP
|
// FixedIP is the IP of the instance related to the Floating IP
|
||||||
FixedIP string `json:"fixed_ip,omitempty"`
|
FixedIP string `json:"fixed_ip,omitempty"`
|
||||||
|
@ -23,6 +26,29 @@ type FloatingIP struct {
|
||||||
Pool string `json:"pool"`
|
Pool string `json:"pool"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *FloatingIP) UnmarshalJSON(b []byte) error {
|
||||||
|
type tmp FloatingIP
|
||||||
|
var s struct {
|
||||||
|
tmp
|
||||||
|
ID interface{} `json:"id"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(b, &s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*r = FloatingIP(s.tmp)
|
||||||
|
|
||||||
|
switch t := s.ID.(type) {
|
||||||
|
case float64:
|
||||||
|
r.ID = strconv.FormatFloat(t, 'f', -1, 64)
|
||||||
|
case string:
|
||||||
|
r.ID = t
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// FloatingIPPage stores a single, only page of FloatingIPs
|
// FloatingIPPage stores a single, only page of FloatingIPs
|
||||||
// results from a List call.
|
// results from a List call.
|
||||||
type FloatingIPPage struct {
|
type FloatingIPPage struct {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyPair is an SSH key known to the OpenStack cluster that is available to be injected into
|
// KeyPair is an SSH key known to the OpenStack Cloud that is available to be injected into
|
||||||
// servers.
|
// servers.
|
||||||
type KeyPair struct {
|
type KeyPair struct {
|
||||||
// Name is used to refer to this keypair from other services within this region.
|
// Name is used to refer to this keypair from other services within this region.
|
||||||
|
|
63
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
63
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go
generated
vendored
|
@ -11,6 +11,24 @@ type ListOptsBuilder interface {
|
||||||
ToFlavorListQuery() (string, error)
|
ToFlavorListQuery() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccessType maps to OpenStack's Flavor.is_public field. Although the is_public field is boolean, the
|
||||||
|
// request options are ternary, which is why AccessType is a string. The following values are
|
||||||
|
// allowed:
|
||||||
|
//
|
||||||
|
// PublicAccess (the default): Returns public flavors and private flavors associated with that project.
|
||||||
|
// PrivateAccess (admin only): Returns private flavors, across all projects.
|
||||||
|
// AllAccess (admin only): Returns public and private flavors across all projects.
|
||||||
|
//
|
||||||
|
// The AccessType arguement is optional, and if it is not supplied, OpenStack returns the PublicAccess
|
||||||
|
// flavors.
|
||||||
|
type AccessType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PublicAccess AccessType = "true"
|
||||||
|
PrivateAccess AccessType = "false"
|
||||||
|
AllAccess AccessType = "None"
|
||||||
|
)
|
||||||
|
|
||||||
// ListOpts helps control the results returned by the List() function.
|
// ListOpts helps control the results returned by the List() function.
|
||||||
// For example, a flavor with a minDisk field of 10 will not be returned if you specify MinDisk set to 20.
|
// For example, a flavor with a minDisk field of 10 will not be returned if you specify MinDisk set to 20.
|
||||||
// Typically, software will use the last ID of the previous call to List to set the Marker for the current call.
|
// Typically, software will use the last ID of the previous call to List to set the Marker for the current call.
|
||||||
|
@ -29,6 +47,10 @@ type ListOpts struct {
|
||||||
|
|
||||||
// Limit instructs List to refrain from sending excessively large lists of flavors.
|
// Limit instructs List to refrain from sending excessively large lists of flavors.
|
||||||
Limit int `q:"limit"`
|
Limit int `q:"limit"`
|
||||||
|
|
||||||
|
// AccessType, if provided, instructs List which set of flavors to return. If IsPublic not provided,
|
||||||
|
// flavors for the current project are returned.
|
||||||
|
AccessType AccessType `q:"is_public"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToFlavorListQuery formats a ListOpts into a query string.
|
// ToFlavorListQuery formats a ListOpts into a query string.
|
||||||
|
@ -54,6 +76,47 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToFlavorCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts is passed to Create to create a flavor
|
||||||
|
// Source:
|
||||||
|
// https://github.com/openstack/nova/blob/stable/newton/nova/api/openstack/compute/schemas/flavor_manage.py#L20
|
||||||
|
type CreateOpts struct {
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
// memory size, in MBs
|
||||||
|
RAM int `json:"ram" required:"true"`
|
||||||
|
VCPUs int `json:"vcpus" required:"true"`
|
||||||
|
// disk size, in GBs
|
||||||
|
Disk *int `json:"disk" required:"true"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
// non-zero, positive
|
||||||
|
Swap *int `json:"swap,omitempty"`
|
||||||
|
RxTxFactor float64 `json:"rxtx_factor,omitempty"`
|
||||||
|
IsPublic *bool `json:"os-flavor-access:is_public,omitempty"`
|
||||||
|
// ephemeral disk size, in GBs, non-zero, positive
|
||||||
|
Ephemeral *int `json:"OS-FLV-EXT-DATA:ephemeral,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFlavorCreateMap satisfies the CreateOptsBuilder interface
|
||||||
|
func (opts *CreateOpts) ToFlavorCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "flavor")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a flavor
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToFlavorCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 201},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get instructs OpenStack to provide details on a single flavor, identified by its ID.
|
// Get instructs OpenStack to provide details on a single flavor, identified by its ID.
|
||||||
// Use ExtractFlavor to convert its result into a Flavor.
|
// Use ExtractFlavor to convert its result into a Flavor.
|
||||||
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
|
47
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go
generated
vendored
47
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go
generated
vendored
|
@ -8,13 +8,21 @@ import (
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetResult temporarily holds the response from a Get call.
|
type commonResult struct {
|
||||||
type GetResult struct {
|
|
||||||
gophercloud.Result
|
gophercloud.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract provides access to the individual Flavor returned by the Get function.
|
type CreateResult struct {
|
||||||
func (r GetResult) Extract() (*Flavor, error) {
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult temporarily holds the response from a Get call.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract provides access to the individual Flavor returned by the Get and Create functions.
|
||||||
|
func (r commonResult) Extract() (*Flavor, error) {
|
||||||
var s struct {
|
var s struct {
|
||||||
Flavor *Flavor `json:"flavor"`
|
Flavor *Flavor `json:"flavor"`
|
||||||
}
|
}
|
||||||
|
@ -38,43 +46,36 @@ type Flavor struct {
|
||||||
Swap int `json:"swap"`
|
Swap int `json:"swap"`
|
||||||
// VCPUs indicates how many (virtual) CPUs are available for this flavor.
|
// VCPUs indicates how many (virtual) CPUs are available for this flavor.
|
||||||
VCPUs int `json:"vcpus"`
|
VCPUs int `json:"vcpus"`
|
||||||
|
// IsPublic indicates whether the flavor is public.
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Flavor) UnmarshalJSON(b []byte) error {
|
func (r *Flavor) UnmarshalJSON(b []byte) error {
|
||||||
var flavor struct {
|
type tmp Flavor
|
||||||
ID string `json:"id"`
|
var s struct {
|
||||||
Disk int `json:"disk"`
|
tmp
|
||||||
RAM int `json:"ram"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
RxTxFactor float64 `json:"rxtx_factor"`
|
|
||||||
Swap interface{} `json:"swap"`
|
Swap interface{} `json:"swap"`
|
||||||
VCPUs int `json:"vcpus"`
|
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(b, &flavor)
|
err := json.Unmarshal(b, &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.ID = flavor.ID
|
*r = Flavor(s.tmp)
|
||||||
f.Disk = flavor.Disk
|
|
||||||
f.RAM = flavor.RAM
|
|
||||||
f.Name = flavor.Name
|
|
||||||
f.RxTxFactor = flavor.RxTxFactor
|
|
||||||
f.VCPUs = flavor.VCPUs
|
|
||||||
|
|
||||||
switch t := flavor.Swap.(type) {
|
switch t := s.Swap.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
f.Swap = int(t)
|
r.Swap = int(t)
|
||||||
case string:
|
case string:
|
||||||
switch t {
|
switch t {
|
||||||
case "":
|
case "":
|
||||||
f.Swap = 0
|
r.Swap = 0
|
||||||
default:
|
default:
|
||||||
swap, err := strconv.ParseFloat(t, 64)
|
swap, err := strconv.ParseFloat(t, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.Swap = int(swap)
|
r.Swap = int(swap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,3 +11,7 @@ func getURL(client *gophercloud.ServiceClient, id string) string {
|
||||||
func listURL(client *gophercloud.ServiceClient) string {
|
func listURL(client *gophercloud.ServiceClient) string {
|
||||||
return client.ServiceURL("flavors", "detail")
|
return client.ServiceURL("flavors", "detail")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("flavors")
|
||||||
|
}
|
||||||
|
|
2
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go
generated
vendored
2
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go
generated
vendored
|
@ -46,7 +46,7 @@ type Image struct {
|
||||||
|
|
||||||
Updated string
|
Updated string
|
||||||
|
|
||||||
Metadata map[string]string
|
Metadata map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImagePage contains a single page of results from a List operation.
|
// ImagePage contains a single page of results from a List operation.
|
||||||
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go
generated
vendored
|
@ -160,7 +160,7 @@ type CreateOpts struct {
|
||||||
|
|
||||||
// Personality includes files to inject into the server at launch.
|
// Personality includes files to inject into the server at launch.
|
||||||
// Create will base64-encode file contents for you.
|
// Create will base64-encode file contents for you.
|
||||||
Personality Personality `json:"-"`
|
Personality Personality `json:"personality,omitempty"`
|
||||||
|
|
||||||
// ConfigDrive enables metadata injection through a configuration drive.
|
// ConfigDrive enables metadata injection through a configuration drive.
|
||||||
ConfigDrive *bool `json:"config_drive,omitempty"`
|
ConfigDrive *bool `json:"config_drive,omitempty"`
|
||||||
|
@ -401,11 +401,10 @@ type RebuildOptsBuilder interface {
|
||||||
// operation
|
// operation
|
||||||
type RebuildOpts struct {
|
type RebuildOpts struct {
|
||||||
// The server's admin password
|
// The server's admin password
|
||||||
AdminPass string `json:"adminPass" required:"true"`
|
AdminPass string `json:"adminPass,omitempty"`
|
||||||
// The ID of the image you want your server to be provisioned on
|
// The ID of the image you want your server to be provisioned on
|
||||||
ImageID string `json:"imageRef"`
|
ImageID string `json:"imageRef"`
|
||||||
ImageName string `json:"-"`
|
ImageName string `json:"-"`
|
||||||
//ImageName string `json:"-"`
|
|
||||||
// Name to set the server to
|
// Name to set the server to
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
// AccessIPv4 [optional] provides a new IPv4 address for the instance.
|
// AccessIPv4 [optional] provides a new IPv4 address for the instance.
|
||||||
|
|
82
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
82
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go
generated
vendored
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
@ -18,11 +19,17 @@ type serverResult struct {
|
||||||
|
|
||||||
// Extract interprets any serverResult as a Server, if possible.
|
// Extract interprets any serverResult as a Server, if possible.
|
||||||
func (r serverResult) Extract() (*Server, error) {
|
func (r serverResult) Extract() (*Server, error) {
|
||||||
var s struct {
|
var s Server
|
||||||
Server *Server `json:"server"`
|
|
||||||
}
|
|
||||||
err := r.ExtractInto(&s)
|
err := r.ExtractInto(&s)
|
||||||
return s.Server, err
|
return &s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r serverResult) ExtractInto(v interface{}) error {
|
||||||
|
return r.Result.ExtractIntoStructPtr(v, "server")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractServersInto(r pagination.Page, v interface{}) error {
|
||||||
|
return r.(ServerPage).Result.ExtractIntoSlicePtr(v, "servers")
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateResult temporarily contains the response from a Create call.
|
// CreateResult temporarily contains the response from a Create call.
|
||||||
|
@ -101,12 +108,12 @@ func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (r CreateImageResult) ExtractImageID() (string, error) {
|
||||||
if res.Err != nil {
|
if r.Err != nil {
|
||||||
return "", res.Err
|
return "", r.Err
|
||||||
}
|
}
|
||||||
// Get the image id from the header
|
// Get the image id from the header
|
||||||
u, err := url.ParseRequestURI(res.Header.Get("Location"))
|
u, err := url.ParseRequestURI(r.Header.Get("Location"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -137,26 +144,27 @@ type Server struct {
|
||||||
// Name contains the human-readable name for the server.
|
// Name contains the human-readable name for the server.
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
|
// Updated and Created contain ISO-8601 timestamps of when the state of the server last changed, and when it was created.
|
||||||
Updated string
|
Updated time.Time `json:"updated"`
|
||||||
Created string
|
Created time.Time `json:"created"`
|
||||||
HostID string
|
HostID string `json:"hostid"`
|
||||||
// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
|
// Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE.
|
||||||
Status string
|
Status string `json:"status"`
|
||||||
// Progress ranges from 0..100.
|
// Progress ranges from 0..100.
|
||||||
// A request made against the server completes only once Progress reaches 100.
|
// A request made against the server completes only once Progress reaches 100.
|
||||||
Progress int
|
Progress int `json:"progress"`
|
||||||
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
|
// AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration.
|
||||||
AccessIPv4, AccessIPv6 string
|
AccessIPv4 string `json:"accessIPv4"`
|
||||||
|
AccessIPv6 string `json:"accessIPv6"`
|
||||||
// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
|
// Image refers to a JSON object, which itself indicates the OS image used to deploy the server.
|
||||||
Image map[string]interface{}
|
Image map[string]interface{} `json:"-"`
|
||||||
// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
|
// Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server.
|
||||||
Flavor map[string]interface{}
|
Flavor map[string]interface{} `json:"flavor"`
|
||||||
// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
|
// Addresses includes a list of all IP addresses assigned to the server, keyed by pool.
|
||||||
Addresses map[string]interface{}
|
Addresses map[string]interface{} `json:"addresses"`
|
||||||
// Metadata includes a list of all user-specified key-value pairs attached to the server.
|
// Metadata includes a list of all user-specified key-value pairs attached to the server.
|
||||||
Metadata map[string]string
|
Metadata map[string]string `json:"metadata"`
|
||||||
// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
|
// Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference.
|
||||||
Links []interface{}
|
Links []interface{} `json:"links"`
|
||||||
// KeyName indicates which public key was injected into the server on launch.
|
// KeyName indicates which public key was injected into the server on launch.
|
||||||
KeyName string `json:"key_name"`
|
KeyName string `json:"key_name"`
|
||||||
// AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
|
// AdminPass will generally be empty (""). However, it will contain the administrative password chosen when provisioning a new server without a set AdminPass setting in the first place.
|
||||||
|
@ -166,30 +174,30 @@ type Server struct {
|
||||||
SecurityGroups []map[string]interface{} `json:"security_groups"`
|
SecurityGroups []map[string]interface{} `json:"security_groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) UnmarshalJSON(b []byte) error {
|
func (r *Server) UnmarshalJSON(b []byte) error {
|
||||||
type tmp Server
|
type tmp Server
|
||||||
var server *struct {
|
var s struct {
|
||||||
tmp
|
tmp
|
||||||
Image interface{}
|
Image interface{} `json:"image"`
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(b, &server)
|
err := json.Unmarshal(b, &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*s = Server(server.tmp)
|
*r = Server(s.tmp)
|
||||||
|
|
||||||
switch t := server.Image.(type) {
|
switch t := s.Image.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
s.Image = t
|
r.Image = t
|
||||||
case string:
|
case string:
|
||||||
switch t {
|
switch t {
|
||||||
case "":
|
case "":
|
||||||
s.Image = nil
|
r.Image = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerPage abstracts the raw results of making a List() request against the API.
|
// ServerPage abstracts the raw results of making a List() request against the API.
|
||||||
|
@ -200,17 +208,17 @@ type ServerPage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmpty returns true if a page contains no Server results.
|
// IsEmpty returns true if a page contains no Server results.
|
||||||
func (page ServerPage) IsEmpty() (bool, error) {
|
func (r ServerPage) IsEmpty() (bool, error) {
|
||||||
servers, err := ExtractServers(page)
|
s, err := ExtractServers(r)
|
||||||
return len(servers) == 0, err
|
return len(s) == 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
// NextPageURL uses the response's embedded link reference to navigate to the next page of results.
|
||||||
func (page ServerPage) NextPageURL() (string, error) {
|
func (r ServerPage) NextPageURL() (string, error) {
|
||||||
var s struct {
|
var s struct {
|
||||||
Links []gophercloud.Link `json:"servers_links"`
|
Links []gophercloud.Link `json:"servers_links"`
|
||||||
}
|
}
|
||||||
err := page.ExtractInto(&s)
|
err := r.ExtractInto(&s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -219,11 +227,9 @@ func (page ServerPage) NextPageURL() (string, error) {
|
||||||
|
|
||||||
// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
|
// ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities.
|
||||||
func ExtractServers(r pagination.Page) ([]Server, error) {
|
func ExtractServers(r pagination.Page) ([]Server, error) {
|
||||||
var s struct {
|
var s []Server
|
||||||
Servers []Server `json:"servers"`
|
err := ExtractServersInto(r, &s)
|
||||||
}
|
return s, err
|
||||||
err := (r.(ServerPage)).ExtractInto(&s)
|
|
||||||
return s.Servers, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
|
// MetadataResult contains the result of a call for (potentially) multiple key-value pairs.
|
||||||
|
|
78
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
78
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
generated
vendored
|
@ -27,3 +27,81 @@ func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager {
|
||||||
return TenantPage{pagination.LinkedPageBase{PageResult: r}}
|
return TenantPage{pagination.LinkedPageBase{PageResult: r}}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateOpts represents the options needed when creating new tenant.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Name is the name of the tenant.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
// Description is the description of the tenant.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
// Enabled sets the tenant status to enabled or disabled.
|
||||||
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder describes struct types that can be accepted by the Create call.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToTenantCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTenantCreateMap assembles a request body based on the contents of a CreateOpts.
|
||||||
|
func (opts CreateOpts) ToTenantCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "tenant")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create is the operation responsible for creating new tenant.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToTenantCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 201},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get requests details on a single tenant by ID.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder allows extensions to add additional attributes to the Update request.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToTenantUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts specifies the base attributes that may be updated on an existing server.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
// Name is the name of the tenant.
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
// Description is the description of the tenant.
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
// Enabled sets the tenant status to enabled or disabled.
|
||||||
|
Enabled *bool `json:"enabled,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTenantUpdateMap formats an UpdateOpts structure into a request body.
|
||||||
|
func (opts UpdateOpts) ToTenantUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "tenant")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is the operation responsible for updating exist tenants by their TenantID.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||||
|
b, err := opts.ToTenantUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete is the operation responsible for permanently deleting an API tenant.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
33
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
generated
vendored
33
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go
generated
vendored
|
@ -51,3 +51,36 @@ func ExtractTenants(r pagination.Page) ([]Tenant, error) {
|
||||||
err := (r.(TenantPage)).ExtractInto(&s)
|
err := (r.(TenantPage)).ExtractInto(&s)
|
||||||
return s.Tenants, err
|
return s.Tenants, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type tenantResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract interprets any tenantResults as a tenant.
|
||||||
|
func (r tenantResult) Extract() (*Tenant, error) {
|
||||||
|
var s struct {
|
||||||
|
Tenant *Tenant `json:"tenant"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Tenant, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult temporarily contains the response from the Get call.
|
||||||
|
type GetResult struct {
|
||||||
|
tenantResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult temporarily contains the reponse from the Create call.
|
||||||
|
type CreateResult struct {
|
||||||
|
tenantResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult temporarily contains the response from the Delete call.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult temporarily contains the response from the Update call.
|
||||||
|
type UpdateResult struct {
|
||||||
|
tenantResult
|
||||||
|
}
|
||||||
|
|
16
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
generated
vendored
16
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go
generated
vendored
|
@ -5,3 +5,19 @@ import "github.com/gophercloud/gophercloud"
|
||||||
func listURL(client *gophercloud.ServiceClient) string {
|
func listURL(client *gophercloud.ServiceClient) string {
|
||||||
return client.ServiceURL("tenants")
|
return client.ServiceURL("tenants")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return client.ServiceURL("tenants", tenantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createURL(client *gophercloud.ServiceClient) string {
|
||||||
|
return client.ServiceURL("tenants")
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return client.ServiceURL("tenants", tenantID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateURL(client *gophercloud.ServiceClient, tenantID string) string {
|
||||||
|
return client.ServiceURL("tenants", tenantID)
|
||||||
|
}
|
||||||
|
|
5
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
5
vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go
generated
vendored
|
@ -132,11 +132,6 @@ func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||||
return &ServiceCatalog{Entries: s.Access.Entries}, err
|
return &ServiceCatalog{Entries: s.Access.Entries}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// createErr quickly packs an error in a CreateResult.
|
|
||||||
func createErr(err error) CreateResult {
|
|
||||||
return CreateResult{gophercloud.Result{Err: err}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractUser returns the User from a GetResult.
|
// ExtractUser returns the User from a GetResult.
|
||||||
func (r GetResult) ExtractUser() (*User, error) {
|
func (r GetResult) ExtractUser() (*User, error) {
|
||||||
var s struct {
|
var s struct {
|
||||||
|
|
14
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
14
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
generated
vendored
|
@ -4,10 +4,10 @@ import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
// Scope allows a created token to be limited to a specific domain or project.
|
// Scope allows a created token to be limited to a specific domain or project.
|
||||||
type Scope struct {
|
type Scope struct {
|
||||||
ProjectID string `json:"scope.project.id,omitempty" not:"ProjectName,DomainID,DomainName"`
|
ProjectID string
|
||||||
ProjectName string `json:"scope.project.name,omitempty"`
|
ProjectName string
|
||||||
DomainID string `json:"scope.project.id,omitempty" not:"ProjectName,ProjectID,DomainName"`
|
DomainID string
|
||||||
DomainName string `json:"scope.project.id,omitempty"`
|
DomainName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
|
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
|
||||||
|
@ -36,7 +36,7 @@ type AuthOptions struct {
|
||||||
|
|
||||||
// At most one of DomainID and DomainName must be provided if using Username
|
// At most one of DomainID and DomainName must be provided if using Username
|
||||||
// with Identity V3. Otherwise, either are optional.
|
// with Identity V3. Otherwise, either are optional.
|
||||||
DomainID string `json:"id,omitempty"`
|
DomainID string `json:"-"`
|
||||||
DomainName string `json:"name,omitempty"`
|
DomainName string `json:"name,omitempty"`
|
||||||
|
|
||||||
// AllowReauth should be set to true if you grant permission for Gophercloud to
|
// AllowReauth should be set to true if you grant permission for Gophercloud to
|
||||||
|
@ -182,13 +182,13 @@ func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
|
||||||
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
|
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
|
||||||
resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
|
resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{
|
||||||
MoreHeaders: subjectTokenHeaders(c, token),
|
MoreHeaders: subjectTokenHeaders(c, token),
|
||||||
OkCodes: []int{204, 404},
|
OkCodes: []int{200, 204, 404},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp.StatusCode == 204, nil
|
return resp.StatusCode == 200 || resp.StatusCode == 204, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revoke immediately makes specified token invalid.
|
// Revoke immediately makes specified token invalid.
|
||||||
|
|
94
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
94
vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
generated
vendored
|
@ -1,7 +1,10 @@
|
||||||
package tokens
|
package tokens
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
import "github.com/gophercloud/gophercloud"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
// Endpoint represents a single API endpoint offered by a service.
|
// Endpoint represents a single API endpoint offered by a service.
|
||||||
// It matches either a public, internal or admin URL.
|
// It matches either a public, internal or admin URL.
|
||||||
|
@ -35,7 +38,33 @@ type CatalogEntry struct {
|
||||||
|
|
||||||
// ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
|
// ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
|
||||||
type ServiceCatalog struct {
|
type ServiceCatalog struct {
|
||||||
Entries []CatalogEntry
|
Entries []CatalogEntry `json:"catalog"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain provides information about the domain to which this token grants access.
|
||||||
|
type Domain struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// User represents a user resource that exists on the API.
|
||||||
|
type User struct {
|
||||||
|
Domain Domain `json:"domain"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Role provides information about roles to which User is authorized.
|
||||||
|
type Role struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Project provides information about project to which User is authorized.
|
||||||
|
type Project struct {
|
||||||
|
Domain Domain `json:"domain"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// commonResult is the deferred result of a Create or a Get call.
|
// commonResult is the deferred result of a Create or a Get call.
|
||||||
|
@ -51,34 +80,50 @@ func (r commonResult) Extract() (*Token, error) {
|
||||||
|
|
||||||
// ExtractToken interprets a commonResult as a Token.
|
// ExtractToken interprets a commonResult as a Token.
|
||||||
func (r commonResult) ExtractToken() (*Token, error) {
|
func (r commonResult) ExtractToken() (*Token, error) {
|
||||||
var s struct {
|
var s Token
|
||||||
Token *Token `json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.ExtractInto(&s)
|
err := r.ExtractInto(&s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Token == nil {
|
|
||||||
return nil, errors.New("'token' missing in JSON response")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the token itself from the stored headers.
|
// Parse the token itself from the stored headers.
|
||||||
s.Token.ID = r.Header.Get("X-Subject-Token")
|
s.ID = r.Header.Get("X-Subject-Token")
|
||||||
|
|
||||||
return s.Token, err
|
return &s, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
|
// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
|
||||||
func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
|
||||||
|
var s ServiceCatalog
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return &s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractUser returns the User that is the owner of the Token.
|
||||||
|
func (r commonResult) ExtractUser() (*User, error) {
|
||||||
var s struct {
|
var s struct {
|
||||||
Token struct {
|
User *User `json:"user"`
|
||||||
Entries []CatalogEntry `json:"catalog"`
|
|
||||||
} `json:"token"`
|
|
||||||
}
|
}
|
||||||
err := r.ExtractInto(&s)
|
err := r.ExtractInto(&s)
|
||||||
return &ServiceCatalog{Entries: s.Token.Entries}, err
|
return s.User, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractRoles returns Roles to which User is authorized.
|
||||||
|
func (r commonResult) ExtractRoles() ([]Role, error) {
|
||||||
|
var s struct {
|
||||||
|
Roles []Role `json:"roles"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Roles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractProject returns Project to which User is authorized.
|
||||||
|
func (r commonResult) ExtractProject() (*Project, error) {
|
||||||
|
var s struct {
|
||||||
|
Project *Project `json:"project"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Project, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateResult defers the interpretation of a created token.
|
// CreateResult defers the interpretation of a created token.
|
||||||
|
@ -87,13 +132,6 @@ type CreateResult struct {
|
||||||
commonResult
|
commonResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// createErr quickly creates a CreateResult that reports an error.
|
|
||||||
func createErr(err error) CreateResult {
|
|
||||||
return CreateResult{
|
|
||||||
commonResult: commonResult{Result: gophercloud.Result{Err: err}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResult is the deferred response from a Get call.
|
// GetResult is the deferred response from a Get call.
|
||||||
type GetResult struct {
|
type GetResult struct {
|
||||||
commonResult
|
commonResult
|
||||||
|
@ -110,5 +148,9 @@ type Token struct {
|
||||||
// ID is the issued token.
|
// ID is the issued token.
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
// ExpiresAt is the timestamp at which this token will no longer be accepted.
|
// ExpiresAt is the timestamp at which this token will no longer be accepted.
|
||||||
ExpiresAt gophercloud.JSONRFC3339Milli `json:"expires_at"`
|
ExpiresAt time.Time `json:"expires_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r commonResult) ExtractInto(v interface{}) error {
|
||||||
|
return r.ExtractIntoStructPtr(v, "token")
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ type CreateOpts struct {
|
||||||
|
|
||||||
// properties is a set of properties, if any, that
|
// properties is a set of properties, if any, that
|
||||||
// are associated with the image.
|
// are associated with the image.
|
||||||
Properties map[string]string `json:"-,omitempty"`
|
Properties map[string]string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToImageCreateMap assembles a request body based on the contents of
|
// ToImageCreateMap assembles a request body based on the contents of
|
||||||
|
|
43
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go
generated
vendored
43
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go
generated
vendored
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/internal"
|
||||||
"github.com/gophercloud/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,13 +64,13 @@ type Image struct {
|
||||||
Metadata map[string]string `json:"metadata"`
|
Metadata map[string]string `json:"metadata"`
|
||||||
|
|
||||||
// Properties is a set of key-value pairs, if any, that are associated with the image.
|
// Properties is a set of key-value pairs, if any, that are associated with the image.
|
||||||
Properties map[string]string `json:"properties"`
|
Properties map[string]interface{} `json:"-"`
|
||||||
|
|
||||||
// CreatedAt is the date when the image has been created.
|
// CreatedAt is the date when the image has been created.
|
||||||
CreatedAt time.Time `json:"-"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
||||||
// UpdatedAt is the date when the last change has been made to the image or it's properties.
|
// UpdatedAt is the date when the last change has been made to the image or it's properties.
|
||||||
UpdatedAt time.Time `json:"-"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
|
||||||
// File is the trailing path after the glance endpoint that represent the location
|
// File is the trailing path after the glance endpoint that represent the location
|
||||||
// of the image or the path to retrieve it.
|
// of the image or the path to retrieve it.
|
||||||
|
@ -77,38 +78,45 @@ type Image struct {
|
||||||
|
|
||||||
// Schema is the path to the JSON-schema that represent the image or image entity.
|
// Schema is the path to the JSON-schema that represent the image or image entity.
|
||||||
Schema string `json:"schema"`
|
Schema string `json:"schema"`
|
||||||
|
|
||||||
|
// VirtualSize is the virtual size of the image
|
||||||
|
VirtualSize int64 `json:"virtual_size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Image) UnmarshalJSON(b []byte) error {
|
func (r *Image) UnmarshalJSON(b []byte) error {
|
||||||
type tmp Image
|
type tmp Image
|
||||||
var p *struct {
|
var s struct {
|
||||||
tmp
|
tmp
|
||||||
SizeBytes interface{} `json:"size"`
|
SizeBytes interface{} `json:"size"`
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(b, &p)
|
err := json.Unmarshal(b, &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*s = Image(p.tmp)
|
*r = Image(s.tmp)
|
||||||
|
|
||||||
switch t := p.SizeBytes.(type) {
|
switch t := s.SizeBytes.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return nil
|
return nil
|
||||||
case float32:
|
case float32:
|
||||||
s.SizeBytes = int64(t)
|
r.SizeBytes = int64(t)
|
||||||
case float64:
|
case float64:
|
||||||
s.SizeBytes = int64(t)
|
r.SizeBytes = int64(t)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t)
|
return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.CreatedAt, err = time.Parse(time.RFC3339, p.CreatedAt)
|
// Bundle all other fields into Properties
|
||||||
|
var result interface{}
|
||||||
|
err = json.Unmarshal(b, &result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.UpdatedAt, err = time.Parse(time.RFC3339, p.UpdatedAt)
|
if resultMap, ok := result.(map[string]interface{}); ok {
|
||||||
|
delete(resultMap, "self")
|
||||||
|
r.Properties = internal.RemainingKeys(Image{}, resultMap)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +171,12 @@ func (r ImagePage) NextPageURL() (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return nextPageURL(r.URL.String(), s.Next), nil
|
|
||||||
|
if s.Next == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextPageURL(r.URL.String(), s.Next)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractImages interprets the results of a single page from a List() call, producing a slice of Image entities.
|
// ExtractImages interprets the results of a single page from a List() call, producing a slice of Image entities.
|
||||||
|
|
15
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go
generated
vendored
15
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go
generated
vendored
|
@ -1,7 +1,7 @@
|
||||||
package images
|
package images
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"net/url"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
)
|
)
|
||||||
|
@ -38,7 +38,14 @@ func deleteURL(c *gophercloud.ServiceClient, imageID string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// builds next page full url based on current url
|
// builds next page full url based on current url
|
||||||
func nextPageURL(currentURL string, next string) string {
|
func nextPageURL(currentURL string, next string) (string, error) {
|
||||||
base := currentURL[:strings.Index(currentURL, "/images")]
|
base, err := url.Parse(currentURL)
|
||||||
return base + next
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
rel, err := url.Parse(next)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base.ResolveReference(rel).String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import (
|
||||||
func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) {
|
func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) {
|
||||||
b := map[string]interface{}{"member": member}
|
b := map[string]interface{}{"member": member}
|
||||||
_, r.Err = client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
_, r.Err = client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
OkCodes: []int{200, 409, 403},
|
OkCodes: []int{200},
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func Get(client *gophercloud.ServiceClient, imageID string, memberID string) (r
|
||||||
// Callee should be image owner
|
// Callee should be image owner
|
||||||
// More details: http://developer.openstack.org/api-ref-image-v2.html#deleteImageMember-v2
|
// More details: http://developer.openstack.org/api-ref-image-v2.html#deleteImageMember-v2
|
||||||
func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) {
|
func Delete(client *gophercloud.ServiceClient, imageID string, memberID string) (r DeleteResult) {
|
||||||
_, r.Err = client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204, 403}})
|
_, r.Err = client.Delete(deleteMemberURL(client, imageID, memberID), &gophercloud.RequestOpts{OkCodes: []int{204}})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/results.go
generated
vendored
26
vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/results.go
generated
vendored
|
@ -1,7 +1,6 @@
|
||||||
package members
|
package members
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gophercloud/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
|
@ -10,33 +9,12 @@ import (
|
||||||
|
|
||||||
// Member model
|
// Member model
|
||||||
type Member struct {
|
type Member struct {
|
||||||
CreatedAt time.Time `json:"-"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
ImageID string `json:"image_id"`
|
ImageID string `json:"image_id"`
|
||||||
MemberID string `json:"member_id"`
|
MemberID string `json:"member_id"`
|
||||||
Schema string `json:"schema"`
|
Schema string `json:"schema"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
UpdatedAt time.Time `json:"-"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Member) UnmarshalJSON(b []byte) error {
|
|
||||||
type tmp Member
|
|
||||||
var p *struct {
|
|
||||||
tmp
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(b, &p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*s = Member(p.tmp)
|
|
||||||
s.CreatedAt, err = time.Parse(time.RFC3339, p.CreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.UpdatedAt, err = time.Parse(time.RFC3339, p.UpdatedAt)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract Member model from request if possible
|
// Extract Member model from request if possible
|
||||||
|
|
|
@ -55,6 +55,6 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult {
|
||||||
func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) {
|
func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) {
|
||||||
return client.Get(url, nil, &gophercloud.RequestOpts{
|
return client.Get(url, nil, &gophercloud.RequestOpts{
|
||||||
MoreHeaders: headers,
|
MoreHeaders: headers,
|
||||||
OkCodes: []int{200, 204},
|
OkCodes: []int{200, 204, 300},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,26 +145,23 @@ func (p Pager) AllPages() (Page, error) {
|
||||||
|
|
||||||
// Switch on the page body type. Recognized types are `map[string]interface{}`,
|
// Switch on the page body type. Recognized types are `map[string]interface{}`,
|
||||||
// `[]byte`, and `[]interface{}`.
|
// `[]byte`, and `[]interface{}`.
|
||||||
switch testPage.GetBody().(type) {
|
switch pb := testPage.GetBody().(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
// key is the map key for the page body if the body type is `map[string]interface{}`.
|
// key is the map key for the page body if the body type is `map[string]interface{}`.
|
||||||
var key string
|
var key string
|
||||||
// Iterate over the pages to concatenate the bodies.
|
// Iterate over the pages to concatenate the bodies.
|
||||||
err = p.EachPage(func(page Page) (bool, error) {
|
err = p.EachPage(func(page Page) (bool, error) {
|
||||||
b := page.GetBody().(map[string]interface{})
|
b := page.GetBody().(map[string]interface{})
|
||||||
for k := range b {
|
for k, v := range b {
|
||||||
// If it's a linked page, we don't want the `links`, we want the other one.
|
// If it's a linked page, we don't want the `links`, we want the other one.
|
||||||
if !strings.HasSuffix(k, "links") {
|
if !strings.HasSuffix(k, "links") {
|
||||||
key = k
|
// check the field's type. we only want []interface{} (which is really []map[string]interface{})
|
||||||
}
|
switch vt := v.(type) {
|
||||||
}
|
|
||||||
switch keyType := b[key].(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
pagesSlice = append(pagesSlice, keyType)
|
|
||||||
case []interface{}:
|
case []interface{}:
|
||||||
pagesSlice = append(pagesSlice, b[key].([]interface{})...)
|
key = k
|
||||||
default:
|
pagesSlice = append(pagesSlice, vt...)
|
||||||
return false, fmt.Errorf("Unsupported page body type: %+v", keyType)
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
})
|
})
|
||||||
|
@ -216,7 +213,7 @@ func (p Pager) AllPages() (Page, error) {
|
||||||
default:
|
default:
|
||||||
err := gophercloud.ErrUnexpectedType{}
|
err := gophercloud.ErrUnexpectedType{}
|
||||||
err.Expected = "map[string]interface{}/[]byte/[]interface{}"
|
err.Expected = "map[string]interface{}/[]byte/[]interface{}"
|
||||||
err.Actual = fmt.Sprintf("%v", reflect.TypeOf(testPage.GetBody()))
|
err.Actual = fmt.Sprintf("%T", pb)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,12 @@ type ServiceClient struct {
|
||||||
// as-is, instead.
|
// as-is, instead.
|
||||||
ResourceBase string
|
ResourceBase string
|
||||||
|
|
||||||
|
// This is the service client type (e.g. compute, sharev2).
|
||||||
|
// NOTE: FOR INTERNAL USE ONLY. DO NOT SET. GOPHERCLOUD WILL SET THIS.
|
||||||
|
// It is only exported because it gets set in a different package.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// The microversion of the service to use. Set this to use a particular microversion.
|
||||||
Microversion string
|
Microversion string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,11 +43,13 @@ func (client *ServiceClient) ServiceURL(parts ...string) string {
|
||||||
return client.ResourceBaseURL() + strings.Join(parts, "/")
|
return client.ResourceBaseURL() + strings.Join(parts, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get calls `Request` with the "GET" HTTP verb.
|
func (client *ServiceClient) initReqOpts(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) {
|
||||||
func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
if v, ok := (JSONBody).(io.Reader); ok {
|
||||||
if opts == nil {
|
opts.RawBody = v
|
||||||
opts = &RequestOpts{}
|
} else if JSONBody != nil {
|
||||||
|
opts.JSONBody = JSONBody
|
||||||
}
|
}
|
||||||
|
|
||||||
if JSONResponse != nil {
|
if JSONResponse != nil {
|
||||||
opts.JSONResponse = JSONResponse
|
opts.JSONResponse = JSONResponse
|
||||||
}
|
}
|
||||||
|
@ -49,93 +57,66 @@ func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *Req
|
||||||
if opts.MoreHeaders == nil {
|
if opts.MoreHeaders == nil {
|
||||||
opts.MoreHeaders = make(map[string]string)
|
opts.MoreHeaders = make(map[string]string)
|
||||||
}
|
}
|
||||||
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
|
||||||
|
|
||||||
|
if client.Microversion != "" {
|
||||||
|
client.setMicroversionHeader(opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get calls `Request` with the "GET" HTTP verb.
|
||||||
|
func (client *ServiceClient) Get(url string, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
|
if opts == nil {
|
||||||
|
opts = new(RequestOpts)
|
||||||
|
}
|
||||||
|
client.initReqOpts(url, nil, JSONResponse, opts)
|
||||||
return client.Request("GET", url, opts)
|
return client.Request("GET", url, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post calls `Request` with the "POST" HTTP verb.
|
// Post calls `Request` with the "POST" HTTP verb.
|
||||||
func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &RequestOpts{}
|
opts = new(RequestOpts)
|
||||||
}
|
}
|
||||||
|
client.initReqOpts(url, JSONBody, JSONResponse, opts)
|
||||||
if v, ok := (JSONBody).(io.Reader); ok {
|
|
||||||
opts.RawBody = v
|
|
||||||
} else if JSONBody != nil {
|
|
||||||
opts.JSONBody = JSONBody
|
|
||||||
}
|
|
||||||
|
|
||||||
if JSONResponse != nil {
|
|
||||||
opts.JSONResponse = JSONResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.MoreHeaders == nil {
|
|
||||||
opts.MoreHeaders = make(map[string]string)
|
|
||||||
}
|
|
||||||
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
|
||||||
|
|
||||||
return client.Request("POST", url, opts)
|
return client.Request("POST", url, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put calls `Request` with the "PUT" HTTP verb.
|
// Put calls `Request` with the "PUT" HTTP verb.
|
||||||
func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &RequestOpts{}
|
opts = new(RequestOpts)
|
||||||
}
|
}
|
||||||
|
client.initReqOpts(url, JSONBody, JSONResponse, opts)
|
||||||
if v, ok := (JSONBody).(io.Reader); ok {
|
|
||||||
opts.RawBody = v
|
|
||||||
} else if JSONBody != nil {
|
|
||||||
opts.JSONBody = JSONBody
|
|
||||||
}
|
|
||||||
|
|
||||||
if JSONResponse != nil {
|
|
||||||
opts.JSONResponse = JSONResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.MoreHeaders == nil {
|
|
||||||
opts.MoreHeaders = make(map[string]string)
|
|
||||||
}
|
|
||||||
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
|
||||||
|
|
||||||
return client.Request("PUT", url, opts)
|
return client.Request("PUT", url, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch calls `Request` with the "PATCH" HTTP verb.
|
// Patch calls `Request` with the "PATCH" HTTP verb.
|
||||||
func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &RequestOpts{}
|
opts = new(RequestOpts)
|
||||||
}
|
}
|
||||||
|
client.initReqOpts(url, JSONBody, JSONResponse, opts)
|
||||||
if v, ok := (JSONBody).(io.Reader); ok {
|
|
||||||
opts.RawBody = v
|
|
||||||
} else if JSONBody != nil {
|
|
||||||
opts.JSONBody = JSONBody
|
|
||||||
}
|
|
||||||
|
|
||||||
if JSONResponse != nil {
|
|
||||||
opts.JSONResponse = JSONResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.MoreHeaders == nil {
|
|
||||||
opts.MoreHeaders = make(map[string]string)
|
|
||||||
}
|
|
||||||
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
|
||||||
|
|
||||||
return client.Request("PATCH", url, opts)
|
return client.Request("PATCH", url, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete calls `Request` with the "DELETE" HTTP verb.
|
// Delete calls `Request` with the "DELETE" HTTP verb.
|
||||||
func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
|
func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &RequestOpts{}
|
opts = new(RequestOpts)
|
||||||
}
|
}
|
||||||
|
client.initReqOpts(url, nil, nil, opts)
|
||||||
if opts.MoreHeaders == nil {
|
|
||||||
opts.MoreHeaders = make(map[string]string)
|
|
||||||
}
|
|
||||||
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
|
||||||
|
|
||||||
return client.Request("DELETE", url, opts)
|
return client.Request("DELETE", url, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (client *ServiceClient) setMicroversionHeader(opts *RequestOpts) {
|
||||||
|
switch client.Type {
|
||||||
|
case "compute":
|
||||||
|
opts.MoreHeaders["X-OpenStack-Nova-API-Version"] = client.Microversion
|
||||||
|
case "sharev2":
|
||||||
|
opts.MoreHeaders["X-OpenStack-Manila-API-Version"] = client.Microversion
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.Type != "" {
|
||||||
|
opts.MoreHeaders["OpenStack-API-Version"] = client.Type + " " + client.Microversion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package gophercloud
|
package gophercloud
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -9,28 +9,48 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// WaitFor polls a predicate function, once per second, up to a timeout limit.
|
// WaitFor polls a predicate function, once per second, up to a timeout limit.
|
||||||
// It usually does this to wait for a resource to transition to a certain state.
|
// This is useful to wait for a resource to transition to a certain state.
|
||||||
|
// To handle situations when the predicate might hang indefinitely, the
|
||||||
|
// predicate will be prematurely cancelled after the timeout.
|
||||||
// Resource packages will wrap this in a more convenient function that's
|
// Resource packages will wrap this in a more convenient function that's
|
||||||
// specific to a certain resource, but it can also be useful on its own.
|
// specific to a certain resource, but it can also be useful on its own.
|
||||||
func WaitFor(timeout int, predicate func() (bool, error)) error {
|
func WaitFor(timeout int, predicate func() (bool, error)) error {
|
||||||
start := time.Now().Second()
|
type WaitForResult struct {
|
||||||
|
Success bool
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now().Unix()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Force a 1s sleep
|
// If a timeout is set, and that's been exceeded, shut it down.
|
||||||
|
if timeout >= 0 && time.Now().Unix()-start >= int64(timeout) {
|
||||||
|
return fmt.Errorf("A timeout occurred")
|
||||||
|
}
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
// If a timeout is set, and that's been exceeded, shut it down
|
var result WaitForResult
|
||||||
if timeout >= 0 && time.Now().Second()-start >= timeout {
|
ch := make(chan bool, 1)
|
||||||
return errors.New("A timeout occurred")
|
go func() {
|
||||||
}
|
defer close(ch)
|
||||||
|
|
||||||
// Execute the function
|
|
||||||
satisfied, err := predicate()
|
satisfied, err := predicate()
|
||||||
if err != nil {
|
result.Success = satisfied
|
||||||
return err
|
result.Error = err
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
if result.Error != nil {
|
||||||
|
return result.Error
|
||||||
}
|
}
|
||||||
if satisfied {
|
if result.Success {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// If the predicate has not finished by the timeout, cancel it.
|
||||||
|
case <-time.After(time.Duration(timeout) * time.Second):
|
||||||
|
return fmt.Errorf("A timeout occurred")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -425,106 +425,112 @@
|
||||||
"revisionTime": "2015-01-27T13:39:51Z"
|
"revisionTime": "2015-01-27T13:39:51Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "DkbpYqirk9i+2YDR5Ujzpot/oAg=",
|
"checksumSHA1": "QTqcF26Y2e0SHe2Z+2wj+fedud4=",
|
||||||
"path": "github.com/gophercloud/gophercloud",
|
"path": "github.com/gophercloud/gophercloud",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "S3zTth9INyj1RfyHkQEvJAvRWvw=",
|
"checksumSHA1": "b7g9TcU1OmW7e2UySYeOAmcfHpY=",
|
||||||
|
"path": "github.com/gophercloud/gophercloud/internal",
|
||||||
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "24DO5BEQdFKNl1rfWgI2b4+ry5U=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack",
|
"path": "github.com/gophercloud/gophercloud/openstack",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Au6MAsI90lewLByg9n+Yjtdqdh8=",
|
"checksumSHA1": "Au6MAsI90lewLByg9n+Yjtdqdh8=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/common/extensions",
|
"path": "github.com/gophercloud/gophercloud/openstack/common/extensions",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "4XWDCGMYqipwJymi9xJo9UffD7g=",
|
"checksumSHA1": "4XWDCGMYqipwJymi9xJo9UffD7g=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "pUlKsepGmWDd4PqPaK4W85pHsRU=",
|
"checksumSHA1": "e7AW3YDVYJPKUjpqsB4AL9RRlTw=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "RWwUliHD65cWApdEo4ckOcPSArg=",
|
"checksumSHA1": "bx6QnHtpgB6nKmN4QRVKa5PszqY=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "qBpGbX7LQMPATdO8XyQmU7IXDiI=",
|
"checksumSHA1": "qBpGbX7LQMPATdO8XyQmU7IXDiI=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "a9xDFPigDjHlPlthknKlBduGvKY=",
|
"checksumSHA1": "vTyXSR+Znw7/o/70UBOWG0F09r8=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "UGeqrw3KdPNRwDxl315MAYyy/uY=",
|
"checksumSHA1": "Rnzx2YgOD41k8KoPA08tR992PxQ=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/images",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/images",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "8rOLNDSqwz/DSKL1BoPqjtWSWAE=",
|
"checksumSHA1": "IjCvcaNnRW++hclt21WUkMYinaA=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/servers",
|
"path": "github.com/gophercloud/gophercloud/openstack/compute/v2/servers",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "1sVqsZBZBNhDXLY9XzjMkcOkcbg=",
|
"checksumSHA1": "S8bHmOP+NjtlYioJC89zIBVvhYc=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants",
|
"path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "q1VGeltZl57OidZ5UDxbMsnyV2g=",
|
"checksumSHA1": "AvUU5En9YpG25iLlcAPDgcQODjI=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens",
|
"path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "6M6ofb8ri5G+sZ8OiExLi7irdx8=",
|
"checksumSHA1": "rqE0NwmQ9qhXADXxg3DcuZ4A3wk=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens",
|
"path": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Eo+cKV/XzaB5TyxK5ZKWYxPqGWY=",
|
"checksumSHA1": "p2ivHupXGBmyHkusnob2NsbsCQk=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images",
|
"path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "HKHLR7xxAb5aJ5zN8XYhDkn/PoM=",
|
"checksumSHA1": "KA5YKF9TwIsTy9KssO27y+wk/6U=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/members",
|
"path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/members",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "TDOZnaS0TO0NirpxV1QwPerAQTY=",
|
"checksumSHA1": "TDOZnaS0TO0NirpxV1QwPerAQTY=",
|
||||||
"path": "github.com/gophercloud/gophercloud/openstack/utils",
|
"path": "github.com/gophercloud/gophercloud/openstack/utils",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "pmpLcbUZ+EgLUmTbzMtGRq3haOU=",
|
"checksumSHA1": "YspETi3tOMvawKIT91HyuqaA5lM=",
|
||||||
"path": "github.com/gophercloud/gophercloud/pagination",
|
"path": "github.com/gophercloud/gophercloud/pagination",
|
||||||
"revision": "d5eda9707e146108e4d424062b602fd97a71c2e6",
|
"revision": "95a28eb606def6aaaed082b6b82d3244b0552184",
|
||||||
"revisionTime": "2016-11-14T18:28:31Z"
|
"revisionTime": "2017-06-23T01:44:30Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "FUiF2WLrih0JdHsUTMMDz3DRokw=",
|
"checksumSHA1": "FUiF2WLrih0JdHsUTMMDz3DRokw=",
|
||||||
|
|
Loading…
Reference in New Issue