From 5c24fb9a750ba123166fdfefe3183246f5c56b29 Mon Sep 17 00:00:00 2001 From: Jesse Haka Date: Sat, 8 Apr 2017 09:26:10 +0300 Subject: [PATCH] update deps --- Godeps/Godeps.json | 62 ++++---- .../github.com/gophercloud/gophercloud/FAQ.md | 148 ++++++++++++++++++ .../gophercloud/gophercloud/README.md | 4 + .../gophercloud/gophercloud/STYLEGUIDE.md | 6 + .../blockstorage/v1/volumes/requests.go | 18 +-- .../blockstorage/v2/volumes/results.go | 87 ++++++---- .../openstack/compute/v2/servers/requests.go | 13 +- .../openstack/compute/v2/servers/results.go | 60 +++---- .../openstack/identity/v2/tokens/results.go | 5 - .../identity/v3/extensions/trusts/results.go | 9 +- .../openstack/identity/v3/tokens/results.go | 43 ++--- .../openstack/networking/v2/ports/requests.go | 4 +- .../gophercloud/gophercloud/results.go | 74 +++++++++ .../gophercloud/gophercloud/util.go | 50 ++++-- 14 files changed, 426 insertions(+), 157 deletions(-) create mode 100644 vendor/github.com/gophercloud/gophercloud/FAQ.md diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index a4402eff08a..62b11d1c22a 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,7 +1,7 @@ { "ImportPath": "k8s.io/kubernetes", "GoVersion": "go1.7", - "GodepVersion": "v74", + "GodepVersion": "v79", "Packages": [ "github.com/ugorji/go/codec/codecgen", "github.com/onsi/ginkgo/ginkgo", @@ -1410,123 +1410,123 @@ }, { "ImportPath": "github.com/gophercloud/gophercloud", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/apiversions", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/common/extensions", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/images", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/compute/v2/servers", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "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", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "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", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "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", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "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", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "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", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "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", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/networking/v2/ports", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/utils", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gophercloud/gophercloud/pagination", - "Rev": "12f19e5e04d617182cffa5c11f189ef0013b9791" + "Rev": "b06120d13e262ceaf890ef38ee30898813696af0" }, { "ImportPath": "github.com/gorilla/context", diff --git a/vendor/github.com/gophercloud/gophercloud/FAQ.md b/vendor/github.com/gophercloud/gophercloud/FAQ.md new file mode 100644 index 00000000000..88a366a288b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/FAQ.md @@ -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 `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 +} +// ... +``` diff --git a/vendor/github.com/gophercloud/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md index 0e1fe0630f5..8d8d0a5c64e 100644 --- a/vendor/github.com/gophercloud/gophercloud/README.md +++ b/vendor/github.com/gophercloud/gophercloud/README.md @@ -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 [`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 None. Vendor it and write tests covering the parts you use. diff --git a/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md b/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md index 18f6dc46b65..5b49ef4882d 100644 --- a/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md +++ b/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md @@ -20,6 +20,12 @@ - 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 `@`. +- 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 service, a PR should only be one route of that service (e.g. create server or get server, but not both). diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go index d66859187e2..566def51811 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go @@ -15,15 +15,15 @@ type CreateOptsBuilder interface { // the volumes.Create function. For more information about these parameters, // see the Volume object. type CreateOpts struct { - Size int `json:"size" required:"true"` - Availability string `json:"availability,omitempty"` - Description string `json:"display_description,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` - Name string `json:"display_name,omitempty"` - SnapshotID string `json:"snapshot_id,omitempty"` - SourceVolID string `json:"source_volid,omitempty"` - ImageID string `json:"imageRef,omitempty"` - VolumeType string `json:"volume_type,omitempty"` + Size int `json:"size" required:"true"` + AvailabilityZone string `json:"availability_zone,omitempty"` + Description string `json:"display_description,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + Name string `json:"display_name,omitempty"` + SnapshotID string `json:"snapshot_id,omitempty"` + SourceVolID string `json:"source_volid,omitempty"` + ImageID string `json:"imageRef,omitempty"` + VolumeType string `json:"volume_type,omitempty"` } // ToVolumeCreateMap assembles a request body based on the contents of a diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go index 2ad94cd9a72..674ec346865 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go @@ -1,18 +1,38 @@ package volumes import ( + "encoding/json" + "time" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) type Attachment struct { - AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"` - AttachmentID string `json:"attachment_id"` - Device string `json:"device"` - HostName string `json:"host_name"` - ID string `json:"id"` - ServerID string `json:"server_id"` - VolumeID string `json:"volume_id"` + AttachedAt time.Time `json:"-"` + AttachmentID string `json:"attachment_id"` + Device string `json:"device"` + HostName string `json:"host_name"` + ID string `json:"id"` + ServerID string `json:"server_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. @@ -26,9 +46,9 @@ type Volume struct { // AvailabilityZone is which availability zone the volume is in. AvailabilityZone string `json:"availability_zone"` // 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 - UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + UpdatedAt time.Time `json:"-"` // Instances onto which the volume is attached. Attachments []Attachment `json:"attachments"` // Human-readable display name for the volume. @@ -57,15 +77,24 @@ type Volume struct { Multiattach bool `json:"multiattach"` } -/* -THESE BELONG IN EXTENSIONS: -// ReplicationDriverData contains data about the replication driver. -ReplicationDriverData string `json:"os-volume-replication:driver_data"` -// ReplicationExtendedStatus contains extended status about replication. -ReplicationExtendedStatus string `json:"os-volume-replication:extended_status"` -// TenantID is the id of the project that owns the volume. -TenantID string `json:"os-vol-tenant-attr:tenant_id"` -*/ +func (r *Volume) UnmarshalJSON(b []byte) error { + type tmp Volume + var s struct { + tmp + CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"` + UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"` + } + 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. 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. func ExtractVolumes(r pagination.Page) ([]Volume, error) { - var s struct { - Volumes []Volume `json:"volumes"` - } - err := (r.(VolumePage)).ExtractInto(&s) - return s.Volumes, err + var s []Volume + err := ExtractVolumesInto(r, &s) + return s, err } type commonResult struct { @@ -93,11 +120,17 @@ type commonResult struct { // Extract will get the Volume object out of the commonResult object. func (r commonResult) Extract() (*Volume, error) { - var s struct { - Volume *Volume `json:"volume"` - } + var s Volume 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. diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go index 4ec2cf078dc..c79a6e6f6b8 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go @@ -145,7 +145,7 @@ type CreateOpts struct { SecurityGroups []string `json:"-"` // 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:"-"` // AvailabilityZone in which to launch the server. @@ -160,7 +160,7 @@ type CreateOpts struct { // Personality includes files to inject into the server at launch. // 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 *bool `json:"config_drive,omitempty"` @@ -190,8 +190,13 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { } if opts.UserData != nil { - encoded := base64.StdEncoding.EncodeToString(opts.UserData) - b["user_data"] = &encoded + var userData string + 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 { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go index a23923a76c1..c121a6be7d6 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go @@ -7,6 +7,7 @@ import ( "fmt" "net/url" "path" + "time" "github.com/gophercloud/gophercloud" "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 -func (res CreateImageResult) ExtractImageID() (string, error) { - if res.Err != nil { - return "", res.Err +func (r CreateImageResult) ExtractImageID() (string, error) { + if r.Err != nil { + return "", r.Err } // 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 { return "", err } @@ -137,26 +138,27 @@ type Server struct { // Name contains the human-readable name for the server. 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 string - Created string - HostID string + Updated time.Time `json:"updated"` + Created time.Time `json:"created"` + HostID string `json:"hostid"` // 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. // 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, 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 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 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 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 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 []interface{} + Links []interface{} `json:"links"` // KeyName indicates which public key was injected into the server on launch. 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. @@ -166,30 +168,30 @@ type Server struct { SecurityGroups []map[string]interface{} `json:"security_groups"` } -func (s *Server) UnmarshalJSON(b []byte) error { +func (r *Server) UnmarshalJSON(b []byte) error { type tmp Server - var server *struct { + var s struct { tmp - Image interface{} + Image interface{} `json:"image"` } - err := json.Unmarshal(b, &server) + err := json.Unmarshal(b, &s) if err != nil { return err } - *s = Server(server.tmp) + *r = Server(s.tmp) - switch t := server.Image.(type) { + switch t := s.Image.(type) { case map[string]interface{}: - s.Image = t + r.Image = t case string: switch t { case "": - s.Image = nil + r.Image = nil } } - return nil + return err } // 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. -func (page ServerPage) IsEmpty() (bool, error) { - servers, err := ExtractServers(page) - return len(servers) == 0, err +func (r ServerPage) IsEmpty() (bool, error) { + s, err := ExtractServers(r) + return len(s) == 0, err } // 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 { Links []gophercloud.Link `json:"servers_links"` } - err := page.ExtractInto(&s) + err := r.ExtractInto(&s) if err != nil { return "", err } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go index 93c0554ae17..6b364937063 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go @@ -132,11 +132,6 @@ func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { 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. func (r GetResult) ExtractUser() (*User, error) { var s struct { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts/results.go index 3d3c7f2924a..bdd8e8479ed 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts/results.go @@ -1,7 +1,5 @@ package trusts -import "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" - type TrusteeUser struct { ID string `json:"id"` } @@ -19,11 +17,6 @@ type Trust struct { RedelegationCount int `json:"redelegation_count"` } -type Token struct { - tokens.Token +type TokenExt struct { Trust Trust `json:"OS-TRUST:trust"` } - -type TokenExt struct { - Token Token `json:"token"` -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go index 36c9ce6194b..0f1e8c2ba76 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go @@ -1,7 +1,10 @@ package tokens -import "errors" -import "github.com/gophercloud/gophercloud" +import ( + "time" + + "github.com/gophercloud/gophercloud" +) // Endpoint represents a single API endpoint offered by a service. // 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. type ServiceCatalog struct { - Entries []CatalogEntry + Entries []CatalogEntry `json:"catalog"` } // 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. func (r commonResult) ExtractToken() (*Token, error) { - var s struct { - Token *Token `json:"token"` - } - + var s Token err := r.ExtractInto(&s) if err != nil { return nil, err } - if s.Token == nil { - return nil, errors.New("'token' missing in JSON response") - } - // 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. func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { - var s struct { - Token struct { - Entries []CatalogEntry `json:"catalog"` - } `json:"token"` - } + var s ServiceCatalog err := r.ExtractInto(&s) - return &ServiceCatalog{Entries: s.Token.Entries}, err + return &s, err } // CreateResult defers the interpretation of a created token. @@ -87,13 +79,6 @@ type CreateResult struct { 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. type GetResult struct { commonResult @@ -110,5 +95,9 @@ type Token struct { // ID is the issued token. ID string `json:"id"` // 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") } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go index 2a53202b32c..d353b7ed38e 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go @@ -119,8 +119,8 @@ type UpdateOpts struct { FixedIPs interface{} `json:"fixed_ips,omitempty"` DeviceID string `json:"device_id,omitempty"` DeviceOwner string `json:"device_owner,omitempty"` - SecurityGroups []string `json:"security_groups,omitempty"` - AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"` + SecurityGroups []string `json:"security_groups"` + AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"` } // ToPortUpdateMap casts an UpdateOpts struct to a map. diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/results.go index 8cca42158f1..76c16ef8ff6 100644 --- a/vendor/github.com/gophercloud/gophercloud/results.go +++ b/vendor/github.com/gophercloud/gophercloud/results.go @@ -3,8 +3,10 @@ package gophercloud import ( "bytes" "encoding/json" + "fmt" "io" "net/http" + "reflect" "strconv" "time" ) @@ -60,6 +62,78 @@ func (r Result) ExtractInto(to interface{}) error { 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 // 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 diff --git a/vendor/github.com/gophercloud/gophercloud/util.go b/vendor/github.com/gophercloud/gophercloud/util.go index 3d6a4e49075..68f9a5d3eca 100644 --- a/vendor/github.com/gophercloud/gophercloud/util.go +++ b/vendor/github.com/gophercloud/gophercloud/util.go @@ -1,7 +1,7 @@ package gophercloud import ( - "errors" + "fmt" "net/url" "path/filepath" "strings" @@ -9,27 +9,47 @@ import ( ) // 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 // specific to a certain resource, but it can also be useful on its own. 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 { - // 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) - // If a timeout is set, and that's been exceeded, shut it down - if timeout >= 0 && time.Now().Second()-start >= timeout { - return errors.New("A timeout occurred") - } + var result WaitForResult + ch := make(chan bool, 1) + go func() { + defer close(ch) + satisfied, err := predicate() + result.Success = satisfied + result.Error = err + }() - // Execute the function - satisfied, err := predicate() - if err != nil { - return err - } - if satisfied { - return nil + select { + case <-ch: + if result.Error != nil { + return result.Error + } + if result.Success { + 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") } } }