ChooseVersion prefers exact URL matches now.
diff --git a/openstack/client.go b/openstack/client.go
index 207ba13..253fcdc 100644
--- a/openstack/client.go
+++ b/openstack/client.go
@@ -2,7 +2,6 @@
import (
"fmt"
- "net/url"
"strings"
"github.com/rackspace/gophercloud"
@@ -23,15 +22,7 @@
// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
// for example.
func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
- // Normalize the identity endpoint that's provided by trimming any path, query or fragment from the URL.
- u, err := url.Parse(endpoint)
- if err != nil {
- return nil, err
- }
- u.Path, u.RawQuery, u.Fragment = "", "", ""
- normalized := u.String()
-
- return &gophercloud.ProviderClient{IdentityEndpoint: normalized}, nil
+ return &gophercloud.ProviderClient{IdentityEndpoint: endpoint}, nil
}
// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
diff --git a/openstack/utils/choose_version.go b/openstack/utils/choose_version.go
index 7604f75..088e086 100644
--- a/openstack/utils/choose_version.go
+++ b/openstack/utils/choose_version.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "net/url"
"strings"
"github.com/racker/perigee"
@@ -22,7 +23,7 @@
// ChooseVersion queries the base endpoint of a API to choose the most recent non-experimental alternative from a service's
// published versions.
// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint.
-func ChooseVersion(baseEndpoint string, recognized []*Version) (*Version, string, error) {
+func ChooseVersion(identityEndpoint string, recognized []*Version) (*Version, string, error) {
type linkResp struct {
Href string `json:"href"`
Rel string `json:"rel"`
@@ -42,8 +43,16 @@
Versions versionsResp `json:"versions"`
}
+ // Normalize the identity endpoint that's provided by trimming any path, query or fragment from the URL.
+ u, err := url.Parse(identityEndpoint)
+ if err != nil {
+ return nil, "", err
+ }
+ u.Path, u.RawQuery, u.Fragment = "", "", ""
+ normalized := u.String()
+
var resp response
- _, err := perigee.Request("GET", baseEndpoint, perigee.Options{
+ _, err = perigee.Request("GET", normalized, perigee.Options{
Results: &resp,
OkCodes: []int{200, 300},
})
@@ -61,27 +70,37 @@
var endpoint string
for _, value := range resp.Versions.Values {
- if matching, ok := byID[value.ID]; ok && goodStatus[strings.ToLower(value.Status)] {
- if highest == nil || matching.Priority > highest.Priority {
- highest = matching
+ href := ""
+ for _, link := range value.Links {
+ if link.Rel == "self" {
+ href = link.Href
+ }
+ }
- found := false
- for _, link := range value.Links {
- if link.Rel == "self" {
- found = true
- endpoint = link.Href
- }
+ if matching, ok := byID[value.ID]; ok {
+ // Prefer a version that exactly matches the provided endpoint.
+ if href == identityEndpoint {
+ if href == "" {
+ return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, normalized)
}
+ return matching, href, nil
+ }
- if !found {
- return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", value.ID, baseEndpoint)
+ // Otherwise, find the highest-priority version with a whitelisted status.
+ if goodStatus[strings.ToLower(value.Status)] {
+ if highest == nil || matching.Priority > highest.Priority {
+ highest = matching
+ endpoint = href
}
}
}
}
- if highest == nil || endpoint == "" {
- return nil, "", fmt.Errorf("No supported version available from endpoint %s", baseEndpoint)
+ if highest == nil {
+ return nil, "", fmt.Errorf("No supported version available from endpoint %s", normalized)
+ }
+ if endpoint == "" {
+ return nil, "", fmt.Errorf("Endpoint missing in version %s response from %s", highest.ID, normalized)
}
return highest, endpoint, nil
diff --git a/openstack/utils/choose_version_test.go b/openstack/utils/choose_version_test.go
index a252790..d2aec8a 100644
--- a/openstack/utils/choose_version_test.go
+++ b/openstack/utils/choose_version_test.go
@@ -8,34 +8,37 @@
"github.com/rackspace/gophercloud/testhelper"
)
+func setupVersionHandler() {
+ testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, `
+ {
+ "versions": {
+ "values": [
+ {
+ "status": "stable",
+ "id": "v3.0",
+ "links": [
+ { "href": "%s/v3.0", "rel": "self" }
+ ]
+ },
+ {
+ "status": "stable",
+ "id": "v2.0",
+ "links": [
+ { "href": "%s/v2.0", "rel": "self" }
+ ]
+ }
+ ]
+ }
+ }
+ `, testhelper.Server.URL, testhelper.Server.URL)
+ })
+}
+
func TestChooseVersion(t *testing.T) {
testhelper.SetupHTTP()
defer testhelper.TeardownHTTP()
-
- testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, `
- {
- "versions": {
- "values": [
- {
- "status": "stable",
- "id": "v3.0",
- "links": [
- { "href": "https://example.com:1000/", "rel": "self" }
- ]
- },
- {
- "status": "stable",
- "id": "v2.0",
- "links": [
- { "href": "https://example.com:2000/", "rel": "self" }
- ]
- }
- ]
- }
- }
- `)
- })
+ setupVersionHandler()
v2 := &Version{ID: "v2.0", Priority: 2}
v3 := &Version{ID: "v3.0", Priority: 3}
@@ -50,7 +53,30 @@
t.Errorf("Expected %#v to win, but %#v did instead", v3, v)
}
- expected := "https://example.com:1000/"
+ expected := testhelper.Endpoint() + "v3.0"
+ if endpoint != expected {
+ t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
+ }
+}
+
+func TestChooseVersionOpinionatedLink(t *testing.T) {
+ testhelper.SetupHTTP()
+ defer testhelper.TeardownHTTP()
+ setupVersionHandler()
+
+ v2 := &Version{ID: "v2.0", Priority: 2}
+ v3 := &Version{ID: "v3.0", Priority: 3}
+
+ v, endpoint, err := ChooseVersion(testhelper.Endpoint()+"v2.0", []*Version{v2, v3})
+ if err != nil {
+ t.Fatalf("Unexpected error from ChooseVersion: %v", err)
+ }
+
+ if v != v2 {
+ t.Errorf("Expected %#v to win, but %#v did instead", v2, v)
+ }
+
+ expected := testhelper.Endpoint() + "v2.0"
if endpoint != expected {
t.Errorf("Expected endpoint [%s], but was [%s] instead", expected, endpoint)
}