update deps

This commit is contained in:
Jesse Haka 2017-04-08 09:26:10 +03:00
parent 2db4affb9d
commit 5c24fb9a75
14 changed files with 426 additions and 157 deletions

62
Godeps/Godeps.json generated
View File

@ -1,7 +1,7 @@
{ {
"ImportPath": "k8s.io/kubernetes", "ImportPath": "k8s.io/kubernetes",
"GoVersion": "go1.7", "GoVersion": "go1.7",
"GodepVersion": "v74", "GodepVersion": "v79",
"Packages": [ "Packages": [
"github.com/ugorji/go/codec/codecgen", "github.com/ugorji/go/codec/codecgen",
"github.com/onsi/ginkgo/ginkgo", "github.com/onsi/ginkgo/ginkgo",
@ -1410,123 +1410,123 @@
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud", "ImportPath": "github.com/gophercloud/gophercloud",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack", "ImportPath": "github.com/gophercloud/gophercloud/openstack",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions", "ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", "ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes", "ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/common/extensions", "ImportPath": "github.com/gophercloud/gophercloud/openstack/common/extensions",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors", "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/images", "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/images",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/servers", "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/servers",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants", "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens", "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts", "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens", "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/ports", "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/ports",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/openstack/utils", "ImportPath": "github.com/gophercloud/gophercloud/openstack/utils",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gophercloud/gophercloud/pagination", "ImportPath": "github.com/gophercloud/gophercloud/pagination",
"Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" "Rev": "b06120d13e262ceaf890ef38ee30898813696af0"
}, },
{ {
"ImportPath": "github.com/gorilla/context", "ImportPath": "github.com/gorilla/context",

148
vendor/github.com/gophercloud/gophercloud/FAQ.md generated vendored Normal file
View File

@ -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
}
// ...
```

View File

@ -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.

View File

@ -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).

View File

@ -16,7 +16,7 @@ type CreateOptsBuilder interface {
// see the Volume object. // see the Volume object.
type CreateOpts struct { type CreateOpts struct {
Size int `json:"size" required:"true"` Size int `json:"size" required:"true"`
Availability string `json:"availability,omitempty"` AvailabilityZone string `json:"availability_zone,omitempty"`
Description string `json:"display_description,omitempty"` Description string `json:"display_description,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"` Metadata map[string]string `json:"metadata,omitempty"`
Name string `json:"display_name,omitempty"` Name string `json:"display_name,omitempty"`

View File

@ -1,12 +1,15 @@
package volumes package volumes
import ( import (
"encoding/json"
"time"
"github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination" "github.com/gophercloud/gophercloud/pagination"
) )
type Attachment struct { type Attachment struct {
AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` AttachedAt time.Time `json:"-"`
AttachmentID string `json:"attachment_id"` AttachmentID string `json:"attachment_id"`
Device string `json:"device"` Device string `json:"device"`
HostName string `json:"host_name"` HostName string `json:"host_name"`
@ -15,6 +18,23 @@ type Attachment struct {
VolumeID string `json:"volume_id"` VolumeID string `json:"volume_id"`
} }
func (r *Attachment) UnmarshalJSON(b []byte) error {
type tmp Attachment
var s struct {
tmp
AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"`
}
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
*r = Attachment(s.tmp)
r.AttachedAt = time.Time(s.AttachedAt)
return err
}
// Volume contains all the information associated with an OpenStack Volume. // Volume contains all the information associated with an OpenStack Volume.
type Volume struct { type Volume struct {
// Unique identifier for the volume. // Unique identifier for the volume.
@ -26,9 +46,9 @@ type Volume struct {
// AvailabilityZone is which availability zone the volume is in. // AvailabilityZone is which availability zone the volume is in.
AvailabilityZone string `json:"availability_zone"` AvailabilityZone string `json:"availability_zone"`
// The date when this volume was created. // The date when this volume was created.
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` CreatedAt time.Time `json:"-"`
// The date when this volume was last updated // The date when this volume was last updated
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` UpdatedAt time.Time `json:"-"`
// Instances onto which the volume is attached. // Instances onto which the volume is attached.
Attachments []Attachment `json:"attachments"` Attachments []Attachment `json:"attachments"`
// Human-readable display name for the volume. // Human-readable display name for the volume.
@ -57,15 +77,24 @@ type Volume struct {
Multiattach bool `json:"multiattach"` Multiattach bool `json:"multiattach"`
} }
/* func (r *Volume) UnmarshalJSON(b []byte) error {
THESE BELONG IN EXTENSIONS: type tmp Volume
// ReplicationDriverData contains data about the replication driver. var s struct {
ReplicationDriverData string `json:"os-volume-replication:driver_data"` tmp
// ReplicationExtendedStatus contains extended status about replication. CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
ReplicationExtendedStatus string `json:"os-volume-replication:extended_status"` UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
// TenantID is the id of the project that owns the volume. }
TenantID string `json:"os-vol-tenant-attr:tenant_id"` err := json.Unmarshal(b, &s)
*/ if err != nil {
return err
}
*r = Volume(s.tmp)
r.CreatedAt = time.Time(s.CreatedAt)
r.UpdatedAt = time.Time(s.UpdatedAt)
return err
}
// VolumePage is a pagination.pager that is returned from a call to the List function. // VolumePage is a pagination.pager that is returned from a call to the List function.
type VolumePage struct { type VolumePage struct {
@ -80,11 +109,9 @@ func (r VolumePage) IsEmpty() (bool, error) {
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call. // ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
func ExtractVolumes(r pagination.Page) ([]Volume, error) { func ExtractVolumes(r pagination.Page) ([]Volume, error) {
var s struct { var s []Volume
Volumes []Volume `json:"volumes"` err := ExtractVolumesInto(r, &s)
} return s, err
err := (r.(VolumePage)).ExtractInto(&s)
return s.Volumes, err
} }
type commonResult struct { type commonResult struct {
@ -93,11 +120,17 @@ type commonResult struct {
// Extract will get the Volume object out of the commonResult object. // Extract will get the Volume object out of the commonResult object.
func (r commonResult) Extract() (*Volume, error) { func (r commonResult) Extract() (*Volume, error) {
var s struct { var s Volume
Volume *Volume `json:"volume"`
}
err := r.ExtractInto(&s) err := r.ExtractInto(&s)
return s.Volume, err return &s, err
}
func (r commonResult) ExtractInto(v interface{}) error {
return r.Result.ExtractIntoStructPtr(v, "volume")
}
func ExtractVolumesInto(r pagination.Page, v interface{}) error {
return r.(VolumePage).Result.ExtractIntoSlicePtr(v, "volumes")
} }
// CreateResult contains the response body and error from a Create request. // CreateResult contains the response body and error from a Create request.

View File

@ -145,7 +145,7 @@ type CreateOpts struct {
SecurityGroups []string `json:"-"` SecurityGroups []string `json:"-"`
// UserData contains configuration information or scripts to use upon launch. // UserData contains configuration information or scripts to use upon launch.
// Create will base64-encode it for you. // Create will base64-encode it for you, if it isn't already.
UserData []byte `json:"-"` UserData []byte `json:"-"`
// AvailabilityZone in which to launch the server. // AvailabilityZone in which to launch the server.
@ -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"`
@ -190,8 +190,13 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) {
} }
if opts.UserData != nil { if opts.UserData != nil {
encoded := base64.StdEncoding.EncodeToString(opts.UserData) var userData string
b["user_data"] = &encoded if _, err := base64.StdEncoding.DecodeString(string(opts.UserData)); err != nil {
userData = base64.StdEncoding.EncodeToString(opts.UserData)
} else {
userData = string(opts.UserData)
}
b["user_data"] = &userData
} }
if len(opts.SecurityGroups) > 0 { if len(opts.SecurityGroups) > 0 {

View File

@ -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"
@ -101,12 +102,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 +138,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 +168,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 +202,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
} }

View File

@ -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 {

View File

@ -1,7 +1,5 @@
package trusts package trusts
import "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
type TrusteeUser struct { type TrusteeUser struct {
ID string `json:"id"` ID string `json:"id"`
} }
@ -19,11 +17,6 @@ type Trust struct {
RedelegationCount int `json:"redelegation_count"` RedelegationCount int `json:"redelegation_count"`
} }
type Token struct { type TokenExt struct {
tokens.Token
Trust Trust `json:"OS-TRUST:trust"` Trust Trust `json:"OS-TRUST:trust"`
} }
type TokenExt struct {
Token Token `json:"token"`
}

View File

@ -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,7 @@ 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"`
} }
// 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 +54,23 @@ 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 CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
var s struct { var s ServiceCatalog
Token struct {
Entries []CatalogEntry `json:"catalog"`
} `json:"token"`
}
err := r.ExtractInto(&s) err := r.ExtractInto(&s)
return &ServiceCatalog{Entries: s.Token.Entries}, err return &s, err
} }
// CreateResult defers the interpretation of a created token. // CreateResult defers the interpretation of a created token.
@ -87,13 +79,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 +95,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")
} }

View File

@ -119,8 +119,8 @@ type UpdateOpts struct {
FixedIPs interface{} `json:"fixed_ips,omitempty"` FixedIPs interface{} `json:"fixed_ips,omitempty"`
DeviceID string `json:"device_id,omitempty"` DeviceID string `json:"device_id,omitempty"`
DeviceOwner string `json:"device_owner,omitempty"` DeviceOwner string `json:"device_owner,omitempty"`
SecurityGroups []string `json:"security_groups,omitempty"` SecurityGroups []string `json:"security_groups"`
AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"`
} }
// ToPortUpdateMap casts an UpdateOpts struct to a map. // ToPortUpdateMap casts an UpdateOpts struct to a map.

View File

@ -3,8 +3,10 @@ package gophercloud
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net/http" "net/http"
"reflect"
"strconv" "strconv"
"time" "time"
) )
@ -60,6 +62,78 @@ func (r Result) ExtractInto(to interface{}) error {
return err return err
} }
func (r Result) extractIntoPtr(to interface{}, label string) error {
if label == "" {
return r.ExtractInto(&to)
}
var m map[string]interface{}
err := r.ExtractInto(&m)
if err != nil {
return err
}
b, err := json.Marshal(m[label])
if err != nil {
return err
}
err = json.Unmarshal(b, &to)
return err
}
// ExtractIntoStructPtr will unmarshal the Result (r) into the provided
// interface{} (to).
//
// NOTE: For internal use only
//
// `to` must be a pointer to an underlying struct type
//
// If provided, `label` will be filtered out of the response
// body prior to `r` being unmarshalled into `to`.
func (r Result) ExtractIntoStructPtr(to interface{}, label string) error {
if r.Err != nil {
return r.Err
}
t := reflect.TypeOf(to)
if k := t.Kind(); k != reflect.Ptr {
return fmt.Errorf("Expected pointer, got %v", k)
}
switch t.Elem().Kind() {
case reflect.Struct:
return r.extractIntoPtr(to, label)
default:
return fmt.Errorf("Expected pointer to struct, got: %v", t)
}
}
// ExtractIntoSlicePtr will unmarshal the Result (r) into the provided
// interface{} (to).
//
// NOTE: For internal use only
//
// `to` must be a pointer to an underlying slice type
//
// If provided, `label` will be filtered out of the response
// body prior to `r` being unmarshalled into `to`.
func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error {
if r.Err != nil {
return r.Err
}
t := reflect.TypeOf(to)
if k := t.Kind(); k != reflect.Ptr {
return fmt.Errorf("Expected pointer, got %v", k)
}
switch t.Elem().Kind() {
case reflect.Slice:
return r.extractIntoPtr(to, label)
default:
return fmt.Errorf("Expected pointer to slice, got: %v", t)
}
}
// PrettyPrintJSON creates a string containing the full response body as // PrettyPrintJSON creates a string containing the full response body as
// pretty-printed JSON. It's useful for capturing test fixtures and for // pretty-printed JSON. It's useful for capturing test fixtures and for
// debugging extraction bugs. If you include its output in an issue related to // debugging extraction bugs. If you include its output in an issue related to

View File

@ -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")
}
} }
} }