Merge lp:~jameinel/goose/handle-missing-endpoint into lp:goose

Proposed by John A Meinel
Status: Merged
Merged at revision: not available
Proposed branch: lp:~jameinel/goose/handle-missing-endpoint
Merge into: lp:goose
Diff against target: 150 lines (+83/-27)
3 files modified
identity/live_test.go (+3/-3)
identity/userpass.go (+41/-24)
identity/userpass_test.go (+39/-0)
To merge this branch: bzr merge lp:~jameinel/goose/handle-missing-endpoint
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+145098@code.launchpad.net

Commit message

identity: Handle when a service is available, but only in a region we aren't currently using.

Add -live tests for the identity suite. Refactor a bit and test the parsing logic to ensure we don't get panic() when talking to canonistack.

Description of the change

With the recent changes to canonistack, we now have a service that is only exposed on lcy02. (fakecinder, I believe).

This was causing failures in the nova test suite (it panic'd because of accessing the 0th item of an empty array).

While doing this, I realized we didn't have any live tests focusing on the identity level, so I added some. This also was easier to test by refactoring it out into smaller helper functions, rather than having everything done inside the request processing.

Because the UserPass object doesn't have much state (none that I needed), I just made them free functions, but I would consider having them as struct methods if people like that more.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'identity/live_test.go'
2--- identity/live_test.go 2013-01-27 13:37:53 +0000
3+++ identity/live_test.go 2013-01-29 04:11:22 +0000
4@@ -34,7 +34,7 @@
5
6 func (s *LiveTests) TestAuth(c *C) {
7 s.client.Authenticate()
8- url, err := s.client.MakeServiceURL("compute", []string{})
9- c.Assert(err, IsNil)
10- c.Assert(url[:len(s.cred.URL)], Equals, s.cred.URL)
11+ url, err := s.client.MakeServiceURL("compute", []string{})
12+ c.Assert(err, IsNil)
13+ c.Assert(url, Not(Equals), "")
14 }
15
16=== modified file 'identity/userpass.go'
17--- identity/userpass.go 2013-01-27 13:37:53 +0000
18+++ identity/userpass.go 2013-01-29 04:11:22 +0000
19@@ -4,7 +4,6 @@
20 "fmt"
21 goosehttp "launchpad.net/goose/http"
22 "net/http"
23- "os"
24 )
25
26 type passwordCredentials struct {
27@@ -71,6 +70,43 @@
28 client *goosehttp.Client
29 }
30
31+func findServiceEndpoint(service serviceResponse, region string) string {
32+ possibleURLs := make([]string, 0, len(service.Endpoints))
33+ for _, e := range service.Endpoints {
34+ if e.Region == region {
35+ possibleURLs = append(possibleURLs, e.PublicURL)
36+ }
37+ }
38+ if len(possibleURLs) == 0 {
39+ // TODO: Add a log entry:
40+ // fmt.Fprintf(os.Stderr, "Found a service Type: %v but no endpoints in region: %v\n",
41+ // service.Type, region)
42+ // fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type)
43+ return ""
44+ }
45+ return possibleURLs[0]
46+}
47+
48+func parseAuthResponse(access *accessResponse, region string) (*AuthDetails, error) {
49+ details := &AuthDetails{}
50+ respToken := access.Token
51+ if respToken.Id == "" {
52+ return nil, fmt.Errorf("Did not get valid Token from auth request")
53+ }
54+ details.Token = respToken.Id
55+ details.TenantId = respToken.Tenant.Id
56+ details.UserId = access.User.Id
57+ details.ServiceURLs = make(map[string]string, len(access.ServiceCatalog))
58+ for _, service := range access.ServiceCatalog {
59+ url := findServiceEndpoint(service, region)
60+ if url == "" {
61+ continue
62+ }
63+ details.ServiceURLs[service.Type] = url
64+ }
65+ return details, nil
66+}
67+
68 func (u *UserPass) Auth(creds *Credentials) (*AuthDetails, error) {
69 if u.client == nil {
70 u.client = goosehttp.New(http.Client{CheckRedirect: nil}, nil, "")
71@@ -88,28 +124,9 @@
72 if err != nil {
73 return nil, err
74 }
75-
76- details := &AuthDetails{}
77- access := accessWrapper.Access
78- respToken := access.Token
79- if respToken.Id == "" {
80- return nil, fmt.Errorf("Did not get valid Token from auth request")
81- }
82- details.Token = respToken.Id
83- details.TenantId = respToken.Tenant.Id
84- details.UserId = access.User.Id
85- details.ServiceURLs = make(map[string]string, len(access.ServiceCatalog))
86- for _, service := range access.ServiceCatalog {
87- for i, e := range service.Endpoints {
88- if e.Region != creds.Region {
89- service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...)
90- }
91- }
92- if len(service.Endpoints) == 0 {
93- fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type)
94- }
95- details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL
96- }
97-
98+ details, err := parseAuthResponse(&accessWrapper.Access, creds.Region)
99+ if err != nil {
100+ return nil, err
101+ }
102 return details, nil
103 }
104
105=== modified file 'identity/userpass_test.go'
106--- identity/userpass_test.go 2013-01-24 01:01:35 +0000
107+++ identity/userpass_test.go 2013-01-29 04:11:22 +0000
108@@ -23,3 +23,42 @@
109 c.Assert(auth.Token, Equals, userInfo.Token)
110 c.Assert(auth.TenantId, Equals, userInfo.TenantId)
111 }
112+
113+func (s *UserPassTestSuite) TestParseResponseWithoutEndpoint(c *C) {
114+ // It is possible to get an endpoint which is available in some regions,
115+ // but not in the region we are currently working in.
116+ myRegion := "region01"
117+ otherRegion := "region02"
118+ otherEndpoint := endpoint{PublicURL: "http://example.invalid", Region: otherRegion}
119+ service := serviceResponse{Name: "nova", Type: "compute",
120+ Endpoints: []endpoint{otherEndpoint}}
121+ response := &accessResponse{
122+ ServiceCatalog: []serviceResponse{service},
123+ Token: tokenResponse{Id: "token"},
124+ User: userResponse{},
125+ }
126+ authDetails, err := parseAuthResponse(response, myRegion)
127+ c.Assert(err, IsNil)
128+ c.Assert(authDetails, NotNil)
129+ // There should be no entries in the ServiceURLs map, because there is
130+ // no URL for the service in this region
131+ c.Assert(authDetails.ServiceURLs, DeepEquals, map[string]string{})
132+}
133+
134+func (s *UserPassTestSuite) TestFindServiceEndpointWrongRegion(c *C) {
135+ myRegion := "region01"
136+ otherRegion := "region02"
137+ otherEndpoint := endpoint{PublicURL: "http://example.invalid", Region: otherRegion}
138+ service := serviceResponse{Name: "nova", Type: "compute",
139+ Endpoints: []endpoint{otherEndpoint}}
140+ c.Assert(findServiceEndpoint(service, myRegion), Equals, "")
141+}
142+
143+func (s *UserPassTestSuite) TestFindServiceMultipleEndpoints(c *C) {
144+ myRegion := "region01"
145+ endpoint1 := endpoint{PublicURL: "http://example.invalid", Region: myRegion}
146+ endpoint2 := endpoint{PublicURL: "http://other.invalid", Region: myRegion}
147+ service := serviceResponse{Name: "nova", Type: "compute",
148+ Endpoints: []endpoint{endpoint1, endpoint2}}
149+ c.Assert(findServiceEndpoint(service, myRegion), Equals, endpoint1.PublicURL)
150+}

Subscribers

People subscribed via source and target branches