Merge lp:~wallyworld/goose/service-double-improvements into lp:~gophers/goose/trunk
- service-double-improvements
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 51 |
Proposed branch: | lp:~wallyworld/goose/service-double-improvements |
Merge into: | lp:~gophers/goose/trunk |
Diff against target: |
2264 lines (+761/-637) 26 files modified
client/local_test.go (+15/-22) identity/legacy_test.go (+3/-3) identity/userpass_test.go (+5/-5) nova/local_test.go (+12/-32) swift/local_test.go (+12/-28) testservices/cmd/main.go (+4/-3) testservices/identityservice/identityservice.go (+11/-5) testservices/identityservice/legacy.go (+17/-9) testservices/identityservice/legacy_test.go (+3/-2) testservices/identityservice/service_test.go (+3/-3) testservices/identityservice/userpass.go (+18/-17) testservices/identityservice/userpass_test.go (+17/-22) testservices/identityservice/users.go (+63/-0) testservices/identityservice/util.go (+4/-2) testservices/novaservice/service.go (+46/-24) testservices/novaservice/service_http.go (+16/-9) testservices/novaservice/service_http_test.go (+343/-334) testservices/novaservice/service_test.go (+16/-17) testservices/openstack/openstack.go (+38/-0) testservices/service.go (+21/-0) testservices/swiftservice/service.go (+46/-8) testservices/swiftservice/service_http.go (+13/-7) testservices/swiftservice/service_http_test.go (+14/-9) testservices/swiftservice/service_test.go (+8/-7) testservices/swiftservice/swiftservice.go (+0/-40) tools/secgroup-delete-all/main_test.go (+13/-29) |
To merge this branch: | bzr merge lp:~wallyworld/goose/service-double-improvements |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
The Go Language Gophers | Pending | ||
Review via email: mp+144435@code.launchpad.net |
Commit message
Description of the change
Goose test infrastructure improvements
This branch improves the usabilty of the goose test infrastructure.
A full Openstack service test double is provided. A bunch of manual coding which was required in each test suite to set up various Openstack module test doubles is replaced by a few lines of code.
New code:
cred := &identity.
openstack := openstack.New(cred)
openstack.
Old code:
// Create the identity service.
s.identityDouble = identityservice
token := s.identityDoubl
s.Mux.
// Register Swift endpoints with identity service.
ep := identityservice
AdminURL: s.Server.URL + baseSwiftURL,
InternalURL: s.Server.URL + baseSwiftURL,
PublicURL: s.Server.URL + baseSwiftURL,
Region: s.cred.Region,
}
service := identityservice
s.identityDoub
s.swiftDouble = swiftservice.
s.Mux.
// Register Nova endpoints with identity service.
ep = identityservice
AdminURL: s.Server.URL + baseNovaURL,
InternalURL: s.Server.URL + baseNovaURL,
PublicURL: s.Server.URL + baseNovaURL,
Region: s.cred.Region,
}
service = identityservice
s.identityDoub
s.novaDouble = novaservice.
s.novaDouble.
Other changes include:
- fix the identity service double to remove the hard coded userId and tenantId.
- do not hard code a fixed token against a service double - each user is assigned their own token just like a real instance, allowing multi-user tests to be written.
- improvements to the Swift service double to make it use URLs which are compliant with how a real Swift instance would do it.
- factor out a common base class for the legacy and userpass double implementations.
- use a SetupHTTP() for all test doubles instead of requiring the coder to know the magic URL paths to use.
Ian Booth (wallyworld) wrote : | # |
Dimiter Naydenov (dimitern) wrote : | # |
LGTM overall, with a couple of comments.
https:/
File client/
https:/
client/
authentication.
As on the other CL, think what happens if OS_TENANT_NAME is not set -
maybe pass it as an argument here?
https:/
File testservices/
https:/
testservices/
a simple request without a body and expected response.
Why a type, when it's only used here?
Ian Booth (wallyworld) wrote : | # |
https:/
File client/
https:/
client/
authentication.
On 2013/01/23 12:28:08, dimitern wrote:
> As on the other CL, think what happens if OS_TENANT_NAME is not set -
maybe pass
> it as an argument here?
The tenant was hard wired, so no issue. However, I've tweaked it so that
the openstack service double is constructed using a credentials object
and the tenant to use comes from there.
https:/
File testservices/
https:/
testservices/
a simple request without a body and expected response.
On 2013/01/23 12:28:08, dimitern wrote:
> Why a type, when it's only used here?
The statically defined array was replaced with a function to allow the
token to be dynamically set in the struct values, since the token is no
longer hard wired. So the function return type and array declaration
inside the function needed to refer to the struct, and so using a type
became necessary.
Ian Booth (wallyworld) wrote : | # |
*** Submitted:
Goose test infrastructure improvements
This branch improves the usabilty of the goose test infrastructure.
A full Openstack service test double is provided. A bunch of manual
coding which was required in each test suite to set up various Openstack
module test doubles is replaced by a few lines of code.
New code:
cred := &identity.
openstack := openstack.New(cred)
openstack.
Old code:
// Create the identity service.
s.identityDouble = identityservice
token := s.identityDoubl
s.Mux.
// Register Swift endpoints with identity service.
ep := identityservice
AdminURL: s.Server.URL + baseSwiftURL,
InternalURL: s.Server.URL + baseSwiftURL,
PublicURL: s.Server.URL + baseSwiftURL,
Region: s.cred.Region,
}
service := identityservice
[]identityservi
s.identityDoub
s.swiftDouble = swiftservice.
s.Mux.
// Register Nova endpoints with identity service.
ep = identityservice
AdminURL: s.Server.URL + baseNovaURL,
InternalURL: s.Server.URL + baseNovaURL,
PublicURL: s.Server.URL + baseNovaURL,
Region: s.cred.Region,
}
service = identityservice
[]identityservi
s.identityDoub
s.novaDouble = novaservice.
s.novaDouble.
Other changes include:
- fix the identity service double to remove the hard coded userId and
tenantId.
- do not hard code a fixed token against a service double - each user is
assigned their own token just like a real instance, allowing multi-user
tests to be written.
- improvements to the Swift service double to make it use URLs which are
compliant with how a real Swift instance would do it.
- factor out a common base class for the legacy and userpass double
implementations.
- use a SetupHTTP() for all test doubles instead of requiring the coder
to know the magic URL paths to use.
R=dimitern
CC=
https:/
Preview Diff
1 | === modified file 'client/local_test.go' |
2 | --- client/local_test.go 2013-01-14 13:01:04 +0000 |
3 | +++ client/local_test.go 2013-01-24 03:17:22 +0000 |
4 | @@ -4,8 +4,9 @@ |
5 | . "launchpad.net/gocheck" |
6 | "launchpad.net/goose/identity" |
7 | "launchpad.net/goose/testing/httpsuite" |
8 | + "launchpad.net/goose/testservices" |
9 | "launchpad.net/goose/testservices/identityservice" |
10 | - "net/http" |
11 | + "launchpad.net/goose/testservices/openstack" |
12 | ) |
13 | |
14 | func registerLocalTests(authMethods []identity.AuthMethod) { |
15 | @@ -24,38 +25,30 @@ |
16 | LiveTests |
17 | // The following attributes are for using testing doubles. |
18 | httpsuite.HTTPSuite |
19 | - identityDouble http.Handler |
20 | + service testservices.HttpService |
21 | } |
22 | |
23 | func (s *localLiveSuite) SetUpSuite(c *C) { |
24 | c.Logf("Using identity service test double") |
25 | s.HTTPSuite.SetUpSuite(c) |
26 | s.cred = &identity.Credentials{ |
27 | - URL: s.Server.URL, |
28 | - User: "fred", |
29 | - Secrets: "secret", |
30 | - Region: "some region"} |
31 | + URL: s.Server.URL, |
32 | + User: "fred", |
33 | + Secrets: "secret", |
34 | + Region: "some region", |
35 | + TenantName: "tenant", |
36 | + } |
37 | switch s.authMethod { |
38 | default: |
39 | panic("Invalid authentication method") |
40 | case identity.AuthUserPass: |
41 | - s.identityDouble = identityservice.NewUserPass() |
42 | - s.identityDouble.(*identityservice.UserPass).AddUser(s.cred.User, s.cred.Secrets) |
43 | - ep := identityservice.Endpoint{ |
44 | - AdminURL: s.Server.URL, |
45 | - InternalURL: s.Server.URL, |
46 | - PublicURL: s.Server.URL, |
47 | - Region: s.LiveTests.cred.Region, |
48 | - } |
49 | - service := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ep}} |
50 | - s.identityDouble.(*identityservice.UserPass).AddService(service) |
51 | - service = identityservice.Service{"swift", "object-store", []identityservice.Endpoint{ep}} |
52 | - s.identityDouble.(*identityservice.UserPass).AddService(service) |
53 | + // The openstack test service sets up userpass authentication. |
54 | + s.service = openstack.New(s.cred) |
55 | case identity.AuthLegacy: |
56 | - s.identityDouble = identityservice.NewLegacy() |
57 | - var legacy = s.identityDouble.(*identityservice.Legacy) |
58 | - legacy.AddUser(s.cred.User, s.cred.Secrets) |
59 | + legacy := identityservice.NewLegacy() |
60 | + legacy.AddUser(s.cred.User, s.cred.Secrets, s.cred.TenantName) |
61 | legacy.SetManagementURL("http://management.test.invalid/url") |
62 | + s.service = legacy |
63 | } |
64 | s.LiveTests.SetUpSuite(c) |
65 | } |
66 | @@ -67,7 +60,7 @@ |
67 | |
68 | func (s *localLiveSuite) SetUpTest(c *C) { |
69 | s.HTTPSuite.SetUpTest(c) |
70 | - s.Mux.Handle("/", s.identityDouble) |
71 | + s.service.SetupHTTP(s.Mux) |
72 | s.LiveTests.SetUpTest(c) |
73 | } |
74 | |
75 | |
76 | === modified file 'identity/legacy_test.go' |
77 | --- identity/legacy_test.go 2013-01-14 03:18:37 +0000 |
78 | +++ identity/legacy_test.go 2013-01-24 03:17:22 +0000 |
79 | @@ -15,13 +15,13 @@ |
80 | func (s *LegacyTestSuite) TestAuthAgainstServer(c *C) { |
81 | service := identityservice.NewLegacy() |
82 | s.Mux.Handle("/", service) |
83 | - token := service.AddUser("joe-user", "secrets") |
84 | + userInfo := service.AddUser("joe-user", "secrets", "tenant") |
85 | service.SetManagementURL("http://management.test.invalid/url") |
86 | var l Authenticator = &Legacy{} |
87 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "secrets"} |
88 | auth, err := l.Auth(&creds) |
89 | c.Assert(err, IsNil) |
90 | - c.Assert(auth.Token, Equals, token) |
91 | + c.Assert(auth.Token, Equals, userInfo.Token) |
92 | c.Assert( |
93 | auth.ServiceURLs, DeepEquals, |
94 | map[string]string{"compute": "http://management.test.invalid/url/compute", "object-store": "http://management.test.invalid/url/object-store"}) |
95 | @@ -30,7 +30,7 @@ |
96 | func (s *LegacyTestSuite) TestBadAuth(c *C) { |
97 | service := identityservice.NewLegacy() |
98 | s.Mux.Handle("/", service) |
99 | - _ = service.AddUser("joe-user", "secrets") |
100 | + _ = service.AddUser("joe-user", "secrets", "tenant") |
101 | var l Authenticator = &Legacy{} |
102 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "bad-secrets"} |
103 | auth, err := l.Auth(&creds) |
104 | |
105 | === modified file 'identity/userpass_test.go' |
106 | --- identity/userpass_test.go 2013-01-14 03:18:37 +0000 |
107 | +++ identity/userpass_test.go 2013-01-24 03:17:22 +0000 |
108 | @@ -14,12 +14,12 @@ |
109 | |
110 | func (s *UserPassTestSuite) TestAuthAgainstServer(c *C) { |
111 | service := identityservice.NewUserPass() |
112 | - s.Mux.Handle("/", service) |
113 | - token := service.AddUser("joe-user", "secrets") |
114 | + service.SetupHTTP(s.Mux) |
115 | + userInfo := service.AddUser("joe-user", "secrets", "tenant") |
116 | var l Authenticator = &UserPass{} |
117 | - creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "secrets"} |
118 | + creds := Credentials{User: "joe-user", URL: s.Server.URL + "/tokens", Secrets: "secrets"} |
119 | auth, err := l.Auth(&creds) |
120 | c.Assert(err, IsNil) |
121 | - c.Assert(auth.Token, Equals, token) |
122 | - // c.Assert(auth.ServiceURLs, DeepEquals, map[string]string{"compute": "http://management.test.invalid/url"}) |
123 | + c.Assert(auth.Token, Equals, userInfo.Token) |
124 | + c.Assert(auth.TenantId, Equals, userInfo.TenantId) |
125 | } |
126 | |
127 | === modified file 'nova/local_test.go' |
128 | --- nova/local_test.go 2013-01-21 11:18:33 +0000 |
129 | +++ nova/local_test.go 2013-01-24 03:17:22 +0000 |
130 | @@ -7,8 +7,7 @@ |
131 | "launchpad.net/goose/errors" |
132 | "launchpad.net/goose/identity" |
133 | "launchpad.net/goose/nova" |
134 | - "launchpad.net/goose/testservices/identityservice" |
135 | - "launchpad.net/goose/testservices/novaservice" |
136 | + "launchpad.net/goose/testservices/openstack" |
137 | "log" |
138 | "net/http" |
139 | "net/http/httptest" |
140 | @@ -19,20 +18,14 @@ |
141 | Suite(&localLiveSuite{}) |
142 | } |
143 | |
144 | -const ( |
145 | - baseNovaURL = "/V1/1" |
146 | -) |
147 | - |
148 | // localLiveSuite runs tests from LiveTests using a fake |
149 | // nova server that runs within the test process itself. |
150 | type localLiveSuite struct { |
151 | LiveTests |
152 | // The following attributes are for using testing doubles. |
153 | - Server *httptest.Server |
154 | - Mux *http.ServeMux |
155 | - oldHandler http.Handler |
156 | - identityDouble *identityservice.UserPass |
157 | - novaDouble *novaservice.Nova |
158 | + Server *httptest.Server |
159 | + Mux *http.ServeMux |
160 | + oldHandler http.Handler |
161 | } |
162 | |
163 | func (s *localLiveSuite) SetUpSuite(c *C) { |
164 | @@ -44,29 +37,16 @@ |
165 | s.Mux = http.NewServeMux() |
166 | s.Server.Config.Handler = s.Mux |
167 | |
168 | + // Set up an Openstack service. |
169 | s.cred = &identity.Credentials{ |
170 | - URL: s.Server.URL, |
171 | - User: "fred", |
172 | - Secrets: "secret", |
173 | - Region: "some region"} |
174 | - // Create an identity service and register a Nova endpoint. |
175 | - s.identityDouble = identityservice.NewUserPass() |
176 | - token := s.identityDouble.AddUser(s.cred.User, s.cred.Secrets) |
177 | - ep := identityservice.Endpoint{ |
178 | - AdminURL: s.Server.URL + baseNovaURL, |
179 | - InternalURL: s.Server.URL + baseNovaURL, |
180 | - PublicURL: s.Server.URL + baseNovaURL, |
181 | - Region: s.cred.Region, |
182 | + URL: s.Server.URL, |
183 | + User: "fred", |
184 | + Secrets: "secret", |
185 | + Region: "some region", |
186 | + TenantName: "tenant", |
187 | } |
188 | - s.Mux.Handle("/tokens", s.identityDouble) |
189 | - |
190 | - service := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ep}} |
191 | - s.identityDouble.AddService(service) |
192 | - // Create a nova service at the registered endpoint. |
193 | - // TODO: identityservice.UserPass always uses tenantId="1", patch this |
194 | - // when that changes. |
195 | - s.novaDouble = novaservice.New("localhost", "V1", token, "1") |
196 | - s.novaDouble.SetupHTTP(s.Mux) |
197 | + openstack := openstack.New(s.cred) |
198 | + openstack.SetupHTTP(s.Mux) |
199 | |
200 | s.LiveTests.SetUpSuite(c) |
201 | } |
202 | |
203 | === modified file 'swift/local_test.go' |
204 | --- swift/local_test.go 2012-12-20 05:47:11 +0000 |
205 | +++ swift/local_test.go 2013-01-24 03:17:22 +0000 |
206 | @@ -4,19 +4,13 @@ |
207 | . "launchpad.net/gocheck" |
208 | "launchpad.net/goose/identity" |
209 | "launchpad.net/goose/testing/httpsuite" |
210 | - "launchpad.net/goose/testservices/identityservice" |
211 | - "launchpad.net/goose/testservices/swiftservice" |
212 | - "net/http" |
213 | + "launchpad.net/goose/testservices/openstack" |
214 | ) |
215 | |
216 | func registerLocalTests() { |
217 | Suite(&localLiveSuite{}) |
218 | } |
219 | |
220 | -const ( |
221 | - baseURL = "/object-store" |
222 | -) |
223 | - |
224 | // localLiveSuite runs tests from LiveTests using a fake |
225 | // swift server that runs within the test process itself. |
226 | type localLiveSuite struct { |
227 | @@ -24,32 +18,23 @@ |
228 | LiveTestsPublicContainer |
229 | // The following attributes are for using testing doubles. |
230 | httpsuite.HTTPSuite |
231 | - identityDouble http.Handler |
232 | - swiftDouble http.Handler |
233 | + openstack *openstack.Openstack |
234 | } |
235 | |
236 | func (s *localLiveSuite) SetUpSuite(c *C) { |
237 | c.Logf("Using identity and swift service test doubles") |
238 | s.HTTPSuite.SetUpSuite(c) |
239 | + // Set up an Openstack service. |
240 | s.LiveTests.cred = &identity.Credentials{ |
241 | - URL: s.Server.URL, |
242 | - User: "fred", |
243 | - Secrets: "secret", |
244 | - Region: "some region"} |
245 | + URL: s.Server.URL, |
246 | + User: "fred", |
247 | + Secrets: "secret", |
248 | + Region: "some region", |
249 | + TenantName: "tenant", |
250 | + } |
251 | s.LiveTestsPublicContainer.cred = s.LiveTests.cred |
252 | - // Create an identity service and register a Swift endpoint. |
253 | - s.identityDouble = identityservice.NewUserPass() |
254 | - token := s.identityDouble.(*identityservice.UserPass).AddUser(s.LiveTests.cred.User, s.LiveTests.cred.Secrets) |
255 | - ep := identityservice.Endpoint{ |
256 | - s.Server.URL + baseURL, //admin |
257 | - s.Server.URL + baseURL, //internal |
258 | - s.Server.URL + baseURL, //public |
259 | - s.LiveTests.cred.Region, |
260 | - } |
261 | - service := identityservice.Service{"swift", "object-store", []identityservice.Endpoint{ep}} |
262 | - s.identityDouble.(*identityservice.UserPass).AddService(service) |
263 | - // Create a swift service at the registered endpoint. |
264 | - s.swiftDouble = swiftservice.New("localhost", baseURL+"/", token) |
265 | + s.openstack = openstack.New(s.LiveTests.cred) |
266 | + |
267 | s.LiveTests.SetUpSuite(c) |
268 | s.LiveTestsPublicContainer.SetUpSuite(c) |
269 | } |
270 | @@ -62,8 +47,7 @@ |
271 | |
272 | func (s *localLiveSuite) SetUpTest(c *C) { |
273 | s.HTTPSuite.SetUpTest(c) |
274 | - s.Mux.Handle(baseURL+"/", s.swiftDouble) |
275 | - s.Mux.Handle("/", s.identityDouble) |
276 | + s.openstack.SetupHTTP(s.Mux) |
277 | s.LiveTests.SetUpTest(c) |
278 | s.LiveTestsPublicContainer.SetUpTest(c) |
279 | } |
280 | |
281 | === added directory 'testservices/cmd' |
282 | === renamed file 'testservices/main.go' => 'testservices/cmd/main.go' |
283 | --- testservices/main.go 2012-11-11 11:13:52 +0000 |
284 | +++ testservices/cmd/main.go 2013-01-24 03:17:22 +0000 |
285 | @@ -61,9 +61,10 @@ |
286 | if !ok { |
287 | log.Fatalf("No such provider: %s, pick one of: %v", provider, providers()) |
288 | } |
289 | - http.Handle("/", p) |
290 | + mux := http.NewServeMux() |
291 | + p.SetupHTTP(mux) |
292 | for _, u := range users.users { |
293 | - p.AddUser(u.user, u.secret) |
294 | + p.AddUser(u.user, u.secret, "tenant") |
295 | } |
296 | - log.Fatal(http.ListenAndServe(*serveAddr, nil)) |
297 | + log.Fatal(http.ListenAndServe(*serveAddr, mux)) |
298 | } |
299 | |
300 | === modified file 'testservices/identityservice/identityservice.go' |
301 | --- testservices/identityservice/identityservice.go 2012-11-11 11:13:52 +0000 |
302 | +++ testservices/identityservice/identityservice.go 2013-01-24 03:17:22 +0000 |
303 | @@ -1,10 +1,16 @@ |
304 | package identityservice |
305 | |
306 | -import ( |
307 | - "net/http" |
308 | -) |
309 | +import "net/http" |
310 | |
311 | +// An IdentityService provides user authentication for an Openstack instance. |
312 | type IdentityService interface { |
313 | - AddUser(user, secret string) (token string) |
314 | - ServeHTTP(w http.ResponseWriter, r *http.Request) |
315 | + AddUser(user, secret, tenant string) *UserInfo |
316 | + FindUser(token string) (*UserInfo, error) |
317 | + RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) |
318 | + SetupHTTP(mux *http.ServeMux) |
319 | +} |
320 | + |
321 | +// A ServiceProvider is an Openstack module which has service endpoints. |
322 | +type ServiceProvider interface { |
323 | + Endpoints() []Endpoint |
324 | } |
325 | |
326 | === modified file 'testservices/identityservice/legacy.go' |
327 | --- testservices/identityservice/legacy.go 2012-12-21 05:07:39 +0000 |
328 | +++ testservices/identityservice/legacy.go 2013-01-24 03:17:22 +0000 |
329 | @@ -5,40 +5,48 @@ |
330 | ) |
331 | |
332 | type Legacy struct { |
333 | - tokens map[string]UserInfo |
334 | + Users |
335 | managementURL string |
336 | } |
337 | |
338 | func NewLegacy() *Legacy { |
339 | service := &Legacy{} |
340 | - service.tokens = make(map[string]UserInfo) |
341 | + service.users = make(map[string]UserInfo) |
342 | + service.tenants = make(map[string]string) |
343 | return service |
344 | } |
345 | |
346 | +func (lis *Legacy) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { |
347 | + // NOOP for legacy identity service. |
348 | +} |
349 | + |
350 | func (lis *Legacy) SetManagementURL(URL string) { |
351 | lis.managementURL = URL |
352 | } |
353 | |
354 | -func (lis *Legacy) AddUser(user, secret string) string { |
355 | - token := randomHexToken() |
356 | - lis.tokens[user] = UserInfo{secret: secret, token: token} |
357 | - return token |
358 | +// setupHTTP attaches all the needed handlers to provide the HTTP API. |
359 | +func (lis *Legacy) SetupHTTP(mux *http.ServeMux) { |
360 | + mux.Handle("/", lis) |
361 | } |
362 | |
363 | func (lis *Legacy) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
364 | username := r.Header.Get("X-Auth-User") |
365 | - info, ok := lis.tokens[username] |
366 | + userInfo, ok := lis.users[username] |
367 | if !ok { |
368 | w.WriteHeader(http.StatusUnauthorized) |
369 | return |
370 | } |
371 | auth_key := r.Header.Get("X-Auth-Key") |
372 | - if auth_key != info.secret { |
373 | + if auth_key != userInfo.secret { |
374 | w.WriteHeader(http.StatusUnauthorized) |
375 | return |
376 | } |
377 | + if userInfo.Token == "" { |
378 | + userInfo.Token = randomHexToken() |
379 | + lis.users[username] = userInfo |
380 | + } |
381 | header := w.Header() |
382 | - header.Set("X-Auth-Token", info.token) |
383 | + header.Set("X-Auth-Token", userInfo.Token) |
384 | header.Set("X-Server-Management-Url", lis.managementURL+"/compute") |
385 | header.Set("X-Storage-Url", lis.managementURL+"/object-store") |
386 | w.WriteHeader(http.StatusNoContent) |
387 | |
388 | === modified file 'testservices/identityservice/legacy_test.go' |
389 | --- testservices/identityservice/legacy_test.go 2012-12-21 05:07:39 +0000 |
390 | +++ testservices/identityservice/legacy_test.go 2013-01-24 03:17:22 +0000 |
391 | @@ -19,9 +19,10 @@ |
392 | // Ensure that it conforms to the interface |
393 | var _ IdentityService = identity |
394 | identity.SetManagementURL(managementURL) |
395 | - s.Mux.Handle("/", identity) |
396 | + identity.SetupHTTP(s.Mux) |
397 | if user != "" { |
398 | - token = identity.AddUser(user, secret) |
399 | + userInfo := identity.AddUser(user, secret, "tenant") |
400 | + token = userInfo.Token |
401 | } |
402 | return |
403 | } |
404 | |
405 | === modified file 'testservices/identityservice/service_test.go' |
406 | --- testservices/identityservice/service_test.go 2012-11-11 15:29:52 +0000 |
407 | +++ testservices/identityservice/service_test.go 2013-01-24 03:17:22 +0000 |
408 | @@ -17,7 +17,7 @@ |
409 | var _ = Suite(&IdentityServiceSuite{service: NewLegacy()}) |
410 | |
411 | func (s *IdentityServiceSuite) TestAddUserGivesNewToken(c *C) { |
412 | - token1 := s.service.AddUser("user-1", "password-1") |
413 | - token2 := s.service.AddUser("user-2", "password-2") |
414 | - c.Assert(token1, Not(Equals), token2) |
415 | + userInfo1 := s.service.AddUser("user-1", "password-1", "tenant") |
416 | + userInfo2 := s.service.AddUser("user-2", "password-2", "tenant") |
417 | + c.Assert(userInfo1.Token, Not(Equals), userInfo2.Token) |
418 | } |
419 | |
420 | === modified file 'testservices/identityservice/userpass.go' |
421 | --- testservices/identityservice/userpass.go 2012-12-21 04:10:14 +0000 |
422 | +++ testservices/identityservice/userpass.go 2013-01-24 03:17:22 +0000 |
423 | @@ -135,25 +135,25 @@ |
424 | }` |
425 | |
426 | type UserPass struct { |
427 | - users map[string]UserInfo |
428 | + Users |
429 | services []Service |
430 | } |
431 | |
432 | func NewUserPass() *UserPass { |
433 | userpass := &UserPass{ |
434 | - users: make(map[string]UserInfo), |
435 | services: make([]Service, 0), |
436 | } |
437 | + userpass.users = make(map[string]UserInfo) |
438 | + userpass.tenants = make(map[string]string) |
439 | return userpass |
440 | } |
441 | |
442 | -func (u *UserPass) AddUser(user, secret string) string { |
443 | - token := randomHexToken() |
444 | - u.users[user] = UserInfo{secret: secret, token: token} |
445 | - return token |
446 | +func (u *UserPass) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { |
447 | + service := Service{name, serviceType, serviceProvider.Endpoints()} |
448 | + u.addService(service) |
449 | } |
450 | |
451 | -func (u *UserPass) AddService(service Service) { |
452 | +func (u *UserPass) addService(service Service) { |
453 | u.services = append(u.services, service) |
454 | } |
455 | |
456 | @@ -189,8 +189,6 @@ |
457 | notJSON = ("Expecting to find application/json in Content-Type header." + |
458 | " The server could not comply with the request since it is either malformed" + |
459 | " or otherwise incorrect. The client is assumed to be in error.") |
460 | - notAuthorized = "The request you have made requires authentication." |
461 | - invalidUser = "Invalid user / password" |
462 | ) |
463 | |
464 | func (u *UserPass) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
465 | @@ -210,13 +208,9 @@ |
466 | return |
467 | } |
468 | } |
469 | - userInfo, ok := u.users[req.Auth.PasswordCredentials.Username] |
470 | - if !ok { |
471 | - u.ReturnFailure(w, http.StatusUnauthorized, notAuthorized) |
472 | - return |
473 | - } |
474 | - if userInfo.secret != req.Auth.PasswordCredentials.Password { |
475 | - u.ReturnFailure(w, http.StatusUnauthorized, invalidUser) |
476 | + userInfo, errmsg := u.authenticate(req.Auth.PasswordCredentials.Username, req.Auth.PasswordCredentials.Password) |
477 | + if errmsg != "" { |
478 | + u.ReturnFailure(w, http.StatusUnauthorized, errmsg) |
479 | return |
480 | } |
481 | res := AccessResponse{} |
482 | @@ -228,7 +222,9 @@ |
483 | return |
484 | } |
485 | res.Access.ServiceCatalog = u.services |
486 | - res.Access.Token.Id = userInfo.token |
487 | + res.Access.Token.Id = userInfo.Token |
488 | + res.Access.Token.Tenant.Id = userInfo.TenantId |
489 | + res.Access.User.Id = userInfo.Id |
490 | if content, err := json.Marshal(res); err != nil { |
491 | u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) |
492 | return |
493 | @@ -239,3 +235,8 @@ |
494 | } |
495 | panic("All paths should have already returned") |
496 | } |
497 | + |
498 | +// setupHTTP attaches all the needed handlers to provide the HTTP API. |
499 | +func (u *UserPass) SetupHTTP(mux *http.ServeMux) { |
500 | + mux.Handle("/tokens", u) |
501 | +} |
502 | |
503 | === modified file 'testservices/identityservice/userpass_test.go' |
504 | --- testservices/identityservice/userpass_test.go 2012-11-16 16:39:16 +0000 |
505 | +++ testservices/identityservice/userpass_test.go 2013-01-24 03:17:22 +0000 |
506 | @@ -16,30 +16,30 @@ |
507 | |
508 | var _ = Suite(&UserPassSuite{}) |
509 | |
510 | -func makeUserPass(user, secret string) (identity *UserPass, token string) { |
511 | +func makeUserPass(user, secret string) (identity *UserPass) { |
512 | identity = NewUserPass() |
513 | // Ensure that it conforms to the interface |
514 | var _ IdentityService = identity |
515 | if user != "" { |
516 | - token = identity.AddUser(user, secret) |
517 | + identity.AddUser(user, secret, "tenant") |
518 | } |
519 | return |
520 | } |
521 | |
522 | -func (s *UserPassSuite) setupUserPass(user, secret string) (token string) { |
523 | +func (s *UserPassSuite) setupUserPass(user, secret string) { |
524 | var identity *UserPass |
525 | - identity, token = makeUserPass(user, secret) |
526 | - s.Mux.Handle("/", identity) |
527 | + identity = makeUserPass(user, secret) |
528 | + identity.SetupHTTP(s.Mux) |
529 | return |
530 | } |
531 | |
532 | -func (s *UserPassSuite) setupUserPassWithServices(user, secret string, services []Service) (token string) { |
533 | +func (s *UserPassSuite) setupUserPassWithServices(user, secret string, services []Service) { |
534 | var identity *UserPass |
535 | - identity, token = makeUserPass(user, secret) |
536 | + identity = makeUserPass(user, secret) |
537 | for _, service := range services { |
538 | - identity.AddService(service) |
539 | + identity.addService(service) |
540 | } |
541 | - s.Mux.Handle("/", identity) |
542 | + identity.SetupHTTP(s.Mux) |
543 | return |
544 | } |
545 | |
546 | @@ -56,7 +56,7 @@ |
547 | func userPassAuthRequest(URL, user, key string) (*http.Response, error) { |
548 | client := &http.Client{} |
549 | body := strings.NewReader(fmt.Sprintf(authTemplate, user, key)) |
550 | - request, err := http.NewRequest("POST", URL, body) |
551 | + request, err := http.NewRequest("POST", URL+"/tokens", body) |
552 | request.Header.Set("Content-Type", "application/json") |
553 | if err != nil { |
554 | return nil, err |
555 | @@ -81,11 +81,10 @@ |
556 | |
557 | func (s *UserPassSuite) TestNotJSON(c *C) { |
558 | // We do everything in userPassAuthRequest, except set the Content-Type |
559 | - token := s.setupUserPass("user", "secret") |
560 | - c.Assert(token, NotNil) |
561 | + s.setupUserPass("user", "secret") |
562 | client := &http.Client{} |
563 | body := strings.NewReader(fmt.Sprintf(authTemplate, "user", "secret")) |
564 | - request, err := http.NewRequest("POST", s.Server.URL, body) |
565 | + request, err := http.NewRequest("POST", s.Server.URL+"/tokens", body) |
566 | c.Assert(err, IsNil) |
567 | res, err := client.Do(request) |
568 | defer res.Body.Close() |
569 | @@ -95,8 +94,7 @@ |
570 | |
571 | func (s *UserPassSuite) TestBadJSON(c *C) { |
572 | // We do everything in userPassAuthRequest, except set the Content-Type |
573 | - token := s.setupUserPass("user", "secret") |
574 | - c.Assert(token, NotNil) |
575 | + s.setupUserPass("user", "secret") |
576 | res, err := userPassAuthRequest(s.Server.URL, "garbage\"in", "secret") |
577 | defer res.Body.Close() |
578 | c.Assert(err, IsNil) |
579 | @@ -104,8 +102,7 @@ |
580 | } |
581 | |
582 | func (s *UserPassSuite) TestNoSuchUser(c *C) { |
583 | - token := s.setupUserPass("user", "secret") |
584 | - c.Assert(token, NotNil) |
585 | + s.setupUserPass("user", "secret") |
586 | res, err := userPassAuthRequest(s.Server.URL, "not-user", "secret") |
587 | defer res.Body.Close() |
588 | c.Assert(err, IsNil) |
589 | @@ -113,8 +110,7 @@ |
590 | } |
591 | |
592 | func (s *UserPassSuite) TestBadPassword(c *C) { |
593 | - token := s.setupUserPass("user", "secret") |
594 | - c.Assert(token, NotNil) |
595 | + s.setupUserPass("user", "secret") |
596 | res, err := userPassAuthRequest(s.Server.URL, "user", "not-secret") |
597 | defer res.Body.Close() |
598 | c.Assert(err, IsNil) |
599 | @@ -123,11 +119,10 @@ |
600 | |
601 | func (s *UserPassSuite) TestValidAuthorization(c *C) { |
602 | compute_url := "http://testing.invalid/compute" |
603 | - token := s.setupUserPassWithServices("user", "secret", []Service{ |
604 | + s.setupUserPassWithServices("user", "secret", []Service{ |
605 | {"nova", "compute", []Endpoint{ |
606 | {PublicURL: compute_url}, |
607 | }}}) |
608 | - c.Assert(token, NotNil) |
609 | res, err := userPassAuthRequest(s.Server.URL, "user", "secret") |
610 | defer res.Body.Close() |
611 | c.Assert(err, IsNil) |
612 | @@ -138,7 +133,7 @@ |
613 | var response AccessResponse |
614 | err = json.Unmarshal(content, &response) |
615 | c.Assert(err, IsNil) |
616 | - c.Check(response.Access.Token.Id, Equals, token) |
617 | + c.Check(response.Access.Token.Id, NotNil) |
618 | novaURL := "" |
619 | for _, service := range response.Access.ServiceCatalog { |
620 | if service.Type == "compute" { |
621 | |
622 | === added file 'testservices/identityservice/users.go' |
623 | --- testservices/identityservice/users.go 1970-01-01 00:00:00 +0000 |
624 | +++ testservices/identityservice/users.go 2013-01-24 03:17:22 +0000 |
625 | @@ -0,0 +1,63 @@ |
626 | +package identityservice |
627 | + |
628 | +import ( |
629 | + "fmt" |
630 | + "strconv" |
631 | +) |
632 | + |
633 | +type Users struct { |
634 | + nextUserId int |
635 | + nextTenantId int |
636 | + users map[string]UserInfo |
637 | + tenants map[string]string |
638 | +} |
639 | + |
640 | +func (u *Users) addTenant(tenant string) string { |
641 | + for id, tenantName := range u.tenants { |
642 | + if tenant == tenantName { |
643 | + return id |
644 | + } |
645 | + } |
646 | + u.nextTenantId++ |
647 | + id := strconv.Itoa(u.nextTenantId) |
648 | + u.tenants[id] = tenant |
649 | + return id |
650 | +} |
651 | + |
652 | +func (u *Users) AddUser(user, secret, tenant string) *UserInfo { |
653 | + tenantId := u.addTenant(tenant) |
654 | + u.nextUserId++ |
655 | + userInfo := &UserInfo{secret: secret, Id: strconv.Itoa(u.nextUserId), TenantId: tenantId} |
656 | + u.users[user] = *userInfo |
657 | + userInfo, _ = u.authenticate(user, secret) |
658 | + return userInfo |
659 | +} |
660 | + |
661 | +func (u *Users) FindUser(token string) (*UserInfo, error) { |
662 | + for _, userInfo := range u.users { |
663 | + if userInfo.Token == token { |
664 | + return &userInfo, nil |
665 | + } |
666 | + } |
667 | + return nil, fmt.Errorf("No user with token %v exists", token) |
668 | +} |
669 | + |
670 | +const ( |
671 | + notAuthorized = "The request you have made requires authentication." |
672 | + invalidUser = "Invalid user / password" |
673 | +) |
674 | + |
675 | +func (u *Users) authenticate(username, password string) (*UserInfo, string) { |
676 | + userInfo, ok := u.users[username] |
677 | + if !ok { |
678 | + return nil, notAuthorized |
679 | + } |
680 | + if userInfo.secret != password { |
681 | + return nil, invalidUser |
682 | + } |
683 | + if userInfo.Token == "" { |
684 | + userInfo.Token = randomHexToken() |
685 | + u.users[username] = userInfo |
686 | + } |
687 | + return &userInfo, "" |
688 | +} |
689 | |
690 | === modified file 'testservices/identityservice/util.go' |
691 | --- testservices/identityservice/util.go 2012-11-23 03:05:16 +0000 |
692 | +++ testservices/identityservice/util.go 2013-01-24 03:17:22 +0000 |
693 | @@ -7,8 +7,10 @@ |
694 | ) |
695 | |
696 | type UserInfo struct { |
697 | - secret string |
698 | - token string |
699 | + Id string |
700 | + TenantId string |
701 | + Token string |
702 | + secret string |
703 | } |
704 | |
705 | // Generate a bit of random hex data for |
706 | |
707 | === modified file 'testservices/novaservice/service.go' |
708 | --- testservices/novaservice/service.go 2013-01-21 23:57:31 +0000 |
709 | +++ testservices/novaservice/service.go 2013-01-24 03:17:22 +0000 |
710 | @@ -5,12 +5,19 @@ |
711 | import ( |
712 | "fmt" |
713 | "launchpad.net/goose/nova" |
714 | + "launchpad.net/goose/testservices" |
715 | + "launchpad.net/goose/testservices/identityservice" |
716 | + "net/url" |
717 | "strings" |
718 | ) |
719 | |
720 | +var _ testservices.HttpService = (*Nova)(nil) |
721 | +var _ identityservice.ServiceProvider = (*Nova)(nil) |
722 | + |
723 | // Nova implements a OpenStack Nova testing service and |
724 | // contains the service double's internal state. |
725 | type Nova struct { |
726 | + testservices.ServiceInstance |
727 | flavors map[string]nova.FlavorDetail |
728 | servers map[string]nova.ServerDetail |
729 | groups map[int]nova.SecurityGroup |
730 | @@ -18,11 +25,6 @@ |
731 | floatingIPs map[int]nova.FloatingIP |
732 | serverGroups map[string][]int |
733 | serverIPs map[string][]int |
734 | - hostname string |
735 | - versionPath string |
736 | - token string |
737 | - tenantId string |
738 | - userId string |
739 | nextGroupId int |
740 | nextRuleId int |
741 | nextIPId int |
742 | @@ -31,17 +33,35 @@ |
743 | |
744 | // endpoint returns either a versioned or non-versioned service |
745 | // endpoint URL from the given path. |
746 | -func (n *Nova) endpoint(version bool, path string) string { |
747 | - ep := "http://" + n.hostname |
748 | +func (n *Nova) endpointURL(version bool, path string) string { |
749 | + ep := "http://" + n.Hostname |
750 | if version { |
751 | - ep += n.versionPath + "/" |
752 | - } |
753 | - ep += n.tenantId + "/" + strings.TrimLeft(path, "/") |
754 | + ep += n.VersionPath + "/" |
755 | + } |
756 | + ep += n.TenantId |
757 | + if path != "" { |
758 | + ep += "/" + strings.TrimLeft(path, "/") |
759 | + } |
760 | return ep |
761 | } |
762 | |
763 | +func (n *Nova) Endpoints() []identityservice.Endpoint { |
764 | + ep := identityservice.Endpoint{ |
765 | + AdminURL: n.endpointURL(true, ""), |
766 | + InternalURL: n.endpointURL(true, ""), |
767 | + PublicURL: n.endpointURL(true, ""), |
768 | + Region: n.Region, |
769 | + } |
770 | + return []identityservice.Endpoint{ep} |
771 | +} |
772 | + |
773 | // New creates an instance of the Nova object, given the parameters. |
774 | -func New(hostname, versionPath, token, tenantId string) *Nova { |
775 | +func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Nova { |
776 | + url, err := url.Parse(hostURL) |
777 | + if err != nil { |
778 | + panic(err) |
779 | + } |
780 | + hostname := url.Host |
781 | if !strings.HasSuffix(hostname, "/") { |
782 | hostname += "/" |
783 | } |
784 | @@ -62,17 +82,19 @@ |
785 | floatingIPs: make(map[int]nova.FloatingIP), |
786 | serverGroups: make(map[string][]int), |
787 | serverIPs: make(map[string][]int), |
788 | - hostname: hostname, |
789 | - versionPath: versionPath, |
790 | - token: token, |
791 | - tenantId: tenantId, |
792 | - // TODO(wallyworld): Identity service double currently hard codes all user ids to "14". This should be fixed |
793 | - // in the identity service but the fix will result in an API change which will break juju-core. So for now |
794 | - // we will also hard code it here too. |
795 | - userId: "14", |
796 | // The following attribute controls whether rate limit responses are sent back to the caller. |
797 | // This is switched off when we want to ensure the client eventually gets a proper response. |
798 | sendFakeRateLimitResponse: true, |
799 | + ServiceInstance: testservices.ServiceInstance{ |
800 | + IdentityService: identityService, |
801 | + Hostname: hostname, |
802 | + VersionPath: versionPath, |
803 | + TenantId: tenantId, |
804 | + Region: region, |
805 | + }, |
806 | + } |
807 | + if identityService != nil { |
808 | + identityService.RegisterServiceProvider("nova", "compute", nova) |
809 | } |
810 | for i, flavor := range defaultFlavors { |
811 | nova.buildFlavorLinks(&flavor) |
812 | @@ -97,8 +119,8 @@ |
813 | func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) { |
814 | url := "/flavors/" + flavor.Id |
815 | flavor.Links = []nova.Link{ |
816 | - nova.Link{Href: n.endpoint(true, url), Rel: "self"}, |
817 | - nova.Link{Href: n.endpoint(false, url), Rel: "bookmark"}, |
818 | + nova.Link{Href: n.endpointURL(true, url), Rel: "self"}, |
819 | + nova.Link{Href: n.endpointURL(false, url), Rel: "bookmark"}, |
820 | } |
821 | } |
822 | |
823 | @@ -170,8 +192,8 @@ |
824 | func (n *Nova) buildServerLinks(server *nova.ServerDetail) { |
825 | url := "/servers/" + server.Id |
826 | server.Links = []nova.Link{ |
827 | - nova.Link{Href: n.endpoint(true, url), Rel: "self"}, |
828 | - nova.Link{Href: n.endpoint(false, url), Rel: "bookmark"}, |
829 | + nova.Link{Href: n.endpointURL(true, url), Rel: "self"}, |
830 | + nova.Link{Href: n.endpointURL(false, url), Rel: "bookmark"}, |
831 | } |
832 | } |
833 | |
834 | @@ -280,7 +302,7 @@ |
835 | if _, err := n.securityGroup(group.Id); err == nil { |
836 | return fmt.Errorf("a security group with id %d already exists", group.Id) |
837 | } |
838 | - group.TenantId = n.tenantId |
839 | + group.TenantId = n.TenantId |
840 | if group.Rules == nil { |
841 | group.Rules = []nova.SecurityGroupRule{} |
842 | } |
843 | |
844 | === modified file 'testservices/novaservice/service_http.go' |
845 | --- testservices/novaservice/service_http.go 2013-01-22 18:14:51 +0000 |
846 | +++ testservices/novaservice/service_http.go 2013-01-24 03:17:22 +0000 |
847 | @@ -9,6 +9,7 @@ |
848 | "io" |
849 | "io/ioutil" |
850 | "launchpad.net/goose/nova" |
851 | + "launchpad.net/goose/testservices/identityservice" |
852 | "net/http" |
853 | "path" |
854 | "strconv" |
855 | @@ -228,7 +229,7 @@ |
856 | body := e.body |
857 | if body != "" { |
858 | if e.nova != nil { |
859 | - body = strings.Replace(body, "$ENDPOINT$", e.nova.endpoint(true, "/"), -1) |
860 | + body = strings.Replace(body, "$ENDPOINT$", e.nova.endpointURL(true, "/"), -1) |
861 | } |
862 | body = strings.Replace(body, "$URL$", url, -1) |
863 | body = strings.Replace(body, "$ERROR$", e.Error(), -1) |
864 | @@ -264,10 +265,15 @@ |
865 | method func(n *Nova, w http.ResponseWriter, r *http.Request) error |
866 | } |
867 | |
868 | +func userInfo(i identityservice.IdentityService, r *http.Request) (*identityservice.UserInfo, error) { |
869 | + return i.FindUser(r.Header.Get(authToken)) |
870 | +} |
871 | + |
872 | func (h *novaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
873 | path := r.URL.Path |
874 | // handle invalid X-Auth-Token header |
875 | - if r.Header.Get(authToken) != h.n.token { |
876 | + _, err := userInfo(h.n.IdentityService, r) |
877 | + if err != nil { |
878 | errUnauthorized.ServeHTTP(w, r) |
879 | return |
880 | } |
881 | @@ -276,7 +282,7 @@ |
882 | errNotFound.ServeHTTP(w, r) |
883 | return |
884 | } |
885 | - err := h.method(h.n, w, r) |
886 | + err = h.method(h.n, w, r) |
887 | if err == nil { |
888 | return |
889 | } |
890 | @@ -541,7 +547,7 @@ |
891 | continue |
892 | } |
893 | if sg, err := n.securityGroupByName(groupName); err != nil { |
894 | - return noGroupError(groupName, n.tenantId) |
895 | + return noGroupError(groupName, n.TenantId) |
896 | } else { |
897 | groups = append(groups, sg.Id) |
898 | } |
899 | @@ -553,11 +559,12 @@ |
900 | flavorEnt := nova.Entity{Id: flavor.Id, Links: flavor.Links} |
901 | image := nova.Entity{Id: req.Server.ImageRef} |
902 | timestr := time.Now().Format(time.RFC3339) |
903 | + userInfo, _ := userInfo(n.IdentityService, r) |
904 | server := nova.ServerDetail{ |
905 | Id: id, |
906 | Name: req.Server.Name, |
907 | - TenantId: n.tenantId, |
908 | - UserId: n.userId, |
909 | + TenantId: n.TenantId, |
910 | + UserId: userInfo.Id, |
911 | HostId: "1", |
912 | Image: image, |
913 | Flavor: flavorEnt, |
914 | @@ -787,7 +794,7 @@ |
915 | Id: nextId, |
916 | Name: req.Group.Name, |
917 | Description: req.Group.Description, |
918 | - TenantId: n.tenantId, |
919 | + TenantId: n.TenantId, |
920 | }) |
921 | if err != nil { |
922 | return err |
923 | @@ -970,8 +977,8 @@ |
924 | "/$v/$t/os-floating-ips": n.handler((*Nova).handleFloatingIPs), |
925 | } |
926 | for path, h := range handlers { |
927 | - path = strings.Replace(path, "$v", n.versionPath, 1) |
928 | - path = strings.Replace(path, "$t", n.tenantId, 1) |
929 | + path = strings.Replace(path, "$v", n.VersionPath, 1) |
930 | + path = strings.Replace(path, "$t", n.TenantId, 1) |
931 | if !strings.HasSuffix(path, "/") { |
932 | mux.Handle(path+"/", h) |
933 | } |
934 | |
935 | === modified file 'testservices/novaservice/service_http_test.go' |
936 | --- testservices/novaservice/service_http_test.go 2013-01-22 18:14:51 +0000 |
937 | +++ testservices/novaservice/service_http_test.go 2013-01-24 03:17:22 +0000 |
938 | @@ -10,8 +10,8 @@ |
939 | . "launchpad.net/gocheck" |
940 | "launchpad.net/goose/nova" |
941 | "launchpad.net/goose/testing/httpsuite" |
942 | + "launchpad.net/goose/testservices/identityservice" |
943 | "net/http" |
944 | - "net/url" |
945 | "strconv" |
946 | "strings" |
947 | ) |
948 | @@ -19,14 +19,17 @@ |
949 | type NovaHTTPSuite struct { |
950 | httpsuite.HTTPSuite |
951 | service *Nova |
952 | + token string |
953 | } |
954 | |
955 | var _ = Suite(&NovaHTTPSuite{}) |
956 | |
957 | func (s *NovaHTTPSuite) SetUpSuite(c *C) { |
958 | s.HTTPSuite.SetUpSuite(c) |
959 | - url, _ := url.Parse(s.Server.URL) |
960 | - s.service = New(url.Host, versionPath, token, tenantId) |
961 | + identityDouble := identityservice.NewUserPass() |
962 | + userInfo := identityDouble.AddUser("fred", "secret", "tenant") |
963 | + s.token = userInfo.Token |
964 | + s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble) |
965 | } |
966 | |
967 | func (s *NovaHTTPSuite) TearDownSuite(c *C) { |
968 | @@ -68,8 +71,8 @@ |
969 | // sendRequest constructs an HTTP request from the parameters and |
970 | // sends it, returning the response or an error. |
971 | func (s *NovaHTTPSuite) sendRequest(method, url string, body []byte, headers http.Header) (*http.Response, error) { |
972 | - if !strings.HasPrefix(url, "http") { //s.service.hostname) { |
973 | - url = "http://" + s.service.hostname + strings.TrimLeft(url, "/") |
974 | + if !strings.HasPrefix(url, "http") { |
975 | + url = "http://" + s.service.Hostname + strings.TrimLeft(url, "/") |
976 | } |
977 | req, err := http.NewRequest(method, url, bytes.NewReader(body)) |
978 | if err != nil { |
979 | @@ -91,8 +94,8 @@ |
980 | if headers == nil { |
981 | headers = make(http.Header) |
982 | } |
983 | - headers.Set(authToken, s.service.token) |
984 | - url := s.service.endpoint(true, path) |
985 | + headers.Set(authToken, s.token) |
986 | + url := s.service.endpointURL(true, path) |
987 | return s.sendRequest(method, url, body, headers) |
988 | } |
989 | |
990 | @@ -114,339 +117,345 @@ |
991 | return h |
992 | } |
993 | |
994 | -// simpleTests defines a simple request without a body and expected response. |
995 | -var simpleTests = []struct { |
996 | +// SimpleTest defines a simple request without a body and expected response. |
997 | +type SimpleTest struct { |
998 | unauth bool |
999 | method string |
1000 | url string |
1001 | headers http.Header |
1002 | expect *errorResponse |
1003 | -}{ |
1004 | - { |
1005 | - unauth: true, |
1006 | - method: "GET", |
1007 | - url: "/any", |
1008 | - headers: make(http.Header), |
1009 | - expect: errUnauthorized, |
1010 | - }, |
1011 | - { |
1012 | - unauth: true, |
1013 | - method: "POST", |
1014 | - url: "/any", |
1015 | - headers: setHeader(authToken, "phony"), |
1016 | - expect: errUnauthorized, |
1017 | - }, |
1018 | - { |
1019 | - unauth: true, |
1020 | - method: "GET", |
1021 | - url: "/", |
1022 | - headers: setHeader(authToken, token), |
1023 | - expect: errNoVersion, |
1024 | - }, |
1025 | - { |
1026 | - unauth: true, |
1027 | - method: "GET", |
1028 | - url: "/any", |
1029 | - headers: setHeader(authToken, token), |
1030 | - expect: errMultipleChoices, |
1031 | - }, |
1032 | - { |
1033 | - unauth: true, |
1034 | - method: "POST", |
1035 | - url: "/any/unknown/one", |
1036 | - headers: setHeader(authToken, token), |
1037 | - expect: errMultipleChoices, |
1038 | - }, |
1039 | - { |
1040 | - method: "POST", |
1041 | - url: "/any/unknown/one", |
1042 | - expect: errNotFound, |
1043 | - }, |
1044 | - { |
1045 | - unauth: true, |
1046 | - method: "GET", |
1047 | - url: versionPath + "/phony_token", |
1048 | - headers: setHeader(authToken, token), |
1049 | - expect: errBadRequest, |
1050 | - }, |
1051 | - { |
1052 | - method: "GET", |
1053 | - url: "/flavors/", |
1054 | - expect: errNotFound, |
1055 | - }, |
1056 | - { |
1057 | - method: "GET", |
1058 | - url: "/flavors/invalid", |
1059 | - expect: errNotFound, |
1060 | - }, |
1061 | - { |
1062 | - method: "POST", |
1063 | - url: "/flavors", |
1064 | - expect: errBadRequest2, |
1065 | - }, |
1066 | - { |
1067 | - method: "POST", |
1068 | - url: "/flavors/invalid", |
1069 | - expect: errNotFound, |
1070 | - }, |
1071 | - { |
1072 | - method: "PUT", |
1073 | - url: "/flavors", |
1074 | - expect: errNotFound, |
1075 | - }, |
1076 | - { |
1077 | - method: "PUT", |
1078 | - url: "/flavors/invalid", |
1079 | - expect: errNotFoundJSON, |
1080 | - }, |
1081 | - { |
1082 | - method: "DELETE", |
1083 | - url: "/flavors", |
1084 | - expect: errNotFound, |
1085 | - }, |
1086 | - { |
1087 | - method: "DELETE", |
1088 | - url: "/flavors/invalid", |
1089 | - expect: errForbidden, |
1090 | - }, |
1091 | - { |
1092 | - method: "GET", |
1093 | - url: "/flavors/detail/invalid", |
1094 | - expect: errNotFound, |
1095 | - }, |
1096 | - { |
1097 | - method: "POST", |
1098 | - url: "/flavors/detail", |
1099 | - expect: errNotFound, |
1100 | - }, |
1101 | - { |
1102 | - method: "POST", |
1103 | - url: "/flavors/detail/invalid", |
1104 | - expect: errNotFound, |
1105 | - }, |
1106 | - { |
1107 | - method: "PUT", |
1108 | - url: "/flavors/detail", |
1109 | - expect: errNotFoundJSON, |
1110 | - }, |
1111 | - { |
1112 | - method: "PUT", |
1113 | - url: "/flavors/detail/invalid", |
1114 | - expect: errNotFound, |
1115 | - }, |
1116 | - { |
1117 | - method: "DELETE", |
1118 | - url: "/flavors/detail", |
1119 | - expect: errForbidden, |
1120 | - }, |
1121 | - { |
1122 | - method: "DELETE", |
1123 | - url: "/flavors/detail/invalid", |
1124 | - expect: errNotFound, |
1125 | - }, |
1126 | - { |
1127 | - method: "GET", |
1128 | - url: "/servers/invalid", |
1129 | - expect: errNotFoundJSON, |
1130 | - }, |
1131 | - { |
1132 | - method: "POST", |
1133 | - url: "/servers", |
1134 | - expect: errBadRequest2, |
1135 | - }, |
1136 | - { |
1137 | - method: "POST", |
1138 | - url: "/servers/invalid", |
1139 | - expect: errNotFound, |
1140 | - }, |
1141 | - { |
1142 | - method: "PUT", |
1143 | - url: "/servers", |
1144 | - expect: errNotFound, |
1145 | - }, |
1146 | - { |
1147 | - method: "PUT", |
1148 | - url: "/servers/invalid", |
1149 | - expect: errBadRequest2, |
1150 | - }, |
1151 | - { |
1152 | - method: "DELETE", |
1153 | - url: "/servers", |
1154 | - expect: errNotFound, |
1155 | - }, |
1156 | - { |
1157 | - method: "DELETE", |
1158 | - url: "/servers/invalid", |
1159 | - expect: errNotFoundJSON, |
1160 | - }, |
1161 | - { |
1162 | - method: "GET", |
1163 | - url: "/servers/detail/invalid", |
1164 | - expect: errNotFound, |
1165 | - }, |
1166 | - { |
1167 | - method: "POST", |
1168 | - url: "/servers/detail", |
1169 | - expect: errNotFound, |
1170 | - }, |
1171 | - { |
1172 | - method: "POST", |
1173 | - url: "/servers/detail/invalid", |
1174 | - expect: errNotFound, |
1175 | - }, |
1176 | - { |
1177 | - method: "PUT", |
1178 | - url: "/servers/detail", |
1179 | - expect: errBadRequest2, |
1180 | - }, |
1181 | - { |
1182 | - method: "PUT", |
1183 | - url: "/servers/detail/invalid", |
1184 | - expect: errNotFound, |
1185 | - }, |
1186 | - { |
1187 | - method: "DELETE", |
1188 | - url: "/servers/detail", |
1189 | - expect: errNotFoundJSON, |
1190 | - }, |
1191 | - { |
1192 | - method: "DELETE", |
1193 | - url: "/servers/detail/invalid", |
1194 | - expect: errNotFound, |
1195 | - }, |
1196 | - { |
1197 | - method: "GET", |
1198 | - url: "/os-security-groups/invalid", |
1199 | - expect: errBadRequestSG, |
1200 | - }, |
1201 | - { |
1202 | - method: "GET", |
1203 | - url: "/os-security-groups/42", |
1204 | - expect: errNotFoundJSONSG, |
1205 | - }, |
1206 | - { |
1207 | - method: "POST", |
1208 | - url: "/os-security-groups", |
1209 | - expect: errBadRequest2, |
1210 | - }, |
1211 | - { |
1212 | - method: "POST", |
1213 | - url: "/os-security-groups/invalid", |
1214 | - expect: errNotFound, |
1215 | - }, |
1216 | - { |
1217 | - method: "PUT", |
1218 | - url: "/os-security-groups", |
1219 | - expect: errNotFound, |
1220 | - }, |
1221 | - { |
1222 | - method: "PUT", |
1223 | - url: "/os-security-groups/invalid", |
1224 | - expect: errNotFoundJSON, |
1225 | - }, |
1226 | - { |
1227 | - method: "DELETE", |
1228 | - url: "/os-security-groups", |
1229 | - expect: errNotFound, |
1230 | - }, |
1231 | - { |
1232 | - method: "DELETE", |
1233 | - url: "/os-security-groups/invalid", |
1234 | - expect: errBadRequestSG, |
1235 | - }, |
1236 | - { |
1237 | - method: "DELETE", |
1238 | - url: "/os-security-groups/42", |
1239 | - expect: errNotFoundJSONSG, |
1240 | - }, |
1241 | - { |
1242 | - method: "GET", |
1243 | - url: "/os-security-group-rules", |
1244 | - expect: errNotFoundJSON, |
1245 | - }, |
1246 | - { |
1247 | - method: "GET", |
1248 | - url: "/os-security-group-rules/invalid", |
1249 | - expect: errNotFoundJSON, |
1250 | - }, |
1251 | - { |
1252 | - method: "GET", |
1253 | - url: "/os-security-group-rules/42", |
1254 | - expect: errNotFoundJSON, |
1255 | - }, |
1256 | - { |
1257 | - method: "POST", |
1258 | - url: "/os-security-group-rules", |
1259 | - expect: errBadRequest2, |
1260 | - }, |
1261 | - { |
1262 | - method: "POST", |
1263 | - url: "/os-security-group-rules/invalid", |
1264 | - expect: errNotFound, |
1265 | - }, |
1266 | - { |
1267 | - method: "PUT", |
1268 | - url: "/os-security-group-rules", |
1269 | - expect: errNotFound, |
1270 | - }, |
1271 | - { |
1272 | - method: "PUT", |
1273 | - url: "/os-security-group-rules/invalid", |
1274 | - expect: errNotFoundJSON, |
1275 | - }, |
1276 | - { |
1277 | - method: "DELETE", |
1278 | - url: "/os-security-group-rules", |
1279 | - expect: errNotFound, |
1280 | - }, |
1281 | - { |
1282 | - method: "DELETE", |
1283 | - url: "/os-security-group-rules/invalid", |
1284 | - expect: errBadRequestSG, // sic; should've been rule-specific |
1285 | - }, |
1286 | - { |
1287 | - method: "DELETE", |
1288 | - url: "/os-security-group-rules/42", |
1289 | - expect: errNotFoundJSONSGR, |
1290 | - }, |
1291 | - { |
1292 | - method: "GET", |
1293 | - url: "/os-floating-ips/42", |
1294 | - expect: errNotFoundJSON, |
1295 | - }, |
1296 | - { |
1297 | - method: "POST", |
1298 | - url: "/os-floating-ips/invalid", |
1299 | - expect: errNotFound, |
1300 | - }, |
1301 | - { |
1302 | - method: "PUT", |
1303 | - url: "/os-floating-ips", |
1304 | - expect: errNotFound, |
1305 | - }, |
1306 | - { |
1307 | - method: "PUT", |
1308 | - url: "/os-floating-ips/invalid", |
1309 | - expect: errNotFoundJSON, |
1310 | - }, |
1311 | - { |
1312 | - method: "DELETE", |
1313 | - url: "/os-floating-ips", |
1314 | - expect: errNotFound, |
1315 | - }, |
1316 | - { |
1317 | - method: "DELETE", |
1318 | - url: "/os-floating-ips/invalid", |
1319 | - expect: errNotFoundJSON, |
1320 | - }, |
1321 | +} |
1322 | + |
1323 | +func (s *NovaHTTPSuite) simpleTests() []SimpleTest { |
1324 | + var simpleTests = []SimpleTest{ |
1325 | + { |
1326 | + unauth: true, |
1327 | + method: "GET", |
1328 | + url: "/any", |
1329 | + headers: make(http.Header), |
1330 | + expect: errUnauthorized, |
1331 | + }, |
1332 | + { |
1333 | + unauth: true, |
1334 | + method: "POST", |
1335 | + url: "/any", |
1336 | + headers: setHeader(authToken, "phony"), |
1337 | + expect: errUnauthorized, |
1338 | + }, |
1339 | + { |
1340 | + unauth: true, |
1341 | + method: "GET", |
1342 | + url: "/", |
1343 | + headers: setHeader(authToken, s.token), |
1344 | + expect: errNoVersion, |
1345 | + }, |
1346 | + { |
1347 | + unauth: true, |
1348 | + method: "GET", |
1349 | + url: "/any", |
1350 | + headers: setHeader(authToken, s.token), |
1351 | + expect: errMultipleChoices, |
1352 | + }, |
1353 | + { |
1354 | + unauth: true, |
1355 | + method: "POST", |
1356 | + url: "/any/unknown/one", |
1357 | + headers: setHeader(authToken, s.token), |
1358 | + expect: errMultipleChoices, |
1359 | + }, |
1360 | + { |
1361 | + method: "POST", |
1362 | + url: "/any/unknown/one", |
1363 | + expect: errNotFound, |
1364 | + }, |
1365 | + { |
1366 | + unauth: true, |
1367 | + method: "GET", |
1368 | + url: versionPath + "/phony_token", |
1369 | + headers: setHeader(authToken, s.token), |
1370 | + expect: errBadRequest, |
1371 | + }, |
1372 | + { |
1373 | + method: "GET", |
1374 | + url: "/flavors/", |
1375 | + expect: errNotFound, |
1376 | + }, |
1377 | + { |
1378 | + method: "GET", |
1379 | + url: "/flavors/invalid", |
1380 | + expect: errNotFound, |
1381 | + }, |
1382 | + { |
1383 | + method: "POST", |
1384 | + url: "/flavors", |
1385 | + expect: errBadRequest2, |
1386 | + }, |
1387 | + { |
1388 | + method: "POST", |
1389 | + url: "/flavors/invalid", |
1390 | + expect: errNotFound, |
1391 | + }, |
1392 | + { |
1393 | + method: "PUT", |
1394 | + url: "/flavors", |
1395 | + expect: errNotFound, |
1396 | + }, |
1397 | + { |
1398 | + method: "PUT", |
1399 | + url: "/flavors/invalid", |
1400 | + expect: errNotFoundJSON, |
1401 | + }, |
1402 | + { |
1403 | + method: "DELETE", |
1404 | + url: "/flavors", |
1405 | + expect: errNotFound, |
1406 | + }, |
1407 | + { |
1408 | + method: "DELETE", |
1409 | + url: "/flavors/invalid", |
1410 | + expect: errForbidden, |
1411 | + }, |
1412 | + { |
1413 | + method: "GET", |
1414 | + url: "/flavors/detail/invalid", |
1415 | + expect: errNotFound, |
1416 | + }, |
1417 | + { |
1418 | + method: "POST", |
1419 | + url: "/flavors/detail", |
1420 | + expect: errNotFound, |
1421 | + }, |
1422 | + { |
1423 | + method: "POST", |
1424 | + url: "/flavors/detail/invalid", |
1425 | + expect: errNotFound, |
1426 | + }, |
1427 | + { |
1428 | + method: "PUT", |
1429 | + url: "/flavors/detail", |
1430 | + expect: errNotFoundJSON, |
1431 | + }, |
1432 | + { |
1433 | + method: "PUT", |
1434 | + url: "/flavors/detail/invalid", |
1435 | + expect: errNotFound, |
1436 | + }, |
1437 | + { |
1438 | + method: "DELETE", |
1439 | + url: "/flavors/detail", |
1440 | + expect: errForbidden, |
1441 | + }, |
1442 | + { |
1443 | + method: "DELETE", |
1444 | + url: "/flavors/detail/invalid", |
1445 | + expect: errNotFound, |
1446 | + }, |
1447 | + { |
1448 | + method: "GET", |
1449 | + url: "/servers/invalid", |
1450 | + expect: errNotFoundJSON, |
1451 | + }, |
1452 | + { |
1453 | + method: "POST", |
1454 | + url: "/servers", |
1455 | + expect: errBadRequest2, |
1456 | + }, |
1457 | + { |
1458 | + method: "POST", |
1459 | + url: "/servers/invalid", |
1460 | + expect: errNotFound, |
1461 | + }, |
1462 | + { |
1463 | + method: "PUT", |
1464 | + url: "/servers", |
1465 | + expect: errNotFound, |
1466 | + }, |
1467 | + { |
1468 | + method: "PUT", |
1469 | + url: "/servers/invalid", |
1470 | + expect: errBadRequest2, |
1471 | + }, |
1472 | + { |
1473 | + method: "DELETE", |
1474 | + url: "/servers", |
1475 | + expect: errNotFound, |
1476 | + }, |
1477 | + { |
1478 | + method: "DELETE", |
1479 | + url: "/servers/invalid", |
1480 | + expect: errNotFoundJSON, |
1481 | + }, |
1482 | + { |
1483 | + method: "GET", |
1484 | + url: "/servers/detail/invalid", |
1485 | + expect: errNotFound, |
1486 | + }, |
1487 | + { |
1488 | + method: "POST", |
1489 | + url: "/servers/detail", |
1490 | + expect: errNotFound, |
1491 | + }, |
1492 | + { |
1493 | + method: "POST", |
1494 | + url: "/servers/detail/invalid", |
1495 | + expect: errNotFound, |
1496 | + }, |
1497 | + { |
1498 | + method: "PUT", |
1499 | + url: "/servers/detail", |
1500 | + expect: errBadRequest2, |
1501 | + }, |
1502 | + { |
1503 | + method: "PUT", |
1504 | + url: "/servers/detail/invalid", |
1505 | + expect: errNotFound, |
1506 | + }, |
1507 | + { |
1508 | + method: "DELETE", |
1509 | + url: "/servers/detail", |
1510 | + expect: errNotFoundJSON, |
1511 | + }, |
1512 | + { |
1513 | + method: "DELETE", |
1514 | + url: "/servers/detail/invalid", |
1515 | + expect: errNotFound, |
1516 | + }, |
1517 | + { |
1518 | + method: "GET", |
1519 | + url: "/os-security-groups/invalid", |
1520 | + expect: errBadRequestSG, |
1521 | + }, |
1522 | + { |
1523 | + method: "GET", |
1524 | + url: "/os-security-groups/42", |
1525 | + expect: errNotFoundJSONSG, |
1526 | + }, |
1527 | + { |
1528 | + method: "POST", |
1529 | + url: "/os-security-groups", |
1530 | + expect: errBadRequest2, |
1531 | + }, |
1532 | + { |
1533 | + method: "POST", |
1534 | + url: "/os-security-groups/invalid", |
1535 | + expect: errNotFound, |
1536 | + }, |
1537 | + { |
1538 | + method: "PUT", |
1539 | + url: "/os-security-groups", |
1540 | + expect: errNotFound, |
1541 | + }, |
1542 | + { |
1543 | + method: "PUT", |
1544 | + url: "/os-security-groups/invalid", |
1545 | + expect: errNotFoundJSON, |
1546 | + }, |
1547 | + { |
1548 | + method: "DELETE", |
1549 | + url: "/os-security-groups", |
1550 | + expect: errNotFound, |
1551 | + }, |
1552 | + { |
1553 | + method: "DELETE", |
1554 | + url: "/os-security-groups/invalid", |
1555 | + expect: errBadRequestSG, |
1556 | + }, |
1557 | + { |
1558 | + method: "DELETE", |
1559 | + url: "/os-security-groups/42", |
1560 | + expect: errNotFoundJSONSG, |
1561 | + }, |
1562 | + { |
1563 | + method: "GET", |
1564 | + url: "/os-security-group-rules", |
1565 | + expect: errNotFoundJSON, |
1566 | + }, |
1567 | + { |
1568 | + method: "GET", |
1569 | + url: "/os-security-group-rules/invalid", |
1570 | + expect: errNotFoundJSON, |
1571 | + }, |
1572 | + { |
1573 | + method: "GET", |
1574 | + url: "/os-security-group-rules/42", |
1575 | + expect: errNotFoundJSON, |
1576 | + }, |
1577 | + { |
1578 | + method: "POST", |
1579 | + url: "/os-security-group-rules", |
1580 | + expect: errBadRequest2, |
1581 | + }, |
1582 | + { |
1583 | + method: "POST", |
1584 | + url: "/os-security-group-rules/invalid", |
1585 | + expect: errNotFound, |
1586 | + }, |
1587 | + { |
1588 | + method: "PUT", |
1589 | + url: "/os-security-group-rules", |
1590 | + expect: errNotFound, |
1591 | + }, |
1592 | + { |
1593 | + method: "PUT", |
1594 | + url: "/os-security-group-rules/invalid", |
1595 | + expect: errNotFoundJSON, |
1596 | + }, |
1597 | + { |
1598 | + method: "DELETE", |
1599 | + url: "/os-security-group-rules", |
1600 | + expect: errNotFound, |
1601 | + }, |
1602 | + { |
1603 | + method: "DELETE", |
1604 | + url: "/os-security-group-rules/invalid", |
1605 | + expect: errBadRequestSG, // sic; should've been rule-specific |
1606 | + }, |
1607 | + { |
1608 | + method: "DELETE", |
1609 | + url: "/os-security-group-rules/42", |
1610 | + expect: errNotFoundJSONSGR, |
1611 | + }, |
1612 | + { |
1613 | + method: "GET", |
1614 | + url: "/os-floating-ips/42", |
1615 | + expect: errNotFoundJSON, |
1616 | + }, |
1617 | + { |
1618 | + method: "POST", |
1619 | + url: "/os-floating-ips/invalid", |
1620 | + expect: errNotFound, |
1621 | + }, |
1622 | + { |
1623 | + method: "PUT", |
1624 | + url: "/os-floating-ips", |
1625 | + expect: errNotFound, |
1626 | + }, |
1627 | + { |
1628 | + method: "PUT", |
1629 | + url: "/os-floating-ips/invalid", |
1630 | + expect: errNotFoundJSON, |
1631 | + }, |
1632 | + { |
1633 | + method: "DELETE", |
1634 | + url: "/os-floating-ips", |
1635 | + expect: errNotFound, |
1636 | + }, |
1637 | + { |
1638 | + method: "DELETE", |
1639 | + url: "/os-floating-ips/invalid", |
1640 | + expect: errNotFoundJSON, |
1641 | + }, |
1642 | + } |
1643 | + return simpleTests |
1644 | } |
1645 | |
1646 | func (s *NovaHTTPSuite) TestSimpleRequestTests(c *C) { |
1647 | + simpleTests := s.simpleTests() |
1648 | for i, t := range simpleTests { |
1649 | c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.code) |
1650 | if t.headers == nil { |
1651 | t.headers = make(http.Header) |
1652 | - t.headers.Set(authToken, s.service.token) |
1653 | + t.headers.Set(authToken, s.token) |
1654 | } |
1655 | var ( |
1656 | resp *http.Response |
1657 | @@ -780,13 +789,13 @@ |
1658 | { |
1659 | Id: 1, |
1660 | Name: "group 1", |
1661 | - TenantId: s.service.tenantId, |
1662 | + TenantId: s.service.TenantId, |
1663 | Rules: []nova.SecurityGroupRule{}, |
1664 | }, |
1665 | { |
1666 | Id: 2, |
1667 | Name: "group 2", |
1668 | - TenantId: s.service.tenantId, |
1669 | + TenantId: s.service.TenantId, |
1670 | Rules: []nova.SecurityGroupRule{}, |
1671 | }, |
1672 | } |
1673 | @@ -816,7 +825,7 @@ |
1674 | Id: 1, |
1675 | Name: "group 1", |
1676 | Description: "desc", |
1677 | - TenantId: s.service.tenantId, |
1678 | + TenantId: s.service.TenantId, |
1679 | Rules: []nova.SecurityGroupRule{}, |
1680 | } |
1681 | _, err := s.service.securityGroup(group.Id) |
1682 | @@ -890,7 +899,7 @@ |
1683 | ParentGroupId: group2.Id, |
1684 | Group: nova.SecurityGroupRef{ |
1685 | Name: group1.Name, |
1686 | - TenantId: s.service.tenantId, |
1687 | + TenantId: s.service.TenantId, |
1688 | }, |
1689 | } |
1690 | ok := s.service.hasSecurityGroupRule(group1.Id, rule1.Id) |
1691 | @@ -990,13 +999,13 @@ |
1692 | { |
1693 | Id: 1, |
1694 | Name: "group1", |
1695 | - TenantId: s.service.tenantId, |
1696 | + TenantId: s.service.TenantId, |
1697 | Rules: []nova.SecurityGroupRule{}, |
1698 | }, |
1699 | { |
1700 | Id: 2, |
1701 | Name: "group2", |
1702 | - TenantId: s.service.tenantId, |
1703 | + TenantId: s.service.TenantId, |
1704 | Rules: []nova.SecurityGroupRule{}, |
1705 | }, |
1706 | } |
1707 | |
1708 | === modified file 'testservices/novaservice/service_test.go' |
1709 | --- testservices/novaservice/service_test.go 2013-01-21 11:18:33 +0000 |
1710 | +++ testservices/novaservice/service_test.go 2013-01-24 03:17:22 +0000 |
1711 | @@ -14,15 +14,14 @@ |
1712 | |
1713 | const ( |
1714 | versionPath = "v2" |
1715 | - token = "token" |
1716 | - hostname = "example.com" |
1717 | - tenantId = "tenant_id" |
1718 | + hostname = "http://example.com" |
1719 | + region = "region" |
1720 | ) |
1721 | |
1722 | var _ = Suite(&NovaSuite{}) |
1723 | |
1724 | func (s *NovaSuite) SetUpSuite(c *C) { |
1725 | - s.service = New(hostname, versionPath, token, tenantId) |
1726 | + s.service = New(hostname, versionPath, "tenant", region, nil) |
1727 | } |
1728 | |
1729 | func (s *NovaSuite) ensureNoFlavor(c *C, flavor nova.FlavorDetail) { |
1730 | @@ -118,8 +117,8 @@ |
1731 | fl, _ := s.service.flavor(flavor.Id) |
1732 | url := "/flavors/" + flavor.Id |
1733 | links := []nova.Link{ |
1734 | - nova.Link{Href: s.service.endpoint(true, url), Rel: "self"}, |
1735 | - nova.Link{Href: s.service.endpoint(false, url), Rel: "bookmark"}, |
1736 | + nova.Link{Href: s.service.endpointURL(true, url), Rel: "self"}, |
1737 | + nova.Link{Href: s.service.endpointURL(false, url), Rel: "bookmark"}, |
1738 | } |
1739 | c.Assert(fl.Links, DeepEquals, links) |
1740 | } |
1741 | @@ -214,8 +213,8 @@ |
1742 | sr, _ := s.service.server(server.Id) |
1743 | url := "/servers/" + server.Id |
1744 | links := []nova.Link{ |
1745 | - {Href: s.service.endpoint(true, url), Rel: "self"}, |
1746 | - {Href: s.service.endpoint(false, url), Rel: "bookmark"}, |
1747 | + {Href: s.service.endpointURL(true, url), Rel: "self"}, |
1748 | + {Href: s.service.endpointURL(false, url), Rel: "bookmark"}, |
1749 | } |
1750 | c.Assert(sr.Links, DeepEquals, links) |
1751 | } |
1752 | @@ -456,7 +455,7 @@ |
1753 | group := nova.SecurityGroup{ |
1754 | Id: 1, |
1755 | Name: "test", |
1756 | - TenantId: s.service.tenantId, |
1757 | + TenantId: s.service.TenantId, |
1758 | Rules: []nova.SecurityGroupRule{ |
1759 | {Id: 10, ParentGroupId: 1}, |
1760 | {Id: 20, ParentGroupId: 1}, |
1761 | @@ -492,13 +491,13 @@ |
1762 | { |
1763 | Id: 1, |
1764 | Name: "one", |
1765 | - TenantId: s.service.tenantId, |
1766 | + TenantId: s.service.TenantId, |
1767 | Rules: []nova.SecurityGroupRule{}, |
1768 | }, |
1769 | { |
1770 | Id: 2, |
1771 | Name: "two", |
1772 | - TenantId: s.service.tenantId, |
1773 | + TenantId: s.service.TenantId, |
1774 | Rules: []nova.SecurityGroupRule{}, |
1775 | }, |
1776 | } |
1777 | @@ -514,7 +513,7 @@ |
1778 | func (s *NovaSuite) TestGetSecurityGroup(c *C) { |
1779 | group := nova.SecurityGroup{ |
1780 | Id: 42, |
1781 | - TenantId: s.service.tenantId, |
1782 | + TenantId: s.service.TenantId, |
1783 | Name: "group", |
1784 | Description: "desc", |
1785 | Rules: []nova.SecurityGroupRule{}, |
1786 | @@ -529,7 +528,7 @@ |
1787 | group := nova.SecurityGroup{ |
1788 | Id: 1, |
1789 | Name: "test", |
1790 | - TenantId: s.service.tenantId, |
1791 | + TenantId: s.service.TenantId, |
1792 | Rules: []nova.SecurityGroupRule{}, |
1793 | } |
1794 | s.ensureNoGroup(c, group) |
1795 | @@ -600,7 +599,7 @@ |
1796 | } |
1797 | |
1798 | func (s *NovaSuite) TestAddGetGroupSecurityGroupRule(c *C) { |
1799 | - srcGroup := nova.SecurityGroup{Id: 1, Name: "source", TenantId: s.service.tenantId} |
1800 | + srcGroup := nova.SecurityGroup{Id: 1, Name: "source", TenantId: s.service.TenantId} |
1801 | tgtGroup := nova.SecurityGroup{Id: 2, Name: "target"} |
1802 | s.createGroup(c, srcGroup) |
1803 | defer s.deleteGroup(c, srcGroup) |
1804 | @@ -696,7 +695,7 @@ |
1805 | group := nova.SecurityGroup{ |
1806 | Id: 1, |
1807 | Name: "test", |
1808 | - TenantId: s.service.tenantId, |
1809 | + TenantId: s.service.TenantId, |
1810 | } |
1811 | s.createGroup(c, group) |
1812 | defer s.deleteGroup(c, group) |
1813 | @@ -800,13 +799,13 @@ |
1814 | { |
1815 | Id: 1, |
1816 | Name: "gr1", |
1817 | - TenantId: s.service.tenantId, |
1818 | + TenantId: s.service.TenantId, |
1819 | Rules: []nova.SecurityGroupRule{}, |
1820 | }, |
1821 | { |
1822 | Id: 2, |
1823 | Name: "gr2", |
1824 | - TenantId: s.service.tenantId, |
1825 | + TenantId: s.service.TenantId, |
1826 | Rules: []nova.SecurityGroupRule{}, |
1827 | }, |
1828 | } |
1829 | |
1830 | === added directory 'testservices/openstack' |
1831 | === added file 'testservices/openstack/openstack.go' |
1832 | --- testservices/openstack/openstack.go 1970-01-01 00:00:00 +0000 |
1833 | +++ testservices/openstack/openstack.go 2013-01-24 03:17:22 +0000 |
1834 | @@ -0,0 +1,38 @@ |
1835 | +package openstack |
1836 | + |
1837 | +import ( |
1838 | + "launchpad.net/goose/identity" |
1839 | + "launchpad.net/goose/testservices/identityservice" |
1840 | + "launchpad.net/goose/testservices/novaservice" |
1841 | + "launchpad.net/goose/testservices/swiftservice" |
1842 | + "net/http" |
1843 | +) |
1844 | + |
1845 | +// Openstack provides an Openstack service double implementation. |
1846 | +type Openstack struct { |
1847 | + identity identityservice.IdentityService |
1848 | + nova *novaservice.Nova |
1849 | + swift *swiftservice.Swift |
1850 | +} |
1851 | + |
1852 | +// New creates an instance of a full Openstack service double. |
1853 | +// An initial user with the specified credentials is registered with the identity service. |
1854 | +func New(cred *identity.Credentials) *Openstack { |
1855 | + openstack := Openstack{ |
1856 | + identity: identityservice.NewUserPass(), |
1857 | + } |
1858 | + userInfo := openstack.identity.AddUser(cred.User, cred.Secrets, cred.TenantName) |
1859 | + if cred.TenantName == "" { |
1860 | + panic("Openstack service double requires a tenant to be specified.") |
1861 | + } |
1862 | + openstack.nova = novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, openstack.identity) |
1863 | + openstack.swift = swiftservice.New(cred.URL, "v1", userInfo.TenantId, cred.Region, openstack.identity) |
1864 | + return &openstack |
1865 | +} |
1866 | + |
1867 | +// setupHTTP attaches all the needed handlers to provide the HTTP API for the Openstack service.. |
1868 | +func (openstack *Openstack) SetupHTTP(mux *http.ServeMux) { |
1869 | + openstack.identity.SetupHTTP(mux) |
1870 | + openstack.nova.SetupHTTP(mux) |
1871 | + openstack.swift.SetupHTTP(mux) |
1872 | +} |
1873 | |
1874 | === added file 'testservices/service.go' |
1875 | --- testservices/service.go 1970-01-01 00:00:00 +0000 |
1876 | +++ testservices/service.go 2013-01-24 03:17:22 +0000 |
1877 | @@ -0,0 +1,21 @@ |
1878 | +package testservices |
1879 | + |
1880 | +import ( |
1881 | + "launchpad.net/goose/testservices/identityservice" |
1882 | + "net/http" |
1883 | +) |
1884 | + |
1885 | +// An HttpService provides the HTTP API for a service double. |
1886 | +type HttpService interface { |
1887 | + SetupHTTP(mux *http.ServeMux) |
1888 | +} |
1889 | + |
1890 | +// A ServiceInstance is an Openstack module, one of nova, swift, glance. |
1891 | +type ServiceInstance struct { |
1892 | + identityservice.ServiceProvider |
1893 | + IdentityService identityservice.IdentityService |
1894 | + Hostname string |
1895 | + VersionPath string |
1896 | + TenantId string |
1897 | + Region string |
1898 | +} |
1899 | |
1900 | === modified file 'testservices/swiftservice/service.go' |
1901 | --- testservices/swiftservice/service.go 2012-12-17 01:30:20 +0000 |
1902 | +++ testservices/swiftservice/service.go 2013-01-24 03:17:22 +0000 |
1903 | @@ -5,29 +5,67 @@ |
1904 | import ( |
1905 | "fmt" |
1906 | "launchpad.net/goose/swift" |
1907 | + "launchpad.net/goose/testservices" |
1908 | + "launchpad.net/goose/testservices/identityservice" |
1909 | + "net/url" |
1910 | + "strings" |
1911 | "time" |
1912 | ) |
1913 | |
1914 | type object map[string][]byte |
1915 | |
1916 | +var _ testservices.HttpService = (*Swift)(nil) |
1917 | +var _ identityservice.ServiceProvider = (*Swift)(nil) |
1918 | + |
1919 | type Swift struct { |
1920 | + testservices.ServiceInstance |
1921 | containers map[string]object |
1922 | - hostname string |
1923 | - baseURL string |
1924 | - token string |
1925 | } |
1926 | |
1927 | // New creates an instance of the Swift object, given the parameters. |
1928 | -func New(hostname, baseURL, token string) *Swift { |
1929 | +func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Swift { |
1930 | + url, err := url.Parse(hostURL) |
1931 | + if err != nil { |
1932 | + panic(err) |
1933 | + } |
1934 | + hostname := url.Host |
1935 | + if !strings.HasSuffix(hostname, "/") { |
1936 | + hostname += "/" |
1937 | + } |
1938 | swift := &Swift{ |
1939 | containers: make(map[string]object), |
1940 | - hostname: hostname, |
1941 | - baseURL: baseURL, |
1942 | - token: token, |
1943 | + ServiceInstance: testservices.ServiceInstance{ |
1944 | + IdentityService: identityService, |
1945 | + Hostname: hostname, |
1946 | + VersionPath: versionPath, |
1947 | + TenantId: tenantId, |
1948 | + Region: region, |
1949 | + }, |
1950 | + } |
1951 | + if identityService != nil { |
1952 | + identityService.RegisterServiceProvider("swift", "object-store", swift) |
1953 | } |
1954 | return swift |
1955 | } |
1956 | |
1957 | +func (s *Swift) endpointURL(path string) string { |
1958 | + ep := "http://" + s.Hostname + s.VersionPath + "/" + s.TenantId |
1959 | + if path != "" { |
1960 | + ep += "/" + strings.TrimLeft(path, "/") |
1961 | + } |
1962 | + return ep |
1963 | +} |
1964 | + |
1965 | +func (s *Swift) Endpoints() []identityservice.Endpoint { |
1966 | + ep := identityservice.Endpoint{ |
1967 | + AdminURL: s.endpointURL(""), |
1968 | + InternalURL: s.endpointURL(""), |
1969 | + PublicURL: s.endpointURL(""), |
1970 | + Region: s.Region, |
1971 | + } |
1972 | + return []identityservice.Endpoint{ep} |
1973 | +} |
1974 | + |
1975 | // HasContainer verifies the given container exists or not. |
1976 | func (s *Swift) HasContainer(name string) bool { |
1977 | _, ok := s.containers[name] |
1978 | @@ -119,5 +157,5 @@ |
1979 | if _, err := s.GetObject(container, object); err != nil { |
1980 | return "", err |
1981 | } |
1982 | - return fmt.Sprintf("%s%s%s/%s", s.hostname, s.baseURL, container, object), nil |
1983 | + return s.endpointURL(fmt.Sprintf("%s/%s", container, object)), nil |
1984 | } |
1985 | |
1986 | === modified file 'testservices/swiftservice/service_http.go' |
1987 | --- testservices/swiftservice/service_http.go 2013-01-21 11:18:33 +0000 |
1988 | +++ testservices/swiftservice/service_http.go 2013-01-24 03:17:22 +0000 |
1989 | @@ -4,6 +4,7 @@ |
1990 | |
1991 | import ( |
1992 | "encoding/json" |
1993 | + "fmt" |
1994 | "io/ioutil" |
1995 | "net/http" |
1996 | "strings" |
1997 | @@ -143,16 +144,15 @@ |
1998 | // For public containers, the token is not required to access the files. For now, if the request |
1999 | // does not provide a token, we will let it through and assume a public container is being accessed. |
2000 | token := r.Header.Get("X-Auth-Token") |
2001 | - if token != "" && token != s.token { |
2002 | + _, err := s.IdentityService.FindUser(token) |
2003 | + if token != "" && err != nil { |
2004 | w.WriteHeader(http.StatusUnauthorized) |
2005 | return |
2006 | } |
2007 | - path := r.URL.Path |
2008 | - if path[:len(s.baseURL)] == s.baseURL { |
2009 | - path = path[len(s.baseURL):] |
2010 | - } |
2011 | - path = strings.TrimRight(path, "/") |
2012 | - parts := strings.SplitN(path, "/", 2) |
2013 | + path := strings.TrimRight(r.URL.Path, "/") |
2014 | + path = strings.Trim(path, "/") |
2015 | + parts := strings.SplitN(path, "/", 4) |
2016 | + parts = parts[2:] |
2017 | if len(parts) == 1 { |
2018 | container := parts[0] |
2019 | s.handleContainers(container, w, r) |
2020 | @@ -164,3 +164,9 @@ |
2021 | panic("not implemented request: " + r.URL.Path) |
2022 | } |
2023 | } |
2024 | + |
2025 | +// setupHTTP attaches all the needed handlers to provide the HTTP API. |
2026 | +func (s *Swift) SetupHTTP(mux *http.ServeMux) { |
2027 | + path := fmt.Sprintf("/%s/%s/", s.VersionPath, s.TenantId) |
2028 | + mux.Handle(path, s) |
2029 | +} |
2030 | |
2031 | === modified file 'testservices/swiftservice/service_http_test.go' |
2032 | --- testservices/swiftservice/service_http_test.go 2012-12-20 05:47:11 +0000 |
2033 | +++ testservices/swiftservice/service_http_test.go 2013-01-24 03:17:22 +0000 |
2034 | @@ -9,24 +9,29 @@ |
2035 | . "launchpad.net/gocheck" |
2036 | "launchpad.net/goose/swift" |
2037 | "launchpad.net/goose/testing/httpsuite" |
2038 | + "launchpad.net/goose/testservices/identityservice" |
2039 | "net/http" |
2040 | ) |
2041 | |
2042 | type SwiftHTTPSuite struct { |
2043 | httpsuite.HTTPSuite |
2044 | - service SwiftService |
2045 | + service *Swift |
2046 | + token string |
2047 | } |
2048 | |
2049 | var _ = Suite(&SwiftHTTPSuite{}) |
2050 | |
2051 | func (s *SwiftHTTPSuite) SetUpSuite(c *C) { |
2052 | s.HTTPSuite.SetUpSuite(c) |
2053 | - s.service = New(s.Server.URL, baseURL, token) |
2054 | + identityDouble := identityservice.NewUserPass() |
2055 | + s.service = New(s.Server.URL, versionPath, tenantId, region, identityDouble) |
2056 | + userInfo := identityDouble.AddUser("fred", "secret", "tenant") |
2057 | + s.token = userInfo.Token |
2058 | } |
2059 | |
2060 | func (s *SwiftHTTPSuite) SetUpTest(c *C) { |
2061 | s.HTTPSuite.SetUpTest(c) |
2062 | - s.Mux.Handle(baseURL, s.service) |
2063 | + s.service.SetupHTTP(s.Mux) |
2064 | } |
2065 | |
2066 | func (s *SwiftHTTPSuite) TearDownTest(c *C) { |
2067 | @@ -41,15 +46,15 @@ |
2068 | expectedStatusCode int) (resp *http.Response) { |
2069 | var req *http.Request |
2070 | var err error |
2071 | - url := s.Server.URL + baseURL + path |
2072 | + url := s.service.endpointURL(path) |
2073 | if body != nil { |
2074 | req, err = http.NewRequest(method, url, bytes.NewBuffer(body)) |
2075 | } else { |
2076 | req, err = http.NewRequest(method, url, nil) |
2077 | } |
2078 | c.Assert(err, IsNil) |
2079 | - if token != "" { |
2080 | - req.Header.Add("X-Auth-Token", token) |
2081 | + if s.token != "" { |
2082 | + req.Header.Add("X-Auth-Token", s.token) |
2083 | } |
2084 | client := &http.Client{} |
2085 | resp, err = client.Do(req) |
2086 | @@ -250,16 +255,16 @@ |
2087 | } |
2088 | |
2089 | func (s *SwiftHTTPSuite) TestUnauthorizedFails(c *C) { |
2090 | - oldtoken := token |
2091 | + oldtoken := s.token |
2092 | defer func() { |
2093 | - token = oldtoken |
2094 | + s.token = oldtoken |
2095 | }() |
2096 | // TODO(wallyworld) - until ACLs are supported, empty tokens are assumed to be used when |
2097 | // we need to access a public container. |
2098 | // token = "" |
2099 | // s.sendRequest(c, "GET", "test", nil, http.StatusUnauthorized) |
2100 | |
2101 | - token = "invalid" |
2102 | + s.token = "invalid" |
2103 | s.sendRequest(c, "PUT", "test", nil, http.StatusUnauthorized) |
2104 | |
2105 | s.sendRequest(c, "DELETE", "test", nil, http.StatusUnauthorized) |
2106 | |
2107 | === modified file 'testservices/swiftservice/service_test.go' |
2108 | --- testservices/swiftservice/service_test.go 2012-12-17 01:30:20 +0000 |
2109 | +++ testservices/swiftservice/service_test.go 2013-01-24 03:17:22 +0000 |
2110 | @@ -3,21 +3,23 @@ |
2111 | package swiftservice |
2112 | |
2113 | import ( |
2114 | + "fmt" |
2115 | . "launchpad.net/gocheck" |
2116 | ) |
2117 | |
2118 | type SwiftServiceSuite struct { |
2119 | - service SwiftService |
2120 | + service *Swift |
2121 | } |
2122 | |
2123 | -var baseURL = "/v1/AUTH_tenant/" |
2124 | -var token = "token" |
2125 | -var hostname = "localhost" // not really used here |
2126 | +var region = "region" // not really used here |
2127 | +var hostname = "http://localhost" // not really used here |
2128 | +var versionPath = "v2" // not really used here |
2129 | +var tenantId = "tenant" // not really used here |
2130 | |
2131 | var _ = Suite(&SwiftServiceSuite{}) |
2132 | |
2133 | func (s *SwiftServiceSuite) SetUpSuite(c *C) { |
2134 | - s.service = New(hostname, baseURL, token) |
2135 | + s.service = New(hostname, versionPath, tenantId, region, nil) |
2136 | } |
2137 | |
2138 | func (s *SwiftServiceSuite) TestAddHasRemoveContainer(c *C) { |
2139 | @@ -76,9 +78,8 @@ |
2140 | err = s.service.AddObject("test", "obj", data) |
2141 | c.Assert(err, IsNil) |
2142 | url, err := s.service.GetURL("test", "obj") |
2143 | - path := baseURL + "test/obj" |
2144 | c.Assert(err, IsNil) |
2145 | - c.Assert(url, Equals, hostname+path) |
2146 | + c.Assert(url, Equals, fmt.Sprintf("%s/%s/%s/test/obj", hostname, versionPath, tenantId)) |
2147 | err = s.service.RemoveContainer("test") |
2148 | c.Assert(err, IsNil) |
2149 | ok = s.service.HasContainer("test") |
2150 | |
2151 | === removed file 'testservices/swiftservice/swiftservice.go' |
2152 | --- testservices/swiftservice/swiftservice.go 2012-12-17 01:30:20 +0000 |
2153 | +++ testservices/swiftservice/swiftservice.go 1970-01-01 00:00:00 +0000 |
2154 | @@ -1,40 +0,0 @@ |
2155 | -// Swift double testing service - mimics OpenStack Swift object |
2156 | -// storage service for testing goose against close-to-live API. |
2157 | - |
2158 | -package swiftservice |
2159 | - |
2160 | -import ( |
2161 | - "launchpad.net/goose/swift" |
2162 | - "net/http" |
2163 | -) |
2164 | - |
2165 | -// SwiftService presents an direct-API to manipulate the internal |
2166 | -// state, as well as an HTTP API double for OpenStack Swift. |
2167 | -type SwiftService interface { |
2168 | - // AddContainer creates a new container with the given name. |
2169 | - AddContainer(name string) error |
2170 | - |
2171 | - // AddObject creates a new named object in an existing container. |
2172 | - AddObject(container, name string, data []byte) error |
2173 | - |
2174 | - // HasContainer verifies the given container exists or not. |
2175 | - HasContainer(name string) bool |
2176 | - |
2177 | - // ListContainer lists the objects in the given container. |
2178 | - ListContainer(name string) ([]swift.ContainerContents, error) |
2179 | - |
2180 | - // GetObject retrieves a given object's data from its container. |
2181 | - GetObject(container, name string) ([]byte, error) |
2182 | - |
2183 | - // RemoveContainer deletes an existing named container. |
2184 | - RemoveContainer(name string) error |
2185 | - |
2186 | - // RemoveObject deletes an existing named object, from its container. |
2187 | - RemoveObject(container, name string) error |
2188 | - |
2189 | - // GetURL returns the named object's full public URL to get its data. |
2190 | - GetURL(container, object string) (string, error) |
2191 | - |
2192 | - // ServeHTTP is the main entry point in the HTTP request processing. |
2193 | - ServeHTTP(w http.ResponseWriter, r *http.Request) |
2194 | -} |
2195 | |
2196 | === modified file 'tools/secgroup-delete-all/main_test.go' |
2197 | --- tools/secgroup-delete-all/main_test.go 2013-01-22 18:46:31 +0000 |
2198 | +++ tools/secgroup-delete-all/main_test.go 2013-01-24 03:17:22 +0000 |
2199 | @@ -7,8 +7,7 @@ |
2200 | "launchpad.net/goose/identity" |
2201 | "launchpad.net/goose/nova" |
2202 | "launchpad.net/goose/testing/httpsuite" |
2203 | - "launchpad.net/goose/testservices/identityservice" |
2204 | - "launchpad.net/goose/testservices/novaservice" |
2205 | + "launchpad.net/goose/testservices/openstack" |
2206 | tool "launchpad.net/goose/tools/secgroup-delete-all" |
2207 | "testing" |
2208 | ) |
2209 | @@ -26,43 +25,28 @@ |
2210 | |
2211 | type ToolSuite struct { |
2212 | httpsuite.HTTPSuite |
2213 | + creds *identity.Credentials |
2214 | } |
2215 | |
2216 | var _ = Suite(&ToolSuite{}) |
2217 | |
2218 | // GZ 2013-01-21: Should require EnvSuite for this, but clashes with HTTPSuite |
2219 | -func createNovaClient(auth_url string) *nova.Client { |
2220 | - creds := identity.Credentials{ |
2221 | - URL: auth_url, |
2222 | +func createNovaClient(creds *identity.Credentials) *nova.Client { |
2223 | + osc := client.NewClient(creds, identity.AuthUserPass, nil) |
2224 | + return nova.New(osc) |
2225 | +} |
2226 | + |
2227 | +func (s *ToolSuite) makeServices(c *C) *nova.Client { |
2228 | + creds := &identity.Credentials{ |
2229 | + URL: s.Server.URL, |
2230 | User: username, |
2231 | Secrets: password, |
2232 | Region: region, |
2233 | TenantName: tenant, |
2234 | } |
2235 | - osc := client.NewClient(&creds, identity.AuthUserPass, nil) |
2236 | - return nova.New(osc) |
2237 | -} |
2238 | - |
2239 | -func (s *ToolSuite) makeServices(c *C) *nova.Client { |
2240 | - ident := identityservice.NewUserPass() |
2241 | - token := ident.AddUser(username, password) |
2242 | - // GZ 2013-01-21: Current novaservice double requires magic url like so |
2243 | - computeurl := s.Server.URL + "/v2.0/" + tenant |
2244 | - ident.AddService(identityservice.Service{ |
2245 | - "nova", |
2246 | - "compute", |
2247 | - []identityservice.Endpoint{ |
2248 | - { |
2249 | - AdminURL: computeurl, |
2250 | - InternalURL: computeurl, |
2251 | - PublicURL: computeurl, |
2252 | - Region: region, |
2253 | - }, |
2254 | - }}) |
2255 | - s.Mux.Handle("/tokens", ident) |
2256 | - comp := novaservice.New("unused.invalid", "v2.0", token, tenant) |
2257 | - comp.SetupHTTP(s.Mux) |
2258 | - return createNovaClient(s.Server.URL) |
2259 | + openstack := openstack.New(creds) |
2260 | + openstack.SetupHTTP(s.Mux) |
2261 | + return createNovaClient(creds) |
2262 | } |
2263 | |
2264 | func (s *ToolSuite) TestNoGroups(c *C) { |
Reviewers: mp+144435_ code.launchpad. net,
Message:
Please take a look.
Description:
Goose test infrastructure improvements
This branch improves the usabilty of the goose test infrastructure.
A full Openstack service test double is provided. A bunch of manual
coding which was required in each test suite to set up various Openstack
module test doubles is replaced by a few lines of code.
New code:
openstack := openstack. New(s.Server. URL, s.cred.User, s.cred.Secrets, cred.Region) SetupHTTP( s.Mux)
s.LiveTests.
openstack.
Old code:
// Create the identity service. .NewUserPass( ) e.AddUser( s.cred. User, s.cred.Secrets) Handle( baseIdentityURL , s.identityDouble)
s.identityDouble = identityservice
token := s.identityDoubl
s.Mux.
// Register Swift endpoints with identity service. .Endpoint{ .Service{ "swift" , "object-store", ce.Endpoint{ ep}} le.AddService( service) New("localhost" , baseSwiftURL+"/", token) Handle( baseSwiftURL+ "/", s.swiftDouble)
ep := identityservice
AdminURL: s.Server.URL + baseSwiftURL,
InternalURL: s.Server.URL + baseSwiftURL,
PublicURL: s.Server.URL + baseSwiftURL,
Region: s.cred.Region,
}
service := identityservice
[]identityservi
s.identityDoub
s.swiftDouble = swiftservice.
s.Mux.
// Register Nova endpoints with identity service. .Endpoint{ .Service{ "nova", "compute", ce.Endpoint{ ep}} le.AddService( service) New("localhost" , "V1", token, "1") SetupHTTP( s.Mux)
ep = identityservice
AdminURL: s.Server.URL + baseNovaURL,
InternalURL: s.Server.URL + baseNovaURL,
PublicURL: s.Server.URL + baseNovaURL,
Region: s.cred.Region,
}
service = identityservice
[]identityservi
s.identityDoub
s.novaDouble = novaservice.
s.novaDouble.
Other changes include:
- fix the identity service double to remove the hard coded userId and
tenantId.
- do not hard code a fixed token against a service double - each user is
assigned their own token just like a real instance, allowing multi-user
tests to be written.
- improvements to the Swift service double to make it use URLs which are
compliant with how a real Swift instance would do it.
- factor out a common base class for the legacy and userpass double
implementations.
- use a SetupHTTP() for all test doubles instead of requiring the coder
to know the magic URL paths to use.
https:/ /code.launchpad .net/~wallyworl d/goose/ service- double- improvements/ +merge/ 144435
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/7194043/
Affected files: local_test. go legacy_ test.go userpass_ test.go identityservice /identityservic e.go identityservice /legacy. go identityservice /legacy_ test.go identityservice /service_ test.go identityservice /userpass. go identityservice /userpass_ test.go identityservice /users. go identityservice /util.go novaservice/ service. go novaservice/ service_ http.go novaservice/ service_ http_test. go
A [revision details]
M client/
M identity/
M identity/
M nova/local_test.go
M swift/local_test.go
M testservices/
M testservices/
M testservices/
M testservices/
M testservices/
M testservices/
A testservices/
M testservices/
M testservices/
M testservices/
M testservices/
M testserv...