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
=== modified file 'identity/live_test.go'
--- identity/live_test.go 2013-01-27 13:37:53 +0000
+++ identity/live_test.go 2013-01-29 04:11:22 +0000
@@ -34,7 +34,7 @@
3434
35func (s *LiveTests) TestAuth(c *C) {35func (s *LiveTests) TestAuth(c *C) {
36 s.client.Authenticate()36 s.client.Authenticate()
37 url, err := s.client.MakeServiceURL("compute", []string{})37 url, err := s.client.MakeServiceURL("compute", []string{})
38 c.Assert(err, IsNil)38 c.Assert(err, IsNil)
39 c.Assert(url[:len(s.cred.URL)], Equals, s.cred.URL)39 c.Assert(url, Not(Equals), "")
40}40}
4141
=== modified file 'identity/userpass.go'
--- identity/userpass.go 2013-01-27 13:37:53 +0000
+++ identity/userpass.go 2013-01-29 04:11:22 +0000
@@ -4,7 +4,6 @@
4 "fmt"4 "fmt"
5 goosehttp "launchpad.net/goose/http"5 goosehttp "launchpad.net/goose/http"
6 "net/http"6 "net/http"
7 "os"
8)7)
98
10type passwordCredentials struct {9type passwordCredentials struct {
@@ -71,6 +70,43 @@
71 client *goosehttp.Client70 client *goosehttp.Client
72}71}
7372
73func findServiceEndpoint(service serviceResponse, region string) string {
74 possibleURLs := make([]string, 0, len(service.Endpoints))
75 for _, e := range service.Endpoints {
76 if e.Region == region {
77 possibleURLs = append(possibleURLs, e.PublicURL)
78 }
79 }
80 if len(possibleURLs) == 0 {
81 // TODO: Add a log entry:
82 // fmt.Fprintf(os.Stderr, "Found a service Type: %v but no endpoints in region: %v\n",
83 // service.Type, region)
84 // fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type)
85 return ""
86 }
87 return possibleURLs[0]
88}
89
90func parseAuthResponse(access *accessResponse, region string) (*AuthDetails, error) {
91 details := &AuthDetails{}
92 respToken := access.Token
93 if respToken.Id == "" {
94 return nil, fmt.Errorf("Did not get valid Token from auth request")
95 }
96 details.Token = respToken.Id
97 details.TenantId = respToken.Tenant.Id
98 details.UserId = access.User.Id
99 details.ServiceURLs = make(map[string]string, len(access.ServiceCatalog))
100 for _, service := range access.ServiceCatalog {
101 url := findServiceEndpoint(service, region)
102 if url == "" {
103 continue
104 }
105 details.ServiceURLs[service.Type] = url
106 }
107 return details, nil
108}
109
74func (u *UserPass) Auth(creds *Credentials) (*AuthDetails, error) {110func (u *UserPass) Auth(creds *Credentials) (*AuthDetails, error) {
75 if u.client == nil {111 if u.client == nil {
76 u.client = goosehttp.New(http.Client{CheckRedirect: nil}, nil, "")112 u.client = goosehttp.New(http.Client{CheckRedirect: nil}, nil, "")
@@ -88,28 +124,9 @@
88 if err != nil {124 if err != nil {
89 return nil, err125 return nil, err
90 }126 }
91127 details, err := parseAuthResponse(&accessWrapper.Access, creds.Region)
92 details := &AuthDetails{}128 if err != nil {
93 access := accessWrapper.Access129 return nil, err
94 respToken := access.Token130 }
95 if respToken.Id == "" {
96 return nil, fmt.Errorf("Did not get valid Token from auth request")
97 }
98 details.Token = respToken.Id
99 details.TenantId = respToken.Tenant.Id
100 details.UserId = access.User.Id
101 details.ServiceURLs = make(map[string]string, len(access.ServiceCatalog))
102 for _, service := range access.ServiceCatalog {
103 for i, e := range service.Endpoints {
104 if e.Region != creds.Region {
105 service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...)
106 }
107 }
108 if len(service.Endpoints) == 0 {
109 fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type)
110 }
111 details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL
112 }
113
114 return details, nil131 return details, nil
115}132}
116133
=== modified file 'identity/userpass_test.go'
--- identity/userpass_test.go 2013-01-24 01:01:35 +0000
+++ identity/userpass_test.go 2013-01-29 04:11:22 +0000
@@ -23,3 +23,42 @@
23 c.Assert(auth.Token, Equals, userInfo.Token)23 c.Assert(auth.Token, Equals, userInfo.Token)
24 c.Assert(auth.TenantId, Equals, userInfo.TenantId)24 c.Assert(auth.TenantId, Equals, userInfo.TenantId)
25}25}
26
27func (s *UserPassTestSuite) TestParseResponseWithoutEndpoint(c *C) {
28 // It is possible to get an endpoint which is available in some regions,
29 // but not in the region we are currently working in.
30 myRegion := "region01"
31 otherRegion := "region02"
32 otherEndpoint := endpoint{PublicURL: "http://example.invalid", Region: otherRegion}
33 service := serviceResponse{Name: "nova", Type: "compute",
34 Endpoints: []endpoint{otherEndpoint}}
35 response := &accessResponse{
36 ServiceCatalog: []serviceResponse{service},
37 Token: tokenResponse{Id: "token"},
38 User: userResponse{},
39 }
40 authDetails, err := parseAuthResponse(response, myRegion)
41 c.Assert(err, IsNil)
42 c.Assert(authDetails, NotNil)
43 // There should be no entries in the ServiceURLs map, because there is
44 // no URL for the service in this region
45 c.Assert(authDetails.ServiceURLs, DeepEquals, map[string]string{})
46}
47
48func (s *UserPassTestSuite) TestFindServiceEndpointWrongRegion(c *C) {
49 myRegion := "region01"
50 otherRegion := "region02"
51 otherEndpoint := endpoint{PublicURL: "http://example.invalid", Region: otherRegion}
52 service := serviceResponse{Name: "nova", Type: "compute",
53 Endpoints: []endpoint{otherEndpoint}}
54 c.Assert(findServiceEndpoint(service, myRegion), Equals, "")
55}
56
57func (s *UserPassTestSuite) TestFindServiceMultipleEndpoints(c *C) {
58 myRegion := "region01"
59 endpoint1 := endpoint{PublicURL: "http://example.invalid", Region: myRegion}
60 endpoint2 := endpoint{PublicURL: "http://other.invalid", Region: myRegion}
61 service := serviceResponse{Name: "nova", Type: "compute",
62 Endpoints: []endpoint{endpoint1, endpoint2}}
63 c.Assert(findServiceEndpoint(service, myRegion), Equals, endpoint1.PublicURL)
64}

Subscribers

People subscribed via source and target branches