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