From 4025d1da2eaf8ed02a048f4ccaf9162062a0fb67 Mon Sep 17 00:00:00 2001 From: Kevin Bulebush Date: Fri, 11 Jan 2019 22:52:50 -0500 Subject: [PATCH] Updated gophercloud to revision with app creds --- go.mod | 2 +- go.sum | 4 +- .../gophercloud/gophercloud/README.md | 2 +- .../gophercloud/gophercloud/auth_options.go | 66 +++++++++++++++++-- .../gophercloud/gophercloud/errors.go | 7 ++ .../gophercloud/provider_client.go | 6 +- .../gophercloud/gophercloud/results.go | 47 ++++++++++++- vendor/vendor.json | 18 ++--- 8 files changed, 131 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index a15531a52..a35426b74 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/google/go-querystring v0.0.0-20151028211038-2a60fc2ba6c1 // indirect github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d - github.com/gophercloud/gophercloud v0.0.0-20190112023249-80c596f406a3 + github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06 github.com/gophercloud/utils v0.0.0-20180806215700-d6e28a8b3199 github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect github.com/gorilla/websocket v0.0.0-20170319172727-a91eba7f9777 // indirect diff --git a/go.sum b/go.sum index b65367acd..53d1ddb7d 100644 --- a/go.sum +++ b/go.sum @@ -133,8 +133,8 @@ github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3 github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d h1:rXQlD9GXkjA/PQZhmEaF/8Pj/sJfdZJK7GJG0gkS8I0= github.com/google/uuid v0.0.0-20171129191014-dec09d789f3d/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gophercloud/gophercloud v0.0.0-20190112023249-80c596f406a3 h1:3v50aN5I1XldxFQRT/oZwJMRYt0Edhs7rPVXXWPihKI= -github.com/gophercloud/gophercloud v0.0.0-20190112023249-80c596f406a3/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06 h1:m7Rt/8En7PLrM7PQpykdZBPKUdgZWN6MwiA/ChVIoxs= +github.com/gophercloud/gophercloud v0.0.0-20180903124057-ea7289ebdf06/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= github.com/gophercloud/utils v0.0.0-20180806215700-d6e28a8b3199 h1:mmwryCmmFkCxL3t5r6syrbk1eyP6tP9q/whDdAiM9Mw= github.com/gophercloud/utils v0.0.0-20180806215700-d6e28a8b3199/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= diff --git a/vendor/github.com/gophercloud/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md index 8c5bfce79..ad29041d9 100644 --- a/vendor/github.com/gophercloud/gophercloud/README.md +++ b/vendor/github.com/gophercloud/gophercloud/README.md @@ -140,7 +140,7 @@ See the [contributing guide](./.github/CONTRIBUTING.md). ## Help and feedback If you're struggling with something or have spotted a potential bug, feel free -to submit an issue to our [bug tracker](/issues). +to submit an issue to our [bug tracker](https://github.com/gophercloud/gophercloud/issues). ## Thank You diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/auth_options.go index 5e693585c..f28c8d2ad 100644 --- a/vendor/github.com/gophercloud/gophercloud/auth_options.go +++ b/vendor/github.com/gophercloud/gophercloud/auth_options.go @@ -84,6 +84,12 @@ type AuthOptions struct { // Scope determines the scoping of the authentication request. Scope *AuthScope `json:"-"` + + // Authentication through Application Credentials requires supplying name, project and secret + // For project we can use TenantID + ApplicationCredentialID string `json:"-"` + ApplicationCredentialName string `json:"-"` + ApplicationCredentialSecret string `json:"-"` } // AuthScope allows a created token to be limited to a specific domain or project. @@ -142,7 +148,7 @@ 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"` + Password string `json:"password,omitempty"` Domain *domainReq `json:"domain,omitempty"` } @@ -154,10 +160,18 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s ID string `json:"id"` } + type applicationCredentialReq struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + User *userReq `json:"user,omitempty"` + Secret *string `json:"secret,omitempty"` + } + type identityReq struct { - Methods []string `json:"methods"` - Password *passwordReq `json:"password,omitempty"` - Token *tokenReq `json:"token,omitempty"` + Methods []string `json:"methods"` + Password *passwordReq `json:"password,omitempty"` + Token *tokenReq `json:"token,omitempty"` + ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"` } type authReq struct { @@ -171,6 +185,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s // Populate the request structure based on the provided arguments. Create and return an error // if insufficient or incompatible information is present. var req request + var userRequest userReq if opts.Password == "" { if opts.TokenID != "" { @@ -194,8 +209,49 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s req.Auth.Identity.Token = &tokenReq{ ID: opts.TokenID, } + + } else if opts.ApplicationCredentialID != "" { + // Configure the request for ApplicationCredentialID authentication. + // https://github.com/openstack/keystoneauth/blob/stable/rocky/keystoneauth1/identity/v3/application_credential.py#L48-L67 + // There are three kinds of possible application_credential requests + // 1. application_credential id + secret + // 2. application_credential name + secret + user_id + // 3. application_credential name + secret + username + domain_id / domain_name + if opts.ApplicationCredentialSecret == "" { + return nil, ErrAppCredMissingSecret{} + } + req.Auth.Identity.Methods = []string{"application_credential"} + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + ID: &opts.ApplicationCredentialID, + Secret: &opts.ApplicationCredentialSecret, + } + } else if opts.ApplicationCredentialName != "" { + if opts.ApplicationCredentialSecret == "" { + return nil, ErrAppCredMissingSecret{} + } + // make sure that only one of DomainName or DomainID were provided + if opts.DomainID == "" && opts.DomainName == "" { + return nil, ErrDomainIDOrDomainName{} + } + req.Auth.Identity.Methods = []string{"application_credential"} + if opts.DomainID != "" { + userRequest = userReq{ + Name: &opts.Username, + Domain: &domainReq{ID: &opts.DomainID}, + } + } else if opts.DomainName != "" { + userRequest = userReq{ + Name: &opts.Username, + Domain: &domainReq{Name: &opts.DomainName}, + } + } + req.Auth.Identity.ApplicationCredential = &applicationCredentialReq{ + Name: &opts.ApplicationCredentialName, + User: &userRequest, + Secret: &opts.ApplicationCredentialSecret, + } } else { - // If no password or token ID are available, authentication can't continue. + // If no password or token ID or ApplicationCredential are available, authentication can't continue. return nil, ErrMissingPassword{} } } else { diff --git a/vendor/github.com/gophercloud/gophercloud/errors.go b/vendor/github.com/gophercloud/gophercloud/errors.go index a5fa68d6d..4bf102468 100644 --- a/vendor/github.com/gophercloud/gophercloud/errors.go +++ b/vendor/github.com/gophercloud/gophercloud/errors.go @@ -451,3 +451,10 @@ type ErrScopeEmpty struct{ BaseError } func (e ErrScopeEmpty) Error() string { return "You must provide either a Project or Domain in a Scope" } + +// ErrAppCredMissingSecret indicates that no Application Credential Secret was provided with Application Credential ID or Name +type ErrAppCredMissingSecret struct{ BaseError } + +func (e ErrAppCredMissingSecret) Error() string { + return "You must provide an Application Credential Secret" +} diff --git a/vendor/github.com/gophercloud/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/provider_client.go index 17e451274..95fa11a78 100644 --- a/vendor/github.com/gophercloud/gophercloud/provider_client.go +++ b/vendor/github.com/gophercloud/gophercloud/provider_client.go @@ -295,7 +295,11 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) seeker.Seek(0, 0) } } + // make a new call to request with a nil reauth func in order to avoid infinite loop + reauthFunc := client.ReauthFunc + client.ReauthFunc = nil resp, err = client.Request(method, url, options) + client.ReauthFunc = reauthFunc if err != nil { switch err.(type) { case *ErrUnexpectedResponseCode: @@ -378,7 +382,7 @@ func defaultOkCodes(method string) []int { case method == "PUT": return []int{201, 202} case method == "PATCH": - return []int{200, 204} + return []int{200, 202, 204} case method == "DELETE": return []int{202, 204} } diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/results.go index fdd4830ec..30d3b1559 100644 --- a/vendor/github.com/gophercloud/gophercloud/results.go +++ b/vendor/github.com/gophercloud/gophercloud/results.go @@ -89,23 +89,45 @@ func (r Result) extractIntoPtr(to interface{}, label string) error { if typeOfV.Kind() == reflect.Struct { if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous { newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0) - newType := reflect.New(typeOfV).Elem() for _, v := range m[label].([]interface{}) { + // For each iteration of the slice, we create a new struct. + // This is to work around a bug where elements of a slice + // are reused and not overwritten when the same copy of the + // struct is used: + // + // https://github.com/golang/go/issues/21092 + // https://github.com/golang/go/issues/24155 + // https://play.golang.org/p/NHo3ywlPZli + newType := reflect.New(typeOfV).Elem() + b, err := json.Marshal(v) if err != nil { return err } + // This is needed for structs with an UnmarshalJSON method. + // Technically this is just unmarshalling the response into + // a struct that is never used, but it's good enough to + // trigger the UnmarshalJSON method. for i := 0; i < newType.NumField(); i++ { s := newType.Field(i).Addr().Interface() - err = json.NewDecoder(bytes.NewReader(b)).Decode(s) + + // Unmarshal is used rather than NewDecoder to also work + // around the above-mentioned bug. + err = json.Unmarshal(b, s) if err != nil { return err } } + newSlice = reflect.Append(newSlice, newType) } + + // "to" should now be properly modeled to receive the + // JSON response body and unmarshal into all the correct + // fields of the struct or composed extension struct + // at the end of this method. toValue.Set(newSlice) } } @@ -366,6 +388,27 @@ func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { return nil } +// RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). +const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" + +type JSONRFC3339ZNoTNoZ time.Time + +func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s == "" { + return nil + } + t, err := time.Parse(RFC3339ZNoTNoZ, s) + if err != nil { + return err + } + *jt = JSONRFC3339ZNoTNoZ(t) + return nil +} + /* Link is an internal type to be used in packages of collection resources that are paginated in a certain way. diff --git a/vendor/vendor.json b/vendor/vendor.json index d12f143bb..e69e62d6c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -811,10 +811,10 @@ "revisionTime": "2017-11-29T19:10:14Z" }, { - "checksumSHA1": "qduT9GZUhXc00XoHEwLx16Xn9gM=", + "checksumSHA1": "90HfuOdlP9yK6xUnJCAnCrL73DQ=", "path": "github.com/gophercloud/gophercloud", - "revision": "7112fcd50da4ea27e8d4d499b30f04eea143bec2", - "revisionTime": "2018-05-31T02:06:30Z" + "revision": "ea7289ebdf06687b792c087e2516317579d3003b", + "revisionTime": "2018-09-03T13:40:57Z" }, { "checksumSHA1": "b7g9TcU1OmW7e2UySYeOAmcfHpY=", @@ -1487,18 +1487,18 @@ "revision": "2d205ac6ec17a839a94bdbfd16d2fa6c6dada2e0", "revisionTime": "2016-03-31T20:48:55Z" }, - { - "checksumSHA1": "6JP37UqrI0H80Gpk0Y2P+KXgn5M=", - "path": "github.com/ryanuber/go-glob", - "revision": "256dc444b735e061061cf46c809487313d5b0065", - "revisionTime": "2017-01-28T01:21:29Z" - }, { "checksumSHA1": "4gVuzkHbQoznf+lCSJhIJnvS5tc=", "path": "github.com/rwtodd/Go.Sed/sed", "revision": "d6d5d585814e4c3560c684f52e3d8aeed721313d", "revisionTime": "2017-05-07T04:53:31Z" }, + { + "checksumSHA1": "6JP37UqrI0H80Gpk0Y2P+KXgn5M=", + "path": "github.com/ryanuber/go-glob", + "revision": "256dc444b735e061061cf46c809487313d5b0065", + "revisionTime": "2017-01-28T01:21:29Z" + }, { "checksumSHA1": "zmC8/3V4ls53DJlNTKDZwPSC/dA=", "path": "github.com/satori/go.uuid",