diff --git a/go.mod b/go.mod index 0cbc266b136ed..ba84e9ad91be7 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ replace k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.18.6 replace k8s.io/code-generator => k8s.io/code-generator v0.18.6 -replace github.com/gophercloud/gophercloud => github.com/gophercloud/gophercloud v0.9.0 +replace github.com/gophercloud/gophercloud => github.com/gophercloud/gophercloud v0.11.0 require ( cloud.google.com/go v0.38.0 @@ -76,7 +76,7 @@ require ( github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.4.2 // indirect github.com/google/uuid v1.1.1 - github.com/gophercloud/gophercloud v0.7.1-0.20200116011225-46fdd1830e9a + github.com/gophercloud/gophercloud v0.11.0 github.com/gorilla/mux v1.7.3 github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl/v2 v2.3.0 diff --git a/go.sum b/go.sum index 0dc95e31c4726..0faa974393562 100644 --- a/go.sum +++ b/go.sum @@ -409,8 +409,8 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsC github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/gophercloud/gophercloud v0.9.0 h1:eJHQQFguQRv2FatH2d2VXH2ueTe2XzjgjwFjFS7SGcs= -github.com/gophercloud/gophercloud v0.9.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= +github.com/gophercloud/gophercloud v0.11.0 h1:pYMP9UZBdQa3lsfIZ1tZor4EbtxiuB6BHhocenkiH/E= +github.com/gophercloud/gophercloud v0.11.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= github.com/gophercloud/utils v0.0.0-20191020172814-bd86af96d544/go.mod h1:SZ9FTKibIotDtCrxAU/evccoyu1yhKST6hgBvwTB5Eg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= diff --git a/upup/pkg/fi/cloudup/openstack/cloud.go b/upup/pkg/fi/cloudup/openstack/cloud.go index 2c64b8deba79d..5356288100e02 100644 --- a/upup/pkg/fi/cloudup/openstack/cloud.go +++ b/upup/pkg/fi/cloudup/openstack/cloud.go @@ -23,6 +23,8 @@ import ( "strings" "time" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud" os "github.com/gophercloud/gophercloud/openstack" cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" @@ -291,6 +293,8 @@ type OpenstackCloud interface { GetImage(name string) (i *images.Image, err error) + GetFlavor(name string) (f *flavors.Flavor, err error) + AssociateFloatingIPToInstance(serverID string, opts floatingips.AssociateOpts) (err error) ListServerFloatingIPs(id string) ([]*string, error) diff --git a/upup/pkg/fi/cloudup/openstack/instance.go b/upup/pkg/fi/cloudup/openstack/instance.go index 5879cfdaeaebb..81985fdd018ba 100644 --- a/upup/pkg/fi/cloudup/openstack/instance.go +++ b/upup/pkg/fi/cloudup/openstack/instance.go @@ -20,6 +20,8 @@ import ( "fmt" "time" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" "github.com/mitchellh/mapstructure" "k8s.io/apimachinery/pkg/util/wait" @@ -189,3 +191,29 @@ func listInstances(c OpenstackCloud, opt servers.ListOptsBuilder) ([]servers.Ser return instances, wait.ErrWaitTimeout } } + +func (c *openstackCloud) GetFlavor(name string) (*flavors.Flavor, error) { + return getFlavor(c, name) +} + +func getFlavor(c OpenstackCloud, name string) (*flavors.Flavor, error) { + opts := flavors.ListOpts{} + pager := flavors.ListDetail(c.ComputeClient(), opts) + page, err := pager.AllPages() + if err != nil { + return nil, fmt.Errorf("failed to list flavors: %v", err) + } + + fs, err := flavors.ExtractFlavors(page) + if err != nil { + return nil, fmt.Errorf("failed to extract flavors: %v", err) + } + for _, f := range fs { + if f.Name == name { + return &f, nil + } + } + + return nil, fmt.Errorf("could not find flavor with name %v", name) + +} diff --git a/upup/pkg/fi/cloudup/openstack/mock_cloud.go b/upup/pkg/fi/cloudup/openstack/mock_cloud.go index d08eeccae30d3..df1d4b1c6694e 100644 --- a/upup/pkg/fi/cloudup/openstack/mock_cloud.go +++ b/upup/pkg/fi/cloudup/openstack/mock_cloud.go @@ -19,6 +19,8 @@ package openstack import ( "fmt" + "github.com/gophercloud/gophercloud/openstack/compute/v2/flavors" + "github.com/gophercloud/gophercloud" cinder "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" az "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones" @@ -331,6 +333,10 @@ func (c *MockCloud) GetImage(name string) (*images.Image, error) { return getImage(c, name) } +func (c *MockCloud) GetFlavor(name string) (*flavors.Flavor, error) { + return getFlavor(c, name) +} + func (c *MockCloud) GetInstance(id string) (*servers.Server, error) { return getInstance(c, id) } diff --git a/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel b/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel index ac73a634aa576..a338ae5e0683b 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel +++ b/upup/pkg/fi/cloudup/openstacktasks/BUILD.bazel @@ -50,7 +50,6 @@ go_library( "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers:go_default_library", - "//vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools:go_default_library", diff --git a/upup/pkg/fi/cloudup/openstacktasks/instance.go b/upup/pkg/fi/cloudup/openstacktasks/instance.go index 1322e078aeb32..5b4e6f859cd20 100644 --- a/upup/pkg/fi/cloudup/openstacktasks/instance.go +++ b/upup/pkg/fi/cloudup/openstacktasks/instance.go @@ -20,8 +20,6 @@ import ( "fmt" "strconv" - "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images" - "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" @@ -181,15 +179,21 @@ func (_ *Instance) RenderOpenstack(t *openstack.OpenstackAPITarget, a, e, change cloud := t.Cloud.(openstack.OpenstackCloud) imageName := fi.StringValue(e.Image) - imageRef, err := images.IDFromName(cloud.ImageClient(), imageName) + image, err := cloud.GetImage(imageName) if err != nil { return fmt.Errorf("failed to find image %v: %v", imageName, err) } + flavorName := fi.StringValue(e.Flavor) + flavor, err := cloud.GetFlavor(flavorName) + if err != nil { + return fmt.Errorf("failed to find flavor %v: %v", flavorName, err) + } + opt := servers.CreateOpts{ - Name: fi.StringValue(e.Name), - ImageRef: imageRef, - FlavorName: fi.StringValue(e.Flavor), + Name: fi.StringValue(e.Name), + ImageRef: image.ID, + FlavorRef: flavor.ID, Networks: []servers.Network{ { Port: fi.StringValue(e.Port.ID), diff --git a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md b/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md index d8bbd1d42c46a..23af89a3f7dc7 100644 --- a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md +++ b/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md @@ -1,4 +1,100 @@ -## 0.10.0 (Unreleased) +## 0.12.0 (Unreleased) + +## 0.11.0 (May 14, 2020) + +UPGRADE NOTES + +* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* All responses now have access to the returned headers. Please report any issues this has caused [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942) +* Changes have been made to the internal HTTP client to ensure response bodies are handled in a way that enables connections to be re-used more efficiently [GH-1952](https://github.com/gophercloud/gophercloud/pull/1952) + +IMPROVEMENTS + +* Added `objectstorage/v1/containers.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* Added `objectstorage/v1/objects.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930) +* All responses now have access to the returned headers [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942) +* Added `compute/v2/extensions/injectnetworkinfo.InjectNetworkInfo` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) +* Added `compute/v2/extensions/resetnetwork.ResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941) +* Added `identity/v3/extensions/trusts.ListRoles` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/trusts.GetRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/trusts.CheckRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Added `identity/v3/extensions/oauth1.Create` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.CreateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.DeleteConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListConsumers` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.UpdateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.RequestToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.AuthorizeToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.CreateAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.RevokeAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListAccessTokens` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.ListAccessTokenRoles` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `identity/v3/extensions/oauth1.GetAccessTokenRole` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935) +* Added `networking/v2/extensions/agents.Update` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `networking/v2/extensions/agents.Delete` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `networking/v2/extensions/agents.ScheduleDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `networking/v2/extensions/agents.RemoveDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954) +* Added `identity/v3/projects.CreateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.CreateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.UpdateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.UpdateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.Project.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `identity/v3/projects.Options.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951) +* Added `imageservice/v2/images.Image.OpenStackImageImportMethods` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962) +* Added `imageservice/v2/images.Image.OpenStackImageStoreIDs` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962) + +BUG FIXES + +* Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939) +* Changed `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from `string` to `*time.Time` [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937) +* Fixed issue with unmarshalling/decoding slices of composed structs [GH-1964](https://github.com/gophercloud/gophercloud/pull/1964) + +## 0.10.0 (April 12, 2020) + +UPGRADE NOTES + +* The various `IDFromName` convenience functions have been moved to https://github.com/gophercloud/utils [GH-1897](https://github.com/gophercloud/gophercloud/pull/1897) +* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) + +IMPROVEMENTS + +* Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891) +* Added `blockstorage/extensions/backups.Export` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894) +* Added `blockstorage/extensions/backups.Import` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894) +* Added `placement/v1/resourceproviders.GetTraits` [GH-1899](https://github.com/gophercloud/gophercloud/pull/1899) +* Added the ability to authenticate with Amazon EC2 Credentials [GH-1900](https://github.com/gophercloud/gophercloud/pull/1900) +* Added ability to list Nova services by binary and host [GH-1904](https://github.com/gophercloud/gophercloud/pull/1904) +* Added `compute/v2/extensions/services.Update` [GH-1902](https://github.com/gophercloud/gophercloud/pull/1902) +* Added system scope to v3 authentication [GH-1908](https://github.com/gophercloud/gophercloud/pull/1908) +* Added `identity/v3/extensions/ec2tokens.ValidateS3Token` [GH-1906](https://github.com/gophercloud/gophercloud/pull/1906) +* Added `containerinfra/v1/clusters.Cluster.HealthStatus` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910) +* Added `containerinfra/v1/clusters.Cluster.HealthStatusReason` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910) +* Added `loadbalancer/v2/amphorae.Failover` [GH-1912](https://github.com/gophercloud/gophercloud/pull/1912) +* Added `identity/v3/extensions/ec2credentials.List` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `identity/v3/extensions/ec2credentials.Get` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `identity/v3/extensions/ec2credentials.Create` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `identity/v3/extensions/ec2credentials.Delete` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916) +* Added `ErrUnexpectedResponseCode.ResponseHeader` [GH-1919](https://github.com/gophercloud/gophercloud/pull/1919) +* Added support for TOTP authentication [GH-1922](https://github.com/gophercloud/gophercloud/pull/1922) +* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) +* Added `sharedfilesystems/v2/shares.GetExportLocation` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932) +* Added `sharedfilesystems/v2/shares.Revert` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `sharedfilesystems/v2/shares.ResetStatus` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `sharedfilesystems/v2/shares.ForceDelete` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `sharedfilesystems/v2/shares.Unmanage` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931) +* Added `blockstorage/v3/attachments.Create` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.List` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Get` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Update` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Delete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) +* Added `blockstorage/v3/attachments.Complete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934) + +BUG FIXES + +* Fixed issue with Orchestration `get_file` only being able to read JSON and YAML files [GH-1915](https://github.com/gophercloud/gophercloud/pull/1915) ## 0.9.0 (March 10, 2020) diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/auth_options.go index 5ffa8d1e0a76c..4f301305e63e1 100644 --- a/vendor/github.com/gophercloud/gophercloud/auth_options.go +++ b/vendor/github.com/gophercloud/gophercloud/auth_options.go @@ -45,6 +45,9 @@ type AuthOptions struct { Password string `json:"password,omitempty"` + // Passcode is used in TOTP authentication method + Passcode string `json:"passcode,omitempty"` + // At most one of DomainID and DomainName must be provided if using Username // with Identity V3. Otherwise, either are optional. DomainID string `json:"-"` @@ -98,6 +101,7 @@ type AuthScope struct { ProjectName string DomainID string DomainName string + System bool } // ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder @@ -133,6 +137,8 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) { return map[string]interface{}{"auth": authMap}, nil } +// ToTokenV3CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) { type domainReq struct { ID *string `json:"id,omitempty"` @@ -148,7 +154,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s type userReq struct { ID *string `json:"id,omitempty"` Name *string `json:"name,omitempty"` - Password string `json:"password,omitempty"` + Password *string `json:"password,omitempty"` + Passcode *string `json:"passcode,omitempty"` Domain *domainReq `json:"domain,omitempty"` } @@ -167,11 +174,16 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s Secret *string `json:"secret,omitempty"` } + type totpReq struct { + User *userReq `json:"user,omitempty"` + } + type identityReq struct { Methods []string `json:"methods"` Password *passwordReq `json:"password,omitempty"` Token *tokenReq `json:"token,omitempty"` ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` + TOTP *totpReq `json:"totp,omitempty"` } type authReq struct { @@ -186,7 +198,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // if insufficient or incompatible information is present. var req request - if opts.Password == "" { + if opts.Password == "" && opts.Passcode == "" { if opts.TokenID != "" { // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication // parameters. @@ -274,7 +286,14 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } } else { // Password authentication. - req.Auth.Identity.Methods = []string{"password"} + if opts.Password != "" { + req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "password") + } + + // TOTP authentication. + if opts.Passcode != "" { + req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "totp") + } // At least one of Username and UserID must be specified. if opts.Username == "" && opts.UserID == "" { @@ -298,23 +317,46 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } // 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.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: &opts.Password, + Domain: &domainReq{ID: &opts.DomainID}, + }, + } + } + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + Name: &opts.Username, + Passcode: &opts.Passcode, + 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.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + Name: &opts.Username, + Password: &opts.Password, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } + } + + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + Name: &opts.Username, + Passcode: &opts.Passcode, + Domain: &domainReq{Name: &opts.DomainName}, + }, + } } } } @@ -329,8 +371,22 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s } // Configure the request for UserID and Password authentication. - req.Auth.Identity.Password = &passwordReq{ - User: userReq{ID: &opts.UserID, Password: opts.Password}, + if opts.Password != "" { + req.Auth.Identity.Password = &passwordReq{ + User: userReq{ + ID: &opts.UserID, + Password: &opts.Password, + }, + } + } + + if opts.Passcode != "" { + req.Auth.Identity.TOTP = &totpReq{ + User: &userReq{ + ID: &opts.UserID, + Passcode: &opts.Passcode, + }, + } } } } @@ -347,6 +403,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s return b, nil } +// ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in +// the v3 tokens package. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { // For backwards compatibility. // If AuthOptions.Scope was not set, try to determine it. @@ -364,6 +422,14 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } } + if opts.Scope.System { + return map[string]interface{}{ + "system": map[string]interface{}{ + "all": true, + }, + }, nil + } + if opts.Scope.ProjectName != "" { // ProjectName provided: either DomainID or DomainName must also be supplied. // ProjectID may not be supplied. @@ -433,5 +499,16 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } func (opts AuthOptions) CanReauth() bool { + if opts.Passcode != "" { + // cannot reauth using TOTP passcode + return false + } + return opts.AllowReauth } + +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { + return nil, nil +} diff --git a/vendor/github.com/gophercloud/gophercloud/errors.go b/vendor/github.com/gophercloud/gophercloud/errors.go index eb899a4a08b72..77cabf6a924be 100644 --- a/vendor/github.com/gophercloud/gophercloud/errors.go +++ b/vendor/github.com/gophercloud/gophercloud/errors.go @@ -2,6 +2,7 @@ package gophercloud import ( "fmt" + "net/http" "strings" ) @@ -77,11 +78,12 @@ func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string { // those listed in OkCodes is encountered. type ErrUnexpectedResponseCode struct { BaseError - URL string - Method string - Expected []int - Actual int - Body []byte + URL string + Method string + Expected []int + Actual int + Body []byte + ResponseHeader http.Header } func (e ErrUnexpectedResponseCode) Error() string { diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/openstack/BUILD.bazel index 95437398c60f6..adfa7964ee2c5 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/openstack/BUILD.bazel @@ -15,6 +15,8 @@ go_library( deps = [ "//vendor/github.com/gophercloud/gophercloud:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens:go_default_library", "//vendor/github.com/gophercloud/gophercloud/openstack/utils:go_default_library", ], diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go index 0e8d90ff8262a..c801de55532e6 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go @@ -38,6 +38,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { username := os.Getenv("OS_USERNAME") userID := os.Getenv("OS_USERID") password := os.Getenv("OS_PASSWORD") + passcode := os.Getenv("OS_PASSCODE") tenantID := os.Getenv("OS_TENANT_ID") tenantName := os.Getenv("OS_TENANT_NAME") domainID := os.Getenv("OS_DOMAIN_ID") @@ -73,8 +74,9 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { } } - if password == "" && applicationCredentialID == "" && applicationCredentialName == "" { + if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" { err := gophercloud.ErrMissingEnvironmentVariable{ + // silently ignore TOTP passcode warning, since it is not a common auth method EnvironmentVariable: "OS_PASSWORD", } return nilOptions, err @@ -112,6 +114,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { UserID: userID, Username: username, Password: password, + Passcode: passcode, TenantID: tenantID, TenantName: tenantName, DomainID: domainID, diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go index 609b5450281b4..f6063c5954c6c 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes/requests.go @@ -60,9 +60,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -96,14 +97,16 @@ func Delete(client *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder } url += query } - _, r.Err = client.Delete(url, nil) + resp, err := client.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves the Volume with the provided ID. To extract the Volume object // from the response, call the Extract method on the GetResult. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -197,44 +200,9 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// IDFromName is a convienience function that returns a volume's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractVolumes(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"} - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/openstack/client.go index 0b21beef93d14..655a9f6b915f6 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/client.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/client.go @@ -7,6 +7,8 @@ import ( "github.com/gophercloud/gophercloud" tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens" + "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1" tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" "github.com/gophercloud/gophercloud/openstack/utils" ) @@ -68,7 +70,7 @@ Example: ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ + client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ @@ -224,7 +226,15 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au return err } } else { - result := tokens3.Create(v3Client, opts) + var result tokens3.CreateResult + switch opts.(type) { + case *ec2tokens.AuthOptions: + result = ec2tokens.Create(v3Client, opts) + case *oauth1.AuthOptions: + result = oauth1.Create(v3Client, opts) + default: + result = tokens3.Create(v3Client, opts) + } err = client.SetTokenAndAuthResult(result) if err != nil { @@ -255,6 +265,14 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au o := *ot o.AllowReauth = false tao = &o + case *ec2tokens.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + case *oauth1.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o default: tao = opts } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go index d2346d4b426e4..096d8be7efdef 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go @@ -125,8 +125,9 @@ func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) ( r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } 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 index a922639dec901..0d8104d24ba71 100644 --- 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 @@ -36,21 +36,24 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns data about a previously created Floating IP. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous allocated Floating IP. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -81,7 +84,8 @@ func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts r.Err = err return } - _, r.Err = client.Post(associateURL(client, serverID), b, nil, nil) + resp, err := client.Post(associateURL(client, serverID), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -109,6 +113,7 @@ func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, op r.Err = err return } - _, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil) + resp, err := client.Post(disassociateURL(client, serverID), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go index b72807770ee8a..39056fdc70ff4 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go @@ -67,20 +67,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns public data about a previously uploaded KeyPair. func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { - _, r.Err = client.Get(getURL(client, name), &r.Body, nil) + resp, err := client.Get(getURL(client, name), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored KeyPair from the server. func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, name), nil) + resp, err := client.Delete(deleteURL(client, name), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups/requests.go index 5740afaf05f51..7a877718d9e40 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups/requests.go @@ -48,20 +48,23 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns data about a previously created ServerGroup. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previously allocated ServerGroup. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go index 6a262c212e129..3b09cdef2b5b4 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach/requests.go @@ -40,21 +40,24 @@ func Create(client *gophercloud.ServiceClient, serverID string, opts CreateOptsB r.Err = err return } - _, r.Err = client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client, serverID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get returns public data about a previously created VolumeAttachment. func Get(client *gophercloud.ServiceClient, serverID, attachmentID string) (r GetResult) { - _, r.Err = client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) + resp, err := client.Get(getURL(client, serverID, attachmentID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests the deletion of a previous stored VolumeAttachment from // the server. func Delete(client *gophercloud.ServiceClient, serverID, attachmentID string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, serverID, attachmentID), nil) + resp, err := client.Delete(deleteURL(client, serverID, attachmentID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go index 753024a18b728..1b7acd0a7e6a0 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors/requests.go @@ -142,22 +142,25 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves details of a single flavor. Use Extract to convert its // result into a Flavor. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete deletes the specified flavor ID. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -194,9 +197,10 @@ func AddAccess(client *gophercloud.ServiceClient, id string, opts AddAccessOptsB r.Err = err return } - _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -224,20 +228,23 @@ func RemoveAccess(client *gophercloud.ServiceClient, id string, opts RemoveAcces r.Err = err return } - _, r.Err = client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(accessActionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ExtraSpecs requests all the extra-specs for the given flavor ID. func ListExtraSpecs(client *gophercloud.ServiceClient, flavorID string) (r ListExtraSpecsResult) { - _, r.Err = client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + resp, err := client.Get(extraSpecsListURL(client, flavorID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } func GetExtraSpec(client *gophercloud.ServiceClient, flavorID string, key string) (r GetExtraSpecResult) { - _, r.Err = client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + resp, err := client.Get(extraSpecsGetURL(client, flavorID, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -264,9 +271,10 @@ func CreateExtraSpecs(client *gophercloud.ServiceClient, flavorID string, opts C r.Err = err return } - _, r.Err = client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(extraSpecsCreateURL(client, flavorID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -302,56 +310,19 @@ func UpdateExtraSpec(client *gophercloud.ServiceClient, flavorID string, opts Up r.Err = err return } - _, r.Err = client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(extraSpecUpdateURL(client, flavorID, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteExtraSpec will delete the key-value pair with the given key for the given // flavor ID. func DeleteExtraSpec(client *gophercloud.ServiceClient, flavorID, key string) (r DeleteExtraSpecResult) { - _, r.Err = client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ + resp, err := client.Delete(extraSpecDeleteURL(client, flavorID, key), &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// IDFromName is a convienience function that returns a flavor's ID given its -// name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - allPages, err := ListDetail(client, nil).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractFlavors(allPages) - if err != nil { - return "", err - } - - for _, f := range all { - if f.Name == name { - count++ - id = f.ID - } - } - - switch count { - case 0: - err := &gophercloud.ErrResourceNotFound{} - err.ResourceType = "flavor" - err.Name = name - return "", err - case 1: - return id, nil - default: - 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/images/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go deleted file mode 100644 index 22410a79a27c9..0000000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/doc.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Package images provides information and interaction with the images through -the OpenStack Compute service. - -This API is deprecated and will be removed from a future version of the Nova -API service. - -An image is a collection of files used to create or rebuild a server. -Operators provide a number of pre-built OS images by default. You may also -create custom images from cloud servers you have launched. - -Example to List Images - - listOpts := images.ListOpts{ - Limit: 2, - } - - allPages, err := images.ListDetail(computeClient, listOpts).AllPages() - if err != nil { - panic(err) - } - - allImages, err := images.ExtractImages(allPages) - if err != nil { - panic(err) - } - - for _, image := range allImages { - fmt.Printf("%+v\n", image) - } -*/ -package images diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go deleted file mode 100644 index 558b481b9e7d4..0000000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/requests.go +++ /dev/null @@ -1,109 +0,0 @@ -package images - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// ListOptsBuilder allows extensions to add additional parameters to the -// ListDetail request. -type ListOptsBuilder interface { - ToImageListQuery() (string, error) -} - -// ListOpts contain options filtering Images returned from a call to ListDetail. -type ListOpts struct { - // ChangesSince filters Images based on the last changed status (in date-time - // format). - ChangesSince string `q:"changes-since"` - - // Limit limits the number of Images to return. - Limit int `q:"limit"` - - // Mark is an Image UUID at which to set a marker. - Marker string `q:"marker"` - - // Name is the name of the Image. - Name string `q:"name"` - - // Server is the name of the Server (in URL format). - Server string `q:"server"` - - // Status is the current status of the Image. - Status string `q:"status"` - - // Type is the type of image (e.g. BASE, SERVER, ALL). - Type string `q:"type"` -} - -// ToImageListQuery formats a ListOpts into a query string. -func (opts ListOpts) ToImageListQuery() (string, error) { - q, err := gophercloud.BuildQueryString(opts) - return q.String(), err -} - -// ListDetail enumerates the available images. -func ListDetail(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { - url := listDetailURL(client) - if opts != nil { - query, err := opts.ToImageListQuery() - if err != nil { - return pagination.Pager{Err: err} - } - url += query - } - return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { - return ImagePage{pagination.LinkedPageBase{PageResult: r}} - }) -} - -// Get returns data about a specific image by its ID. -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) (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) { - count := 0 - id := "" - allPages, err := ListDetail(client, nil).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractImages(allPages) - if err != nil { - return "", err - } - - for _, f := range all { - if f.Name == name { - count++ - id = f.ID - } - } - - switch count { - case 0: - err := &gophercloud.ErrResourceNotFound{} - err.ResourceType = "image" - err.Name = name - return "", err - case 1: - return id, nil - default: - err := &gophercloud.ErrMultipleResourcesFound{} - err.ResourceType = "image" - err.Name = name - err.Count = count - return "", err - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go deleted file mode 100644 index 70d1018c72177..0000000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/results.go +++ /dev/null @@ -1,95 +0,0 @@ -package images - -import ( - "github.com/gophercloud/gophercloud" - "github.com/gophercloud/gophercloud/pagination" -) - -// GetResult is the response from a Get operation. Call its Extract method to -// interpret it as an Image. -type GetResult struct { - gophercloud.Result -} - -// DeleteResult is the result from a Delete operation. Call its ExtractErr -// method to determine if the call succeeded or failed. -type DeleteResult struct { - gophercloud.ErrResult -} - -// Extract interprets a GetResult as an Image. -func (r GetResult) Extract() (*Image, error) { - var s struct { - Image *Image `json:"image"` - } - err := r.ExtractInto(&s) - return s.Image, err -} - -// Image represents an Image returned by the Compute API. -type Image struct { - // ID is the unique ID of an image. - ID string - - // Created is the date when the image was created. - Created string - - // MinDisk is the minimum amount of disk a flavor must have to be able - // to create a server based on the image, measured in GB. - MinDisk int - - // MinRAM is the minimum amount of RAM a flavor must have to be able - // to create a server based on the image, measured in MB. - MinRAM int - - // Name provides a human-readable moniker for the OS image. - Name string - - // The Progress and Status fields indicate image-creation status. - Progress int - - // Status is the current status of the image. - Status string - - // Update is the date when the image was updated. - Updated string - - // Metadata provides free-form key/value pairs that further describe the - // image. - Metadata map[string]interface{} -} - -// ImagePage contains a single page of all Images returne from a ListDetail -// operation. Use ExtractImages to convert it into a slice of usable structs. -type ImagePage struct { - pagination.LinkedPageBase -} - -// IsEmpty returns true if an ImagePage contains no Image results. -func (page ImagePage) IsEmpty() (bool, error) { - images, err := ExtractImages(page) - 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) { - var s struct { - Links []gophercloud.Link `json:"images_links"` - } - err := page.ExtractInto(&s) - if err != nil { - return "", err - } - return gophercloud.ExtractNextURL(s.Links) -} - -// ExtractImages converts a page of List results into a slice of usable Image -// structs. -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/compute/v2/images/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go deleted file mode 100644 index 57787fb725ead..0000000000000 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/urls.go +++ /dev/null @@ -1,15 +0,0 @@ -package images - -import "github.com/gophercloud/gophercloud" - -func listDetailURL(client *gophercloud.ServiceClient) string { - return client.ServiceURL("images", "detail") -} - -func getURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("images", id) -} - -func deleteURL(client *gophercloud.ServiceClient, id string) string { - return client.ServiceURL("images", id) -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel index 27d600316d3d5..aef3f3f14bcc3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/BUILD.bazel @@ -15,8 +15,6 @@ go_library( visibility = ["//visibility:public"], deps = [ "//vendor/github.com/gophercloud/gophercloud:go_default_library", - "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/flavors:go_default_library", - "//vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images:go_default_library", "//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library", ], ) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go index d60018fdd9043..db1c01c1f27fa 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/servers/requests.go @@ -6,8 +6,6 @@ import ( "fmt" "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" ) @@ -148,24 +146,14 @@ type CreateOpts struct { // 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. + // ImageRef 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 `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 `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 is the ID or full URL to the flavor that describes the server's specs. 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 `json:"-"` - // SecurityGroups lists the names of the security groups to which this server // should belong. SecurityGroups []string `json:"-"` @@ -223,7 +211,6 @@ type CreateOpts struct { // ToServerCreateMap assembles a request body based on the contents of a // CreateOpts. func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { - sc := opts.ServiceClient opts.ServiceClient = nil b, err := gophercloud.BuildRequestBody(opts, "") if err != nil { @@ -274,42 +261,6 @@ func (opts CreateOpts) ToServerCreateMap() (map[string]interface{}, error) { } } - // 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 - } - } - - // 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 - } - if opts.Min != 0 { b["min_count"] = opts.Min } @@ -328,28 +279,32 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(listURL(client), reqBody, &r.Body, nil) + resp, err := client.Post(listURL(client), reqBody, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete requests that a server previously provisioned be removed from your // account. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // 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) + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"forceDelete": ""}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single server, by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(getURL(client, id), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -386,9 +341,10 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -400,7 +356,8 @@ func ChangeAdminPassword(client *gophercloud.ServiceClient, id, newPassword stri "adminPass": newPassword, }, } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -441,7 +398,7 @@ func (opts RebootOpts) ToServerRebootMap() (map[string]interface{}, error) { 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 rtored or the VM instance restarted. + Then, after a brief while, power is restored or the VM instance restarted. SoftReboot (aka OSReboot) simply tells the OS to restart under its own procedure. @@ -454,7 +411,8 @@ func Reboot(client *gophercloud.ServiceClient, id string, opts RebootOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -470,11 +428,8 @@ type RebuildOpts struct { // AdminPass is the server's admin password AdminPass string `json:"adminPass,omitempty"` - // ImageID is the ID of the image you want your server to be provisioned on. - ImageID string `json:"imageRef"` - - // ImageName is readable name of an image. - ImageName string `json:"-"` + // ImageRef is the ID of the image you want your server to be provisioned on. + ImageRef string `json:"imageRef"` // Name to set the server to Name string `json:"name,omitempty"` @@ -505,23 +460,6 @@ func (opts RebuildOpts) ToServerRebuildMap() (map[string]interface{}, error) { return nil, err } - // 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 - } - } - return map[string]interface{}{"rebuild": b}, nil } @@ -533,7 +471,8 @@ func Rebuild(client *gophercloud.ServiceClient, id string, opts RebuildOptsBuild r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, nil) + resp, err := client.Post(actionURL(client, id), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -571,23 +510,26 @@ func Resize(client *gophercloud.ServiceClient, id string, opts ResizeOptsBuilder r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, nil, nil) + resp, err := client.Post(actionURL(client, id), b, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // ConfirmResize confirms a previous resize operation on a server. // See Resize() for more details. func ConfirmResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"confirmResize": nil}, nil, &gophercloud.RequestOpts{ OkCodes: []int{201, 202, 204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // RevertResize cancels a previous resize operation on a server. // See Resize() for more details. func RevertResize(client *gophercloud.ServiceClient, id string) (r ActionResult) { - _, r.Err = client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) + resp, err := client.Post(actionURL(client, id), map[string]interface{}{"revertResize": nil}, nil, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -623,15 +565,17 @@ func ResetMetadata(client *gophercloud.ServiceClient, id string, opts ResetMetad r.Err = err return } - _, r.Err = client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadata requests all the metadata for the given server ID. func Metadata(client *gophercloud.ServiceClient, id string) (r GetMetadataResult) { - _, r.Err = client.Get(metadataURL(client, id), &r.Body, nil) + resp, err := client.Get(metadataURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -650,9 +594,10 @@ func UpdateMetadata(client *gophercloud.ServiceClient, id string, opts UpdateMet r.Err = err return } - _, r.Err = client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(metadataURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -690,23 +635,26 @@ func CreateMetadatum(client *gophercloud.ServiceClient, id string, opts Metadatu r.Err = err return } - _, r.Err = client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(metadatumURL(client, id, key), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Metadatum requests the key-value pair with the given key for the given // server ID. func Metadatum(client *gophercloud.ServiceClient, id, key string) (r GetMetadatumResult) { - _, r.Err = client.Get(metadatumURL(client, id, key), &r.Body, nil) + resp, err := client.Get(metadatumURL(client, id, key), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) (r DeleteMetadatumResult) { - _, r.Err = client.Delete(metadatumURL(client, id, key), nil) + resp, err := client.Delete(metadatumURL(client, id, key), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -759,52 +707,15 @@ func CreateImage(client *gophercloud.ServiceClient, id string, opts CreateImageO resp, err := client.Post(actionURL(client, id), b, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) - r.Err = err - r.Header = resp.Header + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } -// IDFromName is a convienience function that returns a server's ID given its -// name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - allPages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractServers(allPages) - if err != nil { - return "", err - } - - for _, f := range all { - if f.Name == name { - count++ - id = f.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "server"} - case 1: - return id, nil - default: - 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) (r GetPasswordResult) { - _, r.Err = client.Get(passwordURL(client, serverId), &r.Body, nil) + resp, err := client.Get(passwordURL(client, serverId), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -833,8 +744,9 @@ func ShowConsoleOutput(client *gophercloud.ServiceClient, id string, opts ShowCo r.Err = err return } - _, r.Err = client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(actionURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go index 1d9a77bcf5759..58414bc0dd136 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets/requests.go @@ -57,7 +57,8 @@ func ListByZone(client *gophercloud.ServiceClient, zoneID string, opts ListOptsB // Get implements the recordset Get request. func Get(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r GetResult) { - _, r.Err = client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) + resp, err := client.Get(rrsetURL(client, zoneID, rrsetID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -103,9 +104,10 @@ func Create(client *gophercloud.ServiceClient, zoneID string, opts CreateOptsBui r.Err = err return } - _, r.Err = client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(baseURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -158,16 +160,18 @@ func Update(client *gophercloud.ServiceClient, zoneID string, rrsetID string, op r.Err = err return } - _, r.Err = client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(rrsetURL(client, zoneID, rrsetID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete removes an existing RecordSet. func Delete(client *gophercloud.ServiceClient, zoneID string, rrsetID string) (r DeleteResult) { - _, r.Err = client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ + resp, err := client.Delete(rrsetURL(client, zoneID, rrsetID), &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go index 78b08ae4f85ad..7fa25359ff4e6 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/dns/v2/zones/requests.go @@ -55,7 +55,8 @@ func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pa // Get returns information about a zone, given its ID. func Get(client *gophercloud.ServiceClient, zoneID string) (r GetResult) { - _, r.Err = client.Get(zoneURL(client, zoneID), &r.Body, nil) + resp, err := client.Get(zoneURL(client, zoneID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -110,9 +111,10 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(baseURL(client), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -158,17 +160,19 @@ func Update(client *gophercloud.ServiceClient, zoneID string, opts UpdateOptsBui r.Err = err return } - _, r.Err = client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Patch(zoneURL(client, zoneID), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements a zone delete request. func Delete(client *gophercloud.ServiceClient, zoneID string) (r DeleteResult) { - _, r.Err = client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ + resp, err := client.Delete(zoneURL(client, zoneID), &gophercloud.RequestOpts{ OkCodes: []int{202}, JSONResponse: &r.Body, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/doc.go index cedf1f4d3a385..af4bd512bf505 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/doc.go @@ -7,7 +7,7 @@ Example of Creating a Service Client ao, err := openstack.AuthOptionsFromEnv() provider, err := openstack.AuthenticatedClient(ao) - client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{ + client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{ Region: os.Getenv("OS_REGION_NAME"), }) */ diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go index f21a58f10c86a..f16df38e5ece4 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go @@ -60,15 +60,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get requests details on a single tenant by ID. func Get(client *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = client.Get(getURL(client, id), &r.Body, nil) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -103,14 +105,16 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return } - _, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is the operation responsible for permanently deleting a tenant. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } 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 index ab32368cc6efe..2b64f108cbd96 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go @@ -87,17 +87,19 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r Creat r.Err = err return } - _, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, MoreHeaders: map[string]string{"X-Auth-Token": ""}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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{ + resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 203}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/BUILD.bazel new file mode 100644 index 0000000000000..31341812939d8 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "requests.go", + "urls.go", + ], + importmap = "k8s.io/kops/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens", + importpath = "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gophercloud/gophercloud:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens:go_default_library", + ], +) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go new file mode 100644 index 0000000000000..1f6f807fe06ef --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go @@ -0,0 +1,41 @@ +/* +Package tokens provides information and interaction with the EC2 token API +resource for the OpenStack Identity service. + +For more information, see: +https://docs.openstack.org/api-ref/identity/v2-ext/ + +Example to Create a Token From an EC2 access and secret keys + + var authOptions tokens.AuthOptionsBuilder + authOptions = &ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + } + + token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken() + if err != nil { + panic(err) + } + +Example to auth a client using EC2 access and secret keys + + client, err := openstack.NewClient("http://localhost:5000/v3") + if err != nil { + panic(err) + } + + var authOptions tokens.AuthOptionsBuilder + authOptions = &ec2tokens.AuthOptions{ + Access: "a7f1e798b7c2417cba4a02de97dc3cdc", + Secret: "18f4f6761ada4e3795fa5273c30349b9", + AllowReauth: true, + } + + err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + if err != nil { + panic(err) + } + +*/ +package ec2tokens diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go new file mode 100644 index 0000000000000..32ba0e621d914 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go @@ -0,0 +1,377 @@ +package ec2tokens + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/sha256" + "encoding/hex" + "fmt" + "math/rand" + "net/url" + "sort" + "strings" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" +) + +const ( + // EC2CredentialsAwsRequestV4 is a constant, used to generate AWS + // Credential V4. + EC2CredentialsAwsRequestV4 = "aws4_request" + // EC2CredentialsHmacSha1V2 is a HMAC SHA1 signature method. Used to + // generate AWS Credential V2. + EC2CredentialsHmacSha1V2 = "HmacSHA1" + // EC2CredentialsHmacSha256V2 is a HMAC SHA256 signature method. Used + // to generate AWS Credential V2. + EC2CredentialsHmacSha256V2 = "HmacSHA256" + // EC2CredentialsAwsHmacV4 is an AWS signature V4 signing method. + // More details: + // https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html + EC2CredentialsAwsHmacV4 = "AWS4-HMAC-SHA256" + // EC2CredentialsTimestampFormatV4 is an AWS signature V4 timestamp + // format. + EC2CredentialsTimestampFormatV4 = "20060102T150405Z" + // EC2CredentialsDateFormatV4 is an AWS signature V4 date format. + EC2CredentialsDateFormatV4 = "20060102" +) + +// AuthOptions represents options for authenticating a user using EC2 credentials. +type AuthOptions struct { + // Access is the EC2 Credential Access ID. + Access string `json:"access" required:"true"` + // Secret is the EC2 Credential Secret, used to calculate signature. + // Not used, when a Signature is is. + Secret string `json:"-"` + // Host is a HTTP request Host header. Used to calculate an AWS + // signature V2. For signature V4 set the Host inside Headers map. + // Optional. + Host string `json:"host"` + // Path is a HTTP request path. Optional. + Path string `json:"path"` + // Verb is a HTTP request method. Optional. + Verb string `json:"verb"` + // Headers is a map of HTTP request headers. Optional. + Headers map[string]string `json:"headers"` + // Region is a region name to calculate an AWS signature V4. Optional. + Region string `json:"-"` + // Service is a service name to calculate an AWS signature V4. Optional. + Service string `json:"-"` + // Params is a map of GET method parameters. Optional. + Params map[string]string `json:"params"` + // AllowReauth allows Gophercloud to re-authenticate automatically + // if/when your token expires. + AllowReauth bool `json:"-"` + // Signature can be either a []byte (encoded to base64 automatically) or + // a string. You can set the singature explicitly, when you already know + // it. In this case default Params won't be automatically set. Optional. + Signature interface{} `json:"signature"` + // BodyHash is a HTTP request body sha256 hash. When nil and Signature + // is not set, a random hash is generated. Optional. + BodyHash *string `json:"body_hash"` + // Timestamp is a timestamp to calculate a V4 signature. Optional. + Timestamp *time.Time `json:"-"` + // Token is a []byte string (encoded to base64 automatically) which was + // signed by an EC2 secret key. Used by S3 tokens for validation only. + // Token must be set with a Signature. If a Signature is not provided, + // a Token will be generated automatically along with a Signature. + Token []byte `json:"token,omitempty"` +} + +// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string +// for an AWS signature V2. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L133 +func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string { + var keys []string + for k := range params { + keys = append(keys, k) + } + sort.Strings(keys) + + var pairs []string + for _, k := range keys { + pairs = append(pairs, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k]))) + } + + return strings.Join(pairs, "&") +} + +// EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature +// V2. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148 +func EC2CredentialsBuildStringToSignV2(opts AuthOptions) []byte { + stringToSign := strings.Join([]string{ + opts.Verb, + opts.Host, + opts.Path, + }, "\n") + + return []byte(strings.Join([]string{ + stringToSign, + EC2CredentialsBuildCanonicalQueryStringV2(opts.Params), + }, "\n")) +} + +// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string +// for an AWS signature V4. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L244 +func EC2CredentialsBuildCanonicalQueryStringV4(verb string, params map[string]string) string { + if verb == "POST" { + return "" + } + return EC2CredentialsBuildCanonicalQueryStringV2(params) +} + +// EC2CredentialsBuildCanonicalHeadersV4 builds a canonical string based on +// "headers" map and "signedHeaders" string parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L216 +func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHeaders string) string { + headersLower := make(map[string]string, len(headers)) + for k, v := range headers { + headersLower[strings.ToLower(k)] = v + } + + var headersList []string + for _, h := range strings.Split(signedHeaders, ";") { + if v, ok := headersLower[h]; ok { + headersList = append(headersList, h+":"+v) + } + } + + return strings.Join(headersList, "\n") + "\n" +} + +// EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on +// input parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169 +func EC2CredentialsBuildSignatureKeyV4(secret, region, service string, date time.Time) []byte { + kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date.Format(EC2CredentialsDateFormatV4))) + kRegion := sumHMAC256(kDate, []byte(region)) + kService := sumHMAC256(kRegion, []byte(service)) + return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4)) +} + +// EC2CredentialsBuildStringToSignV4 builds an AWS v4 signature string to sign +// based on input parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251 +func EC2CredentialsBuildStringToSignV4(opts AuthOptions, signedHeaders string, bodyHash string, date time.Time) []byte { + scope := strings.Join([]string{ + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + }, "/") + + canonicalRequest := strings.Join([]string{ + opts.Verb, + opts.Path, + EC2CredentialsBuildCanonicalQueryStringV4(opts.Verb, opts.Params), + EC2CredentialsBuildCanonicalHeadersV4(opts.Headers, signedHeaders), + signedHeaders, + bodyHash, + }, "\n") + hash := sha256.Sum256([]byte(canonicalRequest)) + + return []byte(strings.Join([]string{ + EC2CredentialsAwsHmacV4, + date.Format(EC2CredentialsTimestampFormatV4), + scope, + hex.EncodeToString(hash[:]), + }, "\n")) +} + +// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input +// parameters. +// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L285..L286 +func EC2CredentialsBuildSignatureV4(key []byte, stringToSign []byte) string { + return hex.EncodeToString(sumHMAC256(key, stringToSign)) +} + +// EC2CredentialsBuildAuthorizationHeaderV4 builds an AWS v4 Authorization +// header based on auth parameters, date and signature +func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders string, signature string, date time.Time) string { + return fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s", + EC2CredentialsAwsHmacV4, + opts.Access, + date.Format(EC2CredentialsDateFormatV4), + opts.Region, + opts.Service, + EC2CredentialsAwsRequestV4, + signedHeaders, + signature) +} + +// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder +// interface. +func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { + return nil, nil +} + +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { + return nil, nil +} + +// CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface +func (opts *AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +// ToTokenV3CreateMap formats an AuthOptions into a create request. +func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { + b, err := gophercloud.BuildRequestBody(opts, "credentials") + if err != nil { + return nil, err + } + + if opts.Signature != nil { + return b, nil + } + + // calculate signature, when it is not set + c, _ := b["credentials"].(map[string]interface{}) + h := interfaceToMap(c, "headers") + p := interfaceToMap(c, "params") + + // detect and process a signature v2 + if v, ok := p["SignatureVersion"]; ok && v == "2" { + if _, ok := c["body_hash"]; ok { + delete(c, "body_hash") + } + if _, ok := c["headers"]; ok { + delete(c, "headers") + } + if v, ok := p["SignatureMethod"]; ok { + // params is a map of strings + strToSign := EC2CredentialsBuildStringToSignV2(*opts) + switch v { + case EC2CredentialsHmacSha1V2: + // keystone uses this method only when HmacSHA256 is not available on the server side + // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156 + c["signature"] = sumHMAC1([]byte(opts.Secret), strToSign) + return b, nil + case EC2CredentialsHmacSha256V2: + c["signature"] = sumHMAC256([]byte(opts.Secret), strToSign) + return b, nil + } + return nil, fmt.Errorf("unsupported signature method: %s", v) + } + return nil, fmt.Errorf("signature method must be provided") + } else if ok { + return nil, fmt.Errorf("unsupported signature version: %s", v) + } + + // it is not a signature v2, but a signature v4 + date := time.Now().UTC() + if opts.Timestamp != nil { + date = *opts.Timestamp + } + if v, _ := c["body_hash"]; v == nil { + // when body_hash is not set, generate a random one + c["body_hash"] = randomBodyHash() + } + + signedHeaders, _ := h["X-Amz-SignedHeaders"] + + stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date) + key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date) + c["signature"] = EC2CredentialsBuildSignatureV4(key, stringToSign) + h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4) + h["Authorization"] = EC2CredentialsBuildAuthorizationHeaderV4(*opts, signedHeaders, c["signature"].(string), date) + + // token is only used for S3 tokens validation and will be removed when using EC2 validation + c["token"] = stringToSign + + return b, nil +} + +// Create authenticates and either generates a new token from EC2 credentials +func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + b, err := opts.ToTokenV3CreateMap(nil) + if err != nil { + r.Err = err + return + } + + // delete "token" element, since it is used in s3tokens + deleteBodyElements(b, "token") + + resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't +// generate a new token ID, but returns a tokens.CreateResult. +func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + b, err := opts.ToTokenV3CreateMap(nil) + if err != nil { + r.Err = err + return + } + + // delete unused element, since it is used in ec2tokens only + deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb") + + resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{"X-Auth-Token": ""}, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// The following are small helper functions used to help build the signature. + +// sumHMAC1 is a func to implement the HMAC SHA1 signature method. +func sumHMAC1(key []byte, data []byte) []byte { + hash := hmac.New(sha1.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// sumHMAC256 is a func to implement the HMAC SHA256 signature method. +func sumHMAC256(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +// randomBodyHash is a func to generate a random sha256 hexdigest. +func randomBodyHash() string { + h := make([]byte, 64) + rand.Read(h) + return hex.EncodeToString(h) +} + +// interfaceToMap is a func used to represent a "credentials" map element as a +// "map[string]string" +func interfaceToMap(c map[string]interface{}, key string) map[string]string { + // convert map[string]interface{} to map[string]string + m := make(map[string]string) + if v, _ := c[key].(map[string]interface{}); v != nil { + for k, v := range v { + m[k] = v.(string) + } + } + + c[key] = m + + return m +} + +// deleteBodyElements deletes map body elements +func deleteBodyElements(b map[string]interface{}, elements ...string) { + if c, ok := b["credentials"].(map[string]interface{}); ok { + for _, k := range elements { + if _, ok := c[k]; ok { + delete(c, k) + } + } + } +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go new file mode 100644 index 0000000000000..84b33b282ead1 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go @@ -0,0 +1,11 @@ +package ec2tokens + +import "github.com/gophercloud/gophercloud" + +func ec2tokensURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("ec2tokens") +} + +func s3tokensURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("s3tokens") +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/BUILD.bazel b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/BUILD.bazel similarity index 66% rename from vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/BUILD.bazel rename to vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/BUILD.bazel index 1ea36fc1205e1..b14830d692063 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images/BUILD.bazel +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/BUILD.bazel @@ -8,11 +8,12 @@ go_library( "results.go", "urls.go", ], - importmap = "k8s.io/kops/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/images", - importpath = "github.com/gophercloud/gophercloud/openstack/compute/v2/images", + importmap = "k8s.io/kops/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1", + importpath = "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1", visibility = ["//visibility:public"], deps = [ "//vendor/github.com/gophercloud/gophercloud:go_default_library", + "//vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens:go_default_library", "//vendor/github.com/gophercloud/gophercloud/pagination:go_default_library", ], ) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go new file mode 100644 index 0000000000000..c5b0831ca1f2c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go @@ -0,0 +1,123 @@ +/* +Package oauth1 enables management of OpenStack OAuth1 tokens and Authentication. + +Example to Create an OAuth1 Consumer + + createConsumerOpts := oauth1.CreateConsumerOpts{ + Description: "My consumer", + } + consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Consumer secret is available only on create response + fmt.Printf("Consumer: %+v\n", consumer) + +Example to Request an unauthorized OAuth1 token + + requestTokenOpts := oauth1.RequestTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthSignatureMethod: oauth1.HMACSHA1, + RequestedProjectID: projectID, + } + requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Request token secret is available only on request response + fmt.Printf("Request token: %+v\n", requestToken) + +Example to Authorize an unauthorized OAuth1 token + + authorizeTokenOpts := oauth1.AuthorizeTokenOpts{ + Roles: []oauth1.Role{ + {Name: "member"}, + }, + } + authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract() + if err != nil { + panic(err) + } + + fmt.Printf("Verifier ID of the unauthorized Token: %+v\n", authToken.OAuthVerifier) + +Example to Create an OAuth1 Access Token + + accessTokenOpts := oauth1.CreateAccessTokenOpts{ + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + OAuthToken: requestToken.OAuthToken, + OAuthTokenSecret: requestToken.OAuthTokenSecret, + OAuthVerifier: authToken.OAuthVerifier, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract() + if err != nil { + panic(err) + } + + // NOTE: Access token secret is available only on create response + fmt.Printf("OAuth1 Access Token: %+v\n", accessToken) + +Example to List User's OAuth1 Access Tokens + + allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages() + if err != nil { + panic(err) + } + accessTokens, err := oauth1.ExtractAccessTokens(allPages) + if err != nil { + panic(err) + } + + for _, accessToken := range accessTokens { + fmt.Printf("Access Token: %+v\n", accessToken) + } + +Example to Authenticate a client using OAuth1 method + + client, err := openstack.NewClient("http://localhost:5000/v3") + if err != nil { + panic(err) + } + + authOptions := &oauth1.AuthOptions{ + // consumer token, created earlier + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + // access token, created earlier + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{}) + if err != nil { + panic(err) + } + +Example to Create a Token using OAuth1 method + + var oauth1Token struct { + tokens.Token + oauth1.TokenExt + } + + createOpts := &oauth1.AuthOptions{ + // consumer token, created earlier + OAuthConsumerKey: consumer.ID, + OAuthConsumerSecret: consumer.Secret, + // access token, created earlier + OAuthToken: accessToken.OAuthToken, + OAuthTokenSecret: accessToken.OAuthTokenSecret, + OAuthSignatureMethod: oauth1.HMACSHA1, + } + err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token) + if err != nil { + panic(err) + } + +*/ +package oauth1 diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go new file mode 100644 index 0000000000000..028b5a45bd7b1 --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go @@ -0,0 +1,587 @@ +package oauth1 + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "io/ioutil" + "math/rand" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens" + "github.com/gophercloud/gophercloud/pagination" +) + +// Type SignatureMethod is a OAuth1 SignatureMethod type. +type SignatureMethod string + +const ( + // HMACSHA1 is a recommended OAuth1 signature method. + HMACSHA1 SignatureMethod = "HMAC-SHA1" + + // PLAINTEXT signature method is not recommended to be used in + // production environment. + PLAINTEXT SignatureMethod = "PLAINTEXT" + + // OAuth1TokenContentType is a supported content type for an OAuth1 + // token. + OAuth1TokenContentType = "application/x-www-form-urlencoded" +) + +// AuthOptions represents options for authenticating a user using OAuth1 tokens. +type AuthOptions struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthToken is the OAuth1 Request Token. + OAuthToken string `q:"oauth_token" required:"true"` + + // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate + // an OAuth1 request signature. + OAuthTokenSecret string `required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` + + // AllowReauth allows Gophercloud to re-authenticate automatically + // if/when your token expires. + AllowReauth bool +} + +// ToTokenV3HeadersMap builds the headers required for an OAuth1-based create +// request. +func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} + + method := headerOpts["method"].(string) + u := headerOpts["url"].(string) + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + + authHeader := buildAuthHeader(q.Query(), signature) + + headers := map[string]string{ + "Authorization": authHeader, + "X-Auth-Token": "", + } + + return headers, nil +} + +// ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder +// interface. +func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { + return nil, nil +} + +// CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder +// interface. +func (opts AuthOptions) CanReauth() bool { + return opts.AllowReauth +} + +// ToTokenV3CreateMap builds a create request body. +func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) { + // identityReq defines the "identity" portion of an OAuth1-based authentication + // create request body. + type identityReq struct { + Methods []string `json:"methods"` + OAuth1 struct{} `json:"oauth1"` + } + + // authReq defines the "auth" portion of an OAuth1-based authentication + // create request body. + type authReq struct { + Identity identityReq `json:"identity"` + } + + // oauth1Request defines how an OAuth1-based authentication create + // request body looks. + type oauth1Request struct { + Auth authReq `json:"auth"` + } + + var req oauth1Request + + req.Auth.Identity.Methods = []string{"oauth1"} + return gophercloud.BuildRequestBody(req, "") +} + +// Create authenticates and either generates a new OpenStack token from an +// OAuth1 token. +func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) { + b, err := opts.ToTokenV3CreateMap(nil) + if err != nil { + r.Err = err + return + } + + headerOpts := map[string]interface{}{ + "method": "POST", + "url": authURL(client), + } + + h, err := opts.ToTokenV3HeadersMap(headerOpts) + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateConsumerOptsBuilder allows extensions to add additional parameters to +// the CreateConsumer request. +type CreateConsumerOptsBuilder interface { + ToOAuth1CreateConsumerMap() (map[string]interface{}, error) +} + +// CreateConsumerOpts provides options used to create a new Consumer. +type CreateConsumerOpts struct { + // Description is the consumer description. + Description string `json:"description"` +} + +// ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request. +func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "consumer") +} + +// Create creates a new Consumer. +func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) { + b, err := opts.ToOAuth1CreateConsumerMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{201}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// Delete deletes a Consumer. +func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) { + resp, err := client.Delete(consumerURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// List enumerates Consumers. +func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page { + return ConsumersPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetConsumer retrieves details on a single Consumer by ID. +func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) { + resp, err := client.Get(consumerURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// UpdateConsumerOpts provides options used to update a consumer. +type UpdateConsumerOpts struct { + // Description is the consumer description. + Description string `json:"description"` +} + +// ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update +// request. +func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "consumer") +} + +// UpdateConsumer updates an existing Consumer. +func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) { + b, err := opts.ToOAuth1UpdateConsumerMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RequestTokenOptsBuilder allows extensions to add additional parameters to the +// RequestToken request. +type RequestTokenOptsBuilder interface { + ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error) +} + +// RequestTokenOpts provides options used to get a consumer unauthorized +// request token. +type RequestTokenOpts struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` + + // RequestedProjectID is a Project ID a consumer user requested an + // access to. + RequestedProjectID string `h:"Requested-Project-Id"` +} + +// ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request +// headers. +func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob") + if err != nil { + return nil, err + } + + h, err := gophercloud.BuildHeaders(opts) + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret} + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + authHeader := buildAuthHeader(q.Query(), signature) + + h["Authorization"] = authHeader + + return h, nil +} + +// RequestToken requests an unauthorized OAuth1 Token. +func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) { + h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client)) + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + KeepResponseBody: true, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err != nil { + return + } + defer resp.Body.Close() + if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType { + r.Err = fmt.Errorf("unsupported Content-Type: %q", v) + return + } + r.Body, r.Err = ioutil.ReadAll(resp.Body) + return +} + +// AuthorizeTokenOptsBuilder allows extensions to add additional parameters to +// the AuthorizeToken request. +type AuthorizeTokenOptsBuilder interface { + ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) +} + +// AuthorizeTokenOpts provides options used to authorize a request token. +type AuthorizeTokenOpts struct { + Roles []Role `json:"roles"` +} + +// Role is a struct representing a role object in a AuthorizeTokenOpts struct. +type Role struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` +} + +// ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token +// request. +func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) { + for _, r := range opts.Roles { + if r == (Role{}) { + return nil, fmt.Errorf("role must not be empty") + } + } + return gophercloud.BuildRequestBody(opts, "") +} + +// AuthorizeToken authorizes an unauthorized consumer token. +func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) { + b, err := opts.ToOAuth1AuthorizeTokenMap() + if err != nil { + r.Err = err + return + } + resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// CreateAccessTokenOptsBuilder allows extensions to add additional parameters +// to the CreateAccessToken request. +type CreateAccessTokenOptsBuilder interface { + ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error) +} + +// CreateAccessTokenOpts provides options used to create an OAuth1 token. +type CreateAccessTokenOpts struct { + // OAuthConsumerKey is the OAuth1 Consumer Key. + OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"` + + // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate + // an OAuth1 request signature. + OAuthConsumerSecret string `required:"true"` + + // OAuthToken is the OAuth1 Request Token. + OAuthToken string `q:"oauth_token" required:"true"` + + // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate + // an OAuth1 request signature. + OAuthTokenSecret string `required:"true"` + + // OAuthVerifier is the OAuth1 verification code. + OAuthVerifier string `q:"oauth_verifier" required:"true"` + + // OAuthSignatureMethod is the OAuth1 signature method the Consumer used + // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT". + // "PLAINTEXT" is not recommended for production usage. + OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"` + + // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix + // timestamp will be used. + OAuthTimestamp *time.Time + + // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string, + // uniquely generated for each request. Will be generated automatically + // when it is not set. + OAuthNonce string `q:"oauth_nonce"` +} + +// ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of +// request headers. +func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) { + q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "") + if err != nil { + return nil, err + } + + signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret} + stringToSign := buildStringToSign(method, u, q.Query()) + signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys)) + authHeader := buildAuthHeader(q.Query(), signature) + + headers := map[string]string{ + "Authorization": authHeader, + } + + return headers, nil +} + +// CreateAccessToken creates a new OAuth1 Access Token +func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) { + h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client)) + if err != nil { + r.Err = err + return + } + + resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{ + MoreHeaders: h, + OkCodes: []int{201}, + KeepResponseBody: true, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + if r.Err != nil { + return + } + defer resp.Body.Close() + if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType { + r.Err = fmt.Errorf("unsupported Content-Type: %q", v) + return + } + r.Body, r.Err = ioutil.ReadAll(resp.Body) + return +} + +// GetAccessToken retrieves details on a single OAuth1 access token by an ID. +func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) { + resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// RevokeAccessToken revokes an OAuth1 access token. +func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) { + resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// ListAccessTokens enumerates authorized access tokens. +func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager { + url := userAccessTokensURL(client, userID) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// ListAccessTokenRoles enumerates authorized access token roles. +func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager { + url := userAccessTokenRolesURL(client, userID, id) + return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page { + return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}} + }) +} + +// GetAccessTokenRole retrieves details on a single OAuth1 access token role by +// an ID. +func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) { + resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// The following are small helper functions used to help build the signature. + +// buildOAuth1QueryString builds a URLEncoded parameters string specific for +// OAuth1-based requests. +func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) { + q, err := gophercloud.BuildQueryString(opts) + if err != nil { + return nil, err + } + + query := q.Query() + + if timestamp != nil { + // use provided timestamp + query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10)) + } else { + // use current timestamp + query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10)) + } + + if query.Get("oauth_nonce") == "" { + // when nonce is not set, generate a random one + query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp")) + } + + if callback != "" { + query.Set("oauth_callback", callback) + } + query.Set("oauth_version", "1.0") + + return &url.URL{RawQuery: query.Encode()}, nil +} + +// buildStringToSign builds a string to be signed. +func buildStringToSign(method string, u string, query url.Values) []byte { + parsedURL, _ := url.Parse(u) + p := parsedURL.Port() + s := parsedURL.Scheme + + // Default scheme port must be stripped + if s == "http" && p == "80" || s == "https" && p == "443" { + parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p) + } + + // Ensure that URL doesn't contain queries + parsedURL.RawQuery = "" + + v := strings.Join( + []string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&") + + return []byte(v) +} + +// signString signs a string using an OAuth1 signature method. +func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string { + var key []byte + for i, k := range signatureKeys { + key = append(key, []byte(url.QueryEscape(k))...) + if i == 0 { + key = append(key, '&') + } + } + + var signedString string + switch signatureMethod { + case PLAINTEXT: + signedString = string(key) + default: + h := hmac.New(sha1.New, key) + h.Write(strToSign) + signedString = base64.StdEncoding.EncodeToString(h.Sum(nil)) + } + + return signedString +} + +// buildAuthHeader generates an OAuth1 Authorization header with a signature +// calculated using an OAuth1 signature method. +func buildAuthHeader(query url.Values, signature string) string { + var authHeader []string + var keys []string + for k := range query { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + for _, v := range query[k] { + authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v))) + } + } + + authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature)) + + return "OAuth " + strings.Join(authHeader, ", ") +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go new file mode 100644 index 0000000000000..bb109e98e315c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go @@ -0,0 +1,305 @@ +package oauth1 + +import ( + "encoding/json" + "net/url" + "time" + + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// Consumer represents a delegated authorization request between two +// identities. +type Consumer struct { + ID string `json:"id"` + Secret string `json:"secret"` + Description string `json:"description"` +} + +type consumerResult struct { + gophercloud.Result +} + +// CreateConsumerResult is the response from a Create operation. Call its +// Extract method to interpret it as a Consumer. +type CreateConsumerResult struct { + consumerResult +} + +// UpdateConsumerResult is the response from a Create operation. Call its +// Extract method to interpret it as a Consumer. +type UpdateConsumerResult struct { + consumerResult +} + +// DeleteConsumerResult is the response from a Delete operation. Call its +// ExtractErr to determine if the request succeeded or failed. +type DeleteConsumerResult struct { + gophercloud.ErrResult +} + +// ConsumersPage is a single page of Region results. +type ConsumersPage struct { + pagination.LinkedPageBase +} + +// GetConsumerResult is the response from a Get operation. Call its Extract +// method to interpret it as a Consumer. +type GetConsumerResult struct { + consumerResult +} + +// IsEmpty determines whether or not a page of Consumers contains any results. +func (c ConsumersPage) IsEmpty() (bool, error) { + consumers, err := ExtractConsumers(c) + return len(consumers) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (c ConsumersPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := c.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractConsumers returns a slice of Consumers contained in a single page of +// results. +func ExtractConsumers(r pagination.Page) ([]Consumer, error) { + var s struct { + Consumers []Consumer `json:"consumers"` + } + err := (r.(ConsumersPage)).ExtractInto(&s) + return s.Consumers, err +} + +// Extract interprets any consumer result as a Consumer. +func (c consumerResult) Extract() (*Consumer, error) { + var s struct { + Consumer *Consumer `json:"consumer"` + } + err := c.ExtractInto(&s) + return s.Consumer, err +} + +// Token contains an OAuth1 token. +type Token struct { + // OAuthToken is the key value for the oauth token that the Identity API returns. + OAuthToken string `q:"oauth_token"` + // OAuthTokenSecret is the secret value associated with the OAuth Token. + OAuthTokenSecret string `q:"oauth_token_secret"` + // OAUthExpiresAt is the date and time when an OAuth token expires. + OAUthExpiresAt *time.Time `q:"-"` +} + +// TokenResult is a struct to handle +// "Content-Type: application/x-www-form-urlencoded" response. +type TokenResult struct { + gophercloud.Result + Body []byte +} + +// Extract interprets any OAuth1 token result as a Token. +func (r TokenResult) Extract() (*Token, error) { + if r.Err != nil { + return nil, r.Err + } + + values, err := url.ParseQuery(string(r.Body)) + if err != nil { + return nil, err + } + + token := &Token{ + OAuthToken: values.Get("oauth_token"), + OAuthTokenSecret: values.Get("oauth_token_secret"), + } + + if v := values.Get("oauth_expires_at"); v != "" { + if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil { + return nil, err + } else { + token.OAUthExpiresAt = &t + } + } + + return token, nil +} + +// AuthorizedToken contains an OAuth1 authorized token info. +type AuthorizedToken struct { + // OAuthVerifier is the ID of the token verifier. + OAuthVerifier string `json:"oauth_verifier"` +} + +type AuthorizeTokenResult struct { + gophercloud.Result +} + +// Extract interprets AuthorizeTokenResult result as a AuthorizedToken. +func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) { + var s struct { + AuthorizedToken *AuthorizedToken `json:"token"` + } + err := r.ExtractInto(&s) + return s.AuthorizedToken, err +} + +// AccessToken represents an AccessToken response as a struct. +type AccessToken struct { + ID string `json:"id"` + ConsumerID string `json:"consumer_id"` + ProjectID string `json:"project_id"` + AuthorizingUserID string `json:"authorizing_user_id"` + ExpiresAt *time.Time `json:"-"` +} + +func (r *AccessToken) UnmarshalJSON(b []byte) error { + type tmp AccessToken + var s struct { + tmp + ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"` + } + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + *r = AccessToken(s.tmp) + + if s.ExpiresAt != nil { + t := time.Time(*s.ExpiresAt) + r.ExpiresAt = &t + } + + return nil +} + +type GetAccessTokenResult struct { + gophercloud.Result +} + +// Extract interprets any GetAccessTokenResult result as an AccessToken. +func (r GetAccessTokenResult) Extract() (*AccessToken, error) { + var s struct { + AccessToken *AccessToken `json:"access_token"` + } + err := r.ExtractInto(&s) + return s.AccessToken, err +} + +// RevokeAccessTokenResult is the response from a Delete operation. Call its +// ExtractErr to determine if the request succeeded or failed. +type RevokeAccessTokenResult struct { + gophercloud.ErrResult +} + +// AccessTokensPage is a single page of Access Tokens results. +type AccessTokensPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessTokensPage contains any results. +func (r AccessTokensPage) IsEmpty() (bool, error) { + accessTokens, err := ExtractAccessTokens(r) + return len(accessTokens) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessTokensPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractAccessTokens returns a slice of AccessTokens contained in a single +// page of results. +func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) { + var s struct { + AccessTokens []AccessToken `json:"access_tokens"` + } + err := (r.(AccessTokensPage)).ExtractInto(&s) + return s.AccessTokens, err +} + +// AccessTokenRole represents an Access Token Role struct. +type AccessTokenRole struct { + ID string `json:"id"` + Name string `json:"name"` + DomainID string `json:"domain_id"` +} + +// AccessTokenRolesPage is a single page of Access Token roles results. +type AccessTokenRolesPage struct { + pagination.LinkedPageBase +} + +// IsEmpty determines whether or not a an AccessTokensPage contains any results. +func (r AccessTokenRolesPage) IsEmpty() (bool, error) { + accessTokenRoles, err := ExtractAccessTokenRoles(r) + return len(accessTokenRoles) == 0, err +} + +// NextPageURL extracts the "next" link from the links section of the result. +func (r AccessTokenRolesPage) NextPageURL() (string, error) { + var s struct { + Links struct { + Next string `json:"next"` + Previous string `json:"previous"` + } `json:"links"` + } + err := r.ExtractInto(&s) + if err != nil { + return "", err + } + return s.Links.Next, err +} + +// ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a +// single page of results. +func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) { + var s struct { + AccessTokenRoles []AccessTokenRole `json:"roles"` + } + err := (r.(AccessTokenRolesPage)).ExtractInto(&s) + return s.AccessTokenRoles, err +} + +type GetAccessTokenRoleResult struct { + gophercloud.Result +} + +// Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole. +func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) { + var s struct { + AccessTokenRole *AccessTokenRole `json:"role"` + } + err := r.ExtractInto(&s) + return s.AccessTokenRole, err +} + +// OAuth1 is an OAuth1 object, returned in OAuth1 token result. +type OAuth1 struct { + AccessTokenID string `json:"access_token_id"` + ConsumerID string `json:"consumer_id"` +} + +// TokenExt represents an extension of the base token result. +type TokenExt struct { + OAuth1 OAuth1 `json:"OS-OAUTH1"` +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go new file mode 100644 index 0000000000000..9b51d53b31d1c --- /dev/null +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go @@ -0,0 +1,43 @@ +package oauth1 + +import "github.com/gophercloud/gophercloud" + +func consumersURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "consumers") +} + +func consumerURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("OS-OAUTH1", "consumers", id) +} + +func requestTokenURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "request_token") +} + +func authorizeTokenURL(c *gophercloud.ServiceClient, id string) string { + return c.ServiceURL("OS-OAUTH1", "authorize", id) +} + +func createAccessTokenURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("OS-OAUTH1", "access_token") +} + +func userAccessTokensURL(c *gophercloud.ServiceClient, userID string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens") +} + +func userAccessTokenURL(c *gophercloud.ServiceClient, userID string, id string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id) +} + +func userAccessTokenRolesURL(c *gophercloud.ServiceClient, userID string, id string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles") +} + +func userAccessTokenRoleURL(c *gophercloud.ServiceClient, userID string, id string, roleID string) string { + return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles", roleID) +} + +func authURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL("auth", "tokens") +} 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 index e4d766b2327e4..d8c455d16022b 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go @@ -8,6 +8,7 @@ type Scope struct { ProjectName string DomainID string DomainName string + System bool } // AuthOptionsBuilder provides the ability for extensions to add additional @@ -16,6 +17,7 @@ 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) + ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) ToTokenV3ScopeMap() (map[string]interface{}, error) CanReauth() bool } @@ -36,6 +38,9 @@ type AuthOptions struct { Password string `json:"password,omitempty"` + // Passcode is used in TOTP authentication method + Passcode string `json:"passcode,omitempty"` + // At most one of DomainID and DomainName must be provided if using Username // with Identity V3. Otherwise, either are optional. DomainID string `json:"-"` @@ -67,6 +72,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s Username: opts.Username, UserID: opts.UserID, Password: opts.Password, + Passcode: opts.Passcode, DomainID: opts.DomainID, DomainName: opts.DomainName, AllowReauth: opts.AllowReauth, @@ -79,7 +85,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s return gophercloudAuthOpts.ToTokenV3CreateMap(scope) } -// ToTokenV3CreateMap builds a scope request body from AuthOptions. +// ToTokenV3ScopeMap builds a scope request body from AuthOptions. func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { scope := gophercloud.AuthScope(opts.Scope) @@ -93,10 +99,21 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) { } func (opts *AuthOptions) CanReauth() bool { + if opts.Passcode != "" { + // cannot reauth using TOTP passcode + return false + } + return opts.AllowReauth } -func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string { +// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder +// interface in the v3 tokens package. +func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) { + return nil, nil +} + +func subjectTokenHeaders(subjectToken string) map[string]string { return map[string]string{ "X-Subject-Token": subjectToken, } @@ -120,30 +137,24 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu 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 - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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), + MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 203}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Validate determines if a specified token is valid or not. func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{ - MoreHeaders: subjectTokenHeaders(c, token), + MoreHeaders: subjectTokenHeaders(token), OkCodes: []int{200, 204, 404}, }) if err != nil { @@ -155,8 +166,9 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) { // 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), + resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{ + MoreHeaders: subjectTokenHeaders(token), }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } 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 index c67a6a9d471b1..f0cd5cbde879e 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/requests.go @@ -206,19 +206,22 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create r.Err = err return r } - _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{OkCodes: []int{201}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete implements image delete request. func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = client.Delete(deleteURL(client, id), nil) + resp, err := client.Delete(deleteURL(client, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) 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) + resp, err := client.Get(getURL(client, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -229,10 +232,11 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder r.Err = err return r } - _, r.Err = client.Patch(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, 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"}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -378,39 +382,3 @@ func (r UpdateImageProperty) ToImagePatchMap() map[string]interface{} { return updateMap } - -// IDFromName is a convienience function that returns an image's ID given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractImages(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "image"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "image"} - } -} 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 index 676181e1f4e5a..f445cc38fcbbc 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/imageservice/v2/images/results.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" "github.com/gophercloud/gophercloud" @@ -86,13 +87,22 @@ type Image struct { // VirtualSize is the virtual size of the image VirtualSize int64 `json:"virtual_size"` + + // OpenStackImageImportMethods is a slice listing the types of import + // methods available in the cloud. + OpenStackImageImportMethods []string `json:"-"` + // OpenStackImageStoreIDs is a slice listing the store IDs available in + // the cloud. + OpenStackImageStoreIDs []string `json:"-"` } func (r *Image) UnmarshalJSON(b []byte) error { type tmp Image var s struct { tmp - SizeBytes interface{} `json:"size"` + SizeBytes interface{} `json:"size"` + OpenStackImageImportMethods string `json:"openstack-image-import-methods"` + OpenStackImageStoreIDs string `json:"openstack-image-store-ids"` } err := json.Unmarshal(b, &s) if err != nil { @@ -120,9 +130,18 @@ func (r *Image) UnmarshalJSON(b []byte) error { if resultMap, ok := result.(map[string]interface{}); ok { delete(resultMap, "self") delete(resultMap, "size") + delete(resultMap, "openstack-image-import-methods") + delete(resultMap, "openstack-image-store-ids") r.Properties = internal.RemainingKeys(Image{}, resultMap) } + if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageImportMethods), splitFunc); len(v) > 0 { + r.OpenStackImageImportMethods = v + } + if v := strings.FieldsFunc(strings.TrimSpace(s.OpenStackImageStoreIDs), splitFunc); len(v) > 0 { + r.OpenStackImageStoreIDs = v + } + return err } @@ -133,6 +152,20 @@ type commonResult struct { // Extract interprets any commonResult as an Image. func (r commonResult) Extract() (*Image, error) { var s *Image + if v, ok := r.Body.(map[string]interface{}); ok { + for k, h := range r.Header { + if strings.ToLower(k) == "openstack-image-import-methods" { + for _, s := range h { + v["openstack-image-import-methods"] = s + } + } + if strings.ToLower(k) == "openstack-image-store-ids" { + for _, s := range h { + v["openstack-image-store-ids"] = s + } + } + } + } err := r.ExtractInto(&s) return s, err } @@ -200,3 +233,8 @@ func ExtractImages(r pagination.Page) ([]Image, error) { err := (r.(ImagePage)).ExtractInto(&s) return s.Images, err } + +// splitFunc is a helper function used to avoid a slice of empty strings. +func splitFunc(c rune) bool { + return c == ',' +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies/requests.go index fc1c809aface5..19f487450f64d 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/l7policies/requests.go @@ -80,7 +80,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -137,13 +138,15 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a particular l7policy based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular l7policy based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -208,9 +211,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -254,7 +258,8 @@ func CreateRule(c *gophercloud.ServiceClient, policyID string, opts CreateRuleOp r.Err = err return } - _, r.Err = c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + resp, err := c.Post(ruleRootURL(c, policyID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -309,13 +314,15 @@ func ListRules(c *gophercloud.ServiceClient, policyID string, opts ListRulesOpts // GetRule retrieves a particular L7Policy Rule based on its unique ID. func GetRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r GetRuleResult) { - _, r.Err = c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + resp, err := c.Get(ruleResourceURL(c, policyID, ruleID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteRule will remove a Rule from a particular L7Policy. func DeleteRule(c *gophercloud.ServiceClient, policyID string, ruleID string) (r DeleteRuleResult) { - _, r.Err = c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + resp, err := c.Delete(ruleResourceURL(c, policyID, ruleID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -369,8 +376,9 @@ func UpdateRule(c *gophercloud.ServiceClient, policyID string, ruleID string, op r.Err = err return } - _, r.Err = c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(ruleResourceURL(c, policyID, ruleID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners/requests.go index 2d2f71f24f3c6..a90d72627376d 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/listeners/requests.go @@ -154,13 +154,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Listeners based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -235,20 +237,23 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular Listeners based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular Listeners. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers/requests.go index f32a7f98538d6..4c7c2889420a5 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers/requests.go @@ -133,13 +133,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Loadbalancer based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -179,9 +181,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) (r UpdateR r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -216,26 +219,30 @@ func Delete(c *gophercloud.ServiceClient, id string, opts DeleteOptsBuilder) (r } url += query } - _, r.Err = c.Delete(url, nil) + resp, err := c.Delete(url, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStatuses will return the status of a particular LoadBalancer. func GetStatuses(c *gophercloud.ServiceClient, id string) (r GetStatusesResult) { - _, r.Err = c.Get(statusRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statusRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetStats will return the shows the current statistics of a particular LoadBalancer. func GetStats(c *gophercloud.ServiceClient, id string) (r StatsResult) { - _, r.Err = c.Get(statisticsRootURL(c, id), &r.Body, nil) + resp, err := c.Get(statisticsRootURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Failover performs a failover of a load balancer. func Failover(c *gophercloud.ServiceClient, id string) (r FailoverResult) { - _, r.Err = c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ + resp, err := c.Put(failoverRootURL(c, id), nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors/requests.go index 1a7e8b8f48a7a..aedf672fc1352 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors/requests.go @@ -166,13 +166,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular Health Monitor based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -235,14 +237,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular Monitor based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools/requests.go index 04df4ddb85985..c522cb41d10bb 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools/requests.go @@ -130,13 +130,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular pool based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,15 +179,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular pool based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -298,13 +302,15 @@ func CreateMember(c *gophercloud.ServiceClient, poolID string, opts CreateMember r.Err = err return } - _, r.Err = c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + resp, err := c.Post(memberRootURL(c, poolID), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // GetMember retrieves a particular Pool Member based on its unique ID. func GetMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r GetMemberResult) { - _, r.Err = c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + resp, err := c.Get(memberResourceURL(c, poolID, memberID), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -343,9 +349,10 @@ func UpdateMember(c *gophercloud.ServiceClient, poolID string, memberID string, r.Err = err return } - _, r.Err = c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(memberResourceURL(c, poolID, memberID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201, 202}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -413,13 +420,15 @@ func BatchUpdateMembers(c *gophercloud.ServiceClient, poolID string, opts []Batc b := map[string]interface{}{"members": members} - _, r.Err = c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + resp, err := c.Put(memberRootURL(c, poolID), b, nil, &gophercloud.RequestOpts{OkCodes: []int{202}}) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DisassociateMember will remove and disassociate a Member from a particular // Pool. func DeleteMember(c *gophercloud.ServiceClient, poolID string, memberID string) (r DeleteMemberResult) { - _, r.Err = c.Delete(memberResourceURL(c, poolID, memberID), nil) + resp, err := c.Delete(memberResourceURL(c, poolID, memberID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags/requests.go index a08bccbb6834b..6d49aeaf6cd1b 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/attributestags/requests.go @@ -29,53 +29,59 @@ func ReplaceAll(client *gophercloud.ServiceClient, resourceType string, resource r.Err = err return } - _, r.Err = client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Put(url, &b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // List all tags on a resource func List(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r ListResult) { url := listURL(client, resourceType, resourceID) - _, r.Err = client.Get(url, &r.Body, &gophercloud.RequestOpts{ + resp, err := client.Get(url, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // DeleteAll deletes all tags on a resource func DeleteAll(client *gophercloud.ServiceClient, resourceType string, resourceID string) (r DeleteResult) { url := deleteAllURL(client, resourceType, resourceID) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Add a tag on a resource func Add(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r AddResult) { url := addURL(client, resourceType, resourceID, tag) - _, r.Err = client.Put(url, nil, nil, &gophercloud.RequestOpts{ + resp, err := client.Put(url, nil, nil, &gophercloud.RequestOpts{ OkCodes: []int{201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete a tag on a resource func Delete(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r DeleteResult) { url := deleteURL(client, resourceType, resourceID, tag) - _, r.Err = client.Delete(url, &gophercloud.RequestOpts{ + resp, err := client.Delete(url, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Confirm if a tag exists on a resource func Confirm(client *gophercloud.ServiceClient, resourceType string, resourceID string, tag string) (r ConfirmResult) { url := confirmURL(client, resourceType, resourceID, tag) - _, r.Err = client.Get(url, nil, &gophercloud.RequestOpts{ + resp, err := client.Get(url, nil, &gophercloud.RequestOpts{ OkCodes: []int{204}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go index 0c0db64d8d301..2a4ff1aca433d 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips/requests.go @@ -116,13 +116,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular floating IP resource based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -167,9 +169,10 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -177,6 +180,7 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r // ensure this is what you want - you can also disassociate the IP from existing // internal ports. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go index cf499f9873dda..e0b1effa175ca 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers/requests.go @@ -84,13 +84,15 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular router based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -126,15 +128,17 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular router based on its unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -182,9 +186,10 @@ func AddInterface(c *gophercloud.ServiceClient, id string, opts AddInterfaceOpts r.Err = err return } - _, r.Err = c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -226,8 +231,9 @@ func RemoveInterface(c *gophercloud.ServiceClient, id string, opts RemoveInterfa r.Err = err return } - _, r.Err = c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(removeInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go index a22cd306e8ab0..566a730eaf60c 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups/requests.go @@ -76,7 +76,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -109,58 +110,24 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r return } - _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// IDFromName is a convenience function that returns a security group's ID, -// given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractGroups(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "security group"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "security group"} - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go index c7741ffcd2c3c..544c24d7f358b 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules/requests.go @@ -141,19 +141,22 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(rootURL(c), b, &r.Body, nil) + resp, err := c.Post(rootURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Get retrieves a particular security group rule based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(resourceURL(c, id), &r.Body, nil) + resp, err := c.Get(resourceURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete will permanently delete a particular security group rule based on its // unique ID. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(resourceURL(c, id), nil) + resp, err := c.Delete(resourceURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go index 8006c481679d6..7a28ba0d19c51 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/networks/requests.go @@ -60,7 +60,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific network based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -99,7 +100,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -130,51 +132,16 @@ func Update(c *gophercloud.ServiceClient, networkID string, opts UpdateOptsBuild r.Err = err return } - _, r.Err = c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, networkID), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the network associated with it. func Delete(c *gophercloud.ServiceClient, networkID string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, networkID), nil) + resp, err := c.Delete(deleteURL(c, networkID), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// IDFromName is a convenience function that returns a network's ID, given -// its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractNetworks(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"} - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go index 18d90a255e130..4dc1600726401 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/ports/requests.go @@ -97,7 +97,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific port based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -136,7 +137,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -171,51 +173,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the port associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// IDFromName is a convenience function that returns a port's ID, -// given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractPorts(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "port"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "port"} - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/subnets/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/subnets/requests.go index 3e56bf389af32..94a5b6b1aa595 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/subnets/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/networking/v2/subnets/requests.go @@ -69,7 +69,8 @@ func List(c *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager { // Get retrieves a specific subnet based on its unique ID. func Get(c *gophercloud.ServiceClient, id string) (r GetResult) { - _, r.Err = c.Get(getURL(c, id), &r.Body, nil) + resp, err := c.Get(getURL(c, id), &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -160,7 +161,8 @@ func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResul r.Err = err return } - _, r.Err = c.Post(createURL(c), b, &r.Body, nil) + resp, err := c.Post(createURL(c), b, &r.Body, nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -219,51 +221,16 @@ func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r r.Err = err return } - _, r.Err = c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ + resp, err := c.Put(updateURL(c, id), b, &r.Body, &gophercloud.RequestOpts{ OkCodes: []int{200, 201}, }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete accepts a unique ID and deletes the subnet associated with it. func Delete(c *gophercloud.ServiceClient, id string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, id), nil) + resp, err := c.Delete(deleteURL(c, id), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } - -// IDFromName is a convenience function that returns a subnet's ID, -// given its name. -func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) { - count := 0 - id := "" - - listOpts := ListOpts{ - Name: name, - } - - pages, err := List(client, listOpts).AllPages() - if err != nil { - return "", err - } - - all, err := ExtractSubnets(pages) - if err != nil { - return "", err - } - - for _, s := range all { - if s.Name == name { - count++ - id = s.ID - } - } - - switch count { - case 0: - return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "subnet"} - case 1: - return id, nil - default: - return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "subnet"} - } -} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/requests.go index 452a331c74ca7..7c9acf85ff227 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/requests.go @@ -39,10 +39,7 @@ func Get(c *gophercloud.ServiceClient, opts GetOptsBuilder) (r GetResult) { MoreHeaders: h, OkCodes: []int{204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -92,9 +89,6 @@ func Update(c *gophercloud.ServiceClient, opts UpdateOptsBuilder) (r UpdateResul MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/results.go index 10661e671571f..c9b7cb7eb1b81 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts/results.go @@ -2,7 +2,6 @@ package accounts import ( "encoding/json" - "strconv" "strings" "time" @@ -17,7 +16,7 @@ type UpdateResult struct { // UpdateHeader represents the headers returned in the response from an Update // request. type UpdateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` TransID string `json:"X-Trans-Id"` Date time.Time `json:"-"` @@ -27,8 +26,7 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -37,16 +35,6 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { *r = UpdateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err @@ -55,18 +43,18 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { // Extract will return a struct of headers returned from a call to Get. To // obtain a map of headers, call the Extract method on the GetResult. func (r UpdateResult) Extract() (*UpdateHeader, error) { - var s *UpdateHeader + var s UpdateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { - BytesUsed int64 `json:"-"` - QuotaBytes *int64 `json:"-"` - ContainerCount int64 `json:"-"` - ContentLength int64 `json:"-"` - ObjectCount int64 `json:"-"` + BytesUsed int64 `json:"X-Account-Bytes-Used,string"` + QuotaBytes *int64 `json:"X-Account-Meta-Quota-Bytes,string"` + ContainerCount int64 `json:"X-Account-Container-Count,string"` + ContentLength int64 `json:"Content-Length,string"` + ObjectCount int64 `json:"X-Account-Object-Count,string"` ContentType string `json:"Content-Type"` TransID string `json:"X-Trans-Id"` TempURLKey string `json:"X-Account-Meta-Temp-URL-Key"` @@ -78,12 +66,7 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - BytesUsed string `json:"X-Account-Bytes-Used"` - QuotaBytes string `json:"X-Account-Meta-Quota-Bytes"` - ContentLength string `json:"Content-Length"` - ContainerCount string `json:"X-Account-Container-Count"` - ObjectCount string `json:"X-Account-Object-Count"` - Date string `json:"Date"` + Date string `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -92,57 +75,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { *r = GetHeader(s.tmp) - switch s.BytesUsed { - case "": - r.BytesUsed = 0 - default: - r.BytesUsed, err = strconv.ParseInt(s.BytesUsed, 10, 64) - if err != nil { - return err - } - } - - switch s.QuotaBytes { - case "": - r.QuotaBytes = nil - default: - v, err := strconv.ParseInt(s.QuotaBytes, 10, 64) - if err != nil { - return err - } - r.QuotaBytes = &v - } - - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - - switch s.ObjectCount { - case "": - r.ObjectCount = 0 - default: - r.ObjectCount, err = strconv.ParseInt(s.ObjectCount, 10, 64) - if err != nil { - return err - } - } - - switch s.ContainerCount { - case "": - r.ContainerCount = 0 - default: - r.ContainerCount, err = strconv.ParseInt(s.ContainerCount, 10, 64) - if err != nil { - return err - } - } - if s.Date != "" { r.Date, err = time.Parse(time.RFC1123, s.Date) } @@ -157,9 +89,9 @@ type GetResult struct { // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { - var s *GetHeader + var s GetHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/requests.go index 2e710ab0c6f41..b8f169345d602 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/requests.go @@ -1,6 +1,9 @@ package containers import ( + "net/url" + "strings" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/pagination" ) @@ -104,21 +107,39 @@ func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOptsB h[k] = v } } - resp, err := c.Request("PUT", createURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Request("PUT", createURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) - if resp != nil { - r.Header = resp.Header - resp.Body.Close() + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} + +// BulkDelete is a function that bulk deletes containers. +func BulkDelete(c *gophercloud.ServiceClient, containers []string) (r BulkDeleteResult) { + // urlencode container names to be on the safe side + // https://github.com/openstack/swift/blob/stable/train/swift/common/middleware/bulk.py#L160 + // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 + encodedContainers := make([]string, len(containers)) + for i, v := range containers { + encodedContainers[i] = url.QueryEscape(v) } - r.Err = err + b := strings.NewReader(strings.Join(encodedContainers, "\n") + "\n") + resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{ + "Accept": "application/json", + "Content-Type": "text/plain", + }, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } // Delete is a function that deletes a container. func Delete(c *gophercloud.ServiceClient, containerName string) (r DeleteResult) { - _, r.Err = c.Delete(deleteURL(c, containerName), nil) + resp, err := c.Delete(deleteURL(c, url.QueryEscape(containerName)), nil) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -180,14 +201,11 @@ func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOptsB h[k] = v } } - resp, err := c.Request("POST", updateURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Request("POST", updateURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201, 202, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -223,13 +241,10 @@ func Get(c *gophercloud.ServiceClient, containerName string, opts GetOptsBuilder h[k] = v } } - resp, err := c.Head(getURL(c, containerName), &gophercloud.RequestOpts{ + resp, err := c.Head(getURL(c, url.QueryEscape(containerName)), &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{200, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/results.go index b94eda95e1b7c..14e390541f3f1 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/results.go @@ -3,7 +3,6 @@ package containers import ( "encoding/json" "fmt" - "strconv" "strings" "time" @@ -92,11 +91,11 @@ func ExtractNames(page pagination.Page) ([]string, error) { // GetHeader represents the headers returned in the response from a Get request. type GetHeader struct { AcceptRanges string `json:"Accept-Ranges"` - BytesUsed int64 `json:"-"` - ContentLength int64 `json:"-"` + BytesUsed int64 `json:"X-Container-Bytes-Used,string"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` - ObjectCount int64 `json:"-"` + ObjectCount int64 `json:"X-Container-Object-Count,string"` Read []string `json:"-"` TransID string `json:"X-Trans-Id"` VersionsLocation string `json:"X-Versions-Location"` @@ -111,12 +110,9 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - BytesUsed string `json:"X-Container-Bytes-Used"` - ContentLength string `json:"Content-Length"` - ObjectCount string `json:"X-Container-Object-Count"` - Write string `json:"X-Container-Write"` - Read string `json:"X-Container-Read"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Write string `json:"X-Container-Write"` + Read string `json:"X-Container-Read"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -125,36 +121,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { *r = GetHeader(s.tmp) - switch s.BytesUsed { - case "": - r.BytesUsed = 0 - default: - r.BytesUsed, err = strconv.ParseInt(s.BytesUsed, 10, 64) - if err != nil { - return err - } - } - - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - - switch s.ObjectCount { - case "": - r.ObjectCount = 0 - default: - r.ObjectCount, err = strconv.ParseInt(s.ObjectCount, 10, 64) - if err != nil { - return err - } - } - r.Read = strings.Split(s.Read, ",") r.Write = strings.Split(s.Write, ",") @@ -170,9 +136,9 @@ type GetResult struct { // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { - var s *GetHeader + var s GetHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) @@ -194,7 +160,7 @@ func (r GetResult) ExtractMetadata() (map[string]string, error) { // CreateHeader represents the headers returned in the response from a Create // request. type CreateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -204,8 +170,7 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { type tmp CreateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -214,16 +179,6 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { *r = CreateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err @@ -238,15 +193,15 @@ type CreateResult struct { // Extract will return a struct of headers returned from a call to Create. // To extract the headers from the HTTP response, call its Extract method. func (r CreateResult) Extract() (*CreateHeader, error) { - var s *CreateHeader + var s CreateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // UpdateHeader represents the headers returned in the response from a Update // request. type UpdateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -256,8 +211,7 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -266,16 +220,6 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { *r = UpdateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err @@ -289,15 +233,15 @@ type UpdateResult struct { // Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { - var s *UpdateHeader + var s UpdateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // DeleteHeader represents the headers returned in the response from a Delete // request. type DeleteHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -307,8 +251,7 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { type tmp DeleteHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -317,30 +260,42 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { *r = DeleteHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return err } // DeleteResult represents the result of a delete operation. To extract the -// the headers from the HTTP response, call its Extract method. +// headers from the HTTP response, call its Extract method. type DeleteResult struct { gophercloud.HeaderResult } // Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { - var s *DeleteHeader + var s DeleteHeader err := r.ExtractInto(&s) - return s, err + return &s, err +} + +type BulkDeleteResponse struct { + ResponseStatus string `json:"Response Status"` + ResponseBody string `json:"Response Body"` + Errors [][]string `json:"Errors"` + NumberDeleted int `json:"Number Deleted"` + NumberNotFound int `json:"Number Not Found"` +} + +// BulkDeleteResult represents the result of a bulk delete operation. To extract +// the response object from the HTTP response, call its Extract method. +type BulkDeleteResult struct { + gophercloud.Result +} + +// Extract will return a BulkDeleteResponse struct returned from a BulkDelete +// call. +func (r BulkDeleteResult) Extract() (*BulkDeleteResponse, error) { + var s BulkDeleteResponse + err := r.ExtractInto(&s) + return &s, err } diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/urls.go index 9b380470dd788..0044a5e206baf 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers/urls.go @@ -21,3 +21,7 @@ func deleteURL(c *gophercloud.ServiceClient, container string) string { func updateURL(c *gophercloud.ServiceClient, container string) string { return createURL(c, container) } + +func bulkDeleteURL(c *gophercloud.ServiceClient) string { + return c.Endpoint + "?bulk-delete=true" +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/doc.go index e9b4b8a9f3213..7714460aadca3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/doc.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/doc.go @@ -98,6 +98,10 @@ Example to Download an Object's Data containerName := "my_container" object := objects.Download(objectStorageClient, containerName, objectName, nil) + if object.Err != nil { + panic(object.Err) + } + // if "ExtractContent" method is not called, the HTTP connection will remain consumed content, err := object.ExtractContent() if err != nil { panic(err) diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/requests.go index a9649d97a80f6..61e4afeff70a3 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/requests.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/requests.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "io/ioutil" + "net/url" "strings" "time" @@ -53,7 +54,7 @@ func (opts ListOpts) ToObjectListParams() (bool, string, error) { func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager { headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"} - url := listURL(c, containerName) + url := listURL(c, url.QueryEscape(containerName)) if opts != nil { full, query, err := opts.ToObjectListParams() if err != nil { @@ -112,7 +113,7 @@ func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, er // To extract just the content, pass the DownloadResult response to the // ExtractContent function. func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) (r DownloadResult) { - url := downloadURL(c, containerName, objectName) + url := downloadURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectDownloadParams() @@ -127,14 +128,11 @@ func Download(c *gophercloud.ServiceClient, containerName, objectName string, op } resp, err := c.Get(url, nil, &gophercloud.RequestOpts{ - MoreHeaders: h, - OkCodes: []int{200, 206, 304}, + MoreHeaders: h, + OkCodes: []int{200, 206, 304}, + KeepResponseBody: true, }) - if resp != nil { - r.Header = resp.Header - r.Body = resp.Body - } - r.Err = err + r.Body, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -221,7 +219,7 @@ func (opts CreateOpts) ToObjectCreateParams() (io.Reader, map[string]string, str // checksum, the failed request will automatically be retried up to a maximum // of 3 times. func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateOptsBuilder) (r CreateResult) { - url := createURL(c, containerName, objectName) + url := createURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) h := make(map[string]string) var b io.Reader if opts != nil { @@ -237,14 +235,10 @@ func Create(c *gophercloud.ServiceClient, containerName, objectName string, opts b = tmpB } - resp, err := c.Put(url, nil, nil, &gophercloud.RequestOpts{ - RawBody: b, + resp, err := c.Put(url, b, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) - r.Err = err - if resp != nil { - r.Header = resp.Header - } + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -289,15 +283,12 @@ func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts C h[k] = v } - url := copyURL(c, containerName, objectName) + url := copyURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) resp, err := c.Request("COPY", url, &gophercloud.RequestOpts{ MoreHeaders: h, OkCodes: []int{201}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -320,7 +311,7 @@ func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) { // Delete is a function that deletes an object. func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) (r DeleteResult) { - url := deleteURL(c, containerName, objectName) + url := deleteURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) if opts != nil { query, err := opts.ToObjectDeleteQuery() if err != nil { @@ -330,10 +321,7 @@ func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts url += query } resp, err := c.Delete(url, nil) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -368,7 +356,7 @@ func (opts GetOpts) ToObjectGetParams() (map[string]string, string, error) { // the custom metadata, pass the GetResult response to the ExtractMetadata // function. func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) (r GetResult) { - url := getURL(c, containerName, objectName) + url := getURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) h := make(map[string]string) if opts != nil { headers, query, err := opts.ToObjectGetParams() @@ -386,10 +374,7 @@ func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts Ge MoreHeaders: h, OkCodes: []int{200, 204}, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -437,14 +422,11 @@ func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts h[k] = v } } - url := updateURL(c, containerName, objectName) + url := updateURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) resp, err := c.Post(url, nil, nil, &gophercloud.RequestOpts{ MoreHeaders: h, }) - if resp != nil { - r.Header = resp.Header - } - r.Err = err + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) return } @@ -483,7 +465,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin } duration := time.Duration(opts.TTL) * time.Second expiry := time.Now().Add(duration).Unix() - getHeader, err := containers.Get(c, containerName, nil).Extract() + getHeader, err := containers.Get(c, url.QueryEscape(containerName), nil).Extract() if err != nil { return "", err } @@ -497,7 +479,7 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin tempURLKey = getHeader.TempURLKey } secretKey := []byte(tempURLKey) - url := getURL(c, containerName, objectName) + url := getURL(c, url.QueryEscape(containerName), url.QueryEscape(objectName)) splitPath := strings.Split(url, opts.Split) baseURL, objectPath := splitPath[0], splitPath[1] objectPath = opts.Split + objectPath @@ -507,3 +489,27 @@ func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName strin hexsum := fmt.Sprintf("%x", hash.Sum(nil)) return fmt.Sprintf("%s%s?temp_url_sig=%s&temp_url_expires=%d", baseURL, objectPath, hexsum, expiry), nil } + +// BulkDelete is a function that bulk deletes objects. +func BulkDelete(c *gophercloud.ServiceClient, container string, objects []string) (r BulkDeleteResult) { + // urlencode object names to be on the safe side + // https://github.com/openstack/swift/blob/stable/train/swift/common/middleware/bulk.py#L160 + // https://github.com/openstack/swift/blob/stable/train/swift/common/swob.py#L302 + encodedObjects := make([]string, len(objects)) + for i, v := range objects { + encodedObjects[i] = strings.Join([]string{ + url.QueryEscape(container), + url.QueryEscape(v)}, + "/") + } + b := strings.NewReader(strings.Join(encodedObjects, "\n") + "\n") + resp, err := c.Post(bulkDeleteURL(c), b, &r.Body, &gophercloud.RequestOpts{ + MoreHeaders: map[string]string{ + "Accept": "application/json", + "Content-Type": "text/plain", + }, + OkCodes: []int{200}, + }) + _, r.Header, r.Err = gophercloud.ParseResponse(resp, err) + return +} diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/results.go index dd7c7044d0e31..75367d8349bc7 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/results.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/results.go @@ -6,7 +6,6 @@ import ( "io" "io/ioutil" "net/url" - "strconv" "strings" "time" @@ -134,7 +133,7 @@ type DownloadHeader struct { AcceptRanges string `json:"Accept-Ranges"` ContentDisposition string `json:"Content-Disposition"` ContentEncoding string `json:"Content-Encoding"` - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` DeleteAt time.Time `json:"-"` @@ -149,7 +148,6 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { type tmp DownloadHeader var s struct { tmp - ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` @@ -162,16 +160,6 @@ func (r *DownloadHeader) UnmarshalJSON(b []byte) error { *r = DownloadHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - switch t := s.StaticLargeObject.(type) { case string: if t == "True" || t == "true" { @@ -197,9 +185,9 @@ type DownloadResult struct { // Extract will return a struct of headers returned from a call to Download. func (r DownloadResult) Extract() (*DownloadHeader, error) { - var s *DownloadHeader + var s DownloadHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractContent is a function that takes a DownloadResult's io.Reader body @@ -216,7 +204,6 @@ func (r *DownloadResult) ExtractContent() ([]byte, error) { if err != nil { return nil, err } - r.Body.Close() return body, nil } @@ -224,7 +211,7 @@ func (r *DownloadResult) ExtractContent() ([]byte, error) { type GetHeader struct { ContentDisposition string `json:"Content-Disposition"` ContentEncoding string `json:"Content-Encoding"` - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` DeleteAt time.Time `json:"-"` @@ -239,7 +226,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { type tmp GetHeader var s struct { tmp - ContentLength string `json:"Content-Length"` Date gophercloud.JSONRFC1123 `json:"Date"` DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` @@ -252,16 +238,6 @@ func (r *GetHeader) UnmarshalJSON(b []byte) error { *r = GetHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - switch t := s.StaticLargeObject.(type) { case string: if t == "True" || t == "true" { @@ -286,9 +262,9 @@ type GetResult struct { // Extract will return a struct of headers returned from a call to Get. func (r GetResult) Extract() (*GetHeader, error) { - var s *GetHeader + var s GetHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // ExtractMetadata is a function that takes a GetResult (of type *http.Response) @@ -310,7 +286,7 @@ func (r GetResult) ExtractMetadata() (map[string]string, error) { // CreateHeader represents the headers returned in the response from a // Create request. type CreateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` ETag string `json:"Etag"` @@ -322,9 +298,8 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { type tmp CreateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` - LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` + Date gophercloud.JSONRFC1123 `json:"Date"` + LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` } err := json.Unmarshal(b, &s) if err != nil { @@ -333,16 +308,6 @@ func (r *CreateHeader) UnmarshalJSON(b []byte) error { *r = CreateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) r.LastModified = time.Time(s.LastModified) @@ -360,15 +325,15 @@ func (r CreateResult) Extract() (*CreateHeader, error) { //if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) { // return nil, ErrWrongChecksum{} //} - var s *CreateHeader + var s CreateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // UpdateHeader represents the headers returned in the response from a // Update request. type UpdateHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -378,8 +343,7 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { type tmp UpdateHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -388,16 +352,6 @@ func (r *UpdateHeader) UnmarshalJSON(b []byte) error { *r = UpdateHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return nil @@ -410,15 +364,15 @@ type UpdateResult struct { // Extract will return a struct of headers returned from a call to Update. func (r UpdateResult) Extract() (*UpdateHeader, error) { - var s *UpdateHeader + var s UpdateHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // DeleteHeader represents the headers returned in the response from a // Delete request. type DeleteHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` Date time.Time `json:"-"` TransID string `json:"X-Trans-Id"` @@ -428,8 +382,7 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { type tmp DeleteHeader var s struct { tmp - ContentLength string `json:"Content-Length"` - Date gophercloud.JSONRFC1123 `json:"Date"` + Date gophercloud.JSONRFC1123 `json:"Date"` } err := json.Unmarshal(b, &s) if err != nil { @@ -438,16 +391,6 @@ func (r *DeleteHeader) UnmarshalJSON(b []byte) error { *r = DeleteHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) return nil @@ -460,15 +403,15 @@ type DeleteResult struct { // Extract will return a struct of headers returned from a call to Delete. func (r DeleteResult) Extract() (*DeleteHeader, error) { - var s *DeleteHeader + var s DeleteHeader err := r.ExtractInto(&s) - return s, err + return &s, err } // CopyHeader represents the headers returned in the response from a // Copy request. type CopyHeader struct { - ContentLength int64 `json:"-"` + ContentLength int64 `json:"Content-Length,string"` ContentType string `json:"Content-Type"` CopiedFrom string `json:"X-Copied-From"` CopiedFromLastModified time.Time `json:"-"` @@ -482,7 +425,6 @@ func (r *CopyHeader) UnmarshalJSON(b []byte) error { type tmp CopyHeader var s struct { tmp - ContentLength string `json:"Content-Length"` CopiedFromLastModified gophercloud.JSONRFC1123 `json:"X-Copied-From-Last-Modified"` Date gophercloud.JSONRFC1123 `json:"Date"` LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"` @@ -494,16 +436,6 @@ func (r *CopyHeader) UnmarshalJSON(b []byte) error { *r = CopyHeader(s.tmp) - switch s.ContentLength { - case "": - r.ContentLength = 0 - default: - r.ContentLength, err = strconv.ParseInt(s.ContentLength, 10, 64) - if err != nil { - return err - } - } - r.Date = time.Time(s.Date) r.CopiedFromLastModified = time.Time(s.CopiedFromLastModified) r.LastModified = time.Time(s.LastModified) @@ -518,9 +450,31 @@ type CopyResult struct { // Extract will return a struct of headers returned from a call to Copy. func (r CopyResult) Extract() (*CopyHeader, error) { - var s *CopyHeader + var s CopyHeader err := r.ExtractInto(&s) - return s, err + return &s, err +} + +type BulkDeleteResponse struct { + ResponseStatus string `json:"Response Status"` + ResponseBody string `json:"Response Body"` + Errors [][]string `json:"Errors"` + NumberDeleted int `json:"Number Deleted"` + NumberNotFound int `json:"Number Not Found"` +} + +// BulkDeleteResult represents the result of a bulk delete operation. To extract +// the response object from the HTTP response, call its Extract method. +type BulkDeleteResult struct { + gophercloud.Result +} + +// Extract will return a BulkDeleteResponse struct returned from a BulkDelete +// call. +func (r BulkDeleteResult) Extract() (*BulkDeleteResponse, error) { + var s BulkDeleteResponse + err := r.ExtractInto(&s) + return &s, err } // extractLastMarker is a function that takes a page of objects and returns the diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/urls.go index b3ac304b74289..918ec94b9bb35 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/urls.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects/urls.go @@ -31,3 +31,7 @@ func downloadURL(c *gophercloud.ServiceClient, container, object string) string func updateURL(c *gophercloud.ServiceClient, container, object string) string { return copyURL(c, container, object) } + +func bulkDeleteURL(c *gophercloud.ServiceClient) string { + return c.Endpoint + "?bulk-delete=true" +} diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/http.go b/vendor/github.com/gophercloud/gophercloud/pagination/http.go index 757295c423ad5..df3503159ae95 100644 --- a/vendor/github.com/gophercloud/gophercloud/pagination/http.go +++ b/vendor/github.com/gophercloud/gophercloud/pagination/http.go @@ -54,7 +54,8 @@ 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.Get(url, nil, &gophercloud.RequestOpts{ - MoreHeaders: headers, - OkCodes: []int{200, 204, 300}, + MoreHeaders: headers, + OkCodes: []int{200, 204, 300}, + KeepResponseBody: true, }) } diff --git a/vendor/github.com/gophercloud/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/provider_client.go index 885bf07a7bd89..43574402e9921 100644 --- a/vendor/github.com/gophercloud/gophercloud/provider_client.go +++ b/vendor/github.com/gophercloud/gophercloud/provider_client.go @@ -305,6 +305,9 @@ type RequestOpts struct { // 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 + // KeepResponseBody specifies whether to keep the HTTP response body. Usually used, when the HTTP + // response body is considered for further use. Valid when JSONResponse is nil. + KeepResponseBody bool } // requestState contains temporary state for a single ProviderClient.Request() call. @@ -346,6 +349,11 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts contentType = &applicationJSON } + // Return an error, when "KeepResponseBody" is true and "JSONResponse" is not nil + if options.KeepResponseBody && options.JSONResponse != nil { + return nil, errors.New("cannot use KeepResponseBody when JSONResponse is not nil") + } + if options.RawBody != nil { body = options.RawBody } @@ -384,9 +392,6 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts req.Header.Set(k, v) } - // Set connection parameter to close the connection immediately when we've got the response - req.Close = true - prereqtok := req.Header.Get("X-Auth-Token") // Issue the request. @@ -414,11 +419,12 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() respErr := ErrUnexpectedResponseCode{ - URL: url, - Method: method, - Expected: options.OkCodes, - Actual: resp.StatusCode, - Body: body, + URL: url, + Method: method, + Expected: options.OkCodes, + Actual: resp.StatusCode, + Body: body, + ResponseHeader: resp.Header, } errType := options.ErrorContext @@ -513,25 +519,40 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts // Parse the response body as JSON, if requested to do so. if options.JSONResponse != nil { defer resp.Body.Close() + // Don't decode JSON when there is no content + if resp.StatusCode == http.StatusNoContent { + // read till EOF, otherwise the connection will be closed and cannot be reused + _, err = io.Copy(ioutil.Discard, resp.Body) + return resp, err + } if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil { return nil, err } } + // Close unused body to allow the HTTP connection to be reused + if !options.KeepResponseBody && options.JSONResponse == nil { + defer resp.Body.Close() + // read till EOF, otherwise the connection will be closed and cannot be reused + if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { + return nil, err + } + } + return resp, nil } func defaultOkCodes(method string) []int { - switch { - case method == "GET": + switch method { + case "GET", "HEAD": return []int{200} - case method == "POST": + case "POST": return []int{201, 202} - case method == "PUT": + case "PUT": return []int{201, 202} - case method == "PATCH": + case "PATCH": return []int{200, 202, 204} - case method == "DELETE": + case "DELETE": return []int{202, 204} } diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/results.go index 94a16bff0b4f3..1b608103b7f3d 100644 --- a/vendor/github.com/gophercloud/gophercloud/results.go +++ b/vendor/github.com/gophercloud/gophercloud/results.go @@ -131,6 +131,18 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { // fields of the struct or composed extension struct // at the end of this method. toValue.Set(newSlice) + + // jtopjian: This was put into place to resolve the issue + // described at + // https://github.com/gophercloud/gophercloud/issues/1963 + // + // This probably isn't the best fix, but it appears to + // be resolving the issue, so I'm going to implement it + // for now. + // + // For future readers, this entire case statement could + // use a review. + return nil } } case reflect.Struct: diff --git a/vendor/github.com/gophercloud/gophercloud/service_client.go b/vendor/github.com/gophercloud/gophercloud/service_client.go index f222f05a66dec..dd54abe30ef5c 100644 --- a/vendor/github.com/gophercloud/gophercloud/service_client.go +++ b/vendor/github.com/gophercloud/gophercloud/service_client.go @@ -152,3 +152,11 @@ func (client *ServiceClient) Request(method, url string, options *RequestOpts) ( } return client.ProviderClient.Request(method, url, options) } + +// ParseResponse is a helper function to parse http.Response to constituents. +func ParseResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, error) { + if resp != nil { + return resp.Body, resp.Header, err + } + return nil, nil, err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7e4dfd27a3977..7cd91f0263b80 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -300,7 +300,7 @@ github.com/googleapis/gax-go/v2 github.com/googleapis/gnostic/OpenAPIv2 github.com/googleapis/gnostic/compiler github.com/googleapis/gnostic/extensions -# github.com/gophercloud/gophercloud v0.7.1-0.20200116011225-46fdd1830e9a => github.com/gophercloud/gophercloud v0.9.0 +# github.com/gophercloud/gophercloud v0.11.0 => github.com/gophercloud/gophercloud v0.11.0 ## explicit github.com/gophercloud/gophercloud github.com/gophercloud/gophercloud/internal @@ -314,12 +314,13 @@ github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhint github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach github.com/gophercloud/gophercloud/openstack/compute/v2/flavors -github.com/gophercloud/gophercloud/openstack/compute/v2/images github.com/gophercloud/gophercloud/openstack/compute/v2/servers github.com/gophercloud/gophercloud/openstack/dns/v2/recordsets github.com/gophercloud/gophercloud/openstack/dns/v2/zones github.com/gophercloud/gophercloud/openstack/identity/v2/tenants github.com/gophercloud/gophercloud/openstack/identity/v2/tokens +github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens +github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1 github.com/gophercloud/gophercloud/openstack/identity/v3/tokens github.com/gophercloud/gophercloud/openstack/imageservice/v2/images github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/apiversions @@ -1363,4 +1364,4 @@ sigs.k8s.io/yaml # k8s.io/cli-runtime => k8s.io/cli-runtime v0.18.6 # k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.18.6 # k8s.io/code-generator => k8s.io/code-generator v0.18.6 -# github.com/gophercloud/gophercloud => github.com/gophercloud/gophercloud v0.9.0 +# github.com/gophercloud/gophercloud => github.com/gophercloud/gophercloud v0.11.0