diff --git a/builder/openstack/access_config.go b/builder/openstack/access_config.go index 9c8c80383..04e7c854d 100644 --- a/builder/openstack/access_config.go +++ b/builder/openstack/access_config.go @@ -6,9 +6,9 @@ import ( "net/http" "os" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" "github.com/mitchellh/packer/template/interpolate" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" ) // AccessConfig is for common configuration related to openstack access @@ -16,7 +16,6 @@ type AccessConfig struct { Username string `mapstructure:"username"` UserID string `mapstructure:"user_id"` Password string `mapstructure:"password"` - APIKey string `mapstructure:"api_key"` IdentityEndpoint string `mapstructure:"identity_endpoint"` TenantID string `mapstructure:"tenant_id"` TenantName string `mapstructure:"tenant_name"` @@ -42,9 +41,6 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { } // Legacy RackSpace stuff. We're keeping this around to keep things BC. - if c.APIKey == "" { - c.APIKey = os.Getenv("SDK_API_KEY") - } if c.Password == "" { c.Password = os.Getenv("SDK_PASSWORD") } @@ -71,7 +67,6 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { {&c.Username, &ao.Username}, {&c.UserID, &ao.UserID}, {&c.Password, &ao.Password}, - {&c.APIKey, &ao.APIKey}, {&c.IdentityEndpoint, &ao.IdentityEndpoint}, {&c.TenantID, &ao.TenantID}, {&c.TenantName, &ao.TenantName}, @@ -115,6 +110,13 @@ func (c *AccessConfig) computeV2Client() (*gophercloud.ServiceClient, error) { }) } +func (c *AccessConfig) imageV2Client() (*gophercloud.ServiceClient, error) { + return openstack.NewImageServiceV2(c.osClient, gophercloud.EndpointOpts{ + Region: c.Region, + Availability: c.getEndpointType(), + }) +} + func (c *AccessConfig) getEndpointType() gophercloud.Availability { if c.EndpointType == "internal" || c.EndpointType == "internalURL" { return gophercloud.AvailabilityInternal diff --git a/builder/openstack/artifact.go b/builder/openstack/artifact.go index aa60d2641..4be50ab35 100644 --- a/builder/openstack/artifact.go +++ b/builder/openstack/artifact.go @@ -4,8 +4,8 @@ import ( "fmt" "log" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/images" ) // Artifact is an artifact implementation that contains built images. diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index 1d444cc84..7eeb18b4b 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -114,6 +114,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &common.StepProvision{}, &StepStopServer{}, &stepCreateImage{}, + &stepUpdateImageVisibility{}, + &stepAddImageMembers{}, } // Run! diff --git a/builder/openstack/image_config.go b/builder/openstack/image_config.go index 6a40296b1..3e4319547 100644 --- a/builder/openstack/image_config.go +++ b/builder/openstack/image_config.go @@ -2,14 +2,18 @@ package openstack import ( "fmt" + "strings" + imageservice "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" "github.com/mitchellh/packer/template/interpolate" ) // ImageConfig is for common configuration related to creating Images. type ImageConfig struct { - ImageName string `mapstructure:"image_name"` - ImageMetadata map[string]string `mapstructure:"metadata"` + ImageName string `mapstructure:"image_name"` + ImageMetadata map[string]string `mapstructure:"metadata"` + ImageVisibility imageservice.ImageVisibility `mapstructure:"image_visibility"` + ImageMembers []string `mapstructure:"image_members"` } func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error { @@ -29,6 +33,23 @@ func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error { c.ImageMetadata["image_type"] = "image" } + // ImageVisibility values + // https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design + if c.ImageVisibility != "" { + validVals := []imageservice.ImageVisibility{"public", "private", "shared", "community"} + valid := false + for _, val := range validVals { + if strings.EqualFold(string(c.ImageVisibility), string(val)) { + valid = true + c.ImageVisibility = val + break + } + } + if !valid { + errs = append(errs, fmt.Errorf("Unknown visibility value %s", c.ImageVisibility)) + } + } + if len(errs) > 0 { return errs } diff --git a/builder/openstack/server.go b/builder/openstack/server.go index 0897821a8..2c2dd8f2a 100644 --- a/builder/openstack/server.go +++ b/builder/openstack/server.go @@ -6,9 +6,9 @@ import ( "log" "time" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) // StateRefreshFunc is a function type used for StateChangeConf that is @@ -38,14 +38,12 @@ func ServerStateRefreshFunc( return func() (interface{}, string, int, error) { serverNew, err := servers.Get(client, s.ID).Extract() if err != nil { - errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) - if ok && errCode.Actual == 404 { + if _, ok := err.(gophercloud.ErrDefault404); ok { log.Printf("[INFO] 404 on ServerStateRefresh, returning DELETED") return nil, "DELETED", 0, nil - } else { - log.Printf("[ERROR] Error on ServerStateRefresh: %s", err) - return nil, "", 0, err } + log.Printf("[ERROR] Error on ServerStateRefresh: %s", err) + return nil, "", 0, err } return serverNew, serverNew.Status, serverNew.Progress, nil diff --git a/builder/openstack/ssh.go b/builder/openstack/ssh.go index 99f8e6669..a5495eed3 100644 --- a/builder/openstack/ssh.go +++ b/builder/openstack/ssh.go @@ -6,11 +6,11 @@ import ( "log" "time" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" packerssh "github.com/mitchellh/packer/communicator/ssh" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" "golang.org/x/crypto/ssh" ) @@ -31,7 +31,7 @@ func CommHost( } // If we have a floating IP, use that - ip := state.Get("access_ip").(*floatingip.FloatingIP) + ip := state.Get("access_ip").(*floatingips.FloatingIP) if ip != nil && ip.IP != "" { log.Printf("[DEBUG] Using floating IP %s to connect", ip.IP) return ip.IP, nil diff --git a/builder/openstack/step_add_image_members.go b/builder/openstack/step_add_image_members.go new file mode 100644 index 000000000..cd26b822f --- /dev/null +++ b/builder/openstack/step_add_image_members.go @@ -0,0 +1,44 @@ +package openstack + +import ( + "fmt" + + "github.com/gophercloud/gophercloud/openstack/imageservice/v2/members" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +type stepAddImageMembers struct{} + +func (s *stepAddImageMembers) Run(state multistep.StateBag) multistep.StepAction { + imageId := state.Get("image").(string) + ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(Config) + + if len(config.ImageMembers) == 0 { + return multistep.ActionContinue + } + + imageClient, err := config.imageV2Client() + if err != nil { + err = fmt.Errorf("Error initializing image service client: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + for _, member := range config.ImageMembers { + ui.Say(fmt.Sprintf("Adding member '%s' to image %s", member, imageId)) + r := members.Create(imageClient, imageId, member) + if _, err = r.Extract(); err != nil { + err = fmt.Errorf("Error adding member to image: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + } + + return multistep.ActionContinue +} + +func (s *stepAddImageMembers) Cleanup(multistep.StateBag) { + // No cleanup... +} diff --git a/builder/openstack/step_allocate_ip.go b/builder/openstack/step_allocate_ip.go index fc386082b..11723026f 100644 --- a/builder/openstack/step_allocate_ip.go +++ b/builder/openstack/step_allocate_ip.go @@ -3,10 +3,10 @@ package openstack import ( "fmt" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) type StepAllocateIp struct { @@ -27,7 +27,7 @@ func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - var instanceIp floatingip.FloatingIP + var instanceIp floatingips.FloatingIP // This is here in case we error out before putting instanceIp into the // statebag below, because it is requested by Cleanup() @@ -38,7 +38,7 @@ func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction { } else if s.FloatingIpPool != "" { ui.Say(fmt.Sprintf("Creating floating IP...")) ui.Message(fmt.Sprintf("Pool: %s", s.FloatingIpPool)) - newIp, err := floatingip.Create(client, floatingip.CreateOpts{ + newIp, err := floatingips.Create(client, floatingips.CreateOpts{ Pool: s.FloatingIpPool, }).Extract() if err != nil { @@ -55,7 +55,9 @@ func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction { if instanceIp.IP != "" { ui.Say(fmt.Sprintf("Associating floating IP with server...")) ui.Message(fmt.Sprintf("IP: %s", instanceIp.IP)) - err := floatingip.Associate(client, server.ID, instanceIp.IP).ExtractErr() + err := floatingips.AssociateInstance(client, server.ID, floatingips.AssociateOpts{ + FloatingIP: instanceIp.IP, + }).ExtractErr() if err != nil { err := fmt.Errorf( "Error associating floating IP %s with instance: %s", @@ -76,7 +78,7 @@ func (s *StepAllocateIp) Run(state multistep.StateBag) multistep.StepAction { func (s *StepAllocateIp) Cleanup(state multistep.StateBag) { config := state.Get("config").(Config) ui := state.Get("ui").(packer.Ui) - instanceIp := state.Get("access_ip").(*floatingip.FloatingIP) + instanceIp := state.Get("access_ip").(*floatingips.FloatingIP) // We need the v2 compute client client, err := config.computeV2Client() @@ -87,7 +89,7 @@ func (s *StepAllocateIp) Cleanup(state multistep.StateBag) { } if s.FloatingIpPool != "" && instanceIp.ID != "" { - if err := floatingip.Delete(client, instanceIp.ID).ExtractErr(); err != nil { + if err := floatingips.Delete(client, instanceIp.ID).ExtractErr(); err != nil { ui.Error(fmt.Sprintf( "Error deleting temporary floating IP %s", instanceIp.IP)) return diff --git a/builder/openstack/step_create_image.go b/builder/openstack/step_create_image.go index 5c39b0ffc..c5b12b85e 100644 --- a/builder/openstack/step_create_image.go +++ b/builder/openstack/step_create_image.go @@ -5,11 +5,11 @@ import ( "log" "time" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/images" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) type stepCreateImage struct{} @@ -68,7 +68,7 @@ func WaitForImage(client *gophercloud.ServiceClient, imageId string) error { for { image, err := images.Get(client, imageId).Extract() if err != nil { - errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) + errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode) if ok && (errCode.Actual == 500 || errCode.Actual == 404) { numErrors++ if numErrors >= maxNumErrors { diff --git a/builder/openstack/step_get_password.go b/builder/openstack/step_get_password.go index 905838d5c..dd11c98f3 100644 --- a/builder/openstack/step_get_password.go +++ b/builder/openstack/step_get_password.go @@ -6,10 +6,10 @@ import ( "log" "time" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/helper/communicator" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" "golang.org/x/crypto/ssh" ) diff --git a/builder/openstack/step_key_pair.go b/builder/openstack/step_key_pair.go index 906f11cee..94ca5c768 100644 --- a/builder/openstack/step_key_pair.go +++ b/builder/openstack/step_key_pair.go @@ -8,10 +8,10 @@ import ( "os/exec" "runtime" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/common/uuid" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" "golang.org/x/crypto/ssh" ) diff --git a/builder/openstack/step_load_extensions.go b/builder/openstack/step_load_extensions.go index 095863612..4a3362fac 100644 --- a/builder/openstack/step_load_extensions.go +++ b/builder/openstack/step_load_extensions.go @@ -4,10 +4,10 @@ import ( "fmt" "log" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions" + "github.com/gophercloud/gophercloud/pagination" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions" - "github.com/rackspace/gophercloud/pagination" ) // StepLoadExtensions gets the FlavorRef from a Flavor. It first assumes diff --git a/builder/openstack/step_load_flavor.go b/builder/openstack/step_load_flavor.go index 8b8cae994..bac84bfa9 100644 --- a/builder/openstack/step_load_flavor.go +++ b/builder/openstack/step_load_flavor.go @@ -4,9 +4,9 @@ import ( "fmt" "log" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" ) // StepLoadFlavor gets the FlavorRef from a Flavor. It first assumes diff --git a/builder/openstack/step_run_source_server.go b/builder/openstack/step_run_source_server.go index afe3af8ff..88a5109ba 100644 --- a/builder/openstack/step_run_source_server.go +++ b/builder/openstack/step_run_source_server.go @@ -5,10 +5,10 @@ import ( "io/ioutil" "log" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) type StepRunSourceServer struct { @@ -63,7 +63,7 @@ func (s *StepRunSourceServer) Run(state multistep.StateBag) multistep.StepAction Networks: networks, AvailabilityZone: s.AvailabilityZone, UserData: userData, - ConfigDrive: s.ConfigDrive, + ConfigDrive: &s.ConfigDrive, } var serverOptsExt servers.CreateOptsBuilder diff --git a/builder/openstack/step_stop_server.go b/builder/openstack/step_stop_server.go index d04a10f60..ede909b54 100644 --- a/builder/openstack/step_stop_server.go +++ b/builder/openstack/step_stop_server.go @@ -3,10 +3,10 @@ package openstack import ( "fmt" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) type StepStopServer struct{} diff --git a/builder/openstack/step_update_image_visibility.go b/builder/openstack/step_update_image_visibility.go new file mode 100644 index 000000000..96bd9d59c --- /dev/null +++ b/builder/openstack/step_update_image_visibility.go @@ -0,0 +1,50 @@ +package openstack + +import ( + "fmt" + + imageservice "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +type stepUpdateImageVisibility struct{} + +func (s *stepUpdateImageVisibility) Run(state multistep.StateBag) multistep.StepAction { + imageId := state.Get("image").(string) + ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(Config) + + if config.ImageVisibility == "" { + return multistep.ActionContinue + } + imageClient, err := config.imageV2Client() + if err != nil { + err = fmt.Errorf("Error initializing image service client: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + ui.Say(fmt.Sprintf("Updating image visibility to %s", config.ImageVisibility)) + r := imageservice.Update( + imageClient, + imageId, + imageservice.UpdateOpts{ + imageservice.UpdateVisibility{ + Visibility: config.ImageVisibility, + }, + }, + ) + + if _, err = r.Extract(); err != nil { + err = fmt.Errorf("Error updating image visibility: %s", err) + state.Put("error", err) + return multistep.ActionHalt + } + + return multistep.ActionContinue +} + +func (s *stepUpdateImageVisibility) Cleanup(multistep.StateBag) { + // No cleanup... +} diff --git a/builder/openstack/step_wait_for_rackconnect.go b/builder/openstack/step_wait_for_rackconnect.go index 7ab42a8f4..0afbe1379 100644 --- a/builder/openstack/step_wait_for_rackconnect.go +++ b/builder/openstack/step_wait_for_rackconnect.go @@ -4,9 +4,9 @@ import ( "fmt" "time" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/multistep" "github.com/mitchellh/packer/packer" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" ) type StepWaitForRackConnect struct { diff --git a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md b/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/github.com/rackspace/gophercloud/LICENSE b/vendor/github.com/gophercloud/gophercloud/LICENSE similarity index 100% rename from vendor/github.com/rackspace/gophercloud/LICENSE rename to vendor/github.com/gophercloud/gophercloud/LICENSE diff --git a/vendor/github.com/gophercloud/gophercloud/MIGRATING.md b/vendor/github.com/gophercloud/gophercloud/MIGRATING.md new file mode 100644 index 000000000..aa383c9cc --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/MIGRATING.md @@ -0,0 +1,32 @@ +# Compute + +## Floating IPs + +* `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip` is now `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips` +* `floatingips.Associate` and `floatingips.Disassociate` have been removed. +* `floatingips.DisassociateOpts` is now required to disassociate a Floating IP. + +## Security Groups + +* `secgroups.AddServerToGroup` is now `secgroups.AddServer`. +* `secgroups.RemoveServerFromGroup` is now `secgroups.RemoveServer`. + +## Servers + +* `servers.Reboot` now requires a `servers.RebootOpts` struct: + + ```golang + rebootOpts := &servers.RebootOpts{ + Type: servers.SoftReboot, + } + res := servers.Reboot(client, server.ID, rebootOpts) + ``` + +# Identity + +## V3 + +### Tokens + +* `Token.ExpiresAt` is now of type `gophercloud.JSONRFC3339Milli` instead of + `time.Time` diff --git a/vendor/github.com/rackspace/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md similarity index 59% rename from vendor/github.com/rackspace/gophercloud/README.md rename to vendor/github.com/gophercloud/gophercloud/README.md index 0a0da59f6..0e1fe0630 100644 --- a/vendor/github.com/rackspace/gophercloud/README.md +++ b/vendor/github.com/gophercloud/gophercloud/README.md @@ -1,17 +1,12 @@ # Gophercloud: an OpenStack SDK for Go -[![Build Status](https://travis-ci.org/rackspace/gophercloud.svg?branch=master)](https://travis-ci.org/rackspace/gophercloud) +[![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud) +[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master) -Gophercloud is a flexible SDK that allows you to consume and work with OpenStack -clouds in a simple and idiomatic way using golang. Many services are supported, -including Compute, Block Storage, Object Storage, Networking, and Identity. -Each service API is backed with getting started guides, code samples, reference -documentation, unit tests and acceptance tests. +Gophercloud is an OpenStack Go SDK. ## Useful links -* [Gophercloud homepage](http://gophercloud.io) -* [Reference documentation](http://godoc.org/github.com/rackspace/gophercloud) -* [Getting started guides](http://gophercloud.io/docs) +* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud) * [Effective Go](https://golang.org/doc/effective_go.html) ## How to install @@ -30,9 +25,9 @@ your projects, such as [godep](https://github.com/tools/godep). Once this is set Gophercloud as a dependency like so: ```bash -go get github.com/rackspace/gophercloud +go get github.com/gophercloud/gophercloud -# Edit your code to import relevant packages from "github.com/rackspace/gophercloud" +# Edit your code to import relevant packages from "github.com/gophercloud/gophercloud" godep save ./... ``` @@ -54,7 +49,6 @@ You will need to retrieve the following: * username * password -* tenant name or tenant ID * a valid Keystone identity URL For users that have the OpenStack dashboard installed, there's a shortcut. If @@ -73,9 +67,9 @@ explicitly, or tell Gophercloud to use environment variables: ```go import ( - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" - "github.com/rackspace/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/utils" ) // Option 1: Pass in the values yourself @@ -83,7 +77,6 @@ opts := gophercloud.AuthOptions{ IdentityEndpoint: "https://my-openstack.com:5000/v2.0", Username: "{username}", Password: "{password}", - TenantID: "{tenant_id}", } // Option 2: Use a utility function to retrieve all your environment variables @@ -119,7 +112,7 @@ in the flavor ID (hardware specification) and image ID (operating system) we're interested in: ```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" +import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" server, err := servers.Create(client, servers.CreateOpts{ Name: "My new server!", @@ -128,33 +121,19 @@ server, err := servers.Create(client, servers.CreateOpts{ }).Extract() ``` -If you are unsure about what images and flavors are, you can read our [Compute -Getting Started guide](http://gophercloud.io/docs/compute). 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/rackspace/gophercloud) struct). +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). -### Next steps +## Backwards-Compatibility Guarantees -Cool! You've handled authentication, got your `ProviderClient` and provisioned -a new server. You're now ready to use more OpenStack services. - -* [Getting started with Compute](http://gophercloud.io/docs/compute) -* [Getting started with Object Storage](http://gophercloud.io/docs/object-storage) -* [Getting started with Networking](http://gophercloud.io/docs/networking) -* [Getting started with Block Storage](http://gophercloud.io/docs/block-storage) -* [Getting started with Identity](http://gophercloud.io/docs/identity) +None. Vendor it and write tests covering the parts you use. ## Contributing -Engaging the community and lowering barriers for contributors is something we -care a lot about. For this reason, we've taken the time to write a [contributing -guide](./CONTRIBUTING.md) for folks interested in getting involved in our project. -If you're not sure how you can get involved, feel free to submit an issue or -[contact us](https://developer.rackspace.com/support/). You don't need to be a -Go expert - all members of the community are welcome! +See the [contributing guide](./.github/CONTRIBUTING.md). ## Help and feedback If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](/issues) or [contact us directly](https://developer.rackspace.com/support/). +to submit an issue to our [bug tracker](/issues). diff --git a/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md b/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md new file mode 100644 index 000000000..18f6dc46b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md @@ -0,0 +1,68 @@ + +## On Pull Requests + +- Before you start a PR there needs to be a Github issue and a discussion about it + on that issue with a core contributor, even if it's just a 'SGTM'. + +- A PR's description must reference the issue it closes with a `For ` (e.g. For #293). + +- A PR's description must contain link(s) to the line(s) in the OpenStack + source code (on Github) that prove(s) the PR code to be valid. Links to documentation + are not good enough. The link(s) should be to a non-`master` branch. For example, + a pull request implementing the creation of a Neutron v2 subnet might put the + following link in the description: + + https://github.com/openstack/neutron/blob/stable/mitaka/neutron/api/v2/attributes.py#L749 + + From that link, a reviewer (or user) can verify the fields in the request/response + objects in the PR. + +- 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 `@`. + +- 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). + +- Unless explicitly asked, do not squash commits in the middle of a review; only + append. It makes it difficult for the reviewer to see what's changed from one + review to the next. + +## On Code + +- In re design: follow as closely as is reasonable the code already in the library. + Most operations (e.g. create, delete) admit the same design. + +- Unit tests and acceptance (integration) tests must be written to cover each PR. + Tests for operations with several options (e.g. list, create) should include all + the options in the tests. This will allow users to verify an operation on their + own infrastructure and see an example of usage. + +- If in doubt, ask in-line on the PR. + +### File Structure + +- The following should be used in most cases: + + - `requests.go`: contains all the functions that make HTTP requests and the + types associated with the HTTP request (parameters for URL, body, etc) + - `results.go`: contains all the response objects and their methods + - `urls.go`: contains the endpoints to which the requests are made + +### Naming + +- For methods on a type in `response.go`, the receiver should be named `r` and the + variable into which it will be unmarshalled `s`. + +- Functions in `requests.go`, with the exception of functions that return a + `pagination.Pager`, should be named returns of the name `r`. + +- Functions in `requests.go` that accept request bodies should accept as their + last parameter an `interface` named `OptsBuilder` (eg `CreateOptsBuilder`). + This `interface` should have at the least a method named `ToMap` + (eg `ToPortCreateMap`). + +- Functions in `requests.go` that accept query strings should accept as their + last parameter an `interface` named `OptsBuilder` (eg `ListOptsBuilder`). + This `interface` should have at the least a method named `ToQuery` + (eg `ToServerListQuery`). diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/auth_options.go new file mode 100644 index 000000000..3ee97dfb3 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/auth_options.go @@ -0,0 +1,331 @@ +package gophercloud + +/* +AuthOptions stores information needed to authenticate to an OpenStack cluster. +You can populate one manually, or use a provider's AuthOptionsFromEnv() function +to read relevant information from the standard environment variables. Pass one +to a provider's AuthenticatedClient function to authenticate and obtain a +ProviderClient representing an active session on that provider. + +Its fields are the union of those recognized by each identity implementation and +provider. +*/ +type AuthOptions struct { + // IdentityEndpoint specifies the HTTP endpoint that is required to work with + // the Identity API of the appropriate version. While it's ultimately needed by + // all of the identity services, it will often be populated by a provider-level + // function. + IdentityEndpoint string `json:"-"` + + // Username is required if using Identity V2 API. Consult with your provider's + // control panel to discover your account's username. In Identity V3, either + // UserID or a combination of Username and DomainID or DomainName are needed. + Username string `json:"username,omitempty"` + UserID string `json:"id,omitempty"` + + Password string `json:"password,omitempty"` + + // At most one of DomainID and DomainName must be provided if using Username + // with Identity V3. Otherwise, either are optional. + DomainID string `json:"id,omitempty"` + DomainName string `json:"name,omitempty"` + + // The TenantID and TenantName fields are optional for the Identity V2 API. + // Some providers allow you to specify a TenantName instead of the TenantId. + // Some require both. Your provider's authentication policies will determine + // how these fields influence authentication. + TenantID string `json:"tenantId,omitempty"` + TenantName string `json:"tenantName,omitempty"` + + // AllowReauth should be set to true if you grant permission for Gophercloud to + // cache your credentials in memory, and to allow Gophercloud to attempt to + // re-authenticate automatically if/when your token expires. If you set it to + // false, it will not cache these settings, but re-authentication will not be + // possible. This setting defaults to false. + // + // NOTE: The reauth function will try to re-authenticate endlessly if left unchecked. + // The way to limit the number of attempts is to provide a custom HTTP client to the provider client + // and provide a transport that implements the RoundTripper interface and stores the number of failed retries. + // For an example of this, see here: https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311 + AllowReauth bool `json:"-"` + + // TokenID allows users to authenticate (possibly as another user) with an + // authentication token ID. + TokenID string `json:"-"` +} + +// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v2 tokens package +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { + // Populate the request map. + authMap := make(map[string]interface{}) + + if opts.Username != "" { + if opts.Password != "" { + authMap["passwordCredentials"] = map[string]interface{}{ + "username": opts.Username, + "password": opts.Password, + } + } else { + return nil, ErrMissingInput{Argument: "Password"} + } + } else if opts.TokenID != "" { + authMap["token"] = map[string]interface{}{ + "id": opts.TokenID, + } + } else { + return nil, ErrMissingInput{Argument: "Username"} + } + + if opts.TenantID != "" { + authMap["tenantId"] = opts.TenantID + } + if opts.TenantName != "" { + authMap["tenantName"] = opts.TenantName + } + + return map[string]interface{}{"auth": authMap}, nil +} + +func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { + type domainReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + } + + type projectReq struct { + Domain *domainReq `json:"domain,omitempty"` + Name *string `json:"name,omitempty"` + ID *string `json:"id,omitempty"` + } + + type userReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Password string `json:"password"` + Domain *domainReq `json:"domain,omitempty"` + } + + type passwordReq struct { + User userReq `json:"user"` + } + + type tokenReq struct { + ID string `json:"id"` + } + + type identityReq struct { + Methods []string `json:"methods"` + Password *passwordReq `json:"password,omitempty"` + Token *tokenReq `json:"token,omitempty"` + } + + type authReq struct { + Identity identityReq `json:"identity"` + } + + type request struct { + Auth authReq `json:"auth"` + } + + // Populate the request structure based on the provided arguments. Create and return an error + // if insufficient or incompatible information is present. + 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.TokenID != "" { + // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication + // parameters. + if opts.Username != "" { + return nil, ErrUsernameWithToken{} + } + if opts.UserID != "" { + return nil, ErrUserIDWithToken{} + } + if opts.DomainID != "" { + return nil, ErrDomainIDWithToken{} + } + if opts.DomainName != "" { + return nil, ErrDomainNameWithToken{} + } + + // Configure the request for Token authentication. + req.Auth.Identity.Methods = []string{"token"} + req.Auth.Identity.Token = &tokenReq{ + ID: opts.TokenID, + } + } else { + // If no password or token ID are available, authentication can't continue. + return nil, ErrMissingPassword{} + } + } else { + // Password authentication. + req.Auth.Identity.Methods = []string{"password"} + + // At least one of Username and UserID must be specified. + if opts.Username == "" && opts.UserID == "" { + return nil, ErrUsernameOrUserID{} + } + + if opts.Username != "" { + // If Username is provided, UserID may not be provided. + if opts.UserID != "" { + return nil, ErrUsernameOrUserID{} + } + + // Either DomainID or DomainName must also be specified. + if opts.DomainID == "" && opts.DomainName == "" { + return nil, ErrDomainIDOrDomainName{} + } + + if opts.DomainID != "" { + if opts.DomainName != "" { + return nil, ErrDomainIDOrDomainName{} + } + + // Configure the request for Username and Password authentication with a DomainID. + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: opts.Password, + Domain: &domainReq{ID: &opts.DomainID}, + }, + } + } + + if opts.DomainName != "" { + // Configure the request for Username and Password authentication with a DomainName. + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: opts.Password, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } + } + } + + if opts.UserID != "" { + // If UserID is specified, neither DomainID nor DomainName may be. + if opts.DomainID != "" { + return nil, ErrDomainIDWithUserID{} + } + if opts.DomainName != "" { + return nil, ErrDomainNameWithUserID{} + } + + // Configure the request for UserID and Password authentication. + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ID: &opts.UserID, Password: opts.Password}, + } + } + } + + b, err := BuildRequestBody(req, "") + if err != nil { + return nil, err + } + + if len(scope) != 0 { + b["auth"].(map[string]interface{})["scope"] = scope + } + + return b, nil +} + +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { + + var scope struct { + ProjectID string + ProjectName string + DomainID string + DomainName string + } + + if opts.TenantID != "" { + scope.ProjectID = opts.TenantID + opts.TenantID = "" + opts.TenantName = "" + } else { + if opts.TenantName != "" { + scope.ProjectName = opts.TenantName + scope.DomainID = opts.DomainID + scope.DomainName = opts.DomainName + } + opts.TenantName = "" + } + + if scope.ProjectName != "" { + // ProjectName provided: either DomainID or DomainName must also be supplied. + // ProjectID may not be supplied. + if scope.DomainID == "" && scope.DomainName == "" { + return nil, ErrScopeDomainIDOrDomainName{} + } + if scope.ProjectID != "" { + return nil, ErrScopeProjectIDOrProjectName{} + } + + if scope.DomainID != "" { + // ProjectName + DomainID + return map[string]interface{}{ + "project": map[string]interface{}{ + "name": &scope.ProjectName, + "domain": map[string]interface{}{"id": &scope.DomainID}, + }, + }, nil + } + + if scope.DomainName != "" { + // ProjectName + DomainName + return map[string]interface{}{ + "project": map[string]interface{}{ + "name": &scope.ProjectName, + "domain": map[string]interface{}{"name": &scope.DomainName}, + }, + }, nil + } + } else if scope.ProjectID != "" { + // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. + if scope.DomainID != "" { + return nil, ErrScopeProjectIDAlone{} + } + if scope.DomainName != "" { + return nil, ErrScopeProjectIDAlone{} + } + + // ProjectID + return map[string]interface{}{ + "project": map[string]interface{}{ + "id": &scope.ProjectID, + }, + }, nil + } else if scope.DomainID != "" { + // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. + if scope.DomainName != "" { + return nil, ErrScopeDomainIDOrDomainName{} + } + + // DomainID + return map[string]interface{}{ + "domain": map[string]interface{}{ + "id": &scope.DomainID, + }, + }, nil + } else if scope.DomainName != "" { + return nil, ErrScopeDomainName{} + } + + return nil, nil +} + +func (opts AuthOptions) CanReauth() bool { + return opts.AllowReauth +} diff --git a/vendor/github.com/rackspace/gophercloud/doc.go b/vendor/github.com/gophercloud/gophercloud/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/doc.go rename to vendor/github.com/gophercloud/gophercloud/doc.go diff --git a/vendor/github.com/rackspace/gophercloud/endpoint_search.go b/vendor/github.com/gophercloud/gophercloud/endpoint_search.go similarity index 81% rename from vendor/github.com/rackspace/gophercloud/endpoint_search.go rename to vendor/github.com/gophercloud/gophercloud/endpoint_search.go index 518943121..9887947f6 100644 --- a/vendor/github.com/rackspace/gophercloud/endpoint_search.go +++ b/vendor/github.com/gophercloud/gophercloud/endpoint_search.go @@ -1,21 +1,5 @@ package gophercloud -import "errors" - -var ( - // ErrServiceNotFound is returned when no service in a service catalog matches - // the provided EndpointOpts. This is generally returned by provider service - // factory methods like "NewComputeV2()" and can mean that a service is not - // enabled for your account. - ErrServiceNotFound = errors.New("No suitable service could be found in the service catalog.") - - // ErrEndpointNotFound is returned when no available endpoints match the - // provided EndpointOpts. This is also generally returned by provider service - // factory methods, and usually indicates that a region was specified - // incorrectly. - ErrEndpointNotFound = errors.New("No suitable endpoint could be found in the service catalog.") -) - // Availability indicates to whom a specific service endpoint is accessible: // the internet at large, internal networks only, or only to administrators. // Different identity services use different terminology for these. Identity v2 diff --git a/vendor/github.com/gophercloud/gophercloud/errors.go b/vendor/github.com/gophercloud/gophercloud/errors.go new file mode 100644 index 000000000..e0fe7c1e0 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/errors.go @@ -0,0 +1,408 @@ +package gophercloud + +import "fmt" + +// BaseError is an error type that all other error types embed. +type BaseError struct { + DefaultErrString string + Info string +} + +func (e BaseError) Error() string { + e.DefaultErrString = "An error occurred while executing a Gophercloud request." + return e.choseErrString() +} + +func (e BaseError) choseErrString() string { + if e.Info != "" { + return e.Info + } + return e.DefaultErrString +} + +// ErrMissingInput is the error when input is required in a particular +// situation but not provided by the user +type ErrMissingInput struct { + BaseError + Argument string +} + +func (e ErrMissingInput) Error() string { + e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument) + return e.choseErrString() +} + +// ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors. +type ErrInvalidInput struct { + ErrMissingInput + Value interface{} +} + +func (e ErrInvalidInput) Error() string { + e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value) + return e.choseErrString() +} + +// ErrUnexpectedResponseCode is returned by the Request method when a response code other than +// those listed in OkCodes is encountered. +type ErrUnexpectedResponseCode struct { + BaseError + URL string + Method string + Expected []int + Actual int + Body []byte +} + +func (e ErrUnexpectedResponseCode) Error() string { + e.DefaultErrString = fmt.Sprintf( + "Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", + e.Expected, e.Method, e.URL, e.Actual, e.Body, + ) + return e.choseErrString() +} + +// ErrDefault400 is the default error type returned on a 400 HTTP response code. +type ErrDefault400 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault401 is the default error type returned on a 401 HTTP response code. +type ErrDefault401 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault404 is the default error type returned on a 404 HTTP response code. +type ErrDefault404 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault405 is the default error type returned on a 405 HTTP response code. +type ErrDefault405 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault408 is the default error type returned on a 408 HTTP response code. +type ErrDefault408 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault429 is the default error type returned on a 429 HTTP response code. +type ErrDefault429 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault500 is the default error type returned on a 500 HTTP response code. +type ErrDefault500 struct { + ErrUnexpectedResponseCode +} + +// ErrDefault503 is the default error type returned on a 503 HTTP response code. +type ErrDefault503 struct { + ErrUnexpectedResponseCode +} + +func (e ErrDefault400) Error() string { + return "Invalid request due to incorrect syntax or missing required parameters." +} +func (e ErrDefault401) Error() string { + return "Authentication failed" +} +func (e ErrDefault404) Error() string { + return "Resource not found" +} +func (e ErrDefault405) Error() string { + return "Method not allowed" +} +func (e ErrDefault408) Error() string { + return "The server timed out waiting for the request" +} +func (e ErrDefault429) Error() string { + return "Too many requests have been sent in a given amount of time. Pause" + + " requests, wait up to one minute, and try again." +} +func (e ErrDefault500) Error() string { + return "Internal Server Error" +} +func (e ErrDefault503) Error() string { + return "The service is currently unable to handle the request due to a temporary" + + " overloading or maintenance. This is a temporary condition. Try again later." +} + +// Err400er is the interface resource error types implement to override the error message +// from a 400 error. +type Err400er interface { + Error400(ErrUnexpectedResponseCode) error +} + +// Err401er is the interface resource error types implement to override the error message +// from a 401 error. +type Err401er interface { + Error401(ErrUnexpectedResponseCode) error +} + +// Err404er is the interface resource error types implement to override the error message +// from a 404 error. +type Err404er interface { + Error404(ErrUnexpectedResponseCode) error +} + +// Err405er is the interface resource error types implement to override the error message +// from a 405 error. +type Err405er interface { + Error405(ErrUnexpectedResponseCode) error +} + +// Err408er is the interface resource error types implement to override the error message +// from a 408 error. +type Err408er interface { + Error408(ErrUnexpectedResponseCode) error +} + +// Err429er is the interface resource error types implement to override the error message +// from a 429 error. +type Err429er interface { + Error429(ErrUnexpectedResponseCode) error +} + +// Err500er is the interface resource error types implement to override the error message +// from a 500 error. +type Err500er interface { + Error500(ErrUnexpectedResponseCode) error +} + +// Err503er is the interface resource error types implement to override the error message +// from a 503 error. +type Err503er interface { + Error503(ErrUnexpectedResponseCode) error +} + +// ErrTimeOut is the error type returned when an operations times out. +type ErrTimeOut struct { + BaseError +} + +func (e ErrTimeOut) Error() string { + e.DefaultErrString = "A time out occurred" + return e.choseErrString() +} + +// ErrUnableToReauthenticate is the error type returned when reauthentication fails. +type ErrUnableToReauthenticate struct { + BaseError + ErrOriginal error +} + +func (e ErrUnableToReauthenticate) Error() string { + e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal) + return e.choseErrString() +} + +// ErrErrorAfterReauthentication is the error type returned when reauthentication +// succeeds, but an error occurs afterword (usually an HTTP error). +type ErrErrorAfterReauthentication struct { + BaseError + ErrOriginal error +} + +func (e ErrErrorAfterReauthentication) Error() string { + e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal) + return e.choseErrString() +} + +// ErrServiceNotFound is returned when no service in a service catalog matches +// the provided EndpointOpts. This is generally returned by provider service +// factory methods like "NewComputeV2()" and can mean that a service is not +// enabled for your account. +type ErrServiceNotFound struct { + BaseError +} + +func (e ErrServiceNotFound) Error() string { + e.DefaultErrString = "No suitable service could be found in the service catalog." + return e.choseErrString() +} + +// ErrEndpointNotFound is returned when no available endpoints match the +// provided EndpointOpts. This is also generally returned by provider service +// factory methods, and usually indicates that a region was specified +// incorrectly. +type ErrEndpointNotFound struct { + BaseError +} + +func (e ErrEndpointNotFound) Error() string { + e.DefaultErrString = "No suitable endpoint could be found in the service catalog." + return e.choseErrString() +} + +// ErrResourceNotFound is the error when trying to retrieve a resource's +// ID by name and the resource doesn't exist. +type ErrResourceNotFound struct { + BaseError + Name string + ResourceType string +} + +func (e ErrResourceNotFound) Error() string { + e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name) + return e.choseErrString() +} + +// ErrMultipleResourcesFound is the error when trying to retrieve a resource's +// ID by name and multiple resources have the user-provided name. +type ErrMultipleResourcesFound struct { + BaseError + Name string + Count int + ResourceType string +} + +func (e ErrMultipleResourcesFound) Error() string { + e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name) + return e.choseErrString() +} + +// ErrUnexpectedType is the error when an unexpected type is encountered +type ErrUnexpectedType struct { + BaseError + Expected string + Actual string +} + +func (e ErrUnexpectedType) Error() string { + e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual) + return e.choseErrString() +} + +func unacceptedAttributeErr(attribute string) string { + return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute) +} + +func redundantWithTokenErr(attribute string) string { + return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute) +} + +func redundantWithUserID(attribute string) string { + return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute) +} + +// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. +type ErrAPIKeyProvided struct{ BaseError } + +func (e ErrAPIKeyProvided) Error() string { + return unacceptedAttributeErr("APIKey") +} + +// ErrTenantIDProvided indicates that a TenantID was provided but can't be used. +type ErrTenantIDProvided struct{ BaseError } + +func (e ErrTenantIDProvided) Error() string { + return unacceptedAttributeErr("TenantID") +} + +// ErrTenantNameProvided indicates that a TenantName was provided but can't be used. +type ErrTenantNameProvided struct{ BaseError } + +func (e ErrTenantNameProvided) Error() string { + return unacceptedAttributeErr("TenantName") +} + +// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. +type ErrUsernameWithToken struct{ BaseError } + +func (e ErrUsernameWithToken) Error() string { + return redundantWithTokenErr("Username") +} + +// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. +type ErrUserIDWithToken struct{ BaseError } + +func (e ErrUserIDWithToken) Error() string { + return redundantWithTokenErr("UserID") +} + +// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. +type ErrDomainIDWithToken struct{ BaseError } + +func (e ErrDomainIDWithToken) Error() string { + return redundantWithTokenErr("DomainID") +} + +// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s +type ErrDomainNameWithToken struct{ BaseError } + +func (e ErrDomainNameWithToken) Error() string { + return redundantWithTokenErr("DomainName") +} + +// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. +type ErrUsernameOrUserID struct{ BaseError } + +func (e ErrUsernameOrUserID) Error() string { + return "Exactly one of Username and UserID must be provided for password authentication" +} + +// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. +type ErrDomainIDWithUserID struct{ BaseError } + +func (e ErrDomainIDWithUserID) Error() string { + return redundantWithUserID("DomainID") +} + +// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. +type ErrDomainNameWithUserID struct{ BaseError } + +func (e ErrDomainNameWithUserID) Error() string { + return redundantWithUserID("DomainName") +} + +// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. +// It may also indicate that both a DomainID and a DomainName were provided at once. +type ErrDomainIDOrDomainName struct{ BaseError } + +func (e ErrDomainIDOrDomainName) Error() string { + return "You must provide exactly one of DomainID or DomainName to authenticate by Username" +} + +// ErrMissingPassword indicates that no password was provided and no token is available. +type ErrMissingPassword struct{ BaseError } + +func (e ErrMissingPassword) Error() string { + return "You must provide a password to authenticate" +} + +// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. +type ErrScopeDomainIDOrDomainName struct{ BaseError } + +func (e ErrScopeDomainIDOrDomainName) Error() string { + return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName" +} + +// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. +type ErrScopeProjectIDOrProjectName struct{ BaseError } + +func (e ErrScopeProjectIDOrProjectName) Error() string { + return "You must provide at most one of ProjectID or ProjectName in a Scope" +} + +// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. +type ErrScopeProjectIDAlone struct{ BaseError } + +func (e ErrScopeProjectIDAlone) Error() string { + return "ProjectID must be supplied alone in a Scope" +} + +// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope. +type ErrScopeDomainName struct{ BaseError } + +func (e ErrScopeDomainName) Error() string { + return "DomainName must be supplied with a ProjectName or ProjectID in a Scope" +} + +// ErrScopeEmpty indicates that no credentials were provided in a Scope. +type ErrScopeEmpty struct{ BaseError } + +func (e ErrScopeEmpty) Error() string { + return "You must provide either a Project or Domain in a Scope" +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go similarity index 60% rename from vendor/github.com/rackspace/gophercloud/openstack/auth_env.go rename to vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go index a4402b6f0..f6d2eb194 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/auth_env.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go @@ -1,23 +1,14 @@ package openstack import ( - "fmt" "os" - "github.com/rackspace/gophercloud" + "github.com/gophercloud/gophercloud" ) var nilOptions = gophercloud.AuthOptions{} -// ErrNoAuthUrl, ErrNoUsername, and ErrNoPassword errors indicate of the required OS_AUTH_URL, OS_USERNAME, or OS_PASSWORD -// environment variables, respectively, remain undefined. See the AuthOptions() function for more details. -var ( - ErrNoAuthURL = fmt.Errorf("Environment variable OS_AUTH_URL needs to be set.") - ErrNoUsername = fmt.Errorf("Environment variable OS_USERNAME needs to be set.") - ErrNoPassword = fmt.Errorf("Environment variable OS_PASSWORD needs to be set.") -) - -// AuthOptions fills out an identity.AuthOptions structure with the settings found on the various OpenStack +// AuthOptionsFromEnv fills out an identity.AuthOptions structure with the settings found on the various OpenStack // OS_* environment variables. The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, // OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must // have settings, or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional. @@ -32,15 +23,18 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { domainName := os.Getenv("OS_DOMAIN_NAME") if authURL == "" { - return nilOptions, ErrNoAuthURL + err := gophercloud.ErrMissingInput{Argument: "authURL"} + return nilOptions, err } if username == "" && userID == "" { - return nilOptions, ErrNoUsername + err := gophercloud.ErrMissingInput{Argument: "username"} + return nilOptions, err } if password == "" { - return nilOptions, ErrNoPassword + err := gophercloud.ErrMissingInput{Argument: "password"} + return nilOptions, err } ao := gophercloud.AuthOptions{ diff --git a/vendor/github.com/rackspace/gophercloud/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/openstack/client.go similarity index 69% rename from vendor/github.com/rackspace/gophercloud/openstack/client.go rename to vendor/github.com/gophercloud/gophercloud/openstack/client.go index 951f4ed40..6e61944a1 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/client.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/client.go @@ -3,12 +3,12 @@ package openstack import ( "fmt" "net/url" - "strings" + "reflect" - "github.com/rackspace/gophercloud" - tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" - "github.com/rackspace/gophercloud/openstack/utils" + "github.com/gophercloud/gophercloud" + tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/openstack/utils" ) const ( @@ -76,9 +76,9 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp switch chosen.ID { case v20: - return v2auth(client, endpoint, options) + return v2auth(client, endpoint, options, gophercloud.EndpointOpts{}) case v30: - return v3auth(client, endpoint, options) + return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{}) default: // The switch statement must be out of date from the versions list. return fmt.Errorf("Unrecognized identity version: %s", chosen.ID) @@ -86,17 +86,31 @@ func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOp } // AuthenticateV2 explicitly authenticates against the identity v2 endpoint. -func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { - return v2auth(client, "", options) +func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { + return v2auth(client, "", options, eo) } -func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error { - v2Client := NewIdentityV2(client) +func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error { + v2Client, err := NewIdentityV2(client, eo) + if err != nil { + return err + } + if endpoint != "" { v2Client.Endpoint = endpoint } - result := tokens2.Create(v2Client, tokens2.AuthOptions{AuthOptions: options}) + v2Opts := tokens2.AuthOptions{ + IdentityEndpoint: options.IdentityEndpoint, + Username: options.Username, + Password: options.Password, + TenantID: options.TenantID, + TenantName: options.TenantName, + AllowReauth: options.AllowReauth, + TokenID: options.TokenID, + } + + result := tokens2.Create(v2Client, v2Opts) token, err := result.ExtractToken() if err != nil { @@ -111,7 +125,7 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc if options.AllowReauth { client.ReauthFunc = func() error { client.TokenID = "" - return v2auth(client, endpoint, options) + return v2auth(client, endpoint, options, eo) } } client.TokenID = token.ID @@ -123,40 +137,22 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } // AuthenticateV3 explicitly authenticates against the identity v3 service. -func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error { - return v3auth(client, "", options) +func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { + return v3auth(client, "", options, eo) } -func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error { +func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error { // Override the generated service endpoint with the one returned by the version endpoint. - v3Client := NewIdentityV3(client) + v3Client, err := NewIdentityV3(client, eo) + if err != nil { + return err + } + if endpoint != "" { v3Client.Endpoint = endpoint } - // copy the auth options to a local variable that we can change. `options` - // needs to stay as-is for reauth purposes - v3Options := options - - var scope *tokens3.Scope - if options.TenantID != "" { - scope = &tokens3.Scope{ - ProjectID: options.TenantID, - } - v3Options.TenantID = "" - v3Options.TenantName = "" - } else { - if options.TenantName != "" { - scope = &tokens3.Scope{ - ProjectName: options.TenantName, - DomainID: options.DomainID, - DomainName: options.DomainName, - } - v3Options.TenantName = "" - } - } - - result := tokens3.Create(v3Client, v3Options, scope) + result := tokens3.Create(v3Client, opts) token, err := result.ExtractToken() if err != nil { @@ -170,10 +166,10 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc client.TokenID = token.ID - if options.AllowReauth { + if opts.CanReauth() { client.ReauthFunc = func() error { client.TokenID = "" - return v3auth(client, endpoint, options) + return v3auth(client, endpoint, opts, eo) } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { @@ -184,57 +180,39 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } // NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service. -func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient { - v2Endpoint := client.IdentityBase + "v2.0/" +func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + endpoint := client.IdentityBase + "v2.0/" + var err error + if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { + eo.ApplyDefaults("identity") + endpoint, err = client.EndpointLocator(eo) + if err != nil { + return nil, err + } + } return &gophercloud.ServiceClient{ ProviderClient: client, - Endpoint: v2Endpoint, - } + Endpoint: endpoint, + }, nil } // NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service. -func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient { - v3Endpoint := client.IdentityBase + "v3/" +func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + endpoint := client.IdentityBase + "v3/" + var err error + if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) { + eo.ApplyDefaults("identity") + endpoint, err = client.EndpointLocator(eo) + if err != nil { + return nil, err + } + } return &gophercloud.ServiceClient{ ProviderClient: client, - Endpoint: v3Endpoint, - } -} - -func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - eo.ApplyDefaults("identity") - eo.Availability = gophercloud.AvailabilityAdmin - - url, err := client.EndpointLocator(eo) - if err != nil { - return nil, err - } - - // Force using v2 API - if strings.Contains(url, "/v3") { - url = strings.Replace(url, "/v3", "/v2.0", -1) - } - - return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil -} - -func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { - eo.ApplyDefaults("identity") - eo.Availability = gophercloud.AvailabilityAdmin - - url, err := client.EndpointLocator(eo) - if err != nil { - return nil, err - } - - // Force using v3 API - if strings.Contains(url, "/v2.0") { - url = strings.Replace(url, "/v2.0", "/v3", -1) - } - - return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil + Endpoint: endpoint, + }, nil } // NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package. @@ -281,6 +259,26 @@ func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.Endpoi return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil } +// 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) { + eo.ApplyDefaults("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. +func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { + eo.ApplyDefaults("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 // CDN service. func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) { @@ -311,3 +309,15 @@ func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (* } return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil } + +// 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) { + eo.ApplyDefaults("image") + url, err := client.EndpointLocator(eo) + if err != nil { + return nil, err + } + return &gophercloud.ServiceClient{ProviderClient: client, + Endpoint: url, + ResourceBase: url + "v2/"}, nil +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/common/extensions/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/doc.go diff --git a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/errors.go b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/errors.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/common/extensions/errors.go rename to vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/errors.go diff --git a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/requests.go similarity index 65% rename from vendor/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go rename to vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/requests.go index 0b7108501..46b7d60cd 100755 --- a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/requests.go @@ -1,15 +1,14 @@ package extensions import ( - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // Get retrieves information for a specific extension using its alias. -func Get(c *gophercloud.ServiceClient, alias string) GetResult { - var res GetResult - _, res.Err = c.Get(ExtensionURL(c, alias), &res.Body, nil) - return res +func Get(c *gophercloud.ServiceClient, alias string) (r GetResult) { + _, r.Err = c.Get(ExtensionURL(c, alias), &r.Body, nil) + return } // List returns a Pager which allows you to iterate over the full collection of extensions. diff --git a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/results.go similarity index 50% rename from vendor/github.com/rackspace/gophercloud/openstack/common/extensions/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/results.go index 777d083fa..d5f865091 100755 --- a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/results.go @@ -1,9 +1,8 @@ package extensions import ( - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // GetResult temporarily stores the result of a Get call. @@ -14,27 +13,21 @@ type GetResult struct { // Extract interprets a GetResult as an Extension. func (r GetResult) Extract() (*Extension, error) { - if r.Err != nil { - return nil, r.Err - } - - var res struct { + var s struct { Extension *Extension `json:"extension"` } - - err := mapstructure.Decode(r.Body, &res) - - return res.Extension, err + err := r.ExtractInto(&s) + return s.Extension, err } // Extension is a struct that represents an OpenStack extension. type Extension struct { - Updated string `json:"updated" mapstructure:"updated"` - Name string `json:"name" mapstructure:"name"` - Links []interface{} `json:"links" mapstructure:"links"` - Namespace string `json:"namespace" mapstructure:"namespace"` - Alias string `json:"alias" mapstructure:"alias"` - Description string `json:"description" mapstructure:"description"` + Updated string `json:"updated"` + Name string `json:"name"` + Links []interface{} `json:"links"` + Namespace string `json:"namespace"` + Alias string `json:"alias"` + Description string `json:"description"` } // ExtensionPage is the page returned by a pager when traversing over a collection of extensions. @@ -45,21 +38,16 @@ type ExtensionPage struct { // IsEmpty checks whether an ExtensionPage struct is empty. func (r ExtensionPage) IsEmpty() (bool, error) { is, err := ExtractExtensions(r) - if err != nil { - return true, err - } - return len(is) == 0, nil + return len(is) == 0, err } // ExtractExtensions accepts a Page struct, specifically an ExtensionPage struct, and extracts the // elements into a slice of Extension structs. // In other words, a generic collection is mapped into a relevant slice. -func ExtractExtensions(page pagination.Page) ([]Extension, error) { - var resp struct { - Extensions []Extension `mapstructure:"extensions"` +func ExtractExtensions(r pagination.Page) ([]Extension, error) { + var s struct { + Extensions []Extension `json:"extensions"` } - - err := mapstructure.Decode(page.(ExtensionPage).Body, &resp) - - return resp.Extensions, err + err := (r.(ExtensionPage)).ExtractInto(&s) + return s.Extensions, err } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/urls.go similarity index 89% rename from vendor/github.com/rackspace/gophercloud/openstack/common/extensions/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/urls.go index 6460c66bc..eaf38b2d1 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/common/extensions/urls.go @@ -1,6 +1,6 @@ package extensions -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" // ExtensionURL generates the URL for an extension resource by name. func ExtensionURL(c *gophercloud.ServiceClient, name string) string { diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/delegate.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/delegate.go similarity index 79% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/delegate.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/delegate.go index 10079097b..00e7c3bec 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/delegate.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/delegate.go @@ -1,9 +1,9 @@ package extensions import ( - "github.com/rackspace/gophercloud" - common "github.com/rackspace/gophercloud/openstack/common/extensions" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + common "github.com/gophercloud/gophercloud/openstack/common/extensions" + "github.com/gophercloud/gophercloud/pagination" ) // ExtractExtensions interprets a Page as a slice of Extensions. diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/doc.go diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go new file mode 100644 index 000000000..6682fa629 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go @@ -0,0 +1,3 @@ +// Package floatingips provides the ability to manage floating ips through +// nova-network +package floatingips diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go new file mode 100644 index 000000000..b36aeba59 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go @@ -0,0 +1,112 @@ +package floatingips + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// List returns a Pager that allows you to iterate over a collection of FloatingIPs. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return FloatingIPPage{pagination.SinglePageBase(r)} + }) +} + +// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the +// CreateOpts struct in this package does. +type CreateOptsBuilder interface { + ToFloatingIPCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies a Floating IP allocation request +type CreateOpts struct { + // Pool is the pool of floating IPs to allocate one from + Pool string `json:"pool" required:"true"` +} + +// ToFloatingIPCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "") +} + +// Create requests the creation of a new floating IP +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToFloatingIPCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Get returns data about a previously created FloatingIP. +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// Delete requests the deletion of a previous allocated FloatingIP. +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// AssociateOptsBuilder is the interface types must satfisfy to be used as +// Associate options +type AssociateOptsBuilder interface { + ToFloatingIPAssociateMap() (map[string]interface{}, error) +} + +// AssociateOpts specifies the required information to associate a floating IP with an instance +type AssociateOpts struct { + // FloatingIP is the floating IP to associate with an instance + FloatingIP string `json:"address" required:"true"` + // FixedIP is an optional fixed IP address of the server + FixedIP string `json:"fixed_address,omitempty"` +} + +// ToFloatingIPAssociateMap constructs a request body from AssociateOpts. +func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "addFloatingIp") +} + +// AssociateInstance pairs an allocated floating IP with an instance. +func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) { + b, err := opts.ToFloatingIPAssociateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(associateURL(client, serverID), b, nil, nil) + return +} + +// DisassociateOptsBuilder is the interface types must satfisfy to be used as +// Disassociate options +type DisassociateOptsBuilder interface { + ToFloatingIPDisassociateMap() (map[string]interface{}, error) +} + +// DisassociateOpts specifies the required information to disassociate a floating IP with an instance +type DisassociateOpts struct { + FloatingIP string `json:"address" required:"true"` +} + +// ToFloatingIPDisassociateMap constructs a request body from AssociateOpts. +func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "removeFloatingIp") +} + +// DisassociateInstance decouples an allocated floating IP from an instance +func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) { + b, err := opts.ToFloatingIPDisassociateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil) + return +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go similarity index 64% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go index be77fa179..753f3afa7 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go @@ -1,54 +1,51 @@ -package floatingip +package floatingips import ( - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // A FloatingIP is an IP that can be associated with an instance type FloatingIP struct { // ID is a unique ID of the Floating IP - ID string `mapstructure:"id"` + ID string `json:"id"` // FixedIP is the IP of the instance related to the Floating IP - FixedIP string `mapstructure:"fixed_ip,omitempty"` + FixedIP string `json:"fixed_ip,omitempty"` // InstanceID is the ID of the instance that is using the Floating IP - InstanceID string `mapstructure:"instance_id"` + InstanceID string `json:"instance_id"` // IP is the actual Floating IP - IP string `mapstructure:"ip"` + IP string `json:"ip"` // Pool is the pool of floating IPs that this floating IP belongs to - Pool string `mapstructure:"pool"` + Pool string `json:"pool"` } -// FloatingIPsPage stores a single, only page of FloatingIPs +// FloatingIPPage stores a single, only page of FloatingIPs // results from a List call. -type FloatingIPsPage struct { +type FloatingIPPage struct { pagination.SinglePageBase } // IsEmpty determines whether or not a FloatingIPsPage is empty. -func (page FloatingIPsPage) IsEmpty() (bool, error) { +func (page FloatingIPPage) IsEmpty() (bool, error) { va, err := ExtractFloatingIPs(page) return len(va) == 0, err } // ExtractFloatingIPs interprets a page of results as a slice of // FloatingIPs. -func ExtractFloatingIPs(page pagination.Page) ([]FloatingIP, error) { - casted := page.(FloatingIPsPage).Body - var response struct { - FloatingIPs []FloatingIP `mapstructure:"floating_ips"` +func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) { + var s struct { + FloatingIPs []FloatingIP `json:"floating_ips"` } - - err := mapstructure.WeakDecode(casted, &response) - - return response.FloatingIPs, err + err := (r.(FloatingIPPage)).ExtractInto(&s) + return s.FloatingIPs, err } +// FloatingIPResult is the raw result from a FloatingIP request. type FloatingIPResult struct { gophercloud.Result } @@ -56,16 +53,11 @@ type FloatingIPResult struct { // Extract is a method that attempts to interpret any FloatingIP resource // response as a FloatingIP struct. func (r FloatingIPResult) Extract() (*FloatingIP, error) { - if r.Err != nil { - return nil, r.Err + var s struct { + FloatingIP *FloatingIP `json:"floating_ip"` } - - var res struct { - FloatingIP *FloatingIP `json:"floating_ip" mapstructure:"floating_ip"` - } - - err := mapstructure.WeakDecode(r.Body, &res) - return res.FloatingIP, err + err := r.ExtractInto(&s) + return s.FloatingIP, err } // CreateResult is the response from a Create operation. Call its Extract method to interpret it diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go similarity index 58% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go index 54198f852..4768e5a89 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go @@ -1,6 +1,6 @@ -package floatingip +package floatingips -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" const resourcePath = "os-floating-ips" @@ -24,14 +24,14 @@ func deleteURL(c *gophercloud.ServiceClient, id string) string { return getURL(c, id) } -func serverURL(c *gophercloud.ServiceClient, serverId string) string { - return c.ServiceURL("servers/" + serverId + "/action") +func serverURL(c *gophercloud.ServiceClient, serverID string) string { + return c.ServiceURL("servers/" + serverID + "/action") } -func associateURL(c *gophercloud.ServiceClient, serverId string) string { - return serverURL(c, serverId) +func associateURL(c *gophercloud.ServiceClient, serverID string) string { + return serverURL(c, serverID) } -func disassociateURL(c *gophercloud.ServiceClient, serverId string) string { - return serverURL(c, serverId) +func disassociateURL(c *gophercloud.ServiceClient, serverID string) string { + return serverURL(c, serverID) } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go similarity index 63% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go index c56ee67ea..adf1e5596 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go @@ -1,11 +1,9 @@ package keypairs import ( - "errors" - - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/pagination" ) // CreateOptsExt adds a KeyPair option to the base CreateOpts. @@ -47,56 +45,40 @@ type CreateOptsBuilder interface { // CreateOpts specifies keypair creation or import parameters. type CreateOpts struct { - // Name [required] is a friendly name to refer to this KeyPair in other services. - Name string - + // Name is a friendly name to refer to this KeyPair in other services. + Name string `json:"name" required:"true"` // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key // will be imported and no new key will be created. - PublicKey string + PublicKey string `json:"public_key,omitempty"` } // ToKeyPairCreateMap constructs a request body from CreateOpts. func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { - if opts.Name == "" { - return nil, errors.New("Missing field required for keypair creation: Name") - } - - keypair := make(map[string]interface{}) - keypair["name"] = opts.Name - if opts.PublicKey != "" { - keypair["public_key"] = opts.PublicKey - } - - return map[string]interface{}{"keypair": keypair}, nil + return gophercloud.BuildRequestBody(opts, "keypair") } // Create requests the creation of a new keypair on the server, or to import a pre-existing // keypair. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { - var res CreateResult - - reqBody, err := opts.ToKeyPairCreateMap() +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToKeyPairCreateMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - - _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return res + return } // Get returns public data about a previously uploaded KeyPair. -func Get(client *gophercloud.ServiceClient, name string) GetResult { - var res GetResult - _, res.Err = client.Get(getURL(client, name), &res.Body, nil) - return res +func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { + _, r.Err = client.Get(getURL(client, name), &r.Body, nil) + return } // Delete requests the deletion of a previous stored KeyPair from the server. -func Delete(client *gophercloud.ServiceClient, name string) DeleteResult { - var res DeleteResult - _, res.Err = client.Delete(deleteURL(client, name), nil) - return res +func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, name), nil) + return } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go similarity index 68% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go index f1a0d8e11..f4d8d35c3 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go @@ -1,31 +1,30 @@ package keypairs import ( - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // KeyPair is an SSH key known to the OpenStack cluster that is available to be injected into // servers. type KeyPair struct { // Name is used to refer to this keypair from other services within this region. - Name string `mapstructure:"name"` + Name string `json:"name"` // Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer // public key. - Fingerprint string `mapstructure:"fingerprint"` + Fingerprint string `json:"fingerprint"` // PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..." - PublicKey string `mapstructure:"public_key"` + PublicKey string `json:"public_key"` // PrivateKey is the private key from this pair, in PEM format. // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just // returned from a Create call - PrivateKey string `mapstructure:"private_key"` + PrivateKey string `json:"private_key"` // UserID is the user who owns this keypair. - UserID string `mapstructure:"user_id"` + UserID string `json:"user_id"` } // KeyPairPage stores a single, only page of KeyPair results from a List call. @@ -40,18 +39,16 @@ func (page KeyPairPage) IsEmpty() (bool, error) { } // ExtractKeyPairs interprets a page of results as a slice of KeyPairs. -func ExtractKeyPairs(page pagination.Page) ([]KeyPair, error) { +func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) { type pair struct { - KeyPair KeyPair `mapstructure:"keypair"` + KeyPair KeyPair `json:"keypair"` } - - var resp struct { - KeyPairs []pair `mapstructure:"keypairs"` + var s struct { + KeyPairs []pair `json:"keypairs"` } - - err := mapstructure.Decode(page.(KeyPairPage).Body, &resp) - results := make([]KeyPair, len(resp.KeyPairs)) - for i, pair := range resp.KeyPairs { + err := (r.(KeyPairPage)).ExtractInto(&s) + results := make([]KeyPair, len(s.KeyPairs)) + for i, pair := range s.KeyPairs { results[i] = pair.KeyPair } return results, err @@ -63,16 +60,11 @@ type keyPairResult struct { // Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct. func (r keyPairResult) Extract() (*KeyPair, error) { - if r.Err != nil { - return nil, r.Err + var s struct { + KeyPair *KeyPair `json:"keypair"` } - - var res struct { - KeyPair *KeyPair `json:"keypair" mapstructure:"keypair"` - } - - err := mapstructure.Decode(r.Body, &res) - return res.KeyPair, err + err := r.ExtractInto(&s) + return s.KeyPair, err } // CreateResult is the response from a Create operation. Call its Extract method to interpret it diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go similarity index 92% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go index 702f5329e..fec38f367 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go @@ -1,6 +1,6 @@ package keypairs -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" const resourcePath = "os-keypairs" diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop/doc.go diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop/requests.go new file mode 100644 index 000000000..1d8a593b9 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop/requests.go @@ -0,0 +1,19 @@ +package startstop + +import "github.com/gophercloud/gophercloud" + +func actionURL(client *gophercloud.ServiceClient, id string) string { + return client.ServiceURL("servers", id, "action") +} + +// Start is the operation responsible for starting a Compute server. +func Start(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-start": nil}, nil, nil) + return +} + +// Stop is the operation responsible for stopping a Compute server. +func Stop(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"os-stop": nil}, nil, nil) + return +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/doc.go diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go similarity index 68% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go index 59123aaf7..ef133ff80 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go @@ -1,10 +1,8 @@ package flavors import ( - "fmt" - - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -36,10 +34,7 @@ type ListOpts struct { // ToFlavorListQuery formats a ListOpts into a query string. func (opts ListOpts) ToFlavorListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), nil + return q.String(), err } // ListDetail instructs OpenStack to provide a list of flavors. @@ -54,50 +49,52 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } url += query } - createPage := func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return FlavorPage{pagination.LinkedPageBase{PageResult: r}} - } - - return pagination.NewPager(client, url, createPage) + }) } // Get instructs OpenStack to provide details on a single flavor, identified by its ID. // Use ExtractFlavor to convert its result into a Flavor. -func Get(client *gophercloud.ServiceClient, id string) GetResult { - var res GetResult - _, res.Err = client.Get(getURL(client, id), &res.Body, nil) - return res +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return } // IDFromName is a convienience function that returns a flavor's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - flavorCount := 0 - flavorID := "" - if name == "" { - return "", fmt.Errorf("A flavor name must be provided.") + count := 0 + id := "" + allPages, err := ListDetail(client, nil).AllPages() + if err != nil { + return "", err } - pager := ListDetail(client, nil) - pager.EachPage(func(page pagination.Page) (bool, error) { - flavorList, err := ExtractFlavors(page) - if err != nil { - return false, err - } - for _, f := range flavorList { - if f.Name == name { - flavorCount++ - flavorID = f.ID - } - } - return true, nil - }) + all, err := ExtractFlavors(allPages) + if err != nil { + return "", err + } - switch flavorCount { + for _, f := range all { + if f.Name == name { + count++ + id = f.ID + } + } + + switch count { case 0: - return "", fmt.Errorf("Unable to find flavor: %s", name) + err := &gophercloud.ErrResourceNotFound{} + err.ResourceType = "flavor" + err.Name = name + return "", err case 1: - return flavorID, nil + return id, nil default: - return "", fmt.Errorf("Found %d flavors matching %s", flavorCount, name) + err := &gophercloud.ErrMultipleResourcesFound{} + err.ResourceType = "flavor" + err.Name = name + err.Count = count + return "", err } } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go new file mode 100644 index 000000000..a49de0da7 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/results.go @@ -0,0 +1,114 @@ +package flavors + +import ( + "encoding/json" + "strconv" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// GetResult temporarily holds the response from a Get call. +type GetResult struct { + gophercloud.Result +} + +// Extract provides access to the individual Flavor returned by the Get function. +func (r GetResult) Extract() (*Flavor, error) { + var s struct { + Flavor *Flavor `json:"flavor"` + } + err := r.ExtractInto(&s) + return s.Flavor, err +} + +// Flavor records represent (virtual) hardware configurations for server resources in a region. +type Flavor struct { + // The Id field contains the flavor's unique identifier. + // For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance. + ID string `json:"id"` + // The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively. + Disk int `json:"disk"` + RAM int `json:"ram"` + // The Name field provides a human-readable moniker for the flavor. + Name string `json:"name"` + RxTxFactor float64 `json:"rxtx_factor"` + // Swap indicates how much space is reserved for swap. + // If not provided, this field will be set to 0. + Swap int `json:"swap"` + // VCPUs indicates how many (virtual) CPUs are available for this flavor. + VCPUs int `json:"vcpus"` +} + +func (f *Flavor) UnmarshalJSON(b []byte) error { + var flavor struct { + ID string `json:"id"` + Disk int `json:"disk"` + RAM int `json:"ram"` + Name string `json:"name"` + RxTxFactor float64 `json:"rxtx_factor"` + Swap interface{} `json:"swap"` + VCPUs int `json:"vcpus"` + } + err := json.Unmarshal(b, &flavor) + if err != nil { + return err + } + + f.ID = flavor.ID + 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) { + case float64: + f.Swap = int(t) + case string: + switch t { + case "": + f.Swap = 0 + default: + swap, err := strconv.ParseFloat(t, 64) + if err != nil { + return err + } + f.Swap = int(swap) + } + } + + return nil +} + +// FlavorPage contains a single page of the response from a List call. +type FlavorPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines if a page contains any results. +func (page FlavorPage) IsEmpty() (bool, error) { + flavors, err := ExtractFlavors(page) + return len(flavors) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +func (page FlavorPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"flavors_links"` + } + err := page.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation. +func ExtractFlavors(r pagination.Page) ([]Flavor, error) { + var s struct { + Flavors []Flavor `json:"flavors"` + } + err := (r.(FlavorPage)).ExtractInto(&s) + return s.Flavors, err +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go similarity index 86% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go index 683c107dc..ee0dfdbe3 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/urls.go @@ -1,7 +1,7 @@ package flavors import ( - "github.com/rackspace/gophercloud" + "github.com/gophercloud/gophercloud" ) func getURL(client *gophercloud.ServiceClient, id string) string { diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go similarity index 60% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go index 1e021ad4c..df9f1da8f 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go @@ -1,10 +1,8 @@ package images import ( - "fmt" - - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -34,10 +32,7 @@ type ListOpts struct { // ToImageListQuery formats a ListOpts into a query string. func (opts ListOpts) ToImageListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), nil + return q.String(), err } // ListDetail enumerates the available images. @@ -50,60 +45,58 @@ func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) paginat } url += query } - - createPage := func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ImagePage{pagination.LinkedPageBase{PageResult: r}} - } - - return pagination.NewPager(client, url, createPage) + }) } // Get acquires additional detail about a specific image by ID. // Use ExtractImage() to interpret the result as an openstack Image. -func Get(client *gophercloud.ServiceClient, id string) GetResult { - var result GetResult - _, result.Err = client.Get(getURL(client, id), &result.Body, nil) - return result +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return } // Delete deletes the specified image ID. -func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { - var result DeleteResult - _, result.Err = client.Delete(deleteURL(client, id), nil) - return result +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return } // IDFromName is a convienience function that returns an image's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - imageCount := 0 - imageID := "" - if name == "" { - return "", fmt.Errorf("An image name must be provided.") + count := 0 + id := "" + allPages, err := ListDetail(client, nil).AllPages() + if err != nil { + return "", err } - pager := ListDetail(client, &ListOpts{ - Name: name, - }) - pager.EachPage(func(page pagination.Page) (bool, error) { - imageList, err := ExtractImages(page) - if err != nil { - return false, err - } - for _, i := range imageList { - if i.Name == name { - imageCount++ - imageID = i.ID - } - } - return true, nil - }) + all, err := ExtractImages(allPages) + if err != nil { + return "", err + } - switch imageCount { + for _, f := range all { + if f.Name == name { + count++ + id = f.ID + } + } + + switch count { case 0: - return "", fmt.Errorf("Unable to find image: %s", name) + err := &gophercloud.ErrResourceNotFound{} + err.ResourceType = "image" + err.Name = name + return "", err case 1: - return imageID, nil + return id, nil default: - return "", fmt.Errorf("Found %d images matching %s", imageCount, name) + err := &gophercloud.ErrMultipleResourcesFound{} + err.ResourceType = "image" + err.Name = name + err.Count = count + return "", err } } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go similarity index 64% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go index 40e814d1d..a55b8f160 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go @@ -1,9 +1,8 @@ package images import ( - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // GetResult temporarily stores a Get response. @@ -17,17 +16,12 @@ type DeleteResult struct { } // Extract interprets a GetResult as an Image. -func (gr GetResult) Extract() (*Image, error) { - if gr.Err != nil { - return nil, gr.Err +func (r GetResult) Extract() (*Image, error) { + var s struct { + Image *Image `json:"image"` } - - var decoded struct { - Image Image `mapstructure:"image"` - } - - err := mapstructure.Decode(gr.Body, &decoded) - return &decoded.Image, err + err := r.ExtractInto(&s) + return s.Image, err } // Image is used for JSON (un)marshalling. @@ -51,6 +45,8 @@ type Image struct { Status string Updated string + + Metadata map[string]string } // ImagePage contains a single page of results from a List operation. @@ -62,34 +58,26 @@ type ImagePage struct { // IsEmpty returns true if a page contains no Image results. func (page ImagePage) IsEmpty() (bool, error) { images, err := ExtractImages(page) - if err != nil { - return true, err - } - return len(images) == 0, nil + return len(images) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the next page of results. func (page ImagePage) NextPageURL() (string, error) { - type resp struct { - Links []gophercloud.Link `mapstructure:"images_links"` + var s struct { + Links []gophercloud.Link `json:"images_links"` } - - var r resp - err := mapstructure.Decode(page.Body, &r) + err := page.ExtractInto(&s) if err != nil { return "", err } - - return gophercloud.ExtractNextURL(r.Links) + return gophercloud.ExtractNextURL(s.Links) } // ExtractImages converts a page of List results into a slice of usable Image structs. -func ExtractImages(page pagination.Page) ([]Image, error) { - casted := page.(ImagePage).Body - var results struct { - Images []Image `mapstructure:"images"` +func ExtractImages(r pagination.Page) ([]Image, error) { + var s struct { + Images []Image `json:"images"` } - - err := mapstructure.Decode(casted, &results) - return results.Images, err + err := (r.(ImagePage)).ExtractInto(&s) + return s.Images, err } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go similarity index 88% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go index b1bf1038f..57787fb72 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/images/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go @@ -1,6 +1,6 @@ package images -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" func listDetailURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("images", "detail") diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/doc.go diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go new file mode 100644 index 000000000..c9f0e3c20 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/errors.go @@ -0,0 +1,71 @@ +package servers + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" +) + +// ErrNeitherImageIDNorImageNameProvided is the error when neither the image +// ID nor the image name is provided for a server operation +type ErrNeitherImageIDNorImageNameProvided struct{ gophercloud.ErrMissingInput } + +func (e ErrNeitherImageIDNorImageNameProvided) Error() string { + return "One and only one of the image ID and the image name must be provided." +} + +// ErrNeitherFlavorIDNorFlavorNameProvided is the error when neither the flavor +// ID nor the flavor name is provided for a server operation +type ErrNeitherFlavorIDNorFlavorNameProvided struct{ gophercloud.ErrMissingInput } + +func (e ErrNeitherFlavorIDNorFlavorNameProvided) Error() string { + return "One and only one of the flavor ID and the flavor name must be provided." +} + +type ErrNoClientProvidedForIDByName struct{ gophercloud.ErrMissingInput } + +func (e ErrNoClientProvidedForIDByName) Error() string { + return "A service client must be provided to find a resource ID by name." +} + +// ErrInvalidHowParameterProvided is the error when an unknown value is given +// for the `how` argument +type ErrInvalidHowParameterProvided struct{ gophercloud.ErrInvalidInput } + +// ErrNoAdminPassProvided is the error when an administrative password isn't +// provided for a server operation +type ErrNoAdminPassProvided struct{ gophercloud.ErrMissingInput } + +// ErrNoImageIDProvided is the error when an image ID isn't provided for a server +// operation +type ErrNoImageIDProvided struct{ gophercloud.ErrMissingInput } + +// ErrNoIDProvided is the error when a server ID isn't provided for a server +// operation +type ErrNoIDProvided struct{ gophercloud.ErrMissingInput } + +// ErrServer is a generic error type for servers HTTP operations. +type ErrServer struct { + gophercloud.ErrUnexpectedResponseCode + ID string +} + +func (se ErrServer) Error() string { + return fmt.Sprintf("Error while executing HTTP request for server [%s]", se.ID) +} + +// Error404 overrides the generic 404 error message. +func (se ErrServer) Error404(e gophercloud.ErrUnexpectedResponseCode) error { + se.ErrUnexpectedResponseCode = e + return &ErrServerNotFound{se} +} + +// ErrServerNotFound is the error when a 404 is received during server HTTP +// operations. +type ErrServerNotFound struct { + ErrServer +} + +func (e ErrServerNotFound) Error() string { + return fmt.Sprintf("I couldn't find server [%s]", e.ID) +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go similarity index 53% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go index e6490539c..0ec5b0fdb 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go @@ -3,13 +3,11 @@ package servers import ( "encoding/base64" "encoding/json" - "errors" - "fmt" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" - "github.com/rackspace/gophercloud/openstack/compute/v2/images" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/openstack/compute/v2/images" + "github.com/gophercloud/gophercloud/pagination" ) // ListOptsBuilder allows extensions to add additional parameters to the @@ -57,16 +55,12 @@ type ListOpts struct { // ToServerListQuery formats a ListOpts into a query string. func (opts ListOpts) ToServerListQuery() (string, error) { q, err := gophercloud.BuildQueryString(opts) - if err != nil { - return "", err - } - return q.String(), nil + return q.String(), err } // List makes a request against the API to list servers accessible to you. func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { url := listDetailURL(client) - if opts != nil { query, err := opts.ToServerListQuery() if err != nil { @@ -74,12 +68,9 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa } url += query } - - createPageFn := func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { return ServerPage{pagination.LinkedPageBase{PageResult: r}} - } - - return pagination.NewPager(client, url, createPageFn) + }) } // CreateOptsBuilder describes struct types that can be accepted by the Create call. @@ -129,93 +120,83 @@ func (f *File) MarshalJSON() ([]byte, error) { // CreateOpts specifies server creation parameters. type CreateOpts struct { - // Name [required] is the name to assign to the newly launched server. - Name string + // Name is the name to assign to the newly launched server. + Name string `json:"name" required:"true"` // ImageRef [optional; required if ImageName is not provided] is the ID or full // URL to the image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. - ImageRef string + ImageRef string `json:"imageRef"` // ImageName [optional; required if ImageRef is not provided] is the name of the // image that contains the server's OS and initial state. // Also optional if using the boot-from-volume extension. - ImageName string + ImageName string `json:"-"` // FlavorRef [optional; required if FlavorName is not provided] is the ID or // full URL to the flavor that describes the server's specs. - FlavorRef string + FlavorRef string `json:"flavorRef"` // FlavorName [optional; required if FlavorRef is not provided] is the name of // the flavor that describes the server's specs. - FlavorName string + FlavorName string `json:"-"` - // SecurityGroups [optional] lists the names of the security groups to which this server should belong. - SecurityGroups []string + // SecurityGroups lists the names of the security groups to which this server should belong. + SecurityGroups []string `json:"-"` - // UserData [optional] contains configuration information or scripts to use upon launch. - // Create will base64-encode it for you. - UserData []byte + // UserData contains configuration information or scripts to use upon launch. + // Create will base64-encode it for you, if it isn't already. + UserData []byte `json:"-"` - // AvailabilityZone [optional] in which to launch the server. - AvailabilityZone string + // AvailabilityZone in which to launch the server. + AvailabilityZone string `json:"availability_zone,omitempty"` - // Networks [optional] dictates how this server will be attached to available networks. + // Networks dictates how this server will be attached to available networks. // By default, the server will be attached to all isolated networks for the tenant. - Networks []Network + Networks []Network `json:"-"` - // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server. - Metadata map[string]string + // Metadata contains key-value pairs (up to 255 bytes each) to attach to the server. + Metadata map[string]string `json:"metadata,omitempty"` - // Personality [optional] 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. - Personality Personality + Personality Personality `json:"-"` - // ConfigDrive [optional] enables metadata injection through a configuration drive. - ConfigDrive bool + // ConfigDrive enables metadata injection through a configuration drive. + ConfigDrive *bool `json:"config_drive,omitempty"` - // AdminPass [optional] sets the root user password. If not set, a randomly-generated - // password will be created and returned in the response. - AdminPass string + // AdminPass sets the root user password. If not set, a randomly-generated + // password will be created and returned in the rponse. + AdminPass string `json:"adminPass,omitempty"` - // AccessIPv4 [optional] specifies an IPv4 address for the instance. - AccessIPv4 string + // AccessIPv4 specifies an IPv4 address for the instance. + AccessIPv4 string `json:"accessIPv4,omitempty"` - // AccessIPv6 [optional] specifies an IPv6 address for the instance. - AccessIPv6 string + // AccessIPv6 pecifies an IPv6 address for the instance. + AccessIPv6 string `json:"accessIPv6,omitempty"` + + // ServiceClient will allow calls to be made to retrieve an image or + // flavor ID by name. + ServiceClient *gophercloud.ServiceClient `json:"-"` } // ToServerCreateMap assembles a request body based on the contents of a CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { - server := make(map[string]interface{}) - - server["name"] = opts.Name - server["imageRef"] = opts.ImageRef - server["imageName"] = opts.ImageName - server["flavorRef"] = opts.FlavorRef - server["flavorName"] = opts.FlavorName + sc := opts.ServiceClient + opts.ServiceClient = nil + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } if opts.UserData != nil { - encoded := base64.StdEncoding.EncodeToString(opts.UserData) - server["user_data"] = &encoded - } - if opts.ConfigDrive { - server["config_drive"] = "true" - } - if opts.AvailabilityZone != "" { - server["availability_zone"] = opts.AvailabilityZone - } - if opts.Metadata != nil { - server["metadata"] = opts.Metadata - } - if opts.AdminPass != "" { - server["adminPass"] = opts.AdminPass - } - if opts.AccessIPv4 != "" { - server["accessIPv4"] = opts.AccessIPv4 - } - if opts.AccessIPv6 != "" { - server["accessIPv6"] = opts.AccessIPv6 + 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 { @@ -223,7 +204,7 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { for i, groupName := range opts.SecurityGroups { securityGroups[i] = map[string]interface{}{"name": groupName} } - server["security_groups"] = securityGroups + b["security_groups"] = securityGroups } if len(opts.Networks) > 0 { @@ -240,172 +221,125 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { networks[i]["fixed_ip"] = net.FixedIP } } - server["networks"] = networks + b["networks"] = networks } - if len(opts.Personality) > 0 { - server["personality"] = opts.Personality + // If ImageRef isn't provided, check if ImageName was provided to ascertain + // the image ID. + if opts.ImageRef == "" { + if opts.ImageName != "" { + if sc == nil { + err := ErrNoClientProvidedForIDByName{} + err.Argument = "ServiceClient" + return nil, err + } + imageID, err := images.IDFromName(sc, opts.ImageName) + if err != nil { + return nil, err + } + b["imageRef"] = imageID + } } - return map[string]interface{}{"server": server}, nil + // If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID. + if opts.FlavorRef == "" { + if opts.FlavorName == "" { + err := ErrNeitherFlavorIDNorFlavorNameProvided{} + err.Argument = "FlavorRef/FlavorName" + return nil, err + } + if sc == nil { + err := ErrNoClientProvidedForIDByName{} + err.Argument = "ServiceClient" + return nil, err + } + flavorID, err := flavors.IDFromName(sc, opts.FlavorName) + if err != nil { + return nil, err + } + b["flavorRef"] = flavorID + } + + return map[string]interface{}{"server": b}, nil } // Create requests a server to be provisioned to the user in the current tenant. -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { - var res CreateResult - +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { reqBody, err := opts.ToServerCreateMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - - // If ImageRef isn't provided, use ImageName to ascertain the image ID. - if reqBody["server"].(map[string]interface{})["imageRef"].(string) == "" { - imageName := reqBody["server"].(map[string]interface{})["imageName"].(string) - if imageName == "" { - res.Err = errors.New("One and only one of ImageRef and ImageName must be provided.") - return res - } - imageID, err := images.IDFromName(client, imageName) - if err != nil { - res.Err = err - return res - } - reqBody["server"].(map[string]interface{})["imageRef"] = imageID - } - delete(reqBody["server"].(map[string]interface{}), "imageName") - - // If FlavorRef isn't provided, use FlavorName to ascertain the flavor ID. - if reqBody["server"].(map[string]interface{})["flavorRef"].(string) == "" { - flavorName := reqBody["server"].(map[string]interface{})["flavorName"].(string) - if flavorName == "" { - res.Err = errors.New("One and only one of FlavorRef and FlavorName must be provided.") - return res - } - flavorID, err := flavors.IDFromName(client, flavorName) - if err != nil { - res.Err = err - return res - } - reqBody["server"].(map[string]interface{})["flavorRef"] = flavorID - } - delete(reqBody["server"].(map[string]interface{}), "flavorName") - - _, res.Err = client.Post(listURL(client), reqBody, &res.Body, nil) - return res + _, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil) + return } // Delete requests that a server previously provisioned be removed from your account. -func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { - var res DeleteResult - _, res.Err = client.Delete(deleteURL(client, id), nil) - return res +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return } -func ForceDelete(client *gophercloud.ServiceClient, id string) ActionResult { - var req struct { - ForceDelete string `json:"forceDelete"` - } - - var res ActionResult - _, res.Err = client.Post(actionURL(client, id), req, nil, nil) - return res - +// ForceDelete forces the deletion of a server +func ForceDelete(client *gophercloud.ServiceClient, id string) (r ActionResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) + return } // Get requests details on a single server, by ID. -func Get(client *gophercloud.ServiceClient, id string) GetResult { - var result GetResult - _, result.Err = client.Get(getURL(client, id), &result.Body, &gophercloud.RequestOpts{ +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) - return result + return } // UpdateOptsBuilder allows extensions to add additional attributes to the Update request. type UpdateOptsBuilder interface { - ToServerUpdateMap() map[string]interface{} + ToServerUpdateMap() (map[string]interface{}, error) } // UpdateOpts specifies the base attributes that may be updated on an existing server. type UpdateOpts struct { - // Name [optional] changes the displayed name of the server. + // Name changes the displayed name of the server. // The server host name will *not* change. // Server names are not constrained to be unique, even within the same tenant. - Name string + Name string `json:"name,omitempty"` - // AccessIPv4 [optional] provides a new IPv4 address for the instance. - AccessIPv4 string + // AccessIPv4 provides a new IPv4 address for the instance. + AccessIPv4 string `json:"accessIPv4,omitempty"` - // AccessIPv6 [optional] provides a new IPv6 address for the instance. - AccessIPv6 string + // AccessIPv6 provides a new IPv6 address for the instance. + AccessIPv6 string `json:"accessIPv6,omitempty"` } // ToServerUpdateMap formats an UpdateOpts structure into a request body. -func (opts UpdateOpts) ToServerUpdateMap() map[string]interface{} { - server := make(map[string]string) - if opts.Name != "" { - server["name"] = opts.Name - } - if opts.AccessIPv4 != "" { - server["accessIPv4"] = opts.AccessIPv4 - } - if opts.AccessIPv6 != "" { - server["accessIPv6"] = opts.AccessIPv6 - } - return map[string]interface{}{"server": server} +func (opts UpdateOpts) ToServerUpdateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "server") } // Update requests that various attributes of the indicated server be changed. -func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult { - var result UpdateResult - reqBody := opts.ToServerUpdateMap() - _, result.Err = client.Put(updateURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{ +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToServerUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return result + return } // ChangeAdminPassword alters the administrator or root password for a specified server. -func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) ActionResult { - var req struct { - ChangePassword struct { - AdminPass string `json:"adminPass"` - } `json:"changePassword"` +func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword string) (r ActionResult) { + b := map[string]interface{}{ + "changePassword": map[string]string{ + "adminPass": newPassword, + }, } - - req.ChangePassword.AdminPass = newPassword - - var res ActionResult - _, res.Err = client.Post(actionURL(client, id), req, nil, nil) - return res -} - -// ErrArgument errors occur when an argument supplied to a package function -// fails to fall within acceptable values. For example, the Reboot() function -// expects the "how" parameter to be one of HardReboot or SoftReboot. These -// constants are (currently) strings, leading someone to wonder if they can pass -// other string values instead, perhaps in an effort to break the API of their -// provider. Reboot() returns this error in this situation. -// -// Function identifies which function was called/which function is generating -// the error. -// Argument identifies which formal argument was responsible for producing the -// error. -// Value provides the value as it was passed into the function. -type ErrArgument struct { - Function, Argument string - Value interface{} -} - -// Error yields a useful diagnostic for debugging purposes. -func (e *ErrArgument) Error() string { - return fmt.Sprintf("Bad argument in call to %s, formal parameter %s, value %#v", e.Function, e.Argument, e.Value) -} - -func (e *ErrArgument) String() string { - return e.Error() + _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + return } // RebootMethod describes the mechanisms by which a server reboot can be requested. @@ -420,36 +354,41 @@ const ( PowerCycle = HardReboot ) +// RebootOptsBuilder is an interface that options must satisfy in order to be +// used when rebooting a server instance +type RebootOptsBuilder interface { + ToServerRebootMap() (map[string]interface{}, error) +} + +// RebootOpts satisfies the RebootOptsBuilder interface +type RebootOpts struct { + Type RebootMethod `json:"type" required:"true"` +} + +// ToServerRebootMap allows RebootOpts to satisfiy the RebootOptsBuilder +// interface +func (opts *RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "reboot") +} + // Reboot requests that a given server reboot. // Two methods exist for rebooting a server: // -// HardReboot (aka PowerCycle) restarts the server instance by physically cutting power to the machine, or if a VM, +// HardReboot (aka PowerCycle) starts the server instance by physically cutting power to the machine, or if a VM, // terminating it at the hypervisor level. // It's done. Caput. Full stop. -// Then, after a brief while, power is restored or the VM instance restarted. +// Then, after a brief while, power is rtored or the VM instance rtarted. // -// SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedures. -// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to restart the machine. -func Reboot(client *gophercloud.ServiceClient, id string, how RebootMethod) ActionResult { - var res ActionResult - - if (how != SoftReboot) && (how != HardReboot) { - res.Err = &ErrArgument{ - Function: "Reboot", - Argument: "how", - Value: how, - } - return res +// SoftReboot (aka OSReboot) simply tells the OS to rtart under its own procedur. +// E.g., in Linux, asking it to enter runlevel 6, or executing "sudo shutdown -r now", or by asking Windows to rtart the machine. +func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder) (r ActionResult) { + b, err := opts.ToServerRebootMap() + if err != nil { + r.Err = err + return } - - reqBody := struct { - C map[string]string `json:"reboot"` - }{ - map[string]string{"type": string(how)}, - } - - _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) - return res + _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + return } // RebuildOptsBuilder is an interface that allows extensions to override the @@ -461,87 +400,63 @@ type RebuildOptsBuilder interface { // RebuildOpts represents the configuration options used in a server rebuild // operation type RebuildOpts struct { - // Required. The ID of the image you want your server to be provisioned on - ImageID string - + // The server's admin password + AdminPass string `json:"adminPass" required:"true"` + // The ID of the image you want your server to be provisioned on + ImageID string `json:"imageRef"` + ImageName string `json:"-"` + //ImageName string `json:"-"` // Name to set the server to - Name string - - // Required. The server's admin password - AdminPass string - + Name string `json:"name,omitempty"` // AccessIPv4 [optional] provides a new IPv4 address for the instance. - AccessIPv4 string - + AccessIPv4 string `json:"accessIPv4,omitempty"` // AccessIPv6 [optional] provides a new IPv6 address for the instance. - AccessIPv6 string - + AccessIPv6 string `json:"accessIPv6,omitempty"` // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the server. - Metadata map[string]string - + Metadata map[string]string `json:"metadata,omitempty"` // Personality [optional] includes files to inject into the server at launch. // Rebuild will base64-encode file contents for you. - Personality Personality + Personality Personality `json:"personality,omitempty"` + ServiceClient *gophercloud.ServiceClient `json:"-"` } // ToServerRebuildMap formats a RebuildOpts struct into a map for use in JSON func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { - var err error - server := make(map[string]interface{}) - - if opts.AdminPass == "" { - err = fmt.Errorf("AdminPass is required") - } - - if opts.ImageID == "" { - err = fmt.Errorf("ImageID is required") - } - + b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { - return server, err + return nil, err } - server["name"] = opts.Name - server["adminPass"] = opts.AdminPass - server["imageRef"] = opts.ImageID - - if opts.AccessIPv4 != "" { - server["accessIPv4"] = opts.AccessIPv4 + // If ImageRef isn't provided, check if ImageName was provided to ascertain + // the image ID. + if opts.ImageID == "" { + if opts.ImageName != "" { + if opts.ServiceClient == nil { + err := ErrNoClientProvidedForIDByName{} + err.Argument = "ServiceClient" + return nil, err + } + imageID, err := images.IDFromName(opts.ServiceClient, opts.ImageName) + if err != nil { + return nil, err + } + b["imageRef"] = imageID + } } - if opts.AccessIPv6 != "" { - server["accessIPv6"] = opts.AccessIPv6 - } - - if opts.Metadata != nil { - server["metadata"] = opts.Metadata - } - - if len(opts.Personality) > 0 { - server["personality"] = opts.Personality - } - - return map[string]interface{}{"rebuild": server}, nil + return map[string]interface{}{"rebuild": b}, nil } // Rebuild will reprovision the server according to the configuration options // provided in the RebuildOpts struct. -func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) RebuildResult { - var result RebuildResult - - if id == "" { - result.Err = fmt.Errorf("ID is required") - return result - } - - reqBody, err := opts.ToServerRebuildMap() +func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuilder) (r RebuildResult) { + b, err := opts.ToServerRebuildMap() if err != nil { - result.Err = err - return result + r.Err = err + return } - - _, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, nil) - return result + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil) + return } // ResizeOptsBuilder is an interface that allows extensions to override the default structure of @@ -553,17 +468,13 @@ type ResizeOptsBuilder interface { // ResizeOpts represents the configuration options used to control a Resize operation. type ResizeOpts struct { // FlavorRef is the ID of the flavor you wish your server to become. - FlavorRef string + FlavorRef string `json:"flavorRef" required:"true"` } // ToServerResizeMap formats a ResizeOpts as a map that can be used as a JSON request body for the // Resize request. func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { - resize := map[string]interface{}{ - "flavorRef": opts.FlavorRef, - } - - return map[string]interface{}{"resize": resize}, nil + return gophercloud.BuildRequestBody(opts, "resize") } // Resize instructs the provider to change the flavor of the server. @@ -573,37 +484,30 @@ func (opts ResizeOpts) ToServerResizeMap() (map[string]interface{}, error) { // While in this state, you can explore the use of the new server's configuration. // If you like it, call ConfirmResize() to commit the resize permanently. // Otherwise, call RevertResize() to restore the old configuration. -func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) ActionResult { - var res ActionResult - reqBody, err := opts.ToServerResizeMap() +func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder) (r ActionResult) { + b, err := opts.ToServerResizeMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - - _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) - return res + _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + return } // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. -func ConfirmResize(client *gophercloud.ServiceClient, id string) ActionResult { - var res ActionResult - - reqBody := map[string]interface{}{"confirmResize": nil} - _, res.Err = client.Post(actionURL(client, id), reqBody, nil, &gophercloud.RequestOpts{ +func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) - return res + return } // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. -func RevertResize(client *gophercloud.ServiceClient, id string) ActionResult { - var res ActionResult - reqBody := map[string]interface{}{"revertResize": nil} - _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) - return res +func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { + _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) + return } // RescueOptsBuilder is an interface that allows extensions to override the @@ -617,38 +521,26 @@ type RescueOptsBuilder interface { type RescueOpts struct { // AdminPass is the desired administrative password for the instance in // RESCUE mode. If it's left blank, the server will generate a password. - AdminPass string + AdminPass string `json:"adminPass,omitempty"` } // ToServerRescueMap formats a RescueOpts as a map that can be used as a JSON // request body for the Rescue request. func (opts RescueOpts) ToServerRescueMap() (map[string]interface{}, error) { - server := make(map[string]interface{}) - if opts.AdminPass != "" { - server["adminPass"] = opts.AdminPass - } - return map[string]interface{}{"rescue": server}, nil + return gophercloud.BuildRequestBody(opts, "rescue") } // Rescue instructs the provider to place the server into RESCUE mode. -func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) RescueResult { - var result RescueResult - - if id == "" { - result.Err = fmt.Errorf("ID is required") - return result - } - reqBody, err := opts.ToServerRescueMap() +func Rescue(client *gophercloud.ServiceClient, id string, opts RescueOptsBuilder) (r RescueResult) { + b, err := opts.ToServerRescueMap() if err != nil { - result.Err = err - return result + r.Err = err + return } - - _, result.Err = client.Post(actionURL(client, id), reqBody, &result.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - - return result + return } // ResetMetadataOptsBuilder allows extensions to add additional parameters to the @@ -674,24 +566,22 @@ func (opts MetadataOpts) ToMetadataUpdateMap() (map[string]interface{}, error) { // Note: Using this operation will erase any already-existing metadata and create // the new metadata provided. To keep any already-existing metadata, use the // UpdateMetadatas or UpdateMetadata function. -func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) ResetMetadataResult { - var res ResetMetadataResult - metadata, err := opts.ToMetadataResetMap() +func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetadataOptsBuilder) (r ResetMetadataResult) { + b, err := opts.ToMetadataResetMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - _, res.Err = client.Put(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return res + return } // Metadata requests all the metadata for the given server ID. -func Metadata(client *gophercloud.ServiceClient, id string) GetMetadataResult { - var res GetMetadataResult - _, res.Err = client.Get(metadataURL(client, id), &res.Body, nil) - return res +func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { + _, r.Err = client.Get(metadataURL(client, id), &r.Body, nil) + return } // UpdateMetadataOptsBuilder allows extensions to add additional parameters to the @@ -703,17 +593,16 @@ type UpdateMetadataOptsBuilder interface { // UpdateMetadata updates (or creates) all the metadata specified by opts for the given server ID. // This operation does not affect already-existing metadata that is not specified // by opts. -func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) UpdateMetadataResult { - var res UpdateMetadataResult - metadata, err := opts.ToMetadataUpdateMap() +func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMetadataOptsBuilder) (r UpdateMetadataResult) { + b, err := opts.ToMetadataUpdateMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - _, res.Err = client.Post(metadataURL(client, id), metadata, &res.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return res + return } // MetadatumOptsBuilder allows extensions to add additional parameters to the @@ -728,7 +617,10 @@ type MetadatumOpts map[string]string // ToMetadatumCreateMap assembles a body for a Create request based on the contents of a MetadataumOpts. func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string, error) { if len(opts) != 1 { - return nil, "", errors.New("CreateMetadatum operation must have 1 and only 1 key-value pair.") + err := gophercloud.ErrInvalidInput{} + err.Argument = "servers.MetadatumOpts" + err.Info = "Must have 1 and only 1 key-value pair" + return nil, "", err } metadatum := map[string]interface{}{"meta": opts} var key string @@ -739,134 +631,112 @@ func (opts MetadatumOpts) ToMetadatumCreateMap() (map[string]interface{}, string } // CreateMetadatum will create or update the key-value pair with the given key for the given server ID. -func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) CreateMetadatumResult { - var res CreateMetadatumResult - metadatum, key, err := opts.ToMetadatumCreateMap() +func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts MetadatumOptsBuilder) (r CreateMetadatumResult) { + b, key, err := opts.ToMetadatumCreateMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - - _, res.Err = client.Put(metadatumURL(client, id, key), metadatum, &res.Body, &gophercloud.RequestOpts{ + _, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) - return res + return } // Metadatum requests the key-value pair with the given key for the given server ID. -func Metadatum(client *gophercloud.ServiceClient, id, key string) GetMetadatumResult { - var res GetMetadatumResult - _, res.Err = client.Request("GET", metadatumURL(client, id, key), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - }) - return res +func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { + _, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil) + return } // DeleteMetadatum will delete the key-value pair with the given key for the given server ID. -func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) DeleteMetadatumResult { - var res DeleteMetadatumResult - _, res.Err = client.Delete(metadatumURL(client, id, key), nil) - return res +func DeleteMetadatum(client *gophercloud.ServiceClient, id, key string) (r DeleteMetadatumResult) { + _, r.Err = client.Delete(metadatumURL(client, id, key), nil) + return } // ListAddresses makes a request against the API to list the servers IP addresses. func ListAddresses(client *gophercloud.ServiceClient, id string) pagination.Pager { - createPageFn := func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, listAddressesURL(client, id), func(r pagination.PageResult) pagination.Page { return AddressPage{pagination.SinglePageBase(r)} - } - return pagination.NewPager(client, listAddressesURL(client, id), createPageFn) + }) } // ListAddressesByNetwork makes a request against the API to list the servers IP addresses // for the given network. func ListAddressesByNetwork(client *gophercloud.ServiceClient, id, network string) pagination.Pager { - createPageFn := func(r pagination.PageResult) pagination.Page { + return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), func(r pagination.PageResult) pagination.Page { return NetworkAddressPage{pagination.SinglePageBase(r)} - } - return pagination.NewPager(client, listAddressesByNetworkURL(client, id, network), createPageFn) -} - -type CreateImageOpts struct { - // Name [required] of the image/snapshot - Name string - // Metadata [optional] contains key-value pairs (up to 255 bytes each) to attach to the created image. - Metadata map[string]string + }) } +// CreateImageOptsBuilder is the interface types must satisfy in order to be +// used as CreateImage options type CreateImageOptsBuilder interface { ToServerCreateImageMap() (map[string]interface{}, error) } +// CreateImageOpts satisfies the CreateImageOptsBuilder +type CreateImageOpts struct { + // Name of the image/snapshot + Name string `json:"name" required:"true"` + // Metadata contains key-value pairs (up to 255 bytes each) to attach to the created image. + Metadata map[string]string `json:"metadata,omitempty"` +} + // ToServerCreateImageMap formats a CreateImageOpts structure into a request body. func (opts CreateImageOpts) ToServerCreateImageMap() (map[string]interface{}, error) { - var err error - img := make(map[string]interface{}) - if opts.Name == "" { - return nil, fmt.Errorf("Cannot create a server image without a name") - } - img["name"] = opts.Name - if opts.Metadata != nil { - img["metadata"] = opts.Metadata - } - createImage := make(map[string]interface{}) - createImage["createImage"] = img - return createImage, err + return gophercloud.BuildRequestBody(opts, "createImage") } // CreateImage makes a request against the nova API to schedule an image to be created of the server -func CreateImage(client *gophercloud.ServiceClient, serverId string, opts CreateImageOptsBuilder) CreateImageResult { - var res CreateImageResult - reqBody, err := opts.ToServerCreateImageMap() +func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageOptsBuilder) (r CreateImageResult) { + b, err := opts.ToServerCreateImageMap() if err != nil { - res.Err = err - return res + r.Err = err + return } - response, err := client.Post(actionURL(client, serverId), reqBody, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - res.Err = err - res.Header = response.Header - return res + r.Err = err + r.Header = resp.Header + return } // IDFromName is a convienience function that returns a server's ID given its name. func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - serverCount := 0 - serverID := "" - if name == "" { - return "", fmt.Errorf("A server name must be provided.") + count := 0 + id := "" + allPages, err := List(client, nil).AllPages() + if err != nil { + return "", err } - pager := List(client, nil) - pager.EachPage(func(page pagination.Page) (bool, error) { - serverList, err := ExtractServers(page) - if err != nil { - return false, err - } - for _, s := range serverList { - if s.Name == name { - serverCount++ - serverID = s.ID - } - } - return true, nil - }) + all, err := ExtractServers(allPages) + if err != nil { + return "", err + } - switch serverCount { + for _, f := range all { + if f.Name == name { + count++ + id = f.ID + } + } + + switch count { case 0: - return "", fmt.Errorf("Unable to find server: %s", name) + return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"} case 1: - return serverID, nil + return id, nil default: - return "", fmt.Errorf("Found %d servers matching %s", serverCount, name) + return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "server"} } } // GetPassword makes a request against the nova API to get the encrypted administrative password. -func GetPassword(client *gophercloud.ServiceClient, serverId string) GetPasswordResult { - var res GetPasswordResult - _, res.Err = client.Request("GET", passwordURL(client, serverId), gophercloud.RequestOpts{ - JSONResponse: &res.Body, - }) - return res +func GetPassword(client *gophercloud.ServiceClient, serverId string) (r GetPasswordResult) { + _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) + return } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go similarity index 70% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go index 406f68935..a23923a76 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/results.go @@ -3,14 +3,13 @@ package servers import ( "crypto/rsa" "encoding/base64" + "encoding/json" "fmt" "net/url" "path" - "reflect" - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) type serverResult struct { @@ -19,29 +18,11 @@ type serverResult struct { // Extract interprets any serverResult as a Server, if possible. func (r serverResult) Extract() (*Server, error) { - if r.Err != nil { - return nil, r.Err + var s struct { + Server *Server `json:"server"` } - - var response struct { - Server Server `mapstructure:"server"` - } - - config := &mapstructure.DecoderConfig{ - DecodeHook: toMapFromString, - Result: &response, - } - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return nil, err - } - - err = decoder.Decode(r.Body) - if err != nil { - return nil, err - } - - return &response.Server, nil + err := r.ExtractInto(&s) + return s.Server, err } // CreateResult temporarily contains the response from a Create call. @@ -94,20 +75,14 @@ type GetPasswordResult struct { // If privateKey == nil the encrypted password is returned and can be decrypted with: // echo '' | base64 -D | openssl rsautl -decrypt -inkey func (r GetPasswordResult) ExtractPassword(privateKey *rsa.PrivateKey) (string, error) { - - if r.Err != nil { - return "", r.Err + var s struct { + Password string `json:"password"` } - - var response struct { - Password string `mapstructure:"password"` + err := r.ExtractInto(&s) + if err == nil && privateKey != nil && s.Password != "" { + return decryptPassword(s.Password, privateKey) } - - err := mapstructure.Decode(r.Body, &response) - if err == nil && privateKey != nil && response.Password != "" { - return decryptPassword(response.Password, privateKey) - } - return response.Password, err + return s.Password, err } func decryptPassword(encryptedPassword string, privateKey *rsa.PrivateKey) (string, error) { @@ -133,83 +108,88 @@ func (res CreateImageResult) ExtractImageID() (string, error) { // Get the image id from the header u, err := url.ParseRequestURI(res.Header.Get("Location")) if err != nil { - return "", fmt.Errorf("Failed to parse the image id: %s", err.Error()) + return "", err } - imageId := path.Base(u.Path) - if imageId == "." || imageId == "/" { + imageID := path.Base(u.Path) + if imageID == "." || imageID == "/" { return "", fmt.Errorf("Failed to parse the ID of newly created image: %s", u) } - return imageId, nil + return imageID, nil } // Extract interprets any RescueResult as an AdminPass, if possible. func (r RescueResult) Extract() (string, error) { - if r.Err != nil { - return "", r.Err + var s struct { + AdminPass string `json:"adminPass"` } - - var response struct { - AdminPass string `mapstructure:"adminPass"` - } - - err := mapstructure.Decode(r.Body, &response) - return response.AdminPass, err + err := r.ExtractInto(&s) + return s.AdminPass, err } // Server exposes only the standard OpenStack fields corresponding to a given server on the user's account. type Server struct { // ID uniquely identifies this server amongst all other servers, including those not accessible to the current tenant. - ID string - + ID string `json:"id"` // TenantID identifies the tenant owning this server resource. - TenantID string `mapstructure:"tenant_id"` - + TenantID string `json:"tenant_id"` // UserID uniquely identifies the user account owning the tenant. - UserID string `mapstructure:"user_id"` - + UserID string `json:"user_id"` // Name contains the human-readable name for the server. - Name string - + 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 - + HostID string // Status contains the current operational status of the server, such as IN_PROGRESS or ACTIVE. Status string - // Progress ranges from 0..100. // A request made against the server completes only once Progress reaches 100. Progress int - // AccessIPv4 and AccessIPv6 contain the IP addresses of the server, suitable for remote access for administration. AccessIPv4, AccessIPv6 string - // Image refers to a JSON object, which itself indicates the OS image used to deploy the server. Image map[string]interface{} - // Flavor refers to a JSON object, which itself indicates the hardware configuration of the deployed server. Flavor map[string]interface{} - // Addresses includes a list of all IP addresses assigned to the server, keyed by pool. Addresses map[string]interface{} - // Metadata includes a list of all user-specified key-value pairs attached to the server. - Metadata map[string]interface{} - + Metadata map[string]string // Links includes HTTP references to the itself, useful for passing along to other APIs that might want a server reference. Links []interface{} - // KeyName indicates which public key was injected into the server on launch. - KeyName string `json:"key_name" mapstructure:"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. // Note that this is the ONLY time this field will be valid. - AdminPass string `json:"adminPass" mapstructure:"adminPass"` - + AdminPass string `json:"adminPass"` // SecurityGroups includes the security groups that this instance has applied to it - SecurityGroups []map[string]interface{} `json:"security_groups" mapstructure:"security_groups"` + SecurityGroups []map[string]interface{} `json:"security_groups"` +} + +func (s *Server) UnmarshalJSON(b []byte) error { + type tmp Server + var server *struct { + tmp + Image interface{} + } + err := json.Unmarshal(b, &server) + if err != nil { + return err + } + + *s = Server(server.tmp) + + switch t := server.Image.(type) { + case map[string]interface{}: + s.Image = t + case string: + switch t { + case "": + s.Image = nil + } + } + + return nil } // ServerPage abstracts the raw results of making a List() request against the API. @@ -222,47 +202,28 @@ type ServerPage struct { // IsEmpty returns true if a page contains no Server results. func (page ServerPage) IsEmpty() (bool, error) { servers, err := ExtractServers(page) - if err != nil { - return true, err - } - return len(servers) == 0, nil + return len(servers) == 0, err } // NextPageURL uses the response's embedded link reference to navigate to the next page of results. func (page ServerPage) NextPageURL() (string, error) { - type resp struct { - Links []gophercloud.Link `mapstructure:"servers_links"` + var s struct { + Links []gophercloud.Link `json:"servers_links"` } - - var r resp - err := mapstructure.Decode(page.Body, &r) + err := page.ExtractInto(&s) if err != nil { return "", err } - - return gophercloud.ExtractNextURL(r.Links) + return gophercloud.ExtractNextURL(s.Links) } // ExtractServers interprets the results of a single page from a List() call, producing a slice of Server entities. -func ExtractServers(page pagination.Page) ([]Server, error) { - casted := page.(ServerPage).Body - - var response struct { - Servers []Server `mapstructure:"servers"` +func ExtractServers(r pagination.Page) ([]Server, error) { + var s struct { + Servers []Server `json:"servers"` } - - config := &mapstructure.DecoderConfig{ - DecodeHook: toMapFromString, - Result: &response, - } - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return nil, err - } - - err = decoder.Decode(casted) - - return response.Servers, err + err := (r.(ServerPage)).ExtractInto(&s) + return s.Servers, err } // MetadataResult contains the result of a call for (potentially) multiple key-value pairs. @@ -307,43 +268,26 @@ type DeleteMetadatumResult struct { // Extract interprets any MetadataResult as a Metadata, if possible. func (r MetadataResult) Extract() (map[string]string, error) { - if r.Err != nil { - return nil, r.Err + var s struct { + Metadata map[string]string `json:"metadata"` } - - var response struct { - Metadata map[string]string `mapstructure:"metadata"` - } - - err := mapstructure.Decode(r.Body, &response) - return response.Metadata, err + err := r.ExtractInto(&s) + return s.Metadata, err } // Extract interprets any MetadatumResult as a Metadatum, if possible. func (r MetadatumResult) Extract() (map[string]string, error) { - if r.Err != nil { - return nil, r.Err + var s struct { + Metadatum map[string]string `json:"meta"` } - - var response struct { - Metadatum map[string]string `mapstructure:"meta"` - } - - err := mapstructure.Decode(r.Body, &response) - return response.Metadatum, err -} - -func toMapFromString(from reflect.Kind, to reflect.Kind, data interface{}) (interface{}, error) { - if (from == reflect.String) && (to == reflect.Map) { - return map[string]interface{}{}, nil - } - return data, nil + err := r.ExtractInto(&s) + return s.Metadatum, err } // Address represents an IP address. type Address struct { - Version int `mapstructure:"version"` - Address string `mapstructure:"addr"` + Version int `json:"version"` + Address string `json:"addr"` } // AddressPage abstracts the raw results of making a ListAddresses() request against the API. @@ -356,27 +300,17 @@ type AddressPage struct { // IsEmpty returns true if an AddressPage contains no networks. func (r AddressPage) IsEmpty() (bool, error) { addresses, err := ExtractAddresses(r) - if err != nil { - return true, err - } - return len(addresses) == 0, nil + return len(addresses) == 0, err } // ExtractAddresses interprets the results of a single page from a ListAddresses() call, // producing a map of addresses. -func ExtractAddresses(page pagination.Page) (map[string][]Address, error) { - casted := page.(AddressPage).Body - - var response struct { - Addresses map[string][]Address `mapstructure:"addresses"` +func ExtractAddresses(r pagination.Page) (map[string][]Address, error) { + var s struct { + Addresses map[string][]Address `json:"addresses"` } - - err := mapstructure.Decode(casted, &response) - if err != nil { - return nil, err - } - - return response.Addresses, err + err := (r.(AddressPage)).ExtractInto(&s) + return s.Addresses, err } // NetworkAddressPage abstracts the raw results of making a ListAddressesByNetwork() request against the API. @@ -389,27 +323,22 @@ type NetworkAddressPage struct { // IsEmpty returns true if a NetworkAddressPage contains no addresses. func (r NetworkAddressPage) IsEmpty() (bool, error) { addresses, err := ExtractNetworkAddresses(r) - if err != nil { - return true, err - } - return len(addresses) == 0, nil + return len(addresses) == 0, err } // ExtractNetworkAddresses interprets the results of a single page from a ListAddressesByNetwork() call, // producing a slice of addresses. -func ExtractNetworkAddresses(page pagination.Page) ([]Address, error) { - casted := page.(NetworkAddressPage).Body - - var response map[string][]Address - err := mapstructure.Decode(casted, &response) +func ExtractNetworkAddresses(r pagination.Page) ([]Address, error) { + var s map[string][]Address + err := (r.(NetworkAddressPage)).ExtractInto(&s) if err != nil { return nil, err } var key string - for k := range response { + for k := range s { key = k } - return response[key], err + return s[key], err } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go similarity index 97% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go index d51fcbe6c..e892e8d92 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/urls.go @@ -1,6 +1,6 @@ package servers -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" func createURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("servers") diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/util.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go similarity index 91% rename from vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/util.go rename to vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go index e6baf7416..494a0e4dc 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/util.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/util.go @@ -1,6 +1,6 @@ package servers -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" // WaitForStatus will continually poll a server until it successfully transitions to a specified // status. It will do this for at most the number of seconds specified. diff --git a/vendor/github.com/rackspace/gophercloud/openstack/endpoint_location.go b/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go similarity index 81% rename from vendor/github.com/rackspace/gophercloud/openstack/endpoint_location.go rename to vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go index 29d02c43f..ea37f5b27 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/endpoint_location.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go @@ -1,11 +1,9 @@ package openstack import ( - "fmt" - - "github.com/rackspace/gophercloud" - tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens" - tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud" + tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" ) // V2EndpointURL discovers the endpoint URL for a specific service from a ServiceCatalog acquired @@ -29,7 +27,9 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt // Report an error if the options were ambiguous. if len(endpoints) > 1 { - return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints) + err := &ErrMultipleMatchingEndpointsV2{} + err.Endpoints = endpoints + return "", err } // Extract the appropriate URL from the matching Endpoint. @@ -42,12 +42,16 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt case gophercloud.AvailabilityAdmin: return gophercloud.NormalizeURL(endpoint.AdminURL), nil default: - return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability) + err := &ErrInvalidAvailabilityProvided{} + err.Argument = "Availability" + err.Value = opts.Availability + return "", err } } // Report an error if there were no matching endpoints. - return "", gophercloud.ErrEndpointNotFound + err := &gophercloud.ErrEndpointNotFound{} + return "", err } // V3EndpointURL discovers the endpoint URL for a specific service from a Catalog acquired @@ -66,7 +70,10 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt if opts.Availability != gophercloud.AvailabilityAdmin && opts.Availability != gophercloud.AvailabilityPublic && opts.Availability != gophercloud.AvailabilityInternal { - return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability) + err := &ErrInvalidAvailabilityProvided{} + err.Argument = "Availability" + err.Value = opts.Availability + return "", err } if (opts.Availability == gophercloud.Availability(endpoint.Interface)) && (opts.Region == "" || endpoint.Region == opts.Region) { @@ -78,7 +85,7 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt // Report an error if the options were ambiguous. if len(endpoints) > 1 { - return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints) + return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints} } // Extract the URL from the matching Endpoint. @@ -87,5 +94,6 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt } // Report an error if there were no matching endpoints. - return "", gophercloud.ErrEndpointNotFound + err := &gophercloud.ErrEndpointNotFound{} + return "", err } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go b/vendor/github.com/gophercloud/gophercloud/openstack/errors.go new file mode 100644 index 000000000..df410b1c6 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/errors.go @@ -0,0 +1,71 @@ +package openstack + +import ( + "fmt" + + "github.com/gophercloud/gophercloud" + tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" +) + +// ErrEndpointNotFound is the error when no suitable endpoint can be found +// in the user's catalog +type ErrEndpointNotFound struct{ gophercloud.BaseError } + +func (e ErrEndpointNotFound) Error() string { + return "No suitable endpoint could be found in the service catalog." +} + +// ErrInvalidAvailabilityProvided is the error when an invalid endpoint +// availability is provided +type ErrInvalidAvailabilityProvided struct{ gophercloud.ErrInvalidInput } + +func (e ErrInvalidAvailabilityProvided) Error() string { + return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value) +} + +// ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint +// for the given options is found in the v2 catalog +type ErrMultipleMatchingEndpointsV2 struct { + gophercloud.BaseError + Endpoints []tokens2.Endpoint +} + +func (e ErrMultipleMatchingEndpointsV2) Error() string { + return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) +} + +// ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint +// for the given options is found in the v3 catalog +type ErrMultipleMatchingEndpointsV3 struct { + gophercloud.BaseError + Endpoints []tokens3.Endpoint +} + +func (e ErrMultipleMatchingEndpointsV3) Error() string { + return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints) +} + +// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not +// found +type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput } + +func (e ErrNoAuthURL) Error() string { + return "Environment variable OS_AUTH_URL needs to be set." +} + +// ErrNoUsername is the error when the OS_USERNAME environment variable is not +// found +type ErrNoUsername struct{ gophercloud.ErrInvalidInput } + +func (e ErrNoUsername) Error() string { + return "Environment variable OS_USERNAME needs to be set." +} + +// ErrNoPassword is the error when the OS_PASSWORD environment variable is not +// found +type ErrNoPassword struct{ gophercloud.ErrInvalidInput } + +func (e ErrNoPassword) Error() string { + return "Environment variable OS_PASSWORD needs to be set." +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/doc.go diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go similarity index 76% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/requests.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go index 5a359f5c9..b9d7de65f 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go @@ -1,25 +1,20 @@ package tenants import ( - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" ) // ListOpts filters the Tenants that are returned by the List call. type ListOpts struct { // Marker is the ID of the last Tenant on the previous page. Marker string `q:"marker"` - // Limit specifies the page size. Limit int `q:"limit"` } // List enumerates the Tenants to which the current token has access. func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { - createPage := func(r pagination.PageResult) pagination.Page { - return TenantPage{pagination.LinkedPageBase{PageResult: r}} - } - url := listURL(client) if opts != nil { q, err := gophercloud.BuildQueryString(opts) @@ -28,6 +23,7 @@ func List(client *gophercloud.ServiceClient, opts *ListOpts) pagination.Pager { } url += q.String() } - - return pagination.NewPager(client, url, createPage) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return TenantPage{pagination.LinkedPageBase{PageResult: r}} + }) } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go new file mode 100644 index 000000000..3ce1e6773 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/results.go @@ -0,0 +1,53 @@ +package tenants + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Tenant is a grouping of users in the identity service. +type Tenant struct { + // ID is a unique identifier for this tenant. + ID string `json:"id"` + + // Name is a friendlier user-facing name for this tenant. + Name string `json:"name"` + + // Description is a human-readable explanation of this Tenant's purpose. + Description string `json:"description"` + + // Enabled indicates whether or not a tenant is active. + Enabled bool `json:"enabled"` +} + +// TenantPage is a single page of Tenant results. +type TenantPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a page of Tenants contains any results. +func (r TenantPage) IsEmpty() (bool, error) { + tenants, err := ExtractTenants(r) + return len(tenants) == 0, err +} + +// NextPageURL extracts the "next" link from the tenants_links section of the result. +func (r TenantPage) NextPageURL() (string, error) { + var s struct { + Links []gophercloud.Link `json:"tenants_links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return gophercloud.ExtractNextURL(s.Links) +} + +// ExtractTenants returns a slice of Tenants contained in a single page of results. +func ExtractTenants(r pagination.Page) ([]Tenant, error) { + var s struct { + Tenants []Tenant `json:"tenants"` + } + err := (r.(TenantPage)).ExtractInto(&s) + return s.Tenants, err +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go similarity index 72% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go index 1dd6ce023..101599bc9 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/urls.go @@ -1,6 +1,6 @@ package tenants -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" func listURL(client *gophercloud.ServiceClient) string { return client.ServiceURL("tenants") diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/doc.go diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go new file mode 100644 index 000000000..4983031e7 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go @@ -0,0 +1,99 @@ +package tokens + +import "github.com/gophercloud/gophercloud" + +type PasswordCredentialsV2 struct { + Username string `json:"username" required:"true"` + Password string `json:"password" required:"true"` +} + +type TokenCredentialsV2 struct { + ID string `json:"id,omitempty" required:"true"` +} + +// AuthOptionsV2 wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder +// interface. +type AuthOptionsV2 struct { + PasswordCredentials *PasswordCredentialsV2 `json:"passwordCredentials,omitempty" xor:"TokenCredentials"` + + // The TenantID and TenantName fields are optional for the Identity V2 API. + // Some providers allow you to specify a TenantName instead of the TenantId. + // Some require both. Your provider's authentication policies will determine + // how these fields influence authentication. + TenantID string `json:"tenantId,omitempty"` + TenantName string `json:"tenantName,omitempty"` + + // TokenCredentials allows users to authenticate (possibly as another user) with an + // authentication token ID. + TokenCredentials *TokenCredentialsV2 `json:"token,omitempty" xor:"PasswordCredentials"` +} + +// AuthOptionsBuilder describes any argument that may be passed to the Create call. +type AuthOptionsBuilder interface { + // ToTokenCreateMap assembles the Create request body, returning an error if parameters are + // missing or inconsistent. + ToTokenV2CreateMap() (map[string]interface{}, error) +} + +// AuthOptions are the valid options for Openstack Identity v2 authentication. +// For field descriptions, see gophercloud.AuthOptions. +type AuthOptions struct { + IdentityEndpoint string `json:"-"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + TenantID string `json:"tenantId,omitempty"` + TenantName string `json:"tenantName,omitempty"` + AllowReauth bool `json:"-"` + TokenID string +} + +// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v2 tokens package +func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { + v2Opts := AuthOptionsV2{ + TenantID: opts.TenantID, + TenantName: opts.TenantName, + } + + if opts.Password != "" { + v2Opts.PasswordCredentials = &PasswordCredentialsV2{ + Username: opts.Username, + Password: opts.Password, + } + } else { + v2Opts.TokenCredentials = &TokenCredentialsV2{ + ID: opts.TokenID, + } + } + + b, err := gophercloud.BuildRequestBody(v2Opts, "auth") + if err != nil { + return nil, err + } + return b, nil +} + +// Create authenticates to the identity service and attempts to acquire a Token. +// If successful, the CreateResult +// Generally, rather than interact with this call directly, end users should call openstack.AuthenticatedClient(), +// which abstracts all of the gory details about navigating service catalogs and such. +func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r CreateResult) { + b, err := auth.ToTokenV2CreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + }) + return +} + +// Get validates and retrieves information for user's token. +func Get(client *gophercloud.ServiceClient, token string) (r GetResult) { + _, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 203}, + }) + return +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go similarity index 61% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go index 67c577b8d..93c0554ae 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/results.go @@ -3,9 +3,8 @@ package tokens import ( "time" - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack/identity/v2/tenants" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants" ) // Token provides only the most basic information related to an authentication token. @@ -25,15 +24,17 @@ type Token struct { Tenant tenants.Tenant } -// Authorization need user info which can get from token authentication's response +// Role is a role for a user. type Role struct { - Name string `mapstructure:"name"` + Name string `json:"name"` } + +// User is an OpenStack user. type User struct { - ID string `mapstructure:"id"` - Name string `mapstructure:"name"` - UserName string `mapstructure:"username"` - Roles []Role `mapstructure:"roles"` + ID string `json:"id"` + Name string `json:"name"` + UserName string `json:"username"` + Roles []Role `json:"roles"` } // Endpoint represents a single API endpoint offered by a service. @@ -45,14 +46,14 @@ type User struct { // // In all cases, fields which aren't supported by the provider and service combined will assume a zero-value (""). type Endpoint struct { - TenantID string `mapstructure:"tenantId"` - PublicURL string `mapstructure:"publicURL"` - InternalURL string `mapstructure:"internalURL"` - AdminURL string `mapstructure:"adminURL"` - Region string `mapstructure:"region"` - VersionID string `mapstructure:"versionId"` - VersionInfo string `mapstructure:"versionInfo"` - VersionList string `mapstructure:"versionList"` + TenantID string `json:"tenantId"` + PublicURL string `json:"publicURL"` + InternalURL string `json:"internalURL"` + AdminURL string `json:"adminURL"` + Region string `json:"region"` + VersionID string `json:"versionId"` + VersionInfo string `json:"versionInfo"` + VersionList string `json:"versionList"` } // CatalogEntry provides a type-safe interface to an Identity API V2 service catalog listing. @@ -63,15 +64,15 @@ type Endpoint struct { // Otherwise, you'll tie the representation of the service to a specific provider. type CatalogEntry struct { // Name will contain the provider-specified name for the service. - Name string `mapstructure:"name"` + Name string `json:"name"` // Type will contain a type string if OpenStack defines a type for the service. // Otherwise, for provider-specific services, the provider may assign their own type strings. - Type string `mapstructure:"type"` + Type string `json:"type"` // Endpoints will let the caller iterate over all the different endpoints that may exist for // the service. - Endpoints []Endpoint `mapstructure:"endpoints"` + Endpoints []Endpoint `json:"endpoints"` } // ServiceCatalog provides a view into the service catalog from a previous, successful authentication. @@ -92,56 +93,43 @@ type GetResult struct { } // ExtractToken returns the just-created Token from a CreateResult. -func (result CreateResult) ExtractToken() (*Token, error) { - if result.Err != nil { - return nil, result.Err - } - - var response struct { +func (r CreateResult) ExtractToken() (*Token, error) { + var s struct { Access struct { Token struct { - Expires string `mapstructure:"expires"` - ID string `mapstructure:"id"` - Tenant tenants.Tenant `mapstructure:"tenant"` - } `mapstructure:"token"` - } `mapstructure:"access"` + Expires string `json:"expires"` + ID string `json:"id"` + Tenant tenants.Tenant `json:"tenant"` + } `json:"token"` + } `json:"access"` } - err := mapstructure.Decode(result.Body, &response) + err := r.ExtractInto(&s) if err != nil { return nil, err } - expiresTs, err := time.Parse(gophercloud.RFC3339Milli, response.Access.Token.Expires) + expiresTs, err := time.Parse(gophercloud.RFC3339Milli, s.Access.Token.Expires) if err != nil { return nil, err } return &Token{ - ID: response.Access.Token.ID, + ID: s.Access.Token.ID, ExpiresAt: expiresTs, - Tenant: response.Access.Token.Tenant, + Tenant: s.Access.Token.Tenant, }, nil } // ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. -func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { - if result.Err != nil { - return nil, result.Err - } - - var response struct { +func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { + var s struct { Access struct { - Entries []CatalogEntry `mapstructure:"serviceCatalog"` - } `mapstructure:"access"` + Entries []CatalogEntry `json:"serviceCatalog"` + } `json:"access"` } - - err := mapstructure.Decode(result.Body, &response) - if err != nil { - return nil, err - } - - return &ServiceCatalog{Entries: response.Access.Entries}, nil + err := r.ExtractInto(&s) + return &ServiceCatalog{Entries: s.Access.Entries}, err } // createErr quickly packs an error in a CreateResult. @@ -150,21 +138,12 @@ func createErr(err error) CreateResult { } // ExtractUser returns the User from a GetResult. -func (result GetResult) ExtractUser() (*User, error) { - if result.Err != nil { - return nil, result.Err - } - - var response struct { +func (r GetResult) ExtractUser() (*User, error) { + var s struct { Access struct { - User User `mapstructure:"user"` - } `mapstructure:"access"` + User User `json:"user"` + } `json:"access"` } - - err := mapstructure.Decode(result.Body, &response) - if err != nil { - return nil, err - } - - return &response.Access.User, nil + err := r.ExtractInto(&s) + return &s.Access.User, err } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go similarity index 88% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go index ee1393299..ee0a28f20 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/urls.go @@ -1,6 +1,6 @@ package tokens -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" // CreateURL generates the URL used to create new Tokens. func CreateURL(client *gophercloud.ServiceClient) string { diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/doc.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/doc.go diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go new file mode 100644 index 000000000..ba4363b2b --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go @@ -0,0 +1,200 @@ +package tokens + +import "github.com/gophercloud/gophercloud" + +// Scope allows a created token to be limited to a specific domain or project. +type Scope struct { + ProjectID string `json:"scope.project.id,omitempty" not:"ProjectName,DomainID,DomainName"` + ProjectName string `json:"scope.project.name,omitempty"` + DomainID string `json:"scope.project.id,omitempty" not:"ProjectName,ProjectID,DomainName"` + DomainName string `json:"scope.project.id,omitempty"` +} + +// AuthOptionsBuilder describes any argument that may be passed to the Create call. +type AuthOptionsBuilder interface { + // ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are + // missing or inconsistent. + ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) + ToTokenV3ScopeMap() (map[string]interface{}, error) + CanReauth() bool +} + +type AuthOptions struct { + // IdentityEndpoint specifies the HTTP endpoint that is required to work with + // the Identity API of the appropriate version. While it's ultimately needed by + // all of the identity services, it will often be populated by a provider-level + // function. + IdentityEndpoint string `json:"-"` + + // Username is required if using Identity V2 API. Consult with your provider's + // control panel to discover your account's username. In Identity V3, either + // UserID or a combination of Username and DomainID or DomainName are needed. + Username string `json:"username,omitempty"` + UserID string `json:"id,omitempty"` + + Password string `json:"password,omitempty"` + + // At most one of DomainID and DomainName must be provided if using Username + // with Identity V3. Otherwise, either are optional. + DomainID string `json:"id,omitempty"` + DomainName string `json:"name,omitempty"` + + // AllowReauth should be set to true if you grant permission for Gophercloud to + // cache your credentials in memory, and to allow Gophercloud to attempt to + // re-authenticate automatically if/when your token expires. If you set it to + // false, it will not cache these settings, but re-authentication will not be + // possible. This setting defaults to false. + AllowReauth bool `json:"-"` + + // TokenID allows users to authenticate (possibly as another user) with an + // authentication token ID. + TokenID string `json:"-"` + + Scope Scope `json:"-"` +} + +func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { + gophercloudAuthOpts := gophercloud.AuthOptions{ + Username: opts.Username, + UserID: opts.UserID, + Password: opts.Password, + DomainID: opts.DomainID, + DomainName: opts.DomainName, + AllowReauth: opts.AllowReauth, + TokenID: opts.TokenID, + } + + return gophercloudAuthOpts.ToTokenV3CreateMap(scope) +} + +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { + if opts.Scope.ProjectName != "" { + // ProjectName provided: either DomainID or DomainName must also be supplied. + // ProjectID may not be supplied. + if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" { + return nil, gophercloud.ErrScopeDomainIDOrDomainName{} + } + if opts.Scope.ProjectID != "" { + return nil, gophercloud.ErrScopeProjectIDOrProjectName{} + } + + if opts.Scope.DomainID != "" { + // ProjectName + DomainID + return map[string]interface{}{ + "project": map[string]interface{}{ + "name": &opts.Scope.ProjectName, + "domain": map[string]interface{}{"id": &opts.Scope.DomainID}, + }, + }, nil + } + + if opts.Scope.DomainName != "" { + // ProjectName + DomainName + return map[string]interface{}{ + "project": map[string]interface{}{ + "name": &opts.Scope.ProjectName, + "domain": map[string]interface{}{"name": &opts.Scope.DomainName}, + }, + }, nil + } + } else if opts.Scope.ProjectID != "" { + // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. + if opts.Scope.DomainID != "" { + return nil, gophercloud.ErrScopeProjectIDAlone{} + } + if opts.Scope.DomainName != "" { + return nil, gophercloud.ErrScopeProjectIDAlone{} + } + + // ProjectID + return map[string]interface{}{ + "project": map[string]interface{}{ + "id": &opts.Scope.ProjectID, + }, + }, nil + } else if opts.Scope.DomainID != "" { + // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. + if opts.Scope.DomainName != "" { + return nil, gophercloud.ErrScopeDomainIDOrDomainName{} + } + + // DomainID + return map[string]interface{}{ + "domain": map[string]interface{}{ + "id": &opts.Scope.DomainID, + }, + }, nil + } else if opts.Scope.DomainName != "" { + return nil, gophercloud.ErrScopeDomainName{} + } + + return nil, nil +} + +func (opts *AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { + return map[string]string{ + "X-Subject-Token": subjectToken, + } +} + +// Create authenticates and either generates a new token, or changes the Scope of an existing token. +func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) { + scope, err := opts.ToTokenV3ScopeMap() + if err != nil { + r.Err = err + return + } + + b, err := opts.ToTokenV3CreateMap(scope) + if err != nil { + r.Err = err + return + } + + resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + }) + r.Err = err + if resp != nil { + r.Header = resp.Header + } + return +} + +// Get validates and retrieves information about another token. +func Get(c *gophercloud.ServiceClient, token string) (r GetResult) { + resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: subjectTokenHeaders(c, token), + OkCodes: []int{200, 203}, + }) + if resp != nil { + r.Err = err + r.Header = resp.Header + } + return +} + +// Validate determines if a specified token is valid or not. +func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { + resp, err := c.Request("HEAD", tokenURL(c), &gophercloud.RequestOpts{ + MoreHeaders: subjectTokenHeaders(c, token), + OkCodes: []int{204, 404}, + }) + if err != nil { + return false, err + } + + return resp.StatusCode == 204, nil +} + +// Revoke immediately makes specified token invalid. +func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) { + _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ + MoreHeaders: subjectTokenHeaders(c, token), + }) + return +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go similarity index 69% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go index d134f7d4d..36c9ce619 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go @@ -1,21 +1,17 @@ package tokens -import ( - "time" - - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" -) +import "errors" +import "github.com/gophercloud/gophercloud" // Endpoint represents a single API endpoint offered by a service. // It matches either a public, internal or admin URL. // If supported, it contains a region specifier, again if provided. // The significance of the Region field will depend upon your provider. type Endpoint struct { - ID string `mapstructure:"id"` - Region string `mapstructure:"region"` - Interface string `mapstructure:"interface"` - URL string `mapstructure:"url"` + ID string `json:"id"` + Region string `json:"region"` + Interface string `json:"interface"` + URL string `json:"url"` } // CatalogEntry provides a type-safe interface to an Identity API V3 service catalog listing. @@ -25,20 +21,16 @@ type Endpoint struct { // Note: when looking for the desired service, try, whenever possible, to key off the type field. // Otherwise, you'll tie the representation of the service to a specific provider. type CatalogEntry struct { - // Service ID - ID string `mapstructure:"id"` - + ID string `json:"id"` // Name will contain the provider-specified name for the service. - Name string `mapstructure:"name"` - + Name string `json:"name"` // Type will contain a type string if OpenStack defines a type for the service. // Otherwise, for provider-specific services, the provider may assign their own type strings. - Type string `mapstructure:"type"` - + Type string `json:"type"` // Endpoints will let the caller iterate over all the different endpoints that may exist for // the service. - Endpoints []Endpoint `mapstructure:"endpoints"` + Endpoints []Endpoint `json:"endpoints"` } // ServiceCatalog provides a view into the service catalog from a previous, successful authentication. @@ -59,50 +51,34 @@ func (r commonResult) Extract() (*Token, error) { // ExtractToken interprets a commonResult as a Token. func (r commonResult) ExtractToken() (*Token, error) { - if r.Err != nil { - return nil, r.Err + var s struct { + Token *Token `json:"token"` } - var response struct { - Token struct { - ExpiresAt string `mapstructure:"expires_at"` - } `mapstructure:"token"` - } - - var token Token - - // Parse the token itself from the stored headers. - token.ID = r.Header.Get("X-Subject-Token") - - err := mapstructure.Decode(r.Body, &response) + err := r.ExtractInto(&s) if err != nil { return nil, err } - // Attempt to parse the timestamp. - token.ExpiresAt, err = time.Parse(gophercloud.RFC3339Milli, response.Token.ExpiresAt) + if s.Token == nil { + return nil, errors.New("'token' missing in JSON response") + } - return &token, err + // Parse the token itself from the stored headers. + s.Token.ID = r.Header.Get("X-Subject-Token") + + return s.Token, err } // ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token. -func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { - if result.Err != nil { - return nil, result.Err - } - - var response struct { +func (r CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) { + var s struct { Token struct { - Entries []CatalogEntry `mapstructure:"catalog"` - } `mapstructure:"token"` + Entries []CatalogEntry `json:"catalog"` + } `json:"token"` } - - err := mapstructure.Decode(result.Body, &response) - if err != nil { - return nil, err - } - - return &ServiceCatalog{Entries: response.Token.Entries}, nil + err := r.ExtractInto(&s) + return &ServiceCatalog{Entries: s.Token.Entries}, err } // CreateResult defers the interpretation of a created token. @@ -132,8 +108,7 @@ type RevokeResult struct { // Each Token is valid for a set length of time. type Token struct { // ID is the issued token. - ID string - + ID string `json:"id"` // ExpiresAt is the timestamp at which this token will no longer be accepted. - ExpiresAt time.Time + ExpiresAt gophercloud.JSONRFC3339Milli `json:"expires_at"` } diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go similarity index 71% rename from vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/urls.go rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go index 360b60a82..2f864a31c 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/urls.go @@ -1,6 +1,6 @@ package tokens -import "github.com/rackspace/gophercloud" +import "github.com/gophercloud/gophercloud" func tokenURL(c *gophercloud.ServiceClient) string { return c.ServiceURL("auth", "tokens") diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go new file mode 100644 index 000000000..32f09ee95 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go @@ -0,0 +1,238 @@ +package images + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// ListOptsBuilder allows extensions to add additional parameters to the +// List request. +type ListOptsBuilder interface { + ToImageListQuery() (string, error) +} + +// ListOpts allows the filtering and sorting of paginated collections through +// the API. Filtering is achieved by passing in struct field values that map to +// the server attributes you want to see returned. Marker and Limit are used +// for pagination. +//http://developer.openstack.org/api-ref-image-v2.html +type ListOpts struct { + // Integer value for the limit of values to return. + Limit int `q:"limit"` + + // UUID of the server at which you want to set a marker. + Marker string `q:"marker"` + + Name string `q:"name"` + Visibility ImageVisibility `q:"visibility"` + MemberStatus ImageMemberStatus `q:"member_status"` + Owner string `q:"owner"` + Status ImageStatus `q:"status"` + SizeMin int64 `q:"size_min"` + SizeMax int64 `q:"size_max"` + SortKey string `q:"sort_key"` + SortDir string `q:"sort_dir"` + Tag string `q:"tag"` +} + +// ToImageListQuery formats a ListOpts into a query string. +func (opts ListOpts) ToImageListQuery() (string, error) { + q, err := gophercloud.BuildQueryString(opts) + return q.String(), err +} + +// List implements image list request +func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { + url := listURL(c) + if opts != nil { + query, err := opts.ToImageListQuery() + if err != nil { + return pagination.Pager{Err: err} + } + url += query + } + return pagination.NewPager(c, url, func(r pagination.PageResult) pagination.Page { + return ImagePage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// CreateOptsBuilder describes struct types that can be accepted by the Create call. +// The CreateOpts struct in this package does. +type CreateOptsBuilder interface { + // Returns value that can be passed to json.Marshal + ToImageCreateMap() (map[string]interface{}, error) +} + +// CreateOpts implements CreateOptsBuilder +type CreateOpts struct { + // Name is the name of the new image. + Name string `json:"name" required:"true"` + + // Id is the the image ID. + ID string `json:"id,omitempty"` + + // Visibility defines who can see/use the image. + Visibility *ImageVisibility `json:"visibility,omitempty"` + + // Tags is a set of image tags. + Tags []string `json:"tags,omitempty"` + + // ContainerFormat is the format of the + // container. Valid values are ami, ari, aki, bare, and ovf. + ContainerFormat string `json:"container_format,omitempty"` + + // DiskFormat is the format of the disk. If set, + // valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, + // and iso. + DiskFormat string `json:"disk_format,omitempty"` + + // MinDisk is the amount of disk space in + // GB that is required to boot the image. + MinDisk int `json:"min_disk,omitempty"` + + // MinRAM is the amount of RAM in MB that + // is required to boot the image. + MinRAM int `json:"min_ram,omitempty"` + + // protected is whether the image is not deletable. + Protected *bool `json:"protected,omitempty"` + + // properties is a set of properties, if any, that + // are associated with the image. + Properties map[string]string `json:"-,omitempty"` +} + +// ToImageCreateMap assembles a request body based on the contents of +// a CreateOpts. +func (opts CreateOpts) ToImageCreateMap() (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "") + if err != nil { + return nil, err + } + + if opts.Properties != nil { + for k, v := range opts.Properties { + b[k] = v + } + } + return b, nil +} + +// Create implements create image request +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToImageCreateMap() + if err != nil { + r.Err = err + return r + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + return +} + +// Delete implements image delete request +func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, id), nil) + return +} + +// Get implements image get request +func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { + _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + return +} + +// Update implements image updated request +func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToImageUpdateMap() + if err != nil { + r.Err = err + return r + } + _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + MoreHeaders: map[string]string{"Content-Type": "application/openstack-images-v2.1-json-patch"}, + }) + return +} + +// UpdateOptsBuilder implements UpdateOptsBuilder +type UpdateOptsBuilder interface { + // returns value implementing json.Marshaler which when marshaled matches the patch schema: + // http://specs.openstack.org/openstack/glance-specs/specs/api/v2/http-patch-image-api-v2.html + ToImageUpdateMap() ([]interface{}, error) +} + +// UpdateOpts implements UpdateOpts +type UpdateOpts []Patch + +// ToImageUpdateMap builder +func (opts UpdateOpts) ToImageUpdateMap() ([]interface{}, error) { + m := make([]interface{}, len(opts)) + for i, patch := range opts { + patchJSON := patch.ToImagePatchMap() + m[i] = patchJSON + } + return m, nil +} + +// Patch represents a single update to an existing image. Multiple updates to an image can be +// submitted at the same time. +type Patch interface { + ToImagePatchMap() map[string]interface{} +} + +// UpdateVisibility updated visibility +type UpdateVisibility struct { + Visibility ImageVisibility +} + +// ToImagePatchMap builder +func (u UpdateVisibility) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/visibility", + "value": u.Visibility, + } +} + +// ReplaceImageName implements Patch +type ReplaceImageName struct { + NewName string +} + +// ToImagePatchMap builder +func (r ReplaceImageName) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/name", + "value": r.NewName, + } +} + +// ReplaceImageChecksum implements Patch +type ReplaceImageChecksum struct { + Checksum string +} + +// ReplaceImageChecksum builder +func (rc ReplaceImageChecksum) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/checksum", + "value": rc.Checksum, + } +} + +// ReplaceImageTags implements Patch +type ReplaceImageTags struct { + NewTags []string +} + +// ToImagePatchMap builder +func (r ReplaceImageTags) ToImagePatchMap() map[string]interface{} { + return map[string]interface{}{ + "op": "replace", + "path": "/tags", + "value": r.NewTags, + } +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go new file mode 100644 index 000000000..653c68c73 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go @@ -0,0 +1,176 @@ +package images + +import ( + "encoding/json" + "fmt" + "reflect" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Image model +// Does not include the literal image data; just metadata. +// returned by listing images, and by fetching a specific image. +type Image struct { + // ID is the image UUID + ID string `json:"id"` + + // Name is the human-readable display name for the image. + Name string `json:"name"` + + // Status is the image status. It can be "queued" or "active" + // See imageservice/v2/images/type.go + Status ImageStatus `json:"status"` + + // Tags is a list of image tags. Tags are arbitrarily defined strings + // attached to an image. + Tags []string `json:"tags"` + + // ContainerFormat is the format of the container. + // Valid values are ami, ari, aki, bare, and ovf. + ContainerFormat string `json:"container_format"` + + // DiskFormat is the format of the disk. + // If set, valid values are ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, and iso. + DiskFormat string `json:"disk_format"` + + // MinDiskGigabytes is the amount of disk space in GB that is required to boot the image. + MinDiskGigabytes int `json:"min_disk"` + + // MinRAMMegabytes [optional] is the amount of RAM in MB that is required to boot the image. + MinRAMMegabytes int `json:"min_ram"` + + // Owner is the tenant the image belongs to. + Owner string `json:"owner"` + + // Protected is whether the image is deletable or not. + Protected bool `json:"protected"` + + // Visibility defines who can see/use the image. + Visibility ImageVisibility `json:"visibility"` + + // Checksum is the checksum of the data that's associated with the image + Checksum string `json:"checksum"` + + // SizeBytes is the size of the data that's associated with the image. + SizeBytes int64 `json:"size"` + + // Metadata is a set of metadata associated with the image. + // Image metadata allow for meaningfully define the image properties + // and tags. See http://docs.openstack.org/developer/glance/metadefs-concepts.html. + Metadata map[string]string `json:"metadata"` + + // Properties is a set of key-value pairs, if any, that are associated with the image. + Properties map[string]string `json:"properties"` + + // CreatedAt is the date when the image has been created. + CreatedAt time.Time `json:"-"` + + // UpdatedAt is the date when the last change has been made to the image or it's properties. + UpdatedAt time.Time `json:"-"` + + // File is the trailing path after the glance endpoint that represent the location + // of the image or the path to retrieve it. + File string `json:"file"` + + // Schema is the path to the JSON-schema that represent the image or image entity. + Schema string `json:"schema"` +} + +func (s *Image) UnmarshalJSON(b []byte) error { + type tmp Image + var p *struct { + tmp + SizeBytes interface{} `json:"size"` + CreatedAt string `json:"created_at"` + UpdatedAt string `json:"updated_at"` + } + err := json.Unmarshal(b, &p) + if err != nil { + return err + } + *s = Image(p.tmp) + + switch t := p.SizeBytes.(type) { + case nil: + return nil + case float32: + s.SizeBytes = int64(t) + case float64: + s.SizeBytes = int64(t) + default: + return fmt.Errorf("Unknown type for SizeBytes: %v (value: %v)", reflect.TypeOf(t), t) + } + + 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 +} + +type commonResult struct { + gophercloud.Result +} + +// Extract interprets any commonResult as an Image. +func (r commonResult) Extract() (*Image, error) { + var s *Image + err := r.ExtractInto(&s) + return s, err +} + +// CreateResult represents the result of a Create operation +type CreateResult struct { + commonResult +} + +// UpdateResult represents the result of an Update operation +type UpdateResult struct { + commonResult +} + +// GetResult represents the result of a Get operation +type GetResult struct { + commonResult +} + +//DeleteResult model +type DeleteResult struct { + gophercloud.ErrResult +} + +// ImagePage represents page +type ImagePage struct { + pagination.LinkedPageBase +} + +// IsEmpty returns true if a page contains no Images results. +func (r ImagePage) IsEmpty() (bool, error) { + images, err := ExtractImages(r) + return len(images) == 0, err +} + +// NextPageURL uses the response's embedded link reference to navigate to the next page of results. +func (r ImagePage) NextPageURL() (string, error) { + var s struct { + Next string `json:"next"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return nextPageURL(r.URL.String(), s.Next), nil +} + +// ExtractImages interprets the results of a single page from a List() call, producing a slice of Image entities. +func ExtractImages(r pagination.Page) ([]Image, error) { + var s struct { + Images []Image `json:"images"` + } + err := (r.(ImagePage)).ExtractInto(&s) + return s.Images, err +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go new file mode 100644 index 000000000..086e7e5d5 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/types.go @@ -0,0 +1,75 @@ +package images + +// ImageStatus image statuses +// http://docs.openstack.org/developer/glance/statuses.html +type ImageStatus string + +const ( + // ImageStatusQueued is a status for an image which identifier has + // been reserved for an image in the image registry. + ImageStatusQueued ImageStatus = "queued" + + // ImageStatusSaving denotes that an image’s raw data is currently being uploaded to Glance + ImageStatusSaving ImageStatus = "saving" + + // ImageStatusActive denotes an image that is fully available in Glance. + ImageStatusActive ImageStatus = "active" + + // ImageStatusKilled denotes that an error occurred during the uploading + // of an image’s data, and that the image is not readable. + ImageStatusKilled ImageStatus = "killed" + + // ImageStatusDeleted is used for an image that is no longer available to use. + // The image information is retained in the image registry. + ImageStatusDeleted ImageStatus = "deleted" + + // ImageStatusPendingDelete is similar to Delete, but the image is not yet deleted. + ImageStatusPendingDelete ImageStatus = "pending_delete" + + // ImageStatusDeactivated denotes that access to image data is not allowed to any non-admin user. + ImageStatusDeactivated ImageStatus = "deactivated" +) + +// ImageVisibility denotes an image that is fully available in Glance. +// This occurs when the image data is uploaded, or the image size +// is explicitly set to zero on creation. +// According to design +// https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design +type ImageVisibility string + +const ( + // ImageVisibilityPublic all users + ImageVisibilityPublic ImageVisibility = "public" + + // ImageVisibilityPrivate users with tenantId == tenantId(owner) + ImageVisibilityPrivate ImageVisibility = "private" + + // ImageVisibilityShared images are visible to: + // - users with tenantId == tenantId(owner) + // - users with tenantId in the member-list of the image + // - users with tenantId in the member-list with member_status == 'accepted' + ImageVisibilityShared ImageVisibility = "shared" + + // ImageVisibilityCommunity images: + // - all users can see and boot it + // - users with tenantId in the member-list of the image with member_status == 'accepted' + // have this image in their default image-list + ImageVisibilityCommunity ImageVisibility = "community" +) + +// MemberStatus is a status for adding a new member (tenant) to an image member list. +type ImageMemberStatus string + +const ( + // ImageMemberStatusAccepted is the status for an accepted image member. + ImageMemberStatusAccepted ImageMemberStatus = "accepted" + + // ImageMemberStatusPending shows that the member addition is pending + ImageMemberStatusPending ImageMemberStatus = "pending" + + // ImageMemberStatusAccepted is the status for a rejected image member + ImageMemberStatusRejected ImageMemberStatus = "rejected" + + // ImageMemberStatusAll + ImageMemberStatusAll ImageMemberStatus = "all" +) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go new file mode 100644 index 000000000..58cb8f715 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/urls.go @@ -0,0 +1,44 @@ +package images + +import ( + "strings" + + "github.com/gophercloud/gophercloud" +) + +// `listURL` is a pure function. `listURL(c)` is a URL for which a GET +// request will respond with a list of images in the service `c`. +func listURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("images") +} + +func createURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("images") +} + +// `imageURL(c,i)` is the URL for the image identified by ID `i` in +// the service `c`. +func imageURL(c *gophercloud.ServiceClient, imageID string) string { + return c.ServiceURL("images", imageID) +} + +// `getURL(c,i)` is a URL for which a GET request will respond with +// information about the image identified by ID `i` in the service +// `c`. +func getURL(c *gophercloud.ServiceClient, imageID string) string { + return imageURL(c, imageID) +} + +func updateURL(c *gophercloud.ServiceClient, imageID string) string { + return imageURL(c, imageID) +} + +func deleteURL(c *gophercloud.ServiceClient, imageID string) string { + return imageURL(c, imageID) +} + +// builds next page full url based on current url +func nextPageURL(currentURL string, next string) string { + base := currentURL[:strings.Index(currentURL, "/images")] + return base + next +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/requests.go new file mode 100644 index 000000000..8c667cbdd --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/requests.go @@ -0,0 +1,77 @@ +package members + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Create member for specific image +// +// Preconditions +// The specified images must exist. +// You can only add a new member to an image which 'visibility' attribute is private. +// You must be the owner of the specified image. +// Synchronous Postconditions +// With correct permissions, you can see the member status of the image as pending through API calls. +// +// More details here: http://developer.openstack.org/api-ref-image-v2.html#createImageMember-v2 +func Create(client *gophercloud.ServiceClient, id string, member string) (r CreateResult) { + b := map[string]interface{}{"member": member} + _, r.Err = client.Post(createMemberURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200, 409, 403}, + }) + return +} + +// List members returns list of members for specifed image id +// More details: http://developer.openstack.org/api-ref-image-v2.html#listImageMembers-v2 +func List(client *gophercloud.ServiceClient, id string) pagination.Pager { + return pagination.NewPager(client, listMembersURL(client, id), func(r pagination.PageResult) pagination.Page { + return MemberPage{pagination.SinglePageBase(r)} + }) +} + +// Get image member details. +// More details: http://developer.openstack.org/api-ref-image-v2.html#getImageMember-v2 +func Get(client *gophercloud.ServiceClient, imageID string, memberID string) (r DetailsResult) { + _, r.Err = client.Get(getMemberURL(client, imageID, memberID), &r.Body, &gophercloud.RequestOpts{OkCodes: []int{200}}) + return +} + +// Delete membership for given image. +// Callee should be image owner +// More details: http://developer.openstack.org/api-ref-image-v2.html#deleteImageMember-v2 +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}}) + return +} + +// UpdateOptsBuilder allows extensions to add additional attributes to the Update request. +type UpdateOptsBuilder interface { + ToImageMemberUpdateMap() (map[string]interface{}, error) +} + +// UpdateOpts implements UpdateOptsBuilder +type UpdateOpts struct { + Status string +} + +// ToMemberUpdateMap formats an UpdateOpts structure into a request body. +func (opts UpdateOpts) ToImageMemberUpdateMap() (map[string]interface{}, error) { + return map[string]interface{}{ + "status": opts.Status, + }, nil +} + +// Update function updates member +// More details: http://developer.openstack.org/api-ref-image-v2.html#updateImageMember-v2 +func Update(client *gophercloud.ServiceClient, imageID string, memberID string, opts UpdateOptsBuilder) (r UpdateResult) { + b, err := opts.ToImageMemberUpdateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Put(updateMemberURL(client, imageID, memberID), b, &r.Body, + &gophercloud.RequestOpts{OkCodes: []int{200}}) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/results.go new file mode 100644 index 000000000..1d2a9c05c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/results.go @@ -0,0 +1,91 @@ +package members + +import ( + "encoding/json" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Member model +type Member struct { + CreatedAt time.Time `json:"-"` + ImageID string `json:"image_id"` + MemberID string `json:"member_id"` + Schema string `json:"schema"` + Status string `json:"status"` + UpdatedAt time.Time `json:"-"` +} + +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 +func (r commonResult) Extract() (*Member, error) { + var s *Member + err := r.ExtractInto(&s) + return s, err +} + +// MemberPage is a single page of Members results. +type MemberPage struct { + pagination.SinglePageBase +} + +// ExtractMembers returns a slice of Members contained in a single page of results. +func ExtractMembers(r pagination.Page) ([]Member, error) { + var s struct { + Members []Member `json:"members"` + } + err := r.(MemberPage).ExtractInto(&s) + return s.Members, err +} + +// IsEmpty determines whether or not a page of Members contains any results. +func (r MemberPage) IsEmpty() (bool, error) { + members, err := ExtractMembers(r) + return len(members) == 0, err +} + +type commonResult struct { + gophercloud.Result +} + +// CreateResult result model +type CreateResult struct { + commonResult +} + +// DetailsResult model +type DetailsResult struct { + commonResult +} + +// UpdateResult model +type UpdateResult struct { + commonResult +} + +// DeleteResult model +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/urls.go new file mode 100644 index 000000000..0898364e7 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/members/urls.go @@ -0,0 +1,31 @@ +package members + +import "github.com/gophercloud/gophercloud" + +func imageMembersURL(c *gophercloud.ServiceClient, imageID string) string { + return c.ServiceURL("images", imageID, "members") +} + +func listMembersURL(c *gophercloud.ServiceClient, imageID string) string { + return imageMembersURL(c, imageID) +} + +func createMemberURL(c *gophercloud.ServiceClient, imageID string) string { + return imageMembersURL(c, imageID) +} + +func imageMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { + return c.ServiceURL("images", imageID, "members", memberID) +} + +func getMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { + return imageMemberURL(c, imageID, memberID) +} + +func updateMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { + return imageMemberURL(c, imageID, memberID) +} + +func deleteMemberURL(c *gophercloud.ServiceClient, imageID string, memberID string) string { + return imageMemberURL(c, imageID, memberID) +} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/utils/choose_version.go b/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go similarity index 95% rename from vendor/github.com/rackspace/gophercloud/openstack/utils/choose_version.go rename to vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go index b697ba816..c605d0844 100644 --- a/vendor/github.com/rackspace/gophercloud/openstack/utils/choose_version.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/utils/choose_version.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/rackspace/gophercloud" + "github.com/gophercloud/gophercloud" ) // Version is a supported API version, corresponding to a vN package within the appropriate service. @@ -59,7 +59,7 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (* } var resp response - _, err := client.Request("GET", client.IdentityBase, gophercloud.RequestOpts{ + _, err := client.Request("GET", client.IdentityBase, &gophercloud.RequestOpts{ JSONResponse: &resp, OkCodes: []int{200, 300}, }) diff --git a/vendor/github.com/rackspace/gophercloud/pagination/http.go b/vendor/github.com/gophercloud/gophercloud/pagination/http.go similarity index 93% rename from vendor/github.com/rackspace/gophercloud/pagination/http.go rename to vendor/github.com/gophercloud/gophercloud/pagination/http.go index 1b3fe94ab..cb4b4ae6b 100644 --- a/vendor/github.com/rackspace/gophercloud/pagination/http.go +++ b/vendor/github.com/gophercloud/gophercloud/pagination/http.go @@ -7,7 +7,7 @@ import ( "net/url" "strings" - "github.com/rackspace/gophercloud" + "github.com/gophercloud/gophercloud" ) // PageResult stores the HTTP response that returned the current page of results. @@ -53,7 +53,7 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult { // Request performs an HTTP request and extracts the http.Response from the result. func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) { - return client.Request("GET", url, gophercloud.RequestOpts{ + return client.Get(url, nil, &gophercloud.RequestOpts{ MoreHeaders: headers, OkCodes: []int{200, 204}, }) diff --git a/vendor/github.com/rackspace/gophercloud/pagination/linked.go b/vendor/github.com/gophercloud/gophercloud/pagination/linked.go similarity index 64% rename from vendor/github.com/rackspace/gophercloud/pagination/linked.go rename to vendor/github.com/gophercloud/gophercloud/pagination/linked.go index e9bd8dec9..3656fb7f8 100644 --- a/vendor/github.com/rackspace/gophercloud/pagination/linked.go +++ b/vendor/github.com/gophercloud/gophercloud/pagination/linked.go @@ -1,6 +1,11 @@ package pagination -import "fmt" +import ( + "fmt" + "reflect" + + "github.com/gophercloud/gophercloud" +) // LinkedPageBase may be embedded to implement a page that provides navigational "Next" and "Previous" links within its result. type LinkedPageBase struct { @@ -28,7 +33,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { submap, ok := current.Body.(map[string]interface{}) if !ok { - return "", fmt.Errorf("Expected an object, but was %#v", current.Body) + err := gophercloud.ErrUnexpectedType{} + err.Expected = "map[string]interface{}" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) + return "", err } for { @@ -42,7 +50,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { if len(path) > 0 { submap, ok = value.(map[string]interface{}) if !ok { - return "", fmt.Errorf("Expected an object, but was %#v", value) + err := gophercloud.ErrUnexpectedType{} + err.Expected = "map[string]interface{}" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) + return "", err } } else { if value == nil { @@ -52,7 +63,10 @@ func (current LinkedPageBase) NextPageURL() (string, error) { url, ok := value.(string) if !ok { - return "", fmt.Errorf("Expected a string, but was %#v", value) + err := gophercloud.ErrUnexpectedType{} + err.Expected = "string" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(value)) + return "", err } return url, nil @@ -60,6 +74,17 @@ func (current LinkedPageBase) NextPageURL() (string, error) { } } +// IsEmpty satisifies the IsEmpty method of the Page interface +func (current LinkedPageBase) IsEmpty() (bool, error) { + if b, ok := current.Body.([]interface{}); ok { + return len(b) == 0, nil + } + err := gophercloud.ErrUnexpectedType{} + err.Expected = "[]interface{}" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) + return true, err +} + // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. func (current LinkedPageBase) GetBody() interface{} { diff --git a/vendor/github.com/rackspace/gophercloud/pagination/marker.go b/vendor/github.com/gophercloud/gophercloud/pagination/marker.go similarity index 71% rename from vendor/github.com/rackspace/gophercloud/pagination/marker.go rename to vendor/github.com/gophercloud/gophercloud/pagination/marker.go index f355afc54..52e53bae8 100644 --- a/vendor/github.com/rackspace/gophercloud/pagination/marker.go +++ b/vendor/github.com/gophercloud/gophercloud/pagination/marker.go @@ -1,5 +1,12 @@ package pagination +import ( + "fmt" + "reflect" + + "github.com/gophercloud/gophercloud" +) + // MarkerPage is a stricter Page interface that describes additional functionality required for use with NewMarkerPager. // For convenience, embed the MarkedPageBase struct. type MarkerPage interface { @@ -33,6 +40,17 @@ func (current MarkerPageBase) NextPageURL() (string, error) { return currentURL.String(), nil } +// IsEmpty satisifies the IsEmpty method of the Page interface +func (current MarkerPageBase) IsEmpty() (bool, error) { + if b, ok := current.Body.([]interface{}); ok { + return len(b) == 0, nil + } + err := gophercloud.ErrUnexpectedType{} + err.Expected = "[]interface{}" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) + return true, err +} + // GetBody returns the linked page's body. This method is needed to satisfy the // Page interface. func (current MarkerPageBase) GetBody() interface{} { diff --git a/vendor/github.com/rackspace/gophercloud/pagination/pager.go b/vendor/github.com/gophercloud/gophercloud/pagination/pager.go similarity index 88% rename from vendor/github.com/rackspace/gophercloud/pagination/pager.go rename to vendor/github.com/gophercloud/gophercloud/pagination/pager.go index a7593ac88..1b5192ad6 100644 --- a/vendor/github.com/rackspace/gophercloud/pagination/pager.go +++ b/vendor/github.com/gophercloud/gophercloud/pagination/pager.go @@ -7,7 +7,7 @@ import ( "reflect" "strings" - "github.com/rackspace/gophercloud" + "github.com/gophercloud/gophercloud" ) var ( @@ -138,6 +138,11 @@ func (p Pager) AllPages() (Page, error) { // that type. pageType := reflect.TypeOf(testPage) + // if it's a single page, just return the testPage (first page) + if _, found := pageType.FieldByName("SinglePageBase"); found { + return testPage, nil + } + // Switch on the page body type. Recognized types are `map[string]interface{}`, // `[]byte`, and `[]interface{}`. switch testPage.GetBody().(type) { @@ -145,7 +150,7 @@ func (p Pager) AllPages() (Page, error) { // key is the map key for the page body if the body type is `map[string]interface{}`. var key string // 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{}) for k := range b { // If it's a linked page, we don't want the `links`, we want the other one. @@ -153,7 +158,14 @@ func (p Pager) AllPages() (Page, error) { key = k } } - pagesSlice = append(pagesSlice, b[key].([]interface{})...) + switch keyType := b[key].(type) { + case map[string]interface{}: + pagesSlice = append(pagesSlice, keyType) + case []interface{}: + pagesSlice = append(pagesSlice, b[key].([]interface{})...) + default: + return false, fmt.Errorf("Unsupported page body type: %+v", keyType) + } return true, nil }) if err != nil { @@ -164,7 +176,7 @@ func (p Pager) AllPages() (Page, error) { body.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(pagesSlice)) case []byte: // 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().([]byte) pagesSlice = append(pagesSlice, b) // seperate pages with a comma @@ -188,7 +200,7 @@ func (p Pager) AllPages() (Page, error) { body.SetBytes(b) case []interface{}: // 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().([]interface{}) pagesSlice = append(pagesSlice, b...) return true, nil @@ -202,7 +214,10 @@ func (p Pager) AllPages() (Page, error) { body.Index(i).Set(reflect.ValueOf(s)) } default: - return nil, fmt.Errorf("Page body has unrecognized type.") + err := gophercloud.ErrUnexpectedType{} + err.Expected = "map[string]interface{}/[]byte/[]interface{}" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(testPage.GetBody())) + return nil, err } // Each `Extract*` function is expecting a specific type of page coming back, diff --git a/vendor/github.com/rackspace/gophercloud/pagination/pkg.go b/vendor/github.com/gophercloud/gophercloud/pagination/pkg.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/pagination/pkg.go rename to vendor/github.com/gophercloud/gophercloud/pagination/pkg.go diff --git a/vendor/github.com/rackspace/gophercloud/pagination/single.go b/vendor/github.com/gophercloud/gophercloud/pagination/single.go similarity index 54% rename from vendor/github.com/rackspace/gophercloud/pagination/single.go rename to vendor/github.com/gophercloud/gophercloud/pagination/single.go index f78d4ab5d..4251d6491 100644 --- a/vendor/github.com/rackspace/gophercloud/pagination/single.go +++ b/vendor/github.com/gophercloud/gophercloud/pagination/single.go @@ -1,5 +1,12 @@ package pagination +import ( + "fmt" + "reflect" + + "github.com/gophercloud/gophercloud" +) + // SinglePageBase may be embedded in a Page that contains all of the results from an operation at once. type SinglePageBase PageResult @@ -8,6 +15,17 @@ func (current SinglePageBase) NextPageURL() (string, error) { return "", nil } +// IsEmpty satisifies the IsEmpty method of the Page interface +func (current SinglePageBase) IsEmpty() (bool, error) { + if b, ok := current.Body.([]interface{}); ok { + return len(b) == 0, nil + } + err := gophercloud.ErrUnexpectedType{} + err.Expected = "[]interface{}" + err.Actual = fmt.Sprintf("%v", reflect.TypeOf(current.Body)) + return true, err +} + // GetBody returns the single page's body. This method is needed to satisfy the // Page interface. func (current SinglePageBase) GetBody() interface{} { diff --git a/vendor/github.com/rackspace/gophercloud/params.go b/vendor/github.com/gophercloud/gophercloud/params.go similarity index 58% rename from vendor/github.com/rackspace/gophercloud/params.go rename to vendor/github.com/gophercloud/gophercloud/params.go index 4d0f1e6e0..e484fe1c1 100644 --- a/vendor/github.com/rackspace/gophercloud/params.go +++ b/vendor/github.com/gophercloud/gophercloud/params.go @@ -1,6 +1,7 @@ package gophercloud import ( + "encoding/json" "fmt" "net/url" "reflect" @@ -9,6 +10,146 @@ import ( "time" ) +// BuildRequestBody builds a map[string]interface from the given `struct`. If +// parent is not the empty string, the final map[string]interface returned will +// encapsulate the built one +// +func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) { + optsValue := reflect.ValueOf(opts) + if optsValue.Kind() == reflect.Ptr { + optsValue = optsValue.Elem() + } + + optsType := reflect.TypeOf(opts) + if optsType.Kind() == reflect.Ptr { + optsType = optsType.Elem() + } + + optsMap := make(map[string]interface{}) + if optsValue.Kind() == reflect.Struct { + //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind()) + for i := 0; i < optsValue.NumField(); i++ { + v := optsValue.Field(i) + f := optsType.Field(i) + + if f.Name != strings.Title(f.Name) { + //fmt.Printf("Skipping field: %s...\n", f.Name) + continue + } + + //fmt.Printf("Starting on field: %s...\n", f.Name) + + zero := isZero(v) + //fmt.Printf("v is zero?: %v\n", zero) + + // if the field has a required tag that's set to "true" + if requiredTag := f.Tag.Get("required"); requiredTag == "true" { + //fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero) + // if the field's value is zero, return a missing-argument error + if zero { + // if the field has a 'required' tag, it can't have a zero-value + err := ErrMissingInput{} + err.Argument = f.Name + return nil, err + } + } + + if xorTag := f.Tag.Get("xor"); xorTag != "" { + //fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag) + xorField := optsValue.FieldByName(xorTag) + var xorFieldIsZero bool + if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) { + xorFieldIsZero = true + } else { + if xorField.Kind() == reflect.Ptr { + xorField = xorField.Elem() + } + xorFieldIsZero = isZero(xorField) + } + if !(zero != xorFieldIsZero) { + err := ErrMissingInput{} + err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag) + err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag) + return nil, err + } + } + + if orTag := f.Tag.Get("or"); orTag != "" { + //fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag) + //fmt.Printf("field is zero?: %v\n", zero) + if zero { + orField := optsValue.FieldByName(orTag) + var orFieldIsZero bool + if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) { + orFieldIsZero = true + } else { + if orField.Kind() == reflect.Ptr { + orField = orField.Elem() + } + orFieldIsZero = isZero(orField) + } + if orFieldIsZero { + err := ErrMissingInput{} + err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag) + err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag) + return nil, err + } + } + } + + if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) { + if zero { + //fmt.Printf("value before change: %+v\n", optsValue.Field(i)) + if jsonTag := f.Tag.Get("json"); jsonTag != "" { + jsonTagPieces := strings.Split(jsonTag, ",") + if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" { + if v.CanSet() { + if !v.IsNil() { + if v.Kind() == reflect.Ptr { + v.Set(reflect.Zero(v.Type())) + } + } + //fmt.Printf("value after change: %+v\n", optsValue.Field(i)) + } + } + } + continue + } + + //fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name) + _, err := BuildRequestBody(v.Interface(), f.Name) + if err != nil { + return nil, err + } + } + } + + //fmt.Printf("opts: %+v \n", opts) + + b, err := json.Marshal(opts) + if err != nil { + return nil, err + } + + //fmt.Printf("string(b): %s\n", string(b)) + + err = json.Unmarshal(b, &optsMap) + if err != nil { + return nil, err + } + + //fmt.Printf("optsMap: %+v\n", optsMap) + + if parent != "" { + optsMap = map[string]interface{}{parent: optsMap} + } + //fmt.Printf("optsMap after parent added: %+v\n", optsMap) + return optsMap, nil + } + // Return an error if the underlying type of 'opts' isn't a struct. + return nil, fmt.Errorf("Options type is not a struct.") +} + // EnabledState is a convenience type, mostly used in Create and Update // operations. Because the zero value of a bool is FALSE, we need to use a // pointer instead to indicate zero-ness. @@ -23,6 +164,17 @@ var ( Disabled EnabledState = &iFalse ) +// IPVersion is a type for the possible IP address versions. Valid instances +// are IPv4 and IPv6 +type IPVersion int + +const ( + // IPv4 is used for IP version 4 addresses + IPv4 IPVersion = 4 + // IPv6 is used for IP version 6 addresses + IPv6 IPVersion = 6 +) + // IntToPointer is a function for converting integers into integer pointers. // This is useful when passing in options to operations. func IntToPointer(i int) *int { @@ -60,10 +212,27 @@ func MaybeInt(original int) *int { return nil } +/* +func isUnderlyingStructZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Ptr: + return isUnderlyingStructZero(v.Elem()) + default: + return isZero(v) + } +} +*/ + var t time.Time func isZero(v reflect.Value) bool { + //fmt.Printf("\n\nchecking isZero for value: %+v\n", v) switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return true + } + return false case reflect.Func, reflect.Map, reflect.Slice: return v.IsNil() case reflect.Array: @@ -87,6 +256,7 @@ func isZero(v reflect.Value) bool { } // Compare other types directly: z := reflect.Zero(v.Type()) + //fmt.Printf("zero type for value: %+v\n\n\n", z) return v.Interface() == z.Interface() } @@ -137,7 +307,11 @@ func BuildQueryString(opts interface{}) (*url.URL, error) { // if the field is set, add it to the slice of query pieces if !isZero(v) { + loop: switch v.Kind() { + case reflect.Ptr: + v = v.Elem() + goto loop case reflect.String: params.Add(tags[0], v.String()) case reflect.Int: diff --git a/vendor/github.com/rackspace/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/provider_client.go similarity index 68% rename from vendor/github.com/rackspace/gophercloud/provider_client.go rename to vendor/github.com/gophercloud/gophercloud/provider_client.go index 53fce7370..f88682381 100644 --- a/vendor/github.com/rackspace/gophercloud/provider_client.go +++ b/vendor/github.com/gophercloud/gophercloud/provider_client.go @@ -3,7 +3,6 @@ package gophercloud import ( "bytes" "encoding/json" - "fmt" "io" "io/ioutil" "net/http" @@ -11,7 +10,7 @@ import ( ) // DefaultUserAgent is the default User-Agent string set in the request header. -const DefaultUserAgent = "gophercloud/1.0.0" +const DefaultUserAgent = "gophercloud/2.0.0" // UserAgent represents a User-Agent header. type UserAgent struct { @@ -68,6 +67,8 @@ type ProviderClient struct { // fails with a 401 HTTP response code. This a needed because there may be multiple // authentication functions for different Identity service versions. ReauthFunc func() error + + Debug bool } // AuthenticatedHeaders returns a map of HTTP headers that are common for all @@ -85,46 +86,30 @@ type RequestOpts struct { // content type of the request will default to "application/json" unless overridden by MoreHeaders. // It's an error to specify both a JSONBody and a RawBody. JSONBody interface{} - // RawBody contains an io.ReadSeeker that will be consumed by the request directly. No content-type + // RawBody contains an io.Reader that will be consumed by the request directly. No content-type // will be set unless one is provided explicitly by MoreHeaders. - RawBody io.ReadSeeker - + RawBody io.Reader // JSONResponse, if provided, will be populated with the contents of the response body parsed as // JSON. JSONResponse interface{} // OkCodes contains a list of numeric HTTP status codes that should be interpreted as success. If // the response has a different code, an error will be returned. OkCodes []int - // MoreHeaders specifies additional HTTP headers to be provide on the request. If a header is // provided with a blank value (""), that header will be *omitted* instead: use this to suppress // the default Accept header or an inferred Content-Type, for example. MoreHeaders map[string]string -} - -// UnexpectedResponseCodeError is returned by the Request method when a response code other than -// those listed in OkCodes is encountered. -type UnexpectedResponseCodeError struct { - URL string - Method string - Expected []int - Actual int - Body []byte -} - -func (err *UnexpectedResponseCodeError) Error() string { - return fmt.Sprintf( - "Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s", - err.Expected, err.Method, err.URL, err.Actual, err.Body, - ) + // ErrorContext specifies the resource error type to return if an error is encountered. + // This lets resources override default error messages based on the response status code. + ErrorContext error } var applicationJSON = "application/json" // Request performs an HTTP request using the ProviderClient's current HTTPClient. An authentication // header will automatically be provided. -func (client *ProviderClient) Request(method, url string, options RequestOpts) (*http.Response, error) { - var body io.ReadSeeker +func (client *ProviderClient) Request(method, url string, options *RequestOpts) (*http.Response, error) { + var body io.Reader var contentType *string // Derive the content body by either encoding an arbitrary object as JSON, or by taking a provided @@ -179,32 +164,13 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) ( // Set connection parameter to close the connection immediately when we've got the response req.Close = true - + // Issue the request. resp, err := client.HTTPClient.Do(req) if err != nil { return nil, err } - if resp.StatusCode == http.StatusUnauthorized { - if client.ReauthFunc != nil { - err = client.ReauthFunc() - if err != nil { - return nil, fmt.Errorf("Error trying to re-authenticate: %s", err) - } - if options.RawBody != nil { - options.RawBody.Seek(0, 0) - } - resp.Body.Close() - resp, err = client.Request(method, url, options) - if err != nil { - return nil, fmt.Errorf("Successfully re-authenticated, but got error executing request: %s", err) - } - - return resp, nil - } - } - // Allow default OkCodes if none explicitly set if options.OkCodes == nil { options.OkCodes = defaultOkCodes(method) @@ -218,16 +184,98 @@ func (client *ProviderClient) Request(method, url string, options RequestOpts) ( break } } + if !ok { body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() - return resp, &UnexpectedResponseCodeError{ + //pc := make([]uintptr, 1) + //runtime.Callers(2, pc) + //f := runtime.FuncForPC(pc[0]) + respErr := ErrUnexpectedResponseCode{ URL: url, Method: method, Expected: options.OkCodes, Actual: resp.StatusCode, Body: body, } + //respErr.Function = "gophercloud.ProviderClient.Request" + + errType := options.ErrorContext + switch resp.StatusCode { + case http.StatusBadRequest: + err = ErrDefault400{respErr} + if error400er, ok := errType.(Err400er); ok { + err = error400er.Error400(respErr) + } + case http.StatusUnauthorized: + if client.ReauthFunc != nil { + err = client.ReauthFunc() + if err != nil { + e := &ErrUnableToReauthenticate{} + e.ErrOriginal = respErr + return nil, e + } + if options.RawBody != nil { + if seeker, ok := options.RawBody.(io.Seeker); ok { + seeker.Seek(0, 0) + } + } + resp, err = client.Request(method, url, options) + if err != nil { + switch err.(type) { + case *ErrUnexpectedResponseCode: + e := &ErrErrorAfterReauthentication{} + e.ErrOriginal = err.(*ErrUnexpectedResponseCode) + return nil, e + default: + e := &ErrErrorAfterReauthentication{} + e.ErrOriginal = err + return nil, e + } + } + return resp, nil + } + err = ErrDefault401{respErr} + if error401er, ok := errType.(Err401er); ok { + err = error401er.Error401(respErr) + } + case http.StatusNotFound: + err = ErrDefault404{respErr} + if error404er, ok := errType.(Err404er); ok { + err = error404er.Error404(respErr) + } + case http.StatusMethodNotAllowed: + err = ErrDefault405{respErr} + if error405er, ok := errType.(Err405er); ok { + err = error405er.Error405(respErr) + } + case http.StatusRequestTimeout: + err = ErrDefault408{respErr} + if error408er, ok := errType.(Err408er); ok { + err = error408er.Error408(respErr) + } + case 429: + err = ErrDefault429{respErr} + if error429er, ok := errType.(Err429er); ok { + err = error429er.Error429(respErr) + } + case http.StatusInternalServerError: + err = ErrDefault500{respErr} + if error500er, ok := errType.(Err500er); ok { + err = error500er.Error500(respErr) + } + case http.StatusServiceUnavailable: + err = ErrDefault503{respErr} + if error503er, ok := errType.(Err503er); ok { + err = error503er.Error503(respErr) + } + } + + if err == nil { + err = respErr + } + + return resp, err } // Parse the response body as JSON, if requested to do so. @@ -257,75 +305,3 @@ func defaultOkCodes(method string) []int { return []int{} } - -func (client *ProviderClient) Get(url string, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = &RequestOpts{} - } - if JSONResponse != nil { - opts.JSONResponse = JSONResponse - } - return client.Request("GET", url, *opts) -} - -func (client *ProviderClient) Post(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = &RequestOpts{} - } - - if v, ok := (JSONBody).(io.ReadSeeker); ok { - opts.RawBody = v - } else if JSONBody != nil { - opts.JSONBody = JSONBody - } - - if JSONResponse != nil { - opts.JSONResponse = JSONResponse - } - - return client.Request("POST", url, *opts) -} - -func (client *ProviderClient) Put(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = &RequestOpts{} - } - - if v, ok := (JSONBody).(io.ReadSeeker); ok { - opts.RawBody = v - } else if JSONBody != nil { - opts.JSONBody = JSONBody - } - - if JSONResponse != nil { - opts.JSONResponse = JSONResponse - } - - return client.Request("PUT", url, *opts) -} - -func (client *ProviderClient) Patch(url string, JSONBody interface{}, JSONResponse *interface{}, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = &RequestOpts{} - } - - if v, ok := (JSONBody).(io.ReadSeeker); ok { - opts.RawBody = v - } else if JSONBody != nil { - opts.JSONBody = JSONBody - } - - if JSONResponse != nil { - opts.JSONResponse = JSONResponse - } - - return client.Request("PATCH", url, *opts) -} - -func (client *ProviderClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { - if opts == nil { - opts = &RequestOpts{} - } - - return client.Request("DELETE", url, *opts) -} diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/results.go new file mode 100644 index 000000000..76c16ef8f --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/results.go @@ -0,0 +1,336 @@ +package gophercloud + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "reflect" + "strconv" + "time" +) + +/* +Result is an internal type to be used by individual resource packages, but its +methods will be available on a wide variety of user-facing embedding types. + +It acts as a base struct that other Result types, returned from request +functions, can embed for convenience. All Results capture basic information +from the HTTP transaction that was performed, including the response body, +HTTP headers, and any errors that happened. + +Generally, each Result type will have an Extract method that can be used to +further interpret the result's payload in a specific context. Extensions or +providers can then provide additional extraction functions to pull out +provider- or extension-specific information as well. +*/ +type Result struct { + // Body is the payload of the HTTP response from the server. In most cases, + // this will be the deserialized JSON structure. + Body interface{} + + // Header contains the HTTP header structure from the original response. + Header http.Header + + // Err is an error that occurred during the operation. It's deferred until + // extraction to make it easier to chain the Extract call. + Err error +} + +// ExtractInto allows users to provide an object into which `Extract` will extract +// the `Result.Body`. This would be useful for OpenStack providers that have +// different fields in the response object than OpenStack proper. +func (r Result) ExtractInto(to interface{}) error { + if r.Err != nil { + return r.Err + } + + if reader, ok := r.Body.(io.Reader); ok { + if readCloser, ok := reader.(io.Closer); ok { + defer readCloser.Close() + } + return json.NewDecoder(reader).Decode(to) + } + + b, err := json.Marshal(r.Body) + if err != nil { + return err + } + err = json.Unmarshal(b, to) + + 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 +// a buggy extraction function, we will all love you forever. +func (r Result) PrettyPrintJSON() string { + pretty, err := json.MarshalIndent(r.Body, "", " ") + if err != nil { + panic(err.Error()) + } + return string(pretty) +} + +// ErrResult is an internal type to be used by individual resource packages, but +// its methods will be available on a wide variety of user-facing embedding +// types. +// +// It represents results that only contain a potential error and +// nothing else. Usually, if the operation executed successfully, the Err field +// will be nil; otherwise it will be stocked with a relevant error. Use the +// ExtractErr method +// to cleanly pull it out. +type ErrResult struct { + Result +} + +// ExtractErr is a function that extracts error information, or nil, from a result. +func (r ErrResult) ExtractErr() error { + return r.Err +} + +/* +HeaderResult is an internal type to be used by individual resource packages, but +its methods will be available on a wide variety of user-facing embedding types. + +It represents a result that only contains an error (possibly nil) and an +http.Header. This is used, for example, by the objectstorage packages in +openstack, because most of the operations don't return response bodies, but do +have relevant information in headers. +*/ +type HeaderResult struct { + Result +} + +// ExtractHeader will return the http.Header and error from the HeaderResult. +// +// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader() +func (r HeaderResult) ExtractInto(to interface{}) error { + if r.Err != nil { + return r.Err + } + + tmpHeaderMap := map[string]string{} + for k, v := range r.Header { + if len(v) > 0 { + tmpHeaderMap[k] = v[0] + } + } + + b, err := json.Marshal(tmpHeaderMap) + if err != nil { + return err + } + err = json.Unmarshal(b, to) + + return err +} + +// RFC3339Milli describes a common time format used by some API responses. +const RFC3339Milli = "2006-01-02T15:04:05.999999Z" + +type JSONRFC3339Milli time.Time + +func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error { + b := bytes.NewBuffer(data) + dec := json.NewDecoder(b) + var s string + if err := dec.Decode(&s); err != nil { + return err + } + t, err := time.Parse(RFC3339Milli, s) + if err != nil { + return err + } + *jt = JSONRFC3339Milli(t) + return nil +} + +const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999" + +type JSONRFC3339MilliNoZ time.Time + +func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339MilliNoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339MilliNoZ(t) + return nil +} + +type JSONRFC1123 time.Time + +func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(time.RFC1123, s) + if err != nil { + return err + } + *jt = JSONRFC1123(t) + return nil +} + +type JSONUnix time.Time + +func (jt *JSONUnix) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + unix, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return err + } + t = time.Unix(unix, 0) + *jt = JSONUnix(t) + return nil +} + +// RFC3339NoZ is the time format used in Heat (Orchestration). +const RFC3339NoZ = "2006-01-02T15:04:05" + +type JSONRFC3339NoZ time.Time + +func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339NoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339NoZ(t) + return nil +} + +/* +Link is an internal type to be used in packages of collection resources that are +paginated in a certain way. + +It's a response substructure common to many paginated collection results that is +used to point to related pages. Usually, the one we care about is the one with +Rel field set to "next". +*/ +type Link struct { + Href string `json:"href"` + Rel string `json:"rel"` +} + +/* +ExtractNextURL is an internal function useful for packages of collection +resources that are paginated in a certain way. + +It attempts to extract the "next" URL from slice of Link structs, or +"" if no such URL is present. +*/ +func ExtractNextURL(links []Link) (string, error) { + var url string + + for _, l := range links { + if l.Rel == "next" { + url = l.Href + } + } + + if url == "" { + return "", nil + } + + return url, nil +} diff --git a/vendor/github.com/gophercloud/gophercloud/service_client.go b/vendor/github.com/gophercloud/gophercloud/service_client.go new file mode 100644 index 000000000..7484c67e5 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/service_client.go @@ -0,0 +1,141 @@ +package gophercloud + +import ( + "io" + "net/http" + "strings" +) + +// ServiceClient stores details required to interact with a specific service API implemented by a provider. +// Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient. +type ServiceClient struct { + // ProviderClient is a reference to the provider that implements this service. + *ProviderClient + + // Endpoint is the base URL of the service's API, acquired from a service catalog. + // It MUST end with a /. + Endpoint string + + // ResourceBase is the base URL shared by the resources within a service's API. It should include + // the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used + // as-is, instead. + ResourceBase string + + Microversion string +} + +// ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. +func (client *ServiceClient) ResourceBaseURL() string { + if client.ResourceBase != "" { + return client.ResourceBase + } + return client.Endpoint +} + +// ServiceURL constructs a URL for a resource belonging to this provider. +func (client *ServiceClient) ServiceURL(parts ...string) string { + return client.ResourceBaseURL() + strings.Join(parts, "/") +} + +// 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 = &RequestOpts{} + } + 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("GET", url, opts) +} + +// Post calls `Request` with the "POST" HTTP verb. +func (client *ServiceClient) Post(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + 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) +} + +// Put calls `Request` with the "PUT" HTTP verb. +func (client *ServiceClient) Put(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + 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) +} + +// Patch calls `Request` with the "PATCH" HTTP verb. +func (client *ServiceClient) Patch(url string, JSONBody interface{}, JSONResponse interface{}, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + 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) +} + +// Delete calls `Request` with the "DELETE" HTTP verb. +func (client *ServiceClient) Delete(url string, opts *RequestOpts) (*http.Response, error) { + if opts == nil { + opts = &RequestOpts{} + } + + 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) +} diff --git a/vendor/github.com/rackspace/gophercloud/util.go b/vendor/github.com/gophercloud/gophercloud/util.go similarity index 100% rename from vendor/github.com/rackspace/gophercloud/util.go rename to vendor/github.com/gophercloud/gophercloud/util.go diff --git a/vendor/github.com/rackspace/gophercloud/CONTRIBUTING.md b/vendor/github.com/rackspace/gophercloud/CONTRIBUTING.md deleted file mode 100644 index 6ba5beb85..000000000 --- a/vendor/github.com/rackspace/gophercloud/CONTRIBUTING.md +++ /dev/null @@ -1,274 +0,0 @@ -# Contributing to gophercloud - -- [Getting started](#getting-started) -- [Tests](#tests) -- [Style guide](#basic-style-guide) -- [5 ways to get involved](#5-ways-to-get-involved) - -## Setting up your git workspace - -As a contributor you will need to setup your workspace in a slightly different -way than just downloading it. Here are the basic installation instructions: - -1. Configure your `$GOPATH` and run `go get` as described in the main -[README](/README.md#how-to-install). - -2. Move into the directory that houses your local repository: - - ```bash - cd ${GOPATH}/src/github.com/rackspace/gophercloud - ``` - -3. Fork the `rackspace/gophercloud` repository and update your remote refs. You -will need to rename the `origin` remote branch to `upstream`, and add your -fork as `origin` instead: - - ```bash - git remote rename origin upstream - git remote add origin git@github.com//gophercloud - ``` - -4. Checkout the latest development branch: - - ```bash - git checkout master - ``` - -5. If you're working on something (discussed more in detail below), you will -need to checkout a new feature branch: - - ```bash - git checkout -b my-new-feature - ``` - -Another thing to bear in mind is that you will need to add a few extra -environment variables for acceptance tests - this is documented in our -[acceptance tests readme](/acceptance). - -## Tests - -When working on a new or existing feature, testing will be the backbone of your -work since it helps uncover and prevent regressions in the codebase. There are -two types of test we use in gophercloud: unit tests and acceptance tests, which -are both described below. - -### Unit tests - -Unit tests are the fine-grained tests that establish and ensure the behaviour -of individual units of functionality. We usually test on an -operation-by-operation basis (an operation typically being an API action) with -the use of mocking to set up explicit expectations. Each operation will set up -its HTTP response expectation, and then test how the system responds when fed -this controlled, pre-determined input. - -To make life easier, we've introduced a bunch of test helpers to simplify the -process of testing expectations with assertions: - -```go -import ( - "testing" - - "github.com/rackspace/gophercloud/testhelper" -) - -func TestSomething(t *testing.T) { - result, err := Operation() - - testhelper.AssertEquals(t, "foo", result.Bar) - testhelper.AssertNoErr(t, err) -} - -func TestSomethingElse(t *testing.T) { - testhelper.CheckEquals(t, "expected", "actual") -} -``` - -`AssertEquals` and `AssertNoErr` will throw a fatal error if a value does not -match an expected value or if an error has been declared, respectively. You can -also use `CheckEquals` and `CheckNoErr` for the same purpose; the only difference -being that `t.Errorf` is raised rather than `t.Fatalf`. - -Here is a truncated example of mocked HTTP responses: - -```go -import ( - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - fake "github.com/rackspace/gophercloud/testhelper/client" -) - -func TestGet(t *testing.T) { - // Setup the HTTP request multiplexer and server - th.SetupHTTP() - defer th.TeardownHTTP() - - th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) { - // Test we're using the correct HTTP method - th.TestMethod(t, r, "GET") - - // Test we're setting the auth token - th.TestHeader(t, r, "X-Auth-Token", fake.TokenID) - - // Set the appropriate headers for our mocked response - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - // Set the HTTP body - fmt.Fprintf(w, ` -{ - "network": { - "status": "ACTIVE", - "name": "private-network", - "admin_state_up": true, - "tenant_id": "4fd44f30292945e481c7b8a0c8908869", - "shared": true, - "id": "d32019d3-bc6e-4319-9c1d-6722fc136a22" - } -} - `) - }) - - // Call our API operation - network, err := Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract() - - // Assert no errors and equality - th.AssertNoErr(t, err) - th.AssertEquals(t, n.Status, "ACTIVE") -} -``` - -### Acceptance tests - -As we've already mentioned, unit tests have a very narrow and confined focus - -they test small units of behaviour. Acceptance tests on the other hand have a -far larger scope: they are fully functional tests that test the entire API of a -service in one fell swoop. They don't care about unit isolation or mocking -expectations, they instead do a full run-through and consequently test how the -entire system _integrates_ together. When an API satisfies expectations, it -proves by default that the requirements for a contract have been met. - -Please be aware that acceptance tests will hit a live API - and may incur -service charges from your provider. Although most tests handle their own -teardown procedures, it is always worth manually checking that resources are -deleted after the test suite finishes. - -### Running tests - -To run all tests: - -```bash -go test ./... -``` - -To run all tests with verbose output: - -```bash -go test -v ./... -``` - -To run tests that match certain [build tags](): - -```bash -go test -tags "foo bar" ./... -``` - -To run tests for a particular sub-package: - -```bash -cd ./path/to/package && go test . -``` - -## Basic style guide - -We follow the standard formatting recommendations and language idioms set out -in the [Effective Go](https://golang.org/doc/effective_go.html) guide. It's -definitely worth reading - but the relevant sections are -[formatting](https://golang.org/doc/effective_go.html#formatting) -and [names](https://golang.org/doc/effective_go.html#names). - -## 5 ways to get involved - -There are five main ways you can get involved in our open-source project, and -each is described briefly below. Once you've made up your mind and decided on -your fix, you will need to follow the same basic steps that all submissions are -required to adhere to: - -1. [fork](https://help.github.com/articles/fork-a-repo/) the `rackspace/gophercloud` repository -2. checkout a [new branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches) -3. submit your branch as a [pull request](https://help.github.com/articles/creating-a-pull-request/) - -### 1. Providing feedback - -On of the easiest ways to get readily involved in our project is to let us know -about your experiences using our SDK. Feedback like this is incredibly useful -to us, because it allows us to refine and change features based on what our -users want and expect of us. There are a bunch of ways to get in contact! You -can [ping us](https://developer.rackspace.com/support/) via e-mail, talk to us on irc -(#rackspace-dev on freenode), [tweet us](https://twitter.com/rackspace), or -submit an issue on our [bug tracker](/issues). Things you might like to tell us -are: - -* how easy was it to start using our SDK? -* did it meet your expectations? If not, why not? -* did our documentation help or hinder you? -* what could we improve in general? - -### 2. Fixing bugs - -If you want to start fixing open bugs, we'd really appreciate that! Bug fixing -is central to any project. The best way to get started is by heading to our -[bug tracker](https://github.com/rackspace/gophercloud/issues) and finding open -bugs that you think nobody is working on. It might be useful to comment on the -thread to see the current state of the issue and if anybody has made any -breakthroughs on it so far. - -### 3. Improving documentation - -We have three forms of documentation: - -* short README documents that briefly introduce a topic -* reference documentation on [godoc.org](http://godoc.org) that is automatically -generated from source code comments -* user documentation on our [homepage](http://gophercloud.io) that includes -getting started guides, installation guides and code samples - -If you feel that a certain section could be improved - whether it's to clarify -ambiguity, correct a technical mistake, or to fix a grammatical error - please -feel entitled to do so! We welcome doc pull requests with the same childlike -enthusiasm as any other contribution! - -### 4. Optimizing existing features - -If you would like to improve or optimize an existing feature, please be aware -that we adhere to [semantic versioning](http://semver.org) - which means that -we cannot introduce breaking changes to the API without a major version change -(v1.x -> v2.x). Making that leap is a big step, so we encourage contributors to -refactor rather than rewrite. Running tests will prevent regression and avoid -the possibility of breaking somebody's current implementation. - -Another tip is to keep the focus of your work as small as possible - try not to -introduce a change that affects lots and lots of files because it introduces -added risk and increases the cognitive load on the reviewers checking your -work. Change-sets which are easily understood and will not negatively impact -users are more likely to be integrated quickly. - -Lastly, if you're seeking to optimize a particular operation, you should try to -demonstrate a negative performance impact - perhaps using go's inbuilt -[benchmark capabilities](http://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go). - -### 5. Working on a new feature - -If you've found something we've left out, definitely feel free to start work on -introducing that feature. It's always useful to open an issue or submit a pull -request early on to indicate your intent to a core contributor - this enables -quick/early feedback and can help steer you in the right direction by avoiding -known issues. It might also help you avoid losing time implementing something -that might not ever work. One tip is to prefix your Pull Request issue title -with [wip] - then people know it's a work in progress. - -You must ensure that all of your work is well tested - both in terms of unit -and acceptance tests. Untested code will not be merged because it introduces -too much of a risk to end-users. - -Happy hacking! diff --git a/vendor/github.com/rackspace/gophercloud/CONTRIBUTORS.md b/vendor/github.com/rackspace/gophercloud/CONTRIBUTORS.md deleted file mode 100644 index 63beb30b2..000000000 --- a/vendor/github.com/rackspace/gophercloud/CONTRIBUTORS.md +++ /dev/null @@ -1,13 +0,0 @@ -Contributors -============ - -| Name | Email | -| ---- | ----- | -| Samuel A. Falvo II | -| Glen Campbell | -| Jesse Noller | -| Jon Perritt | -| Ash Wilson | -| Jamie Hannaford | -| Don Schenck | don.schenck@rackspace.com> -| Joe Topjian | diff --git a/vendor/github.com/rackspace/gophercloud/UPGRADING.md b/vendor/github.com/rackspace/gophercloud/UPGRADING.md deleted file mode 100644 index 76a94d570..000000000 --- a/vendor/github.com/rackspace/gophercloud/UPGRADING.md +++ /dev/null @@ -1,338 +0,0 @@ -# Upgrading to v1.0.0 - -With the arrival of this new major version increment, the unfortunate news is -that breaking changes have been introduced to existing services. The API -has been completely rewritten from the ground up to make the library more -extensible, maintainable and easy-to-use. - -Below we've compiled upgrade instructions for the various services that -existed before. If you have a specific issue that is not addressed below, -please [submit an issue](/issues/new) or -[e-mail our support team](https://developer.rackspace.com/support/). - -* [Authentication](#authentication) -* [Servers](#servers) - * [List servers](#list-servers) - * [Get server details](#get-server-details) - * [Create server](#create-server) - * [Resize server](#resize-server) - * [Reboot server](#reboot-server) - * [Update server](#update-server) - * [Rebuild server](#rebuild-server) - * [Change admin password](#change-admin-password) - * [Delete server](#delete-server) - * [Rescue server](#rescue-server) -* [Images and flavors](#images-and-flavors) - * [List images](#list-images) - * [List flavors](#list-flavors) - * [Create/delete image](#createdelete-image) -* [Other](#other) - * [List keypairs](#list-keypairs) - * [Create/delete keypair](#createdelete-keypair) - * [List IP addresses](#list-ip-addresses) - -# Authentication - -One of the major differences that this release introduces is the level of -sub-packaging to differentiate between services and providers. You now have -the option of authenticating with OpenStack and other providers (like Rackspace). - -To authenticate with a vanilla OpenStack installation, you can either specify -your credentials like this: - -```go -import ( - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/openstack" -) - -opts := gophercloud.AuthOptions{ - IdentityEndpoint: "https://my-openstack.com:5000/v2.0", - Username: "{username}", - Password: "{password}", - TenantID: "{tenant_id}", -} -``` - -Or have them pulled in through environment variables, like this: - -```go -opts, err := openstack.AuthOptionsFromEnv() -``` - -Once you have your `AuthOptions` struct, you pass it in to get back a `Provider`, -like so: - -```go -provider, err := openstack.AuthenticatedClient(opts) -``` - -This provider is the top-level structure that all services are created from. - -# Servers - -Before you can interact with the Compute API, you need to retrieve a -`gophercloud.ServiceClient`. To do this: - -```go -// Define your region, etc. -opts := gophercloud.EndpointOpts{Region: "RegionOne"} - -client, err := openstack.NewComputeV2(provider, opts) -``` - -## List servers - -All operations that involve API collections (servers, flavors, images) now use -the `pagination.Pager` interface. This interface represents paginated entities -that can be iterated over. - -Once you have a Pager, you can then pass a callback function into its `EachPage` -method, and this will allow you to traverse over the collection and execute -arbitrary functionality. So, an example with list servers: - -```go -import ( - "fmt" - "github.com/rackspace/gophercloud/pagination" - "github.com/rackspace/gophercloud/openstack/compute/v2/servers" -) - -// We have the option of filtering the server list. If we want the full -// collection, leave it as an empty struct or nil -opts := servers.ListOpts{Name: "server_1"} - -// Retrieve a pager (i.e. a paginated collection) -pager := servers.List(client, opts) - -// Define an anonymous function to be executed on each page's iteration -err := pager.EachPage(func(page pagination.Page) (bool, error) { - serverList, err := servers.ExtractServers(page) - - // `s' will be a servers.Server struct - for _, s := range serverList { - fmt.Printf("We have a server. ID=%s, Name=%s", s.ID, s.Name) - } -}) -``` - -## Get server details - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -// Get the HTTP result -response := servers.Get(client, "server_id") - -// Extract a Server struct from the response -server, err := response.Extract() -``` - -## Create server - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -// Define our options -opts := servers.CreateOpts{ - Name: "new_server", - FlavorRef: "flavorID", - ImageRef: "imageID", -} - -// Get our response -response := servers.Create(client, opts) - -// Extract -server, err := response.Extract() -``` - -## Change admin password - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -result := servers.ChangeAdminPassword(client, "server_id", "newPassword_&123") -``` - -## Resize server - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -result := servers.Resize(client, "server_id", "new_flavor_id") -``` - -## Reboot server - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -// You have a choice of two reboot methods: servers.SoftReboot or servers.HardReboot -result := servers.Reboot(client, "server_id", servers.SoftReboot) -``` - -## Update server - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -opts := servers.UpdateOpts{Name: "new_name"} - -server, err := servers.Update(client, "server_id", opts).Extract() -``` - -## Rebuild server - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -// You have the option of specifying additional options -opts := RebuildOpts{ - Name: "new_name", - AdminPass: "admin_password", - ImageID: "image_id", - Metadata: map[string]string{"owner": "me"}, -} - -result := servers.Rebuild(client, "server_id", opts) - -// You can extract a servers.Server struct from the HTTP response -server, err := result.Extract() -``` - -## Delete server - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/servers" - -response := servers.Delete(client, "server_id") -``` - -## Rescue server - -The server rescue extension for Compute is not currently supported. - -# Images and flavors - -## List images - -As with listing servers (see above), you first retrieve a Pager, and then pass -in a callback over each page: - -```go -import ( - "github.com/rackspace/gophercloud/pagination" - "github.com/rackspace/gophercloud/openstack/compute/v2/images" -) - -// We have the option of filtering the image list. If we want the full -// collection, leave it as an empty struct -opts := images.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", Name: "Ubuntu 12.04"} - -// Retrieve a pager (i.e. a paginated collection) -pager := images.List(client, opts) - -// Define an anonymous function to be executed on each page's iteration -err := pager.EachPage(func(page pagination.Page) (bool, error) { - imageList, err := images.ExtractImages(page) - - for _, i := range imageList { - // "i" will be an images.Image - } -}) -``` - -## List flavors - -```go -import ( - "github.com/rackspace/gophercloud/pagination" - "github.com/rackspace/gophercloud/openstack/compute/v2/flavors" -) - -// We have the option of filtering the flavor list. If we want the full -// collection, leave it as an empty struct -opts := flavors.ListOpts{ChangesSince: "2014-01-01T01:02:03Z", MinRAM: 4} - -// Retrieve a pager (i.e. a paginated collection) -pager := flavors.List(client, opts) - -// Define an anonymous function to be executed on each page's iteration -err := pager.EachPage(func(page pagination.Page) (bool, error) { - flavorList, err := networks.ExtractFlavors(page) - - for _, f := range flavorList { - // "f" will be a flavors.Flavor - } -}) -``` - -## Create/delete image - -Image management has been shifted to Glance, but unfortunately this service is -not supported as of yet. You can, however, list Compute images like so: - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/images" - -// Retrieve a pager (i.e. a paginated collection) -pager := images.List(client, opts) - -// Define an anonymous function to be executed on each page's iteration -err := pager.EachPage(func(page pagination.Page) (bool, error) { - imageList, err := images.ExtractImages(page) - - for _, i := range imageList { - // "i" will be an images.Image - } -}) -``` - -# Other - -## List keypairs - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" - -// Retrieve a pager (i.e. a paginated collection) -pager := keypairs.List(client, opts) - -// Define an anonymous function to be executed on each page's iteration -err := pager.EachPage(func(page pagination.Page) (bool, error) { - keyList, err := keypairs.ExtractKeyPairs(page) - - for _, k := range keyList { - // "k" will be a keypairs.KeyPair - } -}) -``` - -## Create/delete keypairs - -To create a new keypair, you need to specify its name and, optionally, a -pregenerated OpenSSH-formatted public key. - -```go -import "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" - -opts := keypairs.CreateOpts{ - Name: "new_key", - PublicKey: "...", -} - -response := keypairs.Create(client, opts) - -key, err := response.Extract() -``` - -To delete an existing keypair: - -```go -response := keypairs.Delete(client, "keypair_id") -``` - -## List IP addresses - -This operation is not currently supported. diff --git a/vendor/github.com/rackspace/gophercloud/acceptance/README.md b/vendor/github.com/rackspace/gophercloud/acceptance/README.md deleted file mode 100644 index 3199837c2..000000000 --- a/vendor/github.com/rackspace/gophercloud/acceptance/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# Gophercloud Acceptance tests - -The purpose of these acceptance tests is to validate that SDK features meet -the requirements of a contract - to consumers, other parts of the library, and -to a remote API. - -> **Note:** Because every test will be run against a real API endpoint, you -> may incur bandwidth and service charges for all the resource usage. These -> tests *should* remove their remote products automatically. However, there may -> be certain cases where this does not happen; always double-check to make sure -> you have no stragglers left behind. - -### Step 1. Set environment variables - -A lot of tests rely on environment variables for configuration - so you will need -to set them before running the suite. If you're testing against pure OpenStack APIs, -you can download a file that contains all of these variables for you: just visit -the `project/access_and_security` page in your control panel and click the "Download -OpenStack RC File" button at the top right. For all other providers, you will need -to set them manually. - -#### Authentication - -|Name|Description| -|---|---| -|`OS_USERNAME`|Your API username| -|`OS_PASSWORD`|Your API password| -|`OS_AUTH_URL`|The identity URL you need to authenticate| -|`OS_TENANT_NAME`|Your API tenant name| -|`OS_TENANT_ID`|Your API tenant ID| -|`RS_USERNAME`|Your Rackspace username| -|`RS_API_KEY`|Your Rackspace API key| - -#### General - -|Name|Description| -|---|---| -|`OS_REGION_NAME`|The region you want your resources to reside in| -|`RS_REGION`|Rackspace region you want your resource to reside in| - -#### Compute - -|Name|Description| -|---|---| -|`OS_IMAGE_ID`|The ID of the image your want your server to be based on| -|`OS_FLAVOR_ID`|The ID of the flavor you want your server to be based on| -|`OS_FLAVOR_ID_RESIZE`|The ID of the flavor you want your server to be resized to| -|`RS_IMAGE_ID`|The ID of the image you want servers to be created with| -|`RS_FLAVOR_ID`|The ID of the flavor you want your server to be created with| - -### 2. Run the test suite - -From the root directory, run: - -``` -./script/acceptancetest -``` diff --git a/vendor/github.com/rackspace/gophercloud/auth_options.go b/vendor/github.com/rackspace/gophercloud/auth_options.go deleted file mode 100644 index d26e16ac1..000000000 --- a/vendor/github.com/rackspace/gophercloud/auth_options.go +++ /dev/null @@ -1,50 +0,0 @@ -package gophercloud - -/* -AuthOptions stores information needed to authenticate to an OpenStack cluster. -You can populate one manually, or use a provider's AuthOptionsFromEnv() function -to read relevant information from the standard environment variables. Pass one -to a provider's AuthenticatedClient function to authenticate and obtain a -ProviderClient representing an active session on that provider. - -Its fields are the union of those recognized by each identity implementation and -provider. -*/ -type AuthOptions struct { - // IdentityEndpoint specifies the HTTP endpoint that is required to work with - // the Identity API of the appropriate version. While it's ultimately needed by - // all of the identity services, it will often be populated by a provider-level - // function. - IdentityEndpoint string - - // Username is required if using Identity V2 API. Consult with your provider's - // control panel to discover your account's username. In Identity V3, either - // UserID or a combination of Username and DomainID or DomainName are needed. - Username, UserID string - - // Exactly one of Password or APIKey is required for the Identity V2 and V3 - // APIs. Consult with your provider's control panel to discover your account's - // preferred method of authentication. - Password, APIKey string - - // At most one of DomainID and DomainName must be provided if using Username - // with Identity V3. Otherwise, either are optional. - DomainID, DomainName string - - // The TenantID and TenantName fields are optional for the Identity V2 API. - // Some providers allow you to specify a TenantName instead of the TenantId. - // Some require both. Your provider's authentication policies will determine - // how these fields influence authentication. - TenantID, TenantName string - - // AllowReauth should be set to true if you grant permission for Gophercloud to - // cache your credentials in memory, and to allow Gophercloud to attempt to - // re-authenticate automatically if/when your token expires. If you set it to - // false, it will not cache these settings, but re-authentication will not be - // possible. This setting defaults to false. - AllowReauth bool - - // TokenID allows users to authenticate (possibly as another user) with an - // authentication token ID. - TokenID string -} diff --git a/vendor/github.com/rackspace/gophercloud/auth_results.go b/vendor/github.com/rackspace/gophercloud/auth_results.go deleted file mode 100644 index 856a23382..000000000 --- a/vendor/github.com/rackspace/gophercloud/auth_results.go +++ /dev/null @@ -1,14 +0,0 @@ -package gophercloud - -import "time" - -// AuthResults [deprecated] is a leftover type from the v0.x days. It was -// intended to describe common functionality among identity service results, but -// is not actually used anywhere. -type AuthResults interface { - // TokenID returns the token's ID value from the authentication response. - TokenID() (string, error) - - // ExpiresAt retrieves the token's expiration time. - ExpiresAt() (time.Time, error) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/fixtures.go deleted file mode 100644 index 0ed7de9f1..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/common/extensions/fixtures.go +++ /dev/null @@ -1,91 +0,0 @@ -// +build fixtures - -package extensions - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - "github.com/rackspace/gophercloud/testhelper/client" -) - -// ListOutput provides a single page of Extension results. -const ListOutput = ` -{ - "extensions": [ - { - "updated": "2013-01-20T00:00:00-00:00", - "name": "Neutron Service Type Management", - "links": [], - "namespace": "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", - "alias": "service-type", - "description": "API for retrieving service providers for Neutron advanced services" - } - ] -}` - -// GetOutput provides a single Extension result. -const GetOutput = ` -{ - "extension": { - "updated": "2013-02-03T10:00:00-00:00", - "name": "agent", - "links": [], - "namespace": "http://docs.openstack.org/ext/agent/api/v2.0", - "alias": "agent", - "description": "The agent management extension." - } -} -` - -// ListedExtension is the Extension that should be parsed from ListOutput. -var ListedExtension = Extension{ - Updated: "2013-01-20T00:00:00-00:00", - Name: "Neutron Service Type Management", - Links: []interface{}{}, - Namespace: "http://docs.openstack.org/ext/neutron/service-type/api/v1.0", - Alias: "service-type", - Description: "API for retrieving service providers for Neutron advanced services", -} - -// ExpectedExtensions is a slice containing the Extension that should be parsed from ListOutput. -var ExpectedExtensions = []Extension{ListedExtension} - -// SingleExtension is the Extension that should be parsed from GetOutput. -var SingleExtension = &Extension{ - Updated: "2013-02-03T10:00:00-00:00", - Name: "agent", - Links: []interface{}{}, - Namespace: "http://docs.openstack.org/ext/agent/api/v2.0", - Alias: "agent", - Description: "The agent management extension.", -} - -// HandleListExtensionsSuccessfully creates an HTTP handler at `/extensions` on the test handler -// mux that response with a list containing a single tenant. -func HandleListExtensionsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/extensions", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - - fmt.Fprintf(w, ListOutput) - }) -} - -// HandleGetExtensionSuccessfully creates an HTTP handler at `/extensions/agent` that responds with -// a JSON payload corresponding to SingleExtension. -func HandleGetExtensionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/extensions/agent", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - - fmt.Fprintf(w, GetOutput) - }) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/doc.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/doc.go deleted file mode 100644 index f74f58ce8..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -// Package floatingip provides the ability to manage floating ips through -// nova-network -package floatingip diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/fixtures.go deleted file mode 100644 index e47fa4ccd..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/fixtures.go +++ /dev/null @@ -1,193 +0,0 @@ -// +build fixtures - -package floatingip - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - "github.com/rackspace/gophercloud/testhelper/client" -) - -// ListOutput is a sample response to a List call. -const ListOutput = ` -{ - "floating_ips": [ - { - "fixed_ip": null, - "id": 1, - "instance_id": null, - "ip": "10.10.10.1", - "pool": "nova" - }, - { - "fixed_ip": "166.78.185.201", - "id": 2, - "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - "ip": "10.10.10.2", - "pool": "nova" - } - ] -} -` - -// GetOutput is a sample response to a Get call. -const GetOutput = ` -{ - "floating_ip": { - "fixed_ip": "166.78.185.201", - "id": 2, - "instance_id": "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - "ip": "10.10.10.2", - "pool": "nova" - } -} -` - -// CreateOutput is a sample response to a Post call -const CreateOutput = ` -{ - "floating_ip": { - "fixed_ip": null, - "id": 1, - "instance_id": null, - "ip": "10.10.10.1", - "pool": "nova" - } -} -` - -// FirstFloatingIP is the first result in ListOutput. -var FirstFloatingIP = FloatingIP{ - ID: "1", - IP: "10.10.10.1", - Pool: "nova", -} - -// SecondFloatingIP is the first result in ListOutput. -var SecondFloatingIP = FloatingIP{ - FixedIP: "166.78.185.201", - ID: "2", - InstanceID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0", - IP: "10.10.10.2", - Pool: "nova", -} - -// ExpectedFloatingIPsSlice is the slice of results that should be parsed -// from ListOutput, in the expected order. -var ExpectedFloatingIPsSlice = []FloatingIP{FirstFloatingIP, SecondFloatingIP} - -// CreatedFloatingIP is the parsed result from CreateOutput. -var CreatedFloatingIP = FloatingIP{ - ID: "1", - IP: "10.10.10.1", - Pool: "nova", -} - -// HandleListSuccessfully configures the test server to respond to a List request. -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) - }) -} - -// HandleGetSuccessfully configures the test server to respond to a Get request -// for an existing floating ip -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips/2", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) - }) -} - -// HandleCreateSuccessfully configures the test server to respond to a Create request -// for a new floating ip -func HandleCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, ` -{ - "pool": "nova" -} -`) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutput) - }) -} - -// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a -// an existing floating ip -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-floating-ips/1", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleAssociateSuccessfully configures the test server to respond to a Post request -// to associate an allocated floating IP -func HandleAssociateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, ` -{ - "addFloatingIp": { - "address": "10.10.10.2" - } -} -`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleFixedAssociateSucessfully configures the test server to respond to a Post request -// to associate an allocated floating IP with a specific fixed IP address -func HandleAssociateFixedSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, ` -{ - "addFloatingIp": { - "address": "10.10.10.2", - "fixed_address": "166.78.185.201" - } -} -`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleDisassociateSuccessfully configures the test server to respond to a Post request -// to disassociate an allocated floating IP -func HandleDisassociateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, ` -{ - "removeFloatingIp": { - "address": "10.10.10.2" - } -} -`) - - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go deleted file mode 100644 index 820646297..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip/requests.go +++ /dev/null @@ -1,171 +0,0 @@ -package floatingip - -import ( - "errors" - - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" -) - -// List returns a Pager that allows you to iterate over a collection of FloatingIPs. -func List(client *gophercloud.ServiceClient) pagination.Pager { - return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { - return FloatingIPsPage{pagination.SinglePageBase(r)} - }) -} - -// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the -// CreateOpts struct in this package does. -type CreateOptsBuilder interface { - ToFloatingIPCreateMap() (map[string]interface{}, error) -} - -// CreateOpts specifies a Floating IP allocation request -type CreateOpts struct { - // Pool is the pool of floating IPs to allocate one from - Pool string -} - -// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance -type AssociateOpts struct { - // ServerID is the UUID of the server - ServerID string - - // FixedIP is an optional fixed IP address of the server - FixedIP string - - // FloatingIP is the floating IP to associate with an instance - FloatingIP string -} - -// ToFloatingIPCreateMap constructs a request body from CreateOpts. -func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) { - if opts.Pool == "" { - return nil, errors.New("Missing field required for floating IP creation: Pool") - } - - return map[string]interface{}{"pool": opts.Pool}, nil -} - -// ToAssociateMap constructs a request body from AssociateOpts. -func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) { - if opts.ServerID == "" { - return nil, errors.New("Required field missing for floating IP association: ServerID") - } - - if opts.FloatingIP == "" { - return nil, errors.New("Required field missing for floating IP association: FloatingIP") - } - - associateInfo := map[string]interface{}{ - "serverId": opts.ServerID, - "floatingIp": opts.FloatingIP, - "fixedIp": opts.FixedIP, - } - - return associateInfo, nil - -} - -// Create requests the creation of a new floating IP -func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult { - var res CreateResult - - reqBody, err := opts.ToFloatingIPCreateMap() - if err != nil { - res.Err = err - return res - } - - _, res.Err = client.Post(createURL(client), reqBody, &res.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200}, - }) - return res -} - -// Get returns data about a previously created FloatingIP. -func Get(client *gophercloud.ServiceClient, id string) GetResult { - var res GetResult - _, res.Err = client.Get(getURL(client, id), &res.Body, nil) - return res -} - -// Delete requests the deletion of a previous allocated FloatingIP. -func Delete(client *gophercloud.ServiceClient, id string) DeleteResult { - var res DeleteResult - _, res.Err = client.Delete(deleteURL(client, id), nil) - return res -} - -// association / disassociation - -// Associate pairs an allocated floating IP with an instance -// Deprecated. Use AssociateInstance. -func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult { - var res AssociateResult - - addFloatingIp := make(map[string]interface{}) - addFloatingIp["address"] = fip - reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp} - - _, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil) - return res -} - -// AssociateInstance pairs an allocated floating IP with an instance. -func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult { - var res AssociateResult - - associateInfo, err := opts.ToAssociateMap() - if err != nil { - res.Err = err - return res - } - - addFloatingIp := make(map[string]interface{}) - addFloatingIp["address"] = associateInfo["floatingIp"].(string) - - // fixedIp is not required - if associateInfo["fixedIp"] != "" { - addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string) - } - - serverId := associateInfo["serverId"].(string) - - reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp} - _, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil) - return res -} - -// Disassociate decouples an allocated floating IP from an instance -// Deprecated. Use DisassociateInstance. -func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult { - var res DisassociateResult - - removeFloatingIp := make(map[string]interface{}) - removeFloatingIp["address"] = fip - reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp} - - _, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil) - return res -} - -// DisassociateInstance decouples an allocated floating IP from an instance -func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult { - var res DisassociateResult - - associateInfo, err := opts.ToAssociateMap() - if err != nil { - res.Err = err - return res - } - - removeFloatingIp := make(map[string]interface{}) - removeFloatingIp["address"] = associateInfo["floatingIp"].(string) - reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp} - - serverId := associateInfo["serverId"].(string) - - _, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil) - return res -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/fixtures.go deleted file mode 100644 index d10af99d0..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs/fixtures.go +++ /dev/null @@ -1,171 +0,0 @@ -// +build fixtures - -package keypairs - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - "github.com/rackspace/gophercloud/testhelper/client" -) - -// ListOutput is a sample response to a List call. -const ListOutput = ` -{ - "keypairs": [ - { - "keypair": { - "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", - "name": "firstkey", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n" - } - }, - { - "keypair": { - "fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", - "name": "secondkey", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n" - } - } - ] -} -` - -// GetOutput is a sample response to a Get call. -const GetOutput = ` -{ - "keypair": { - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", - "name": "firstkey", - "fingerprint": "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a" - } -} -` - -// CreateOutput is a sample response to a Create call. -const CreateOutput = ` -{ - "keypair": { - "fingerprint": "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", - "name": "createdkey", - "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", - "user_id": "fake" - } -} -` - -// ImportOutput is a sample response to a Create call that provides its own public key. -const ImportOutput = ` -{ - "keypair": { - "fingerprint": "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", - "name": "importedkey", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova", - "user_id": "fake" - } -} -` - -// FirstKeyPair is the first result in ListOutput. -var FirstKeyPair = KeyPair{ - Name: "firstkey", - Fingerprint: "15:b0:f8:b3:f9:48:63:71:cf:7b:5b:38:6d:44:2d:4a", - PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+Eo/RZRngaGTkFs7I62ZjsIlO79KklKbMXi8F+KITD4bVQHHn+kV+4gRgkgCRbdoDqoGfpaDFs877DYX9n4z6FrAIZ4PES8TNKhatifpn9NdQYWA+IkU8CuvlEKGuFpKRi/k7JLos/gHi2hy7QUwgtRvcefvD/vgQZOVw/mGR9Q== Generated by Nova\n", -} - -// SecondKeyPair is the second result in ListOutput. -var SecondKeyPair = KeyPair{ - Name: "secondkey", - Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", - PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", -} - -// ExpectedKeyPairSlice is the slice of results that should be parsed from ListOutput, in the expected -// order. -var ExpectedKeyPairSlice = []KeyPair{FirstKeyPair, SecondKeyPair} - -// CreatedKeyPair is the parsed result from CreatedOutput. -var CreatedKeyPair = KeyPair{ - Name: "createdkey", - Fingerprint: "35:9d:d0:c3:4a:80:d3:d8:86:f1:ca:f7:df:c4:f9:d8", - PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7DUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5Q== Generated by Nova\n", - PrivateKey: "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQC9mC3WZN9UGLxgPBpP7H5jZMc6pKwOoSgre8yun6REFktn/Kz7\nDUt9jaR1UJyRzHxITfCfAIgSxPdGqB/oF1suMyWgu5i0625vavLB5z5kC8Hq3qZJ\n9zJO1poE1kyD+htiTtPWJ88e12xuH2XB/CZN9OpEiF98hAagiOE0EnOS5QIDAQAB\nAoGAE5XO1mDhORy9COvsg+kYPUhB1GsCYxh+v88wG7HeFDKBY6KUc/Kxo6yoGn5T\nTjRjekyi2KoDZHz4VlIzyZPwFS4I1bf3oCunVoAKzgLdmnTtvRNMC5jFOGc2vUgP\n9bSyRj3S1R4ClVk2g0IDeagko/jc8zzLEYuIK+fbkds79YECQQDt3vcevgegnkga\ntF4NsDmmBPRkcSHCqrANP/7vFcBQN3czxeYYWX3DK07alu6GhH1Y4sHbdm616uU0\nll7xbDzxAkEAzAtN2IyftNygV2EGiaGgqLyo/tD9+Vui2qCQplqe4jvWh/5Sparl\nOjmKo+uAW+hLrLVMnHzRWxbWU8hirH5FNQJATO+ZxCK4etXXAnQmG41NCAqANWB2\nB+2HJbH2NcQ2QHvAHUm741JGn/KI/aBlo7KEjFRDWUVUB5ji64BbUwCsMQJBAIku\nLGcjnBf/oLk+XSPZC2eGd2Ph5G5qYmH0Q2vkTx+wtTn3DV+eNsDfgMtWAJVJ5t61\ngU1QSXyhLPVlKpnnxuUCQC+xvvWjWtsLaFtAsZywJiqLxQzHts8XLGZptYJ5tLWV\nrtmYtBcJCN48RrgQHry/xWYeA4K/AFQpXfNPgprQ96Q=\n-----END RSA PRIVATE KEY-----\n", - UserID: "fake", -} - -// ImportedKeyPair is the parsed result from ImportOutput. -var ImportedKeyPair = KeyPair{ - Name: "importedkey", - Fingerprint: "1e:2c:9b:56:79:4b:45:77:f9:ca:7a:98:2c:b0:d5:3c", - PublicKey: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova", - UserID: "fake", -} - -// HandleListSuccessfully configures the test server to respond to a List request. -func HandleListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ListOutput) - }) -} - -// HandleGetSuccessfully configures the test server to respond to a Get request for "firstkey". -func HandleGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-keypairs/firstkey", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, GetOutput) - }) -} - -// HandleCreateSuccessfully configures the test server to respond to a Create request for a new -// keypair called "createdkey". -func HandleCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ "keypair": { "name": "createdkey" } }`) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, CreateOutput) - }) -} - -// HandleImportSuccessfully configures the test server to respond to an Import request for an -// existing keypair called "importedkey". -func HandleImportSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-keypairs", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, ` - { - "keypair": { - "name": "importedkey", - "public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDx8nkQv/zgGgB4rMYmIf+6A4l6Rr+o/6lHBQdW5aYd44bd8JttDCE/F/pNRr0lRE+PiqSPO8nDPHw0010JeMH9gYgnnFlyY3/OcJ02RhIPyyxYpv9FhY+2YiUkpwFOcLImyrxEsYXpD/0d3ac30bNH6Sw9JD9UZHYcpSxsIbECHw== Generated by Nova" - } - } - `) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, ImportOutput) - }) -} - -// HandleDeleteSuccessfully configures the test server to respond to a Delete request for a -// keypair called "deletedkey". -func HandleDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/os-keypairs/deletedkey", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/fixtures.go deleted file mode 100644 index 670828a98..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/fixtures.go +++ /dev/null @@ -1,27 +0,0 @@ -package startstop - -import ( - "net/http" - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - "github.com/rackspace/gophercloud/testhelper/client" -) - -func mockStartServerResponse(t *testing.T, id string) { - th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{"os-start": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} - -func mockStopServerResponse(t *testing.T, id string) { - th.Mux.HandleFunc("/servers/"+id+"/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{"os-stop": null}`) - w.WriteHeader(http.StatusAccepted) - }) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go deleted file mode 100644 index 0e090e69f..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop/requests.go +++ /dev/null @@ -1,23 +0,0 @@ -package startstop - -import "github.com/rackspace/gophercloud" - -func actionURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("servers", id, "action") -} - -// Start is the operation responsible for starting a Compute server. -func Start(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { - var res gophercloud.ErrResult - reqBody := map[string]interface{}{"os-start": nil} - _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) - return res -} - -// Stop is the operation responsible for stopping a Compute server. -func Stop(client *gophercloud.ServiceClient, id string) gophercloud.ErrResult { - var res gophercloud.ErrResult - reqBody := map[string]interface{}{"os-stop": nil} - _, res.Err = client.Post(actionURL(client, id), reqBody, nil, nil) - return res -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/results.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/results.go deleted file mode 100644 index 8dddd705c..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/flavors/results.go +++ /dev/null @@ -1,122 +0,0 @@ -package flavors - -import ( - "errors" - "reflect" - - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" -) - -// ErrCannotInterpret is returned by an Extract call if the response body doesn't have the expected structure. -var ErrCannotInterpet = errors.New("Unable to interpret a response body.") - -// GetResult temporarily holds the response from a Get call. -type GetResult struct { - gophercloud.Result -} - -// Extract provides access to the individual Flavor returned by the Get function. -func (gr GetResult) Extract() (*Flavor, error) { - if gr.Err != nil { - return nil, gr.Err - } - - var result struct { - Flavor Flavor `mapstructure:"flavor"` - } - - cfg := &mapstructure.DecoderConfig{ - DecodeHook: defaulter, - Result: &result, - } - decoder, err := mapstructure.NewDecoder(cfg) - if err != nil { - return nil, err - } - err = decoder.Decode(gr.Body) - return &result.Flavor, err -} - -// Flavor records represent (virtual) hardware configurations for server resources in a region. -type Flavor struct { - // The Id field contains the flavor's unique identifier. - // For example, this identifier will be useful when specifying which hardware configuration to use for a new server instance. - ID string `mapstructure:"id"` - - // The Disk and RA< fields provide a measure of storage space offered by the flavor, in GB and MB, respectively. - Disk int `mapstructure:"disk"` - RAM int `mapstructure:"ram"` - - // The Name field provides a human-readable moniker for the flavor. - Name string `mapstructure:"name"` - - RxTxFactor float64 `mapstructure:"rxtx_factor"` - - // Swap indicates how much space is reserved for swap. - // If not provided, this field will be set to 0. - Swap int `mapstructure:"swap"` - - // VCPUs indicates how many (virtual) CPUs are available for this flavor. - VCPUs int `mapstructure:"vcpus"` -} - -// FlavorPage contains a single page of the response from a List call. -type FlavorPage struct { - pagination.LinkedPageBase -} - -// IsEmpty determines if a page contains any results. -func (p FlavorPage) IsEmpty() (bool, error) { - flavors, err := ExtractFlavors(p) - if err != nil { - return true, err - } - return len(flavors) == 0, nil -} - -// NextPageURL uses the response's embedded link reference to navigate to the next page of results. -func (p FlavorPage) NextPageURL() (string, error) { - type resp struct { - Links []gophercloud.Link `mapstructure:"flavors_links"` - } - - var r resp - err := mapstructure.Decode(p.Body, &r) - if err != nil { - return "", err - } - - return gophercloud.ExtractNextURL(r.Links) -} - -func defaulter(from, to reflect.Kind, v interface{}) (interface{}, error) { - if (from == reflect.String) && (to == reflect.Int) { - return 0, nil - } - return v, nil -} - -// ExtractFlavors provides access to the list of flavors in a page acquired from the List operation. -func ExtractFlavors(page pagination.Page) ([]Flavor, error) { - casted := page.(FlavorPage).Body - var container struct { - Flavors []Flavor `mapstructure:"flavors"` - } - - cfg := &mapstructure.DecoderConfig{ - DecodeHook: defaulter, - Result: &container, - } - decoder, err := mapstructure.NewDecoder(cfg) - if err != nil { - return container.Flavors, err - } - err = decoder.Decode(casted) - if err != nil { - return container.Flavors, err - } - - return container.Flavors, nil -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go deleted file mode 100644 index 85cea70a8..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/compute/v2/servers/fixtures.go +++ /dev/null @@ -1,692 +0,0 @@ -// +build fixtures - -package servers - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - "github.com/rackspace/gophercloud/testhelper/client" -) - -// ServerListBody contains the canned body of a servers.List response. -const ServerListBody = ` -{ - "servers": [ - { - "status": "ACTIVE", - "updated": "2014-09-25T13:10:10Z", - "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - "OS-EXT-SRV-ATTR:host": "devstack", - "addresses": { - "private": [ - { - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b", - "version": 4, - "addr": "10.0.0.32", - "OS-EXT-IPS:type": "fixed" - } - ] - }, - "links": [ - { - "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", - "rel": "self" - }, - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", - "rel": "bookmark" - } - ], - "key_name": null, - "image": { - "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": [ - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", - "rel": "bookmark" - } - ] - }, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "OS-EXT-SRV-ATTR:instance_name": "instance-0000001e", - "OS-SRV-USG:launched_at": "2014-09-25T13:10:10.000000", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", - "rel": "bookmark" - } - ] - }, - "id": "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", - "security_groups": [ - { - "name": "default" - } - ], - "OS-SRV-USG:terminated_at": null, - "OS-EXT-AZ:availability_zone": "nova", - "user_id": "9349aff8be7545ac9d2f1d00999a23cd", - "name": "herp", - "created": "2014-09-25T13:10:02Z", - "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", - "OS-DCF:diskConfig": "MANUAL", - "os-extended-volumes:volumes_attached": [], - "accessIPv4": "", - "accessIPv6": "", - "progress": 0, - "OS-EXT-STS:power_state": 1, - "config_drive": "", - "metadata": {} - }, - { - "status": "ACTIVE", - "updated": "2014-09-25T13:04:49Z", - "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - "OS-EXT-SRV-ATTR:host": "devstack", - "addresses": { - "private": [ - { - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", - "version": 4, - "addr": "10.0.0.31", - "OS-EXT-IPS:type": "fixed" - } - ] - }, - "links": [ - { - "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "rel": "self" - }, - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "rel": "bookmark" - } - ], - "key_name": null, - "image": { - "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": [ - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", - "rel": "bookmark" - } - ] - }, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", - "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", - "rel": "bookmark" - } - ] - }, - "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "security_groups": [ - { - "name": "default" - } - ], - "OS-SRV-USG:terminated_at": null, - "OS-EXT-AZ:availability_zone": "nova", - "user_id": "9349aff8be7545ac9d2f1d00999a23cd", - "name": "derp", - "created": "2014-09-25T13:04:41Z", - "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", - "OS-DCF:diskConfig": "MANUAL", - "os-extended-volumes:volumes_attached": [], - "accessIPv4": "", - "accessIPv6": "", - "progress": 0, - "OS-EXT-STS:power_state": 1, - "config_drive": "", - "metadata": {} - } - ] -} -` - -// SingleServerBody is the canned body of a Get request on an existing server. -const SingleServerBody = ` -{ - "server": { - "status": "ACTIVE", - "updated": "2014-09-25T13:04:49Z", - "hostId": "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - "OS-EXT-SRV-ATTR:host": "devstack", - "addresses": { - "private": [ - { - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", - "version": 4, - "addr": "10.0.0.31", - "OS-EXT-IPS:type": "fixed" - } - ] - }, - "links": [ - { - "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "rel": "self" - }, - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "rel": "bookmark" - } - ], - "key_name": null, - "image": { - "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": [ - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", - "rel": "bookmark" - } - ] - }, - "OS-EXT-STS:task_state": null, - "OS-EXT-STS:vm_state": "active", - "OS-EXT-SRV-ATTR:instance_name": "instance-0000001d", - "OS-SRV-USG:launched_at": "2014-09-25T13:04:49.000000", - "OS-EXT-SRV-ATTR:hypervisor_hostname": "devstack", - "flavor": { - "id": "1", - "links": [ - { - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", - "rel": "bookmark" - } - ] - }, - "id": "9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "security_groups": [ - { - "name": "default" - } - ], - "OS-SRV-USG:terminated_at": null, - "OS-EXT-AZ:availability_zone": "nova", - "user_id": "9349aff8be7545ac9d2f1d00999a23cd", - "name": "derp", - "created": "2014-09-25T13:04:41Z", - "tenant_id": "fcad67a6189847c4aecfa3c81a05783b", - "OS-DCF:diskConfig": "MANUAL", - "os-extended-volumes:volumes_attached": [], - "accessIPv4": "", - "accessIPv6": "", - "progress": 0, - "OS-EXT-STS:power_state": 1, - "config_drive": "", - "metadata": {} - } -} -` - -const ServerPasswordBody = ` -{ - "password": "xlozO3wLCBRWAa2yDjCCVx8vwNPypxnypmRYDa/zErlQ+EzPe1S/Gz6nfmC52mOlOSCRuUOmG7kqqgejPof6M7bOezS387zjq4LSvvwp28zUknzy4YzfFGhnHAdai3TxUJ26pfQCYrq8UTzmKF2Bq8ioSEtVVzM0A96pDh8W2i7BOz6MdoiVyiev/I1K2LsuipfxSJR7Wdke4zNXJjHHP2RfYsVbZ/k9ANu+Nz4iIH8/7Cacud/pphH7EjrY6a4RZNrjQskrhKYed0YERpotyjYk1eDtRe72GrSiXteqCM4biaQ5w3ruS+AcX//PXk3uJ5kC7d67fPXaVz4WaQRYMg==" -} -` - -var ( - // ServerHerp is a Server struct that should correspond to the first result in ServerListBody. - ServerHerp = Server{ - Status: "ACTIVE", - Updated: "2014-09-25T13:10:10Z", - HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - Addresses: map[string]interface{}{ - "private": []interface{}{ - map[string]interface{}{ - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:7c:1b:2b", - "version": float64(4), - "addr": "10.0.0.32", - "OS-EXT-IPS:type": "fixed", - }, - }, - }, - Links: []interface{}{ - map[string]interface{}{ - "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", - "rel": "self", - }, - map[string]interface{}{ - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", - "rel": "bookmark", - }, - }, - Image: map[string]interface{}{ - "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": []interface{}{ - map[string]interface{}{ - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", - "rel": "bookmark", - }, - }, - }, - Flavor: map[string]interface{}{ - "id": "1", - "links": []interface{}{ - map[string]interface{}{ - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", - "rel": "bookmark", - }, - }, - }, - ID: "ef079b0c-e610-4dfb-b1aa-b49f07ac48e5", - UserID: "9349aff8be7545ac9d2f1d00999a23cd", - Name: "herp", - Created: "2014-09-25T13:10:02Z", - TenantID: "fcad67a6189847c4aecfa3c81a05783b", - Metadata: map[string]interface{}{}, - SecurityGroups: []map[string]interface{}{ - map[string]interface{}{ - "name": "default", - }, - }, - } - - // ServerDerp is a Server struct that should correspond to the second server in ServerListBody. - ServerDerp = Server{ - Status: "ACTIVE", - Updated: "2014-09-25T13:04:49Z", - HostID: "29d3c8c896a45aa4c34e52247875d7fefc3d94bbcc9f622b5d204362", - Addresses: map[string]interface{}{ - "private": []interface{}{ - map[string]interface{}{ - "OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:9e:89:be", - "version": float64(4), - "addr": "10.0.0.31", - "OS-EXT-IPS:type": "fixed", - }, - }, - }, - Links: []interface{}{ - map[string]interface{}{ - "href": "http://104.130.131.164:8774/v2/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "rel": "self", - }, - map[string]interface{}{ - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/servers/9e5476bd-a4ec-4653-93d6-72c93aa682ba", - "rel": "bookmark", - }, - }, - Image: map[string]interface{}{ - "id": "f90f6034-2570-4974-8351-6b49732ef2eb", - "links": []interface{}{ - map[string]interface{}{ - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", - "rel": "bookmark", - }, - }, - }, - Flavor: map[string]interface{}{ - "id": "1", - "links": []interface{}{ - map[string]interface{}{ - "href": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/flavors/1", - "rel": "bookmark", - }, - }, - }, - ID: "9e5476bd-a4ec-4653-93d6-72c93aa682ba", - UserID: "9349aff8be7545ac9d2f1d00999a23cd", - Name: "derp", - Created: "2014-09-25T13:04:41Z", - TenantID: "fcad67a6189847c4aecfa3c81a05783b", - Metadata: map[string]interface{}{}, - SecurityGroups: []map[string]interface{}{ - map[string]interface{}{ - "name": "default", - }, - }, - } -) - -// HandleServerCreationSuccessfully sets up the test server to respond to a server creation request -// with a given response. -func HandleServerCreationSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/servers", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ - "server": { - "name": "derp", - "imageRef": "f90f6034-2570-4974-8351-6b49732ef2eb", - "flavorRef": "1" - } - }`) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// HandleServerListSuccessfully sets up the test server to respond to a server List request. -func HandleServerListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/detail", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - r.ParseForm() - marker := r.Form.Get("marker") - switch marker { - case "": - fmt.Fprintf(w, ServerListBody) - case "9e5476bd-a4ec-4653-93d6-72c93aa682ba": - fmt.Fprintf(w, `{ "servers": [] }`) - default: - t.Fatalf("/servers/detail invoked with unexpected marker=[%s]", marker) - } - }) -} - -// HandleServerDeletionSuccessfully sets up the test server to respond to a server deletion request. -func HandleServerDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/asdfasdfasdf", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusNoContent) - }) -} - -// HandleServerForceDeletionSuccessfully sets up the test server to respond to a server force deletion -// request. -func HandleServerForceDeletionSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/asdfasdfasdf/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ "forceDelete": "" }`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleServerGetSuccessfully sets up the test server to respond to a server Get request. -func HandleServerGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - fmt.Fprintf(w, SingleServerBody) - }) -} - -// HandleServerUpdateSuccessfully sets up the test server to respond to a server Update request. -func HandleServerUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - th.TestHeader(t, r, "Content-Type", "application/json") - th.TestJSONRequest(t, r, `{ "server": { "name": "new-name" } }`) - - fmt.Fprintf(w, SingleServerBody) - }) -} - -// HandleAdminPasswordChangeSuccessfully sets up the test server to respond to a server password -// change request. -func HandleAdminPasswordChangeSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ "changePassword": { "adminPass": "new-password" } }`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleRebootSuccessfully sets up the test server to respond to a reboot request with success. -func HandleRebootSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ "reboot": { "type": "SOFT" } }`) - - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandleRebuildSuccessfully sets up the test server to respond to a rebuild request with success. -func HandleRebuildSuccessfully(t *testing.T, response string) { - th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, ` - { - "rebuild": { - "name": "new-name", - "adminPass": "swordfish", - "imageRef": "http://104.130.131.164:8774/fcad67a6189847c4aecfa3c81a05783b/images/f90f6034-2570-4974-8351-6b49732ef2eb", - "accessIPv4": "1.2.3.4" - } - } - `) - - w.WriteHeader(http.StatusAccepted) - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, response) - }) -} - -// HandleServerRescueSuccessfully sets up the test server to respond to a server Rescue request. -func HandleServerRescueSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ "rescue": { "adminPass": "1234567890" } }`) - - w.WriteHeader(http.StatusOK) - w.Write([]byte(`{ "adminPass": "1234567890" }`)) - }) -} - -// HandleMetadatumGetSuccessfully sets up the test server to respond to a metadatum Get request. -func HandleMetadatumGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) - }) -} - -// HandleMetadatumCreateSuccessfully sets up the test server to respond to a metadatum Create request. -func HandleMetadatumCreateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ - "meta": { - "foo": "bar" - } - }`) - - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "meta": {"foo":"bar"}}`)) - }) -} - -// HandleMetadatumDeleteSuccessfully sets up the test server to respond to a metadatum Delete request. -func HandleMetadatumDeleteSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/metadata/foo", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "DELETE") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.WriteHeader(http.StatusNoContent) - }) -} - -// HandleMetadataGetSuccessfully sets up the test server to respond to a metadata Get request. -func HandleMetadataGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - w.WriteHeader(http.StatusOK) - w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) - }) -} - -// HandleMetadataResetSuccessfully sets up the test server to respond to a metadata Create request. -func HandleMetadataResetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "PUT") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ - "metadata": { - "foo": "bar", - "this": "that" - } - }`) - - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "metadata": {"foo":"bar", "this":"that"}}`)) - }) -} - -// HandleMetadataUpdateSuccessfully sets up the test server to respond to a metadata Update request. -func HandleMetadataUpdateSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/metadata", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestJSONRequest(t, r, `{ - "metadata": { - "foo": "baz", - "this": "those" - } - }`) - - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") - w.Write([]byte(`{ "metadata": {"foo":"baz", "this":"those"}}`)) - }) -} - -// ListAddressesExpected represents an expected repsonse from a ListAddresses request. -var ListAddressesExpected = map[string][]Address{ - "public": []Address{ - Address{ - Version: 4, - Address: "80.56.136.39", - }, - Address{ - Version: 6, - Address: "2001:4800:790e:510:be76:4eff:fe04:82a8", - }, - }, - "private": []Address{ - Address{ - Version: 4, - Address: "10.880.3.154", - }, - }, -} - -// HandleAddressListSuccessfully sets up the test server to respond to a ListAddresses request. -func HandleAddressListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/asdfasdfasdf/ips", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ - "addresses": { - "public": [ - { - "version": 4, - "addr": "50.56.176.35" - }, - { - "version": 6, - "addr": "2001:4800:780e:510:be76:4eff:fe04:84a8" - } - ], - "private": [ - { - "version": 4, - "addr": "10.180.3.155" - } - ] - } - }`) - }) -} - -// ListNetworkAddressesExpected represents an expected repsonse from a ListAddressesByNetwork request. -var ListNetworkAddressesExpected = []Address{ - Address{ - Version: 4, - Address: "50.56.176.35", - }, - Address{ - Version: 6, - Address: "2001:4800:780e:510:be76:4eff:fe04:84a8", - }, -} - -// HandleNetworkAddressListSuccessfully sets up the test server to respond to a ListAddressesByNetwork request. -func HandleNetworkAddressListSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/asdfasdfasdf/ips/public", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Add("Content-Type", "application/json") - fmt.Fprintf(w, `{ - "public": [ - { - "version": 4, - "addr": "50.56.176.35" - }, - { - "version": 6, - "addr": "2001:4800:780e:510:be76:4eff:fe04:84a8" - } - ] - }`) - }) -} - -// HandleCreateServerImageSuccessfully sets up the test server to respond to a TestCreateServerImage request. -func HandleCreateServerImageSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/serverimage/action", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - w.Header().Add("Location", "https://0.0.0.0/images/xxxx-xxxxx-xxxxx-xxxx") - w.WriteHeader(http.StatusAccepted) - }) -} - -// HandlePasswordGetSuccessfully sets up the test server to respond to a password Get request. -func HandlePasswordGetSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/servers/1234asdf/os-server-password", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - th.TestHeader(t, r, "Accept", "application/json") - - fmt.Fprintf(w, ServerPasswordBody) - }) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/fixtures.go deleted file mode 100644 index 7f044ac3b..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/fixtures.go +++ /dev/null @@ -1,65 +0,0 @@ -// +build fixtures - -package tenants - -import ( - "fmt" - "net/http" - "testing" - - th "github.com/rackspace/gophercloud/testhelper" - "github.com/rackspace/gophercloud/testhelper/client" -) - -// ListOutput provides a single page of Tenant results. -const ListOutput = ` -{ - "tenants": [ - { - "id": "1234", - "name": "Red Team", - "description": "The team that is red", - "enabled": true - }, - { - "id": "9876", - "name": "Blue Team", - "description": "The team that is blue", - "enabled": false - } - ] -} -` - -// RedTeam is a Tenant fixture. -var RedTeam = Tenant{ - ID: "1234", - Name: "Red Team", - Description: "The team that is red", - Enabled: true, -} - -// BlueTeam is a Tenant fixture. -var BlueTeam = Tenant{ - ID: "9876", - Name: "Blue Team", - Description: "The team that is blue", - Enabled: false, -} - -// ExpectedTenantSlice is the slice of tenants expected to be returned from ListOutput. -var ExpectedTenantSlice = []Tenant{RedTeam, BlueTeam} - -// HandleListTenantsSuccessfully creates an HTTP handler at `/tenants` on the test handler mux that -// responds with a list of two tenants. -func HandleListTenantsSuccessfully(t *testing.T) { - th.Mux.HandleFunc("/tenants", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "Accept", "application/json") - th.TestHeader(t, r, "X-Auth-Token", client.TokenID) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, ListOutput) - }) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/results.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/results.go deleted file mode 100644 index c1220c384..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tenants/results.go +++ /dev/null @@ -1,62 +0,0 @@ -package tenants - -import ( - "github.com/mitchellh/mapstructure" - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/pagination" -) - -// Tenant is a grouping of users in the identity service. -type Tenant struct { - // ID is a unique identifier for this tenant. - ID string `mapstructure:"id"` - - // Name is a friendlier user-facing name for this tenant. - Name string `mapstructure:"name"` - - // Description is a human-readable explanation of this Tenant's purpose. - Description string `mapstructure:"description"` - - // Enabled indicates whether or not a tenant is active. - Enabled bool `mapstructure:"enabled"` -} - -// TenantPage is a single page of Tenant results. -type TenantPage struct { - pagination.LinkedPageBase -} - -// IsEmpty determines whether or not a page of Tenants contains any results. -func (page TenantPage) IsEmpty() (bool, error) { - tenants, err := ExtractTenants(page) - if err != nil { - return false, err - } - return len(tenants) == 0, nil -} - -// NextPageURL extracts the "next" link from the tenants_links section of the result. -func (page TenantPage) NextPageURL() (string, error) { - type resp struct { - Links []gophercloud.Link `mapstructure:"tenants_links"` - } - - var r resp - err := mapstructure.Decode(page.Body, &r) - if err != nil { - return "", err - } - - return gophercloud.ExtractNextURL(r.Links) -} - -// ExtractTenants returns a slice of Tenants contained in a single page of results. -func ExtractTenants(page pagination.Page) ([]Tenant, error) { - casted := page.(TenantPage).Body - var response struct { - Tenants []Tenant `mapstructure:"tenants"` - } - - err := mapstructure.Decode(casted, &response) - return response.Tenants, err -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/errors.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/errors.go deleted file mode 100644 index 3dfdc08db..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -package tokens - -import ( - "errors" - "fmt" -) - -var ( - // ErrUserIDProvided is returned if you attempt to authenticate with a UserID. - ErrUserIDProvided = unacceptedAttributeErr("UserID") - - // ErrAPIKeyProvided is returned if you attempt to authenticate with an APIKey. - ErrAPIKeyProvided = unacceptedAttributeErr("APIKey") - - // ErrDomainIDProvided is returned if you attempt to authenticate with a DomainID. - ErrDomainIDProvided = unacceptedAttributeErr("DomainID") - - // ErrDomainNameProvided is returned if you attempt to authenticate with a DomainName. - ErrDomainNameProvided = unacceptedAttributeErr("DomainName") - - // ErrUsernameRequired is returned if you attempt to authenticate without a Username. - ErrUsernameRequired = errors.New("You must supply a Username in your AuthOptions.") - - // ErrPasswordRequired is returned if you don't provide a password. - ErrPasswordRequired = errors.New("Please supply a Password in your AuthOptions.") -) - -func unacceptedAttributeErr(attribute string) error { - return fmt.Errorf("The base Identity V2 API does not accept authentication by %s", attribute) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/fixtures.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/fixtures.go deleted file mode 100644 index 6245259e1..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/fixtures.go +++ /dev/null @@ -1,195 +0,0 @@ -// +build fixtures - -package tokens - -import ( - "fmt" - "net/http" - "testing" - "time" - - "github.com/rackspace/gophercloud/openstack/identity/v2/tenants" - th "github.com/rackspace/gophercloud/testhelper" - thclient "github.com/rackspace/gophercloud/testhelper/client" -) - -// ExpectedToken is the token that should be parsed from TokenCreationResponse. -var ExpectedToken = &Token{ - ID: "aaaabbbbccccdddd", - ExpiresAt: time.Date(2014, time.January, 31, 15, 30, 58, 0, time.UTC), - Tenant: tenants.Tenant{ - ID: "fc394f2ab2df4114bde39905f800dc57", - Name: "test", - Description: "There are many tenants. This one is yours.", - Enabled: true, - }, -} - -// ExpectedServiceCatalog is the service catalog that should be parsed from TokenCreationResponse. -var ExpectedServiceCatalog = &ServiceCatalog{ - Entries: []CatalogEntry{ - CatalogEntry{ - Name: "inscrutablewalrus", - Type: "something", - Endpoints: []Endpoint{ - Endpoint{ - PublicURL: "http://something0:1234/v2/", - Region: "region0", - }, - Endpoint{ - PublicURL: "http://something1:1234/v2/", - Region: "region1", - }, - }, - }, - CatalogEntry{ - Name: "arbitrarypenguin", - Type: "else", - Endpoints: []Endpoint{ - Endpoint{ - PublicURL: "http://else0:4321/v3/", - Region: "region0", - }, - }, - }, - }, -} - -// ExpectedUser is the token that should be parsed from TokenGetResponse. -var ExpectedUser = &User{ - ID: "a530fefc3d594c4ba2693a4ecd6be74e", - Name: "apiserver", - Roles: []Role{{"member"}, {"service"}}, - UserName: "apiserver", -} - -// TokenCreationResponse is a JSON response that contains ExpectedToken and ExpectedServiceCatalog. -const TokenCreationResponse = ` -{ - "access": { - "token": { - "issued_at": "2014-01-30T15:30:58.000000Z", - "expires": "2014-01-31T15:30:58Z", - "id": "aaaabbbbccccdddd", - "tenant": { - "description": "There are many tenants. This one is yours.", - "enabled": true, - "id": "fc394f2ab2df4114bde39905f800dc57", - "name": "test" - } - }, - "serviceCatalog": [ - { - "endpoints": [ - { - "publicURL": "http://something0:1234/v2/", - "region": "region0" - }, - { - "publicURL": "http://something1:1234/v2/", - "region": "region1" - } - ], - "type": "something", - "name": "inscrutablewalrus" - }, - { - "endpoints": [ - { - "publicURL": "http://else0:4321/v3/", - "region": "region0" - } - ], - "type": "else", - "name": "arbitrarypenguin" - } - ] - } -} -` - -// TokenGetResponse is a JSON response that contains ExpectedToken and ExpectedUser. -const TokenGetResponse = ` -{ - "access": { - "token": { - "issued_at": "2014-01-30T15:30:58.000000Z", - "expires": "2014-01-31T15:30:58Z", - "id": "aaaabbbbccccdddd", - "tenant": { - "description": "There are many tenants. This one is yours.", - "enabled": true, - "id": "fc394f2ab2df4114bde39905f800dc57", - "name": "test" - } - }, - "serviceCatalog": [], - "user": { - "id": "a530fefc3d594c4ba2693a4ecd6be74e", - "name": "apiserver", - "roles": [ - { - "name": "member" - }, - { - "name": "service" - } - ], - "roles_links": [], - "username": "apiserver" - } - } -}` - -// HandleTokenPost expects a POST against a /tokens handler, ensures that the request body has been -// constructed properly given certain auth options, and returns the result. -func HandleTokenPost(t *testing.T, requestJSON string) { - th.Mux.HandleFunc("/tokens", func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "POST") - th.TestHeader(t, r, "Content-Type", "application/json") - th.TestHeader(t, r, "Accept", "application/json") - if requestJSON != "" { - th.TestJSONRequest(t, r, requestJSON) - } - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TokenCreationResponse) - }) -} - -// HandleTokenGet expects a Get against a /tokens handler, ensures that the request body has been -// constructed properly given certain auth options, and returns the result. -func HandleTokenGet(t *testing.T, token string) { - th.Mux.HandleFunc("/tokens/"+token, func(w http.ResponseWriter, r *http.Request) { - th.TestMethod(t, r, "GET") - th.TestHeader(t, r, "Accept", "application/json") - th.TestHeader(t, r, "X-Auth-Token", thclient.TokenID) - - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, TokenGetResponse) - }) -} - -// IsSuccessful ensures that a CreateResult was successful and contains the correct token and -// service catalog. -func IsSuccessful(t *testing.T, result CreateResult) { - token, err := result.ExtractToken() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedToken, token) - - serviceCatalog, err := result.ExtractServiceCatalog() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedServiceCatalog, serviceCatalog) -} - -// GetIsSuccessful ensures that a GetResult was successful and contains the correct token and -// User Info. -func GetIsSuccessful(t *testing.T, result GetResult) { - token, err := result.ExtractToken() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedToken, token) - - user, err := result.ExtractUser() - th.AssertNoErr(t, err) - th.CheckDeepEquals(t, ExpectedUser, user) -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go deleted file mode 100644 index 1f514386d..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v2/tokens/requests.go +++ /dev/null @@ -1,99 +0,0 @@ -package tokens - -import ( - "fmt" - - "github.com/rackspace/gophercloud" -) - -// AuthOptionsBuilder describes any argument that may be passed to the Create call. -type AuthOptionsBuilder interface { - - // ToTokenCreateMap assembles the Create request body, returning an error if parameters are - // missing or inconsistent. - ToTokenCreateMap() (map[string]interface{}, error) -} - -// AuthOptions wraps a gophercloud AuthOptions in order to adhere to the AuthOptionsBuilder -// interface. -type AuthOptions struct { - gophercloud.AuthOptions -} - -// WrapOptions embeds a root AuthOptions struct in a package-specific one. -func WrapOptions(original gophercloud.AuthOptions) AuthOptions { - return AuthOptions{AuthOptions: original} -} - -// ToTokenCreateMap converts AuthOptions into nested maps that can be serialized into a JSON -// request. -func (auth AuthOptions) ToTokenCreateMap() (map[string]interface{}, error) { - // Error out if an unsupported auth option is present. - if auth.UserID != "" { - return nil, ErrUserIDProvided - } - if auth.APIKey != "" { - return nil, ErrAPIKeyProvided - } - if auth.DomainID != "" { - return nil, ErrDomainIDProvided - } - if auth.DomainName != "" { - return nil, ErrDomainNameProvided - } - - // Populate the request map. - authMap := make(map[string]interface{}) - - if auth.Username != "" { - if auth.Password != "" { - authMap["passwordCredentials"] = map[string]interface{}{ - "username": auth.Username, - "password": auth.Password, - } - } else { - return nil, ErrPasswordRequired - } - } else if auth.TokenID != "" { - authMap["token"] = map[string]interface{}{ - "id": auth.TokenID, - } - } else { - return nil, fmt.Errorf("You must provide either username/password or tenantID/token values.") - } - - if auth.TenantID != "" { - authMap["tenantId"] = auth.TenantID - } - if auth.TenantName != "" { - authMap["tenantName"] = auth.TenantName - } - - return map[string]interface{}{"auth": authMap}, nil -} - -// Create authenticates to the identity service and attempts to acquire a Token. -// If successful, the CreateResult -// Generally, rather than interact with this call directly, end users should call openstack.AuthenticatedClient(), -// which abstracts all of the gory details about navigating service catalogs and such. -func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) CreateResult { - request, err := auth.ToTokenCreateMap() - if err != nil { - return CreateResult{gophercloud.Result{Err: err}} - } - - var result CreateResult - _, result.Err = client.Post(CreateURL(client), request, &result.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 203}, - }) - return result -} - -// Validates and retrieves information for user's token. -func Get(client *gophercloud.ServiceClient, token string) GetResult { - var result GetResult - _, result.Err = client.Get(GetURL(client, token), &result.Body, &gophercloud.RequestOpts{ - OkCodes: []int{200, 203}, - }) - return result -} diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/errors.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/errors.go deleted file mode 100644 index 44761092b..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/errors.go +++ /dev/null @@ -1,72 +0,0 @@ -package tokens - -import ( - "errors" - "fmt" -) - -func unacceptedAttributeErr(attribute string) error { - return fmt.Errorf("The base Identity V3 API does not accept authentication by %s", attribute) -} - -func redundantWithTokenErr(attribute string) error { - return fmt.Errorf("%s may not be provided when authenticating with a TokenID", attribute) -} - -func redundantWithUserID(attribute string) error { - return fmt.Errorf("%s may not be provided when authenticating with a UserID", attribute) -} - -var ( - // ErrAPIKeyProvided indicates that an APIKey was provided but can't be used. - ErrAPIKeyProvided = unacceptedAttributeErr("APIKey") - - // ErrTenantIDProvided indicates that a TenantID was provided but can't be used. - ErrTenantIDProvided = unacceptedAttributeErr("TenantID") - - // ErrTenantNameProvided indicates that a TenantName was provided but can't be used. - ErrTenantNameProvided = unacceptedAttributeErr("TenantName") - - // ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead. - ErrUsernameWithToken = redundantWithTokenErr("Username") - - // ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead. - ErrUserIDWithToken = redundantWithTokenErr("UserID") - - // ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead. - ErrDomainIDWithToken = redundantWithTokenErr("DomainID") - - // ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s - ErrDomainNameWithToken = redundantWithTokenErr("DomainName") - - // ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once. - ErrUsernameOrUserID = errors.New("Exactly one of Username and UserID must be provided for password authentication") - - // ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used. - ErrDomainIDWithUserID = redundantWithUserID("DomainID") - - // ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used. - ErrDomainNameWithUserID = redundantWithUserID("DomainName") - - // ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it. - // It may also indicate that both a DomainID and a DomainName were provided at once. - ErrDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName to authenticate by Username") - - // ErrMissingPassword indicates that no password was provided and no token is available. - ErrMissingPassword = errors.New("You must provide a password to authenticate") - - // ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present. - ErrScopeDomainIDOrDomainName = errors.New("You must provide exactly one of DomainID or DomainName in a Scope with ProjectName") - - // ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope. - ErrScopeProjectIDOrProjectName = errors.New("You must provide at most one of ProjectID or ProjectName in a Scope") - - // ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope. - ErrScopeProjectIDAlone = errors.New("ProjectID must be supplied alone in a Scope") - - // ErrScopeDomainName indicates that a DomainName was provided alone in a Scope. - ErrScopeDomainName = errors.New("DomainName must be supplied with a ProjectName or ProjectID in a Scope.") - - // ErrScopeEmpty indicates that no credentials were provided in a Scope. - ErrScopeEmpty = errors.New("You must provide either a Project or Domain in a Scope") -) diff --git a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go b/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go deleted file mode 100644 index d63b1bb51..000000000 --- a/vendor/github.com/rackspace/gophercloud/openstack/identity/v3/tokens/requests.go +++ /dev/null @@ -1,281 +0,0 @@ -package tokens - -import ( - "net/http" - - "github.com/rackspace/gophercloud" -) - -// Scope allows a created token to be limited to a specific domain or project. -type Scope struct { - ProjectID string - ProjectName string - DomainID string - DomainName string -} - -func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { - return map[string]string{ - "X-Subject-Token": subjectToken, - } -} - -// Create authenticates and either generates a new token, or changes the Scope of an existing token. -func Create(c *gophercloud.ServiceClient, options gophercloud.AuthOptions, scope *Scope) CreateResult { - type domainReq struct { - ID *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - } - - type projectReq struct { - Domain *domainReq `json:"domain,omitempty"` - Name *string `json:"name,omitempty"` - ID *string `json:"id,omitempty"` - } - - type userReq struct { - ID *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - Password string `json:"password"` - Domain *domainReq `json:"domain,omitempty"` - } - - type passwordReq struct { - User userReq `json:"user"` - } - - type tokenReq struct { - ID string `json:"id"` - } - - type identityReq struct { - Methods []string `json:"methods"` - Password *passwordReq `json:"password,omitempty"` - Token *tokenReq `json:"token,omitempty"` - } - - type scopeReq struct { - Domain *domainReq `json:"domain,omitempty"` - Project *projectReq `json:"project,omitempty"` - } - - type authReq struct { - Identity identityReq `json:"identity"` - Scope *scopeReq `json:"scope,omitempty"` - } - - type request struct { - Auth authReq `json:"auth"` - } - - // Populate the request structure based on the provided arguments. Create and return an error - // if insufficient or incompatible information is present. - var req request - - // Test first for unrecognized arguments. - if options.APIKey != "" { - return createErr(ErrAPIKeyProvided) - } - if options.TenantID != "" { - return createErr(ErrTenantIDProvided) - } - if options.TenantName != "" { - return createErr(ErrTenantNameProvided) - } - - if options.Password == "" { - if c.TokenID != "" { - // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication - // parameters. - if options.Username != "" { - return createErr(ErrUsernameWithToken) - } - if options.UserID != "" { - return createErr(ErrUserIDWithToken) - } - if options.DomainID != "" { - return createErr(ErrDomainIDWithToken) - } - if options.DomainName != "" { - return createErr(ErrDomainNameWithToken) - } - - // Configure the request for Token authentication. - req.Auth.Identity.Methods = []string{"token"} - req.Auth.Identity.Token = &tokenReq{ - ID: c.TokenID, - } - } else { - // If no password or token ID are available, authentication can't continue. - return createErr(ErrMissingPassword) - } - } else { - // Password authentication. - req.Auth.Identity.Methods = []string{"password"} - - // At least one of Username and UserID must be specified. - if options.Username == "" && options.UserID == "" { - return createErr(ErrUsernameOrUserID) - } - - if options.Username != "" { - // If Username is provided, UserID may not be provided. - if options.UserID != "" { - return createErr(ErrUsernameOrUserID) - } - - // Either DomainID or DomainName must also be specified. - if options.DomainID == "" && options.DomainName == "" { - return createErr(ErrDomainIDOrDomainName) - } - - if options.DomainID != "" { - if options.DomainName != "" { - return createErr(ErrDomainIDOrDomainName) - } - - // Configure the request for Username and Password authentication with a DomainID. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ - Name: &options.Username, - Password: options.Password, - Domain: &domainReq{ID: &options.DomainID}, - }, - } - } - - if options.DomainName != "" { - // Configure the request for Username and Password authentication with a DomainName. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ - Name: &options.Username, - Password: options.Password, - Domain: &domainReq{Name: &options.DomainName}, - }, - } - } - } - - if options.UserID != "" { - // If UserID is specified, neither DomainID nor DomainName may be. - if options.DomainID != "" { - return createErr(ErrDomainIDWithUserID) - } - if options.DomainName != "" { - return createErr(ErrDomainNameWithUserID) - } - - // Configure the request for UserID and Password authentication. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ID: &options.UserID, Password: options.Password}, - } - } - } - - // Add a "scope" element if a Scope has been provided. - if scope != nil { - if scope.ProjectName != "" { - // ProjectName provided: either DomainID or DomainName must also be supplied. - // ProjectID may not be supplied. - if scope.DomainID == "" && scope.DomainName == "" { - return createErr(ErrScopeDomainIDOrDomainName) - } - if scope.ProjectID != "" { - return createErr(ErrScopeProjectIDOrProjectName) - } - - if scope.DomainID != "" { - // ProjectName + DomainID - req.Auth.Scope = &scopeReq{ - Project: &projectReq{ - Name: &scope.ProjectName, - Domain: &domainReq{ID: &scope.DomainID}, - }, - } - } - - if scope.DomainName != "" { - // ProjectName + DomainName - req.Auth.Scope = &scopeReq{ - Project: &projectReq{ - Name: &scope.ProjectName, - Domain: &domainReq{Name: &scope.DomainName}, - }, - } - } - } else if scope.ProjectID != "" { - // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided. - if scope.DomainID != "" { - return createErr(ErrScopeProjectIDAlone) - } - if scope.DomainName != "" { - return createErr(ErrScopeProjectIDAlone) - } - - // ProjectID - req.Auth.Scope = &scopeReq{ - Project: &projectReq{ID: &scope.ProjectID}, - } - } else if scope.DomainID != "" { - // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided. - if scope.DomainName != "" { - return createErr(ErrScopeDomainIDOrDomainName) - } - - // DomainID - req.Auth.Scope = &scopeReq{ - Domain: &domainReq{ID: &scope.DomainID}, - } - } else if scope.DomainName != "" { - return createErr(ErrScopeDomainName) - } else { - return createErr(ErrScopeEmpty) - } - } - - var result CreateResult - var response *http.Response - response, result.Err = c.Post(tokenURL(c), req, &result.Body, nil) - if result.Err != nil { - return result - } - result.Header = response.Header - return result -} - -// Get validates and retrieves information about another token. -func Get(c *gophercloud.ServiceClient, token string) GetResult { - var result GetResult - var response *http.Response - response, result.Err = c.Get(tokenURL(c), &result.Body, &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - OkCodes: []int{200, 203}, - }) - if result.Err != nil { - return result - } - result.Header = response.Header - return result -} - -// Validate determines if a specified token is valid or not. -func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { - response, err := c.Request("HEAD", tokenURL(c), gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - OkCodes: []int{204, 404}, - }) - if err != nil { - return false, err - } - - return response.StatusCode == 204, nil -} - -// Revoke immediately makes specified token invalid. -func Revoke(c *gophercloud.ServiceClient, token string) RevokeResult { - var res RevokeResult - _, res.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), - }) - return res -} diff --git a/vendor/github.com/rackspace/gophercloud/pagination/null.go b/vendor/github.com/rackspace/gophercloud/pagination/null.go deleted file mode 100644 index ae57e1886..000000000 --- a/vendor/github.com/rackspace/gophercloud/pagination/null.go +++ /dev/null @@ -1,20 +0,0 @@ -package pagination - -// nullPage is an always-empty page that trivially satisfies all Page interfacts. -// It's useful to be returned along with an error. -type nullPage struct{} - -// NextPageURL always returns "" to indicate that there are no more pages to return. -func (p nullPage) NextPageURL() (string, error) { - return "", nil -} - -// IsEmpty always returns true to prevent iteration over nullPages. -func (p nullPage) IsEmpty() (bool, error) { - return true, nil -} - -// LastMark always returns "" because the nullPage contains no items to have a mark. -func (p nullPage) LastMark() (string, error) { - return "", nil -} diff --git a/vendor/github.com/rackspace/gophercloud/results.go b/vendor/github.com/rackspace/gophercloud/results.go deleted file mode 100644 index 27fd1b60f..000000000 --- a/vendor/github.com/rackspace/gophercloud/results.go +++ /dev/null @@ -1,153 +0,0 @@ -package gophercloud - -import ( - "encoding/json" - "net/http" - "reflect" - - "github.com/mitchellh/mapstructure" -) - -/* -Result is an internal type to be used by individual resource packages, but its -methods will be available on a wide variety of user-facing embedding types. - -It acts as a base struct that other Result types, returned from request -functions, can embed for convenience. All Results capture basic information -from the HTTP transaction that was performed, including the response body, -HTTP headers, and any errors that happened. - -Generally, each Result type will have an Extract method that can be used to -further interpret the result's payload in a specific context. Extensions or -providers can then provide additional extraction functions to pull out -provider- or extension-specific information as well. -*/ -type Result struct { - // Body is the payload of the HTTP response from the server. In most cases, - // this will be the deserialized JSON structure. - Body interface{} - - // Header contains the HTTP header structure from the original response. - Header http.Header - - // Err is an error that occurred during the operation. It's deferred until - // extraction to make it easier to chain the Extract call. - Err error -} - -// 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 -// a buggy extraction function, we will all love you forever. -func (r Result) PrettyPrintJSON() string { - pretty, err := json.MarshalIndent(r.Body, "", " ") - if err != nil { - panic(err.Error()) - } - return string(pretty) -} - -// ErrResult is an internal type to be used by individual resource packages, but -// its methods will be available on a wide variety of user-facing embedding -// types. -// -// It represents results that only contain a potential error and -// nothing else. Usually, if the operation executed successfully, the Err field -// will be nil; otherwise it will be stocked with a relevant error. Use the -// ExtractErr method -// to cleanly pull it out. -type ErrResult struct { - Result -} - -// ExtractErr is a function that extracts error information, or nil, from a result. -func (r ErrResult) ExtractErr() error { - return r.Err -} - -/* -HeaderResult is an internal type to be used by individual resource packages, but -its methods will be available on a wide variety of user-facing embedding types. - -It represents a result that only contains an error (possibly nil) and an -http.Header. This is used, for example, by the objectstorage packages in -openstack, because most of the operations don't return response bodies, but do -have relevant information in headers. -*/ -type HeaderResult struct { - Result -} - -// ExtractHeader will return the http.Header and error from the HeaderResult. -// -// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader() -func (hr HeaderResult) ExtractHeader() (http.Header, error) { - return hr.Header, hr.Err -} - -// DecodeHeader is a function that decodes a header (usually of type map[string]interface{}) to -// another type (usually a struct). This function is used by the objectstorage package to give -// users access to response headers without having to query a map. A DecodeHookFunction is used, -// because OpenStack-based clients return header values as arrays (Go slices). -func DecodeHeader(from, to interface{}) error { - config := &mapstructure.DecoderConfig{ - DecodeHook: func(from, to reflect.Kind, data interface{}) (interface{}, error) { - if from == reflect.Slice { - return data.([]string)[0], nil - } - return data, nil - }, - Result: to, - WeaklyTypedInput: true, - } - decoder, err := mapstructure.NewDecoder(config) - if err != nil { - return err - } - if err := decoder.Decode(from); err != nil { - return err - } - return nil -} - -// RFC3339Milli describes a common time format used by some API responses. -const RFC3339Milli = "2006-01-02T15:04:05.999999Z" - -// Time format used in cloud orchestration -const STACK_TIME_FMT = "2006-01-02T15:04:05" - -/* -Link is an internal type to be used in packages of collection resources that are -paginated in a certain way. - -It's a response substructure common to many paginated collection results that is -used to point to related pages. Usually, the one we care about is the one with -Rel field set to "next". -*/ -type Link struct { - Href string `mapstructure:"href"` - Rel string `mapstructure:"rel"` -} - -/* -ExtractNextURL is an internal function useful for packages of collection -resources that are paginated in a certain way. - -It attempts attempts to extract the "next" URL from slice of Link structs, or -"" if no such URL is present. -*/ -func ExtractNextURL(links []Link) (string, error) { - var url string - - for _, l := range links { - if l.Rel == "next" { - url = l.Href - } - } - - if url == "" { - return "", nil - } - - return url, nil -} diff --git a/vendor/github.com/rackspace/gophercloud/script/acceptancetest b/vendor/github.com/rackspace/gophercloud/script/acceptancetest deleted file mode 100644 index f9c89f4df..000000000 --- a/vendor/github.com/rackspace/gophercloud/script/acceptancetest +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# -# Run the acceptance tests. - -exec go test -p=1 -tags 'acceptance fixtures' github.com/rackspace/gophercloud/acceptance/... $@ diff --git a/vendor/github.com/rackspace/gophercloud/script/bootstrap b/vendor/github.com/rackspace/gophercloud/script/bootstrap deleted file mode 100644 index 6bae6e8f1..000000000 --- a/vendor/github.com/rackspace/gophercloud/script/bootstrap +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# This script helps new contributors set up their local workstation for -# gophercloud development and contributions. - -# Create the environment -export GOPATH=$HOME/go/gophercloud -mkdir -p $GOPATH - -# Download gophercloud into that environment -go get github.com/rackspace/gophercloud -cd $GOPATH/src/github.com/rackspace/gophercloud -git checkout master - -# Write out the env.sh convenience file. -cd $GOPATH -cat <env.sh -#!/bin/bash -export GOPATH=$(pwd) -export GOPHERCLOUD=$GOPATH/src/github.com/rackspace/gophercloud -EOF -chmod a+x env.sh - -# Make changes immediately available as a convenience. -. ./env.sh - diff --git a/vendor/github.com/rackspace/gophercloud/script/cibuild b/vendor/github.com/rackspace/gophercloud/script/cibuild deleted file mode 100644 index 1cb389e7d..000000000 --- a/vendor/github.com/rackspace/gophercloud/script/cibuild +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# -# Test script to be invoked by Travis. - -exec script/unittest -v diff --git a/vendor/github.com/rackspace/gophercloud/script/test b/vendor/github.com/rackspace/gophercloud/script/test deleted file mode 100644 index 1e03dff8a..000000000 --- a/vendor/github.com/rackspace/gophercloud/script/test +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# -# Run all the tests. - -exec go test -tags 'acceptance fixtures' ./... $@ diff --git a/vendor/github.com/rackspace/gophercloud/script/unittest b/vendor/github.com/rackspace/gophercloud/script/unittest deleted file mode 100644 index d3440a902..000000000 --- a/vendor/github.com/rackspace/gophercloud/script/unittest +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# -# Run the unit tests. - -exec go test -tags fixtures ./... $@ diff --git a/vendor/github.com/rackspace/gophercloud/service_client.go b/vendor/github.com/rackspace/gophercloud/service_client.go deleted file mode 100644 index 3490da05f..000000000 --- a/vendor/github.com/rackspace/gophercloud/service_client.go +++ /dev/null @@ -1,32 +0,0 @@ -package gophercloud - -import "strings" - -// ServiceClient stores details required to interact with a specific service API implemented by a provider. -// Generally, you'll acquire these by calling the appropriate `New` method on a ProviderClient. -type ServiceClient struct { - // ProviderClient is a reference to the provider that implements this service. - *ProviderClient - - // Endpoint is the base URL of the service's API, acquired from a service catalog. - // It MUST end with a /. - Endpoint string - - // ResourceBase is the base URL shared by the resources within a service's API. It should include - // the API version and, like Endpoint, MUST end with a / if set. If not set, the Endpoint is used - // as-is, instead. - ResourceBase string -} - -// ResourceBaseURL returns the base URL of any resources used by this service. It MUST end with a /. -func (client *ServiceClient) ResourceBaseURL() string { - if client.ResourceBase != "" { - return client.ResourceBase - } - return client.Endpoint -} - -// ServiceURL constructs a URL for a resource belonging to this provider. -func (client *ServiceClient) ServiceURL(parts ...string) string { - return client.ResourceBaseURL() + strings.Join(parts, "/") -} diff --git a/vendor/github.com/rackspace/gophercloud/testhelper/client/fake.go b/vendor/github.com/rackspace/gophercloud/testhelper/client/fake.go deleted file mode 100644 index 5b69b058f..000000000 --- a/vendor/github.com/rackspace/gophercloud/testhelper/client/fake.go +++ /dev/null @@ -1,17 +0,0 @@ -package client - -import ( - "github.com/rackspace/gophercloud" - "github.com/rackspace/gophercloud/testhelper" -) - -// Fake token to use. -const TokenID = "cbc36478b0bd8e67e89469c7749d4127" - -// ServiceClient returns a generic service client for use in tests. -func ServiceClient() *gophercloud.ServiceClient { - return &gophercloud.ServiceClient{ - ProviderClient: &gophercloud.ProviderClient{TokenID: TokenID}, - Endpoint: testhelper.Endpoint(), - } -} diff --git a/vendor/github.com/rackspace/gophercloud/testhelper/convenience.go b/vendor/github.com/rackspace/gophercloud/testhelper/convenience.go deleted file mode 100644 index cf33e1ad1..000000000 --- a/vendor/github.com/rackspace/gophercloud/testhelper/convenience.go +++ /dev/null @@ -1,329 +0,0 @@ -package testhelper - -import ( - "encoding/json" - "fmt" - "path/filepath" - "reflect" - "runtime" - "strings" - "testing" -) - -const ( - logBodyFmt = "\033[1;31m%s %s\033[0m" - greenCode = "\033[0m\033[1;32m" - yellowCode = "\033[0m\033[1;33m" - resetCode = "\033[0m\033[1;31m" -) - -func prefix(depth int) string { - _, file, line, _ := runtime.Caller(depth) - return fmt.Sprintf("Failure in %s, line %d:", filepath.Base(file), line) -} - -func green(str interface{}) string { - return fmt.Sprintf("%s%#v%s", greenCode, str, resetCode) -} - -func yellow(str interface{}) string { - return fmt.Sprintf("%s%#v%s", yellowCode, str, resetCode) -} - -func logFatal(t *testing.T, str string) { - t.Fatalf(logBodyFmt, prefix(3), str) -} - -func logError(t *testing.T, str string) { - t.Errorf(logBodyFmt, prefix(3), str) -} - -type diffLogger func([]string, interface{}, interface{}) - -type visit struct { - a1 uintptr - a2 uintptr - typ reflect.Type -} - -// Recursively visits the structures of "expected" and "actual". The diffLogger function will be -// invoked with each different value encountered, including the reference path that was followed -// to get there. -func deepDiffEqual(expected, actual reflect.Value, visited map[visit]bool, path []string, logDifference diffLogger) { - defer func() { - // Fall back to the regular reflect.DeepEquals function. - if r := recover(); r != nil { - var e, a interface{} - if expected.IsValid() { - e = expected.Interface() - } - if actual.IsValid() { - a = actual.Interface() - } - - if !reflect.DeepEqual(e, a) { - logDifference(path, e, a) - } - } - }() - - if !expected.IsValid() && actual.IsValid() { - logDifference(path, nil, actual.Interface()) - return - } - if expected.IsValid() && !actual.IsValid() { - logDifference(path, expected.Interface(), nil) - return - } - if !expected.IsValid() && !actual.IsValid() { - return - } - - hard := func(k reflect.Kind) bool { - switch k { - case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct: - return true - } - return false - } - - if expected.CanAddr() && actual.CanAddr() && hard(expected.Kind()) { - addr1 := expected.UnsafeAddr() - addr2 := actual.UnsafeAddr() - - if addr1 > addr2 { - addr1, addr2 = addr2, addr1 - } - - if addr1 == addr2 { - // References are identical. We can short-circuit - return - } - - typ := expected.Type() - v := visit{addr1, addr2, typ} - if visited[v] { - // Already visited. - return - } - - // Remember this visit for later. - visited[v] = true - } - - switch expected.Kind() { - case reflect.Array: - for i := 0; i < expected.Len(); i++ { - hop := append(path, fmt.Sprintf("[%d]", i)) - deepDiffEqual(expected.Index(i), actual.Index(i), visited, hop, logDifference) - } - return - case reflect.Slice: - if expected.IsNil() != actual.IsNil() { - logDifference(path, expected.Interface(), actual.Interface()) - return - } - if expected.Len() == actual.Len() && expected.Pointer() == actual.Pointer() { - return - } - for i := 0; i < expected.Len(); i++ { - hop := append(path, fmt.Sprintf("[%d]", i)) - deepDiffEqual(expected.Index(i), actual.Index(i), visited, hop, logDifference) - } - return - case reflect.Interface: - if expected.IsNil() != actual.IsNil() { - logDifference(path, expected.Interface(), actual.Interface()) - return - } - deepDiffEqual(expected.Elem(), actual.Elem(), visited, path, logDifference) - return - case reflect.Ptr: - deepDiffEqual(expected.Elem(), actual.Elem(), visited, path, logDifference) - return - case reflect.Struct: - for i, n := 0, expected.NumField(); i < n; i++ { - field := expected.Type().Field(i) - hop := append(path, "."+field.Name) - deepDiffEqual(expected.Field(i), actual.Field(i), visited, hop, logDifference) - } - return - case reflect.Map: - if expected.IsNil() != actual.IsNil() { - logDifference(path, expected.Interface(), actual.Interface()) - return - } - if expected.Len() == actual.Len() && expected.Pointer() == actual.Pointer() { - return - } - - var keys []reflect.Value - if expected.Len() >= actual.Len() { - keys = expected.MapKeys() - } else { - keys = actual.MapKeys() - } - - for _, k := range keys { - expectedValue := expected.MapIndex(k) - actualValue := expected.MapIndex(k) - - if !expectedValue.IsValid() { - logDifference(path, nil, actual.Interface()) - return - } - if !actualValue.IsValid() { - logDifference(path, expected.Interface(), nil) - return - } - - hop := append(path, fmt.Sprintf("[%v]", k)) - deepDiffEqual(expectedValue, actualValue, visited, hop, logDifference) - } - return - case reflect.Func: - if expected.IsNil() != actual.IsNil() { - logDifference(path, expected.Interface(), actual.Interface()) - } - return - default: - if expected.Interface() != actual.Interface() { - logDifference(path, expected.Interface(), actual.Interface()) - } - } -} - -func deepDiff(expected, actual interface{}, logDifference diffLogger) { - if expected == nil || actual == nil { - logDifference([]string{}, expected, actual) - return - } - - expectedValue := reflect.ValueOf(expected) - actualValue := reflect.ValueOf(actual) - - if expectedValue.Type() != actualValue.Type() { - logDifference([]string{}, expected, actual) - return - } - deepDiffEqual(expectedValue, actualValue, map[visit]bool{}, []string{}, logDifference) -} - -// AssertEquals compares two arbitrary values and performs a comparison. If the -// comparison fails, a fatal error is raised that will fail the test -func AssertEquals(t *testing.T, expected, actual interface{}) { - if expected != actual { - logFatal(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) - } -} - -// CheckEquals is similar to AssertEquals, except with a non-fatal error -func CheckEquals(t *testing.T, expected, actual interface{}) { - if expected != actual { - logError(t, fmt.Sprintf("expected %s but got %s", green(expected), yellow(actual))) - } -} - -// AssertDeepEquals - like Equals - performs a comparison - but on more complex -// structures that requires deeper inspection -func AssertDeepEquals(t *testing.T, expected, actual interface{}) { - pre := prefix(2) - - differed := false - deepDiff(expected, actual, func(path []string, expected, actual interface{}) { - differed = true - t.Errorf("\033[1;31m%sat %s expected %s, but got %s\033[0m", - pre, - strings.Join(path, ""), - green(expected), - yellow(actual)) - }) - if differed { - logFatal(t, "The structures were different.") - } -} - -// CheckDeepEquals is similar to AssertDeepEquals, except with a non-fatal error -func CheckDeepEquals(t *testing.T, expected, actual interface{}) { - pre := prefix(2) - - deepDiff(expected, actual, func(path []string, expected, actual interface{}) { - t.Errorf("\033[1;31m%s at %s expected %s, but got %s\033[0m", - pre, - strings.Join(path, ""), - green(expected), - yellow(actual)) - }) -} - -// isJSONEquals is a utility function that implements JSON comparison for AssertJSONEquals and -// CheckJSONEquals. -func isJSONEquals(t *testing.T, expectedJSON string, actual interface{}) bool { - var parsedExpected, parsedActual interface{} - err := json.Unmarshal([]byte(expectedJSON), &parsedExpected) - if err != nil { - t.Errorf("Unable to parse expected value as JSON: %v", err) - return false - } - - jsonActual, err := json.Marshal(actual) - AssertNoErr(t, err) - err = json.Unmarshal(jsonActual, &parsedActual) - AssertNoErr(t, err) - - if !reflect.DeepEqual(parsedExpected, parsedActual) { - prettyExpected, err := json.MarshalIndent(parsedExpected, "", " ") - if err != nil { - t.Logf("Unable to pretty-print expected JSON: %v\n%s", err, expectedJSON) - } else { - // We can't use green() here because %#v prints prettyExpected as a byte array literal, which - // is... unhelpful. Converting it to a string first leaves "\n" uninterpreted for some reason. - t.Logf("Expected JSON:\n%s%s%s", greenCode, prettyExpected, resetCode) - } - - prettyActual, err := json.MarshalIndent(actual, "", " ") - if err != nil { - t.Logf("Unable to pretty-print actual JSON: %v\n%#v", err, actual) - } else { - // We can't use yellow() for the same reason. - t.Logf("Actual JSON:\n%s%s%s", yellowCode, prettyActual, resetCode) - } - - return false - } - return true -} - -// AssertJSONEquals serializes a value as JSON, parses an expected string as JSON, and ensures that -// both are consistent. If they aren't, the expected and actual structures are pretty-printed and -// shown for comparison. -// -// This is useful for comparing structures that are built as nested map[string]interface{} values, -// which are a pain to construct as literals. -func AssertJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { - if !isJSONEquals(t, expectedJSON, actual) { - logFatal(t, "The generated JSON structure differed.") - } -} - -// CheckJSONEquals is similar to AssertJSONEquals, but nonfatal. -func CheckJSONEquals(t *testing.T, expectedJSON string, actual interface{}) { - if !isJSONEquals(t, expectedJSON, actual) { - logError(t, "The generated JSON structure differed.") - } -} - -// AssertNoErr is a convenience function for checking whether an error value is -// an actual error -func AssertNoErr(t *testing.T, e error) { - if e != nil { - logFatal(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) - } -} - -// CheckNoErr is similar to AssertNoErr, except with a non-fatal error -func CheckNoErr(t *testing.T, e error) { - if e != nil { - logError(t, fmt.Sprintf("unexpected error %s", yellow(e.Error()))) - } -} diff --git a/vendor/github.com/rackspace/gophercloud/testhelper/doc.go b/vendor/github.com/rackspace/gophercloud/testhelper/doc.go deleted file mode 100644 index 25b4dfebb..000000000 --- a/vendor/github.com/rackspace/gophercloud/testhelper/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -/* -Package testhelper container methods that are useful for writing unit tests. -*/ -package testhelper diff --git a/vendor/github.com/rackspace/gophercloud/testhelper/http_responses.go b/vendor/github.com/rackspace/gophercloud/testhelper/http_responses.go deleted file mode 100644 index e1f1f9ac0..000000000 --- a/vendor/github.com/rackspace/gophercloud/testhelper/http_responses.go +++ /dev/null @@ -1,91 +0,0 @@ -package testhelper - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "net/http/httptest" - "net/url" - "reflect" - "testing" -) - -var ( - // Mux is a multiplexer that can be used to register handlers. - Mux *http.ServeMux - - // Server is an in-memory HTTP server for testing. - Server *httptest.Server -) - -// SetupHTTP prepares the Mux and Server. -func SetupHTTP() { - Mux = http.NewServeMux() - Server = httptest.NewServer(Mux) -} - -// TeardownHTTP releases HTTP-related resources. -func TeardownHTTP() { - Server.Close() -} - -// Endpoint returns a fake endpoint that will actually target the Mux. -func Endpoint() string { - return Server.URL + "/" -} - -// TestFormValues ensures that all the URL parameters given to the http.Request are the same as values. -func TestFormValues(t *testing.T, r *http.Request, values map[string]string) { - want := url.Values{} - for k, v := range values { - want.Add(k, v) - } - - r.ParseForm() - if !reflect.DeepEqual(want, r.Form) { - t.Errorf("Request parameters = %v, want %v", r.Form, want) - } -} - -// TestMethod checks that the Request has the expected method (e.g. GET, POST). -func TestMethod(t *testing.T, r *http.Request, expected string) { - if expected != r.Method { - t.Errorf("Request method = %v, expected %v", r.Method, expected) - } -} - -// TestHeader checks that the header on the http.Request matches the expected value. -func TestHeader(t *testing.T, r *http.Request, header string, expected string) { - if actual := r.Header.Get(header); expected != actual { - t.Errorf("Header %s = %s, expected %s", header, actual, expected) - } -} - -// TestBody verifies that the request body matches an expected body. -func TestBody(t *testing.T, r *http.Request, expected string) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Errorf("Unable to read body: %v", err) - } - str := string(b) - if expected != str { - t.Errorf("Body = %s, expected %s", str, expected) - } -} - -// TestJSONRequest verifies that the JSON payload of a request matches an expected structure, without asserting things about -// whitespace or ordering. -func TestJSONRequest(t *testing.T, r *http.Request, expected string) { - b, err := ioutil.ReadAll(r.Body) - if err != nil { - t.Errorf("Unable to read request body: %v", err) - } - - var actualJSON interface{} - err = json.Unmarshal(b, &actualJSON) - if err != nil { - t.Errorf("Unable to parse request body as JSON: %v", err) - } - - CheckJSONEquals(t, expected, actualJSON) -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 8e28de3e9..8afc6c2e1 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -350,6 +350,108 @@ "revision": "6f45313302b9c56850fc17f99e40caebce98c716", "revisionTime": "2015-01-27T13:39:51Z" }, + { + "checksumSHA1": "DkbpYqirk9i+2YDR5Ujzpot/oAg=", + "path": "github.com/gophercloud/gophercloud", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "S3zTth9INyj1RfyHkQEvJAvRWvw=", + "path": "github.com/gophercloud/gophercloud/openstack", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "Au6MAsI90lewLByg9n+Yjtdqdh8=", + "path": "github.com/gophercloud/gophercloud/openstack/common/extensions", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "4XWDCGMYqipwJymi9xJo9UffD7g=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "pUlKsepGmWDd4PqPaK4W85pHsRU=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "RWwUliHD65cWApdEo4ckOcPSArg=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "qBpGbX7LQMPATdO8XyQmU7IXDiI=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "a9xDFPigDjHlPlthknKlBduGvKY=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "UGeqrw3KdPNRwDxl315MAYyy/uY=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/images", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "8rOLNDSqwz/DSKL1BoPqjtWSWAE=", + "path": "github.com/gophercloud/gophercloud/openstack/compute/v2/servers", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "1sVqsZBZBNhDXLY9XzjMkcOkcbg=", + "path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "q1VGeltZl57OidZ5UDxbMsnyV2g=", + "path": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "6M6ofb8ri5G+sZ8OiExLi7irdx8=", + "path": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "Eo+cKV/XzaB5TyxK5ZKWYxPqGWY=", + "path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "HKHLR7xxAb5aJ5zN8XYhDkn/PoM=", + "path": "github.com/gophercloud/gophercloud/openstack/imageservice/v2/members", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "TDOZnaS0TO0NirpxV1QwPerAQTY=", + "path": "github.com/gophercloud/gophercloud/openstack/utils", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, + { + "checksumSHA1": "pmpLcbUZ+EgLUmTbzMtGRq3haOU=", + "path": "github.com/gophercloud/gophercloud/pagination", + "revision": "d5eda9707e146108e4d424062b602fd97a71c2e6", + "revisionTime": "2016-11-14T18:28:31Z" + }, { "checksumSHA1": "FUiF2WLrih0JdHsUTMMDz3DRokw=", "comment": "20141209094003-92-g95fa852", @@ -588,109 +690,6 @@ "path": "github.com/pmezard/go-difflib/difflib", "revision": "792786c7400a136282c1664665ae0a8db921c6c2" }, - { - "checksumSHA1": "reF91HBsVZuUHce8Rxco6k6Bfbc=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "KgPqSv4WKquMdM3Y0FxKIfqf/tw=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack", - "revision": "69cc33768d3318e48fa47a216722d2737e84158b", - "revisionTime": "2016-04-06T18:51:52Z" - }, - { - "checksumSHA1": "/K+RHJM5BFeg+3DYsR3WKPh7oVo=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/common/extensions", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "PPwPUj78EdIs+24vRMlThGWa6kw=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "kCkaqHFDBuZem57TEmfCpCNeUWs=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "A8CBSjxtNeXBtM3M51qqs5FMVNE=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "G3pxSBafMhqdqvuDPsctv74nI2M=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "C2K3kYQgwa65cVRvWrNZACQFX7Q=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/flavors", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "59nKYOu3eBZn82AWlZUGLwzieeM=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/images", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "+GkNX1ZNVOdbevUkfJUt/xGPZU0=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/compute/v2/servers", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "kBNUmTmGZgMc7RA8b/MfF8mNLrw=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/identity/v2/tenants", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "QWhBGvvt47Wpw4qf3bt/rVgXdyw=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/identity/v2/tokens", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "MKnoVPcRvTrooZtNQwOnJy0bglc=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/identity/v3/tokens", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "e4ussrqujJHizWbdBaLjIpWYmgY=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/openstack/utils", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "zNrhzobVrzrPD3NF8K0D8U/J4UM=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/pagination", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "U2yzK8GFNeHZqcoeotcHmK57lAI=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/testhelper", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, - { - "checksumSHA1": "fXtvTbQBML8QLu/qpD9sAt53J00=", - "comment": "v1.0.0-810-g53d1dc4", - "path": "github.com/rackspace/gophercloud/testhelper/client", - "revision": "53d1dc4400e1ebcd37a0e01d8c1fe2f4db3b99d2" - }, { "checksumSHA1": "TvF3ym5sZVNqGlUOS9HgOIl/sZM=", "path": "github.com/satori/go.uuid", diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index 1f499d189..dcea1991b 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -73,9 +73,6 @@ builder. ### Optional: -- `api_key` (string) - The API key used to access OpenStack. Some OpenStack - installations require this. - - `availability_zone` (string) - The availability zone to launch the server in. If this isn't specified, the default enforced by your OpenStack cluster will be used. This may be required for some OpenStack clusters. @@ -96,6 +93,13 @@ builder. - `floating_ip_pool` (string) - The name of the floating IP pool to use to allocate a floating IP. +- `image_members` (array of strings) - List of members to add to the image + after creation. An image member is usually a project (also called the + “tenant”) with whom the image is shared. + +- `image_visibility` (string) - One of "public", "private", "shared", or + "community". + - `insecure` (boolean) - Whether or not the connection to OpenStack can be done over an insecure connection. By default this is false.