diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 7dd419ac..edb2e6da 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -172,31 +172,31 @@ }, { "ImportPath": "github.com/gophercloud/gophercloud", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tenants", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gophercloud/gophercloud/openstack/utils", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gophercloud/gophercloud/pagination", - "Rev": "8183543f90d1aef267a5ecc209f2e0715b355acb" + "Rev": "8e59687aa4b27ab22a0bf3295f1e165ff7bd5f97" }, { "ImportPath": "github.com/gregjones/httpcache", diff --git a/vendor/github.com/gophercloud/gophercloud/README.md b/vendor/github.com/gophercloud/gophercloud/README.md index 60ca479d..bb218c3f 100644 --- a/vendor/github.com/gophercloud/gophercloud/README.md +++ b/vendor/github.com/gophercloud/gophercloud/README.md @@ -141,3 +141,19 @@ See the [contributing guide](./.github/CONTRIBUTING.md). If you're struggling with something or have spotted a potential bug, feel free to submit an issue to our [bug tracker](/issues). + +## Thank You + +We'd like to extend special thanks and appreciation to the following: + +### OpenLab + + + +OpenLab is providing a full CI environment to test each PR and merge for a variety of OpenStack releases. + +### VEXXHOST + + + +VEXXHOST is providing their services to assist with the development and testing of Gophercloud. diff --git a/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md b/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md index e7531a83..22a29009 100644 --- a/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md +++ b/vendor/github.com/gophercloud/gophercloud/STYLEGUIDE.md @@ -1,6 +1,8 @@ ## On Pull Requests +- Please make sure to read our [contributing guide](/.github/CONTRIBUTING.md). + - Before you start a PR there needs to be a Github issue and a discussion about it on that issue with a core contributor, even if it's just a 'SGTM'. @@ -34,6 +36,9 @@ append. It makes it difficult for the reviewer to see what's changed from one review to the next. +- See [#583](https://github.com/gophercloud/gophercloud/issues/583) as an example of a + well-formatted issue which contains all relevant information we need to review and approve. + ## On Code - In re design: follow as closely as is reasonable the code already in the library. diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go index 95286041..b5482ba8 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go @@ -16,7 +16,12 @@ The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must have settings, -or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional. +or an error will result. OS_TENANT_ID, OS_TENANT_NAME, OS_PROJECT_ID, and +OS_PROJECT_NAME are optional. + +OS_TENANT_ID and OS_TENANT_NAME are mutually exclusive to OS_PROJECT_ID and +OS_PROJECT_NAME. If OS_PROJECT_ID and OS_PROJECT_NAME are set, they will +still be referred as "tenant" in Gophercloud. To use this function, first set the OS_* environment variables (for example, by sourcing an `openrc` file), then: @@ -34,6 +39,16 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) { domainID := os.Getenv("OS_DOMAIN_ID") domainName := os.Getenv("OS_DOMAIN_NAME") + // If OS_PROJECT_ID is set, overwrite tenantID with the value. + if v := os.Getenv("OS_PROJECT_ID"); v != "" { + tenantID = v + } + + // If OS_PROJECT_NAME is set, overwrite tenantName with the value. + if v := os.Getenv("OS_PROJECT_NAME"); v != "" { + tenantName = v + } + if authURL == "" { err := gophercloud.ErrMissingInput{Argument: "authURL"} return nilOptions, err diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/openstack/client.go index c796795b..5a52e579 100644 --- a/vendor/github.com/gophercloud/gophercloud/openstack/client.go +++ b/vendor/github.com/gophercloud/gophercloud/openstack/client.go @@ -56,11 +56,12 @@ func NewClient(endpoint string) (*gophercloud.ProviderClient, error) { endpoint = gophercloud.NormalizeURL(endpoint) base = gophercloud.NormalizeURL(base) - return &gophercloud.ProviderClient{ - IdentityBase: base, - IdentityEndpoint: endpoint, - }, nil + p := new(gophercloud.ProviderClient) + p.IdentityBase = base + p.IdentityEndpoint = endpoint + p.UseTokenLock() + return p, nil } /* @@ -158,9 +159,21 @@ func v2auth(client *gophercloud.ProviderClient, endpoint string, options gopherc } if options.AllowReauth { + // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but + // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, + // this should retry authentication only once + tac := *client + tac.ReauthFunc = nil + tac.TokenID = "" + tao := options + tao.AllowReauth = false client.ReauthFunc = func() error { - client.TokenID = "" - return v2auth(client, endpoint, options, eo) + err := v2auth(&tac, endpoint, tao, eo) + if err != nil { + return err + } + client.TokenID = tac.TokenID + return nil } } client.TokenID = token.ID @@ -202,9 +215,32 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au client.TokenID = token.ID if opts.CanReauth() { + // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but + // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`, + // this should retry authentication only once + tac := *client + tac.ReauthFunc = nil + tac.TokenID = "" + var tao tokens3.AuthOptionsBuilder + switch ot := opts.(type) { + case *gophercloud.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + case *tokens3.AuthOptions: + o := *ot + o.AllowReauth = false + tao = &o + default: + tao = opts + } client.ReauthFunc = func() error { - client.TokenID = "" - return v3auth(client, endpoint, opts, eo) + err := v3auth(&tac, endpoint, tao, eo) + if err != nil { + return err + } + client.TokenID = tac.TokenID + return nil } } client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { diff --git a/vendor/github.com/gophercloud/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/provider_client.go index 01b30107..72daeb0a 100644 --- a/vendor/github.com/gophercloud/gophercloud/provider_client.go +++ b/vendor/github.com/gophercloud/gophercloud/provider_client.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "strings" + "sync" ) // DefaultUserAgent is the default User-Agent string set in the request header. @@ -51,6 +52,8 @@ type ProviderClient struct { IdentityEndpoint string // TokenID is the ID of the most recently issued valid token. + // NOTE: Aside from within a custom ReauthFunc, this field shouldn't be set by an application. + // To safely read or write this value, call `Token` or `SetToken`, respectively TokenID string // EndpointLocator describes how this provider discovers the endpoints for @@ -68,16 +71,59 @@ type ProviderClient struct { // authentication functions for different Identity service versions. ReauthFunc func() error - Debug bool + mut *sync.RWMutex + + reauthmut *reauthlock +} + +type reauthlock struct { + sync.RWMutex + reauthing bool } // AuthenticatedHeaders returns a map of HTTP headers that are common for all // authenticated service requests. -func (client *ProviderClient) AuthenticatedHeaders() map[string]string { - if client.TokenID == "" { - return map[string]string{} +func (client *ProviderClient) AuthenticatedHeaders() (m map[string]string) { + if client.reauthmut != nil { + client.reauthmut.RLock() + if client.reauthmut.reauthing { + client.reauthmut.RUnlock() + return + } + client.reauthmut.RUnlock() } - return map[string]string{"X-Auth-Token": client.TokenID} + t := client.Token() + if t == "" { + return + } + return map[string]string{"X-Auth-Token": t} +} + +// UseTokenLock creates a mutex that is used to allow safe concurrent access to the auth token. +// If the application's ProviderClient is not used concurrently, this doesn't need to be called. +func (client *ProviderClient) UseTokenLock() { + client.mut = new(sync.RWMutex) + client.reauthmut = new(reauthlock) +} + +// Token safely reads the value of the auth token from the ProviderClient. Applications should +// call this method to access the token instead of the TokenID field +func (client *ProviderClient) Token() string { + if client.mut != nil { + client.mut.RLock() + defer client.mut.RUnlock() + } + return client.TokenID +} + +// SetToken safely sets the value of the auth token in the ProviderClient. Applications may +// use this method in a custom ReauthFunc +func (client *ProviderClient) SetToken(t string) { + if client.mut != nil { + client.mut.Lock() + defer client.mut.Unlock() + } + client.TokenID = t } // RequestOpts customizes the behavior of the provider.Request() method. @@ -166,6 +212,8 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) // Set connection parameter to close the connection immediately when we've got the response req.Close = true + prereqtok := req.Header.Get("X-Auth-Token") + // Issue the request. resp, err := client.HTTPClient.Do(req) if err != nil { @@ -189,9 +237,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) if !ok { body, _ := ioutil.ReadAll(resp.Body) resp.Body.Close() - //pc := make([]uintptr, 1) - //runtime.Callers(2, pc) - //f := runtime.FuncForPC(pc[0]) respErr := ErrUnexpectedResponseCode{ URL: url, Method: method, @@ -199,7 +244,6 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) Actual: resp.StatusCode, Body: body, } - //respErr.Function = "gophercloud.ProviderClient.Request" errType := options.ErrorContext switch resp.StatusCode { @@ -210,7 +254,21 @@ func (client *ProviderClient) Request(method, url string, options *RequestOpts) } case http.StatusUnauthorized: if client.ReauthFunc != nil { - err = client.ReauthFunc() + if client.mut != nil { + client.mut.Lock() + client.reauthmut.Lock() + client.reauthmut.reauthing = true + client.reauthmut.Unlock() + if curtok := client.TokenID; curtok == prereqtok { + err = client.ReauthFunc() + } + client.reauthmut.Lock() + client.reauthmut.reauthing = false + client.reauthmut.Unlock() + client.mut.Unlock() + } else { + err = client.ReauthFunc() + } if err != nil { e := &ErrUnableToReauthenticate{} e.ErrOriginal = respErr