Status: | Merged |
---|---|
Merged at revision: | 53 |
Proposed branch: | lp:~gophers/goose/trunk |
Merge into: | lp:goose |
Diff against target: |
2525 lines (+1013/-609) 32 files modified
client/local_test.go (+15/-22) identity/legacy_test.go (+3/-3) identity/live_test.go (+40/-0) identity/local_test.go (+63/-0) identity/setup_test.go (+13/-1) identity/userpass.go (+4/-0) identity/userpass_test.go (+5/-5) nova/local_test.go (+12/-32) swift/local_test.go (+12/-28) test.py (+1/-0) 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.go (+73/-0) tools/secgroup-delete-all/main_test.go (+71/-0) |
To merge this branch: | bzr merge lp:~gophers/goose/trunk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Go Bot | Pending | ||
Review via email: mp+144438@code.launchpad.net |
Commit message
Merge trunk into goose-bot's trunk
Description of the change
Merge trunk into goose bot, to see if this works.
Go Bot (go-bot) wrote : | # |
- 50. By Martin Packman
-
Add simple tool for deleting security groups
Basic test of using the current api for doing some actual task. As the
live tests create lots of security groups without ever deleting them
it's also sort of useful to have around.R=wallyworld, jameinel, rog, dimitern
CC=
https://codereview. appspot. com/6948051 - 51. By Ian Booth
-
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.
Credentials{ ...}
openstack := openstack.New(cred)
openstack.SetupHTTP( s.Mux) Old code:
// Create the identity service.
s.identityDouble = identityservice.NewUserPass( )
token := s.identityDouble.AddUser( s.cred. User, s.cred.Secrets)
s.Mux.Handle( baseIdentityURL , s.identityDouble) // Register Swift endpoints with identity service.
ep := identityservice.Endpoint{
AdminURL: s.Server.URL + baseSwiftURL,
InternalURL: s.Server.URL + baseSwiftURL,
PublicURL: s.Server.URL + baseSwiftURL,
Region: s.cred.Region,
}
service := identityservice.Service{ "swift" , "object-store", []identityservi ce.Endpoint{ ep}}
s.identityDouble.AddService( service)
s.swiftDouble = swiftservice.New("localhost" , baseSwiftURL+"/", token)
s.Mux.Handle( baseSwiftURL+ "/", s.swiftDouble) // Register Nova endpoints with identity service.
ep = identityservice.Endpoint{
AdminURL: s.Server.URL + baseNovaURL,
InternalURL: s.Server.URL + baseNovaURL,
PublicURL: s.Server.URL + baseNovaURL,
Region: s.cred.Region,
}
service = identityservice.Service{ "nova", "compute", []identityservi ce.Endpoint{ ep}}
s.identityDouble.AddService( service)
s.novaDouble = novaservice.New("localhost" , "V1", token, "1")
s.novaDouble.SetupHTTP( s.Mux) 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://codereview. appspot. com/7194043 - 52. By John A Meinel
-
Add live and local tests for identity code.
- 53. By John A Meinel
-
Include the fixes for no-repository present.
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-28 18:22:27 +0000 | |||
4 | @@ -4,8 +4,9 @@ | |||
5 | 4 | . "launchpad.net/gocheck" | 4 | . "launchpad.net/gocheck" |
6 | 5 | "launchpad.net/goose/identity" | 5 | "launchpad.net/goose/identity" |
7 | 6 | "launchpad.net/goose/testing/httpsuite" | 6 | "launchpad.net/goose/testing/httpsuite" |
8 | 7 | "launchpad.net/goose/testservices" | ||
9 | 7 | "launchpad.net/goose/testservices/identityservice" | 8 | "launchpad.net/goose/testservices/identityservice" |
11 | 8 | "net/http" | 9 | "launchpad.net/goose/testservices/openstack" |
12 | 9 | ) | 10 | ) |
13 | 10 | 11 | ||
14 | 11 | func registerLocalTests(authMethods []identity.AuthMethod) { | 12 | func registerLocalTests(authMethods []identity.AuthMethod) { |
15 | @@ -24,38 +25,30 @@ | |||
16 | 24 | LiveTests | 25 | LiveTests |
17 | 25 | // The following attributes are for using testing doubles. | 26 | // The following attributes are for using testing doubles. |
18 | 26 | httpsuite.HTTPSuite | 27 | httpsuite.HTTPSuite |
20 | 27 | identityDouble http.Handler | 28 | service testservices.HttpService |
21 | 28 | } | 29 | } |
22 | 29 | 30 | ||
23 | 30 | func (s *localLiveSuite) SetUpSuite(c *C) { | 31 | func (s *localLiveSuite) SetUpSuite(c *C) { |
24 | 31 | c.Logf("Using identity service test double") | 32 | c.Logf("Using identity service test double") |
25 | 32 | s.HTTPSuite.SetUpSuite(c) | 33 | s.HTTPSuite.SetUpSuite(c) |
26 | 33 | s.cred = &identity.Credentials{ | 34 | s.cred = &identity.Credentials{ |
31 | 34 | URL: s.Server.URL, | 35 | URL: s.Server.URL, |
32 | 35 | User: "fred", | 36 | User: "fred", |
33 | 36 | Secrets: "secret", | 37 | Secrets: "secret", |
34 | 37 | Region: "some region"} | 38 | Region: "some region", |
35 | 39 | TenantName: "tenant", | ||
36 | 40 | } | ||
37 | 38 | switch s.authMethod { | 41 | switch s.authMethod { |
38 | 39 | default: | 42 | default: |
39 | 40 | panic("Invalid authentication method") | 43 | panic("Invalid authentication method") |
40 | 41 | case identity.AuthUserPass: | 44 | case identity.AuthUserPass: |
53 | 42 | s.identityDouble = identityservice.NewUserPass() | 45 | // The openstack test service sets up userpass authentication. |
54 | 43 | s.identityDouble.(*identityservice.UserPass).AddUser(s.cred.User, s.cred.Secrets) | 46 | s.service = openstack.New(s.cred) |
43 | 44 | ep := identityservice.Endpoint{ | ||
44 | 45 | AdminURL: s.Server.URL, | ||
45 | 46 | InternalURL: s.Server.URL, | ||
46 | 47 | PublicURL: s.Server.URL, | ||
47 | 48 | Region: s.LiveTests.cred.Region, | ||
48 | 49 | } | ||
49 | 50 | service := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ep}} | ||
50 | 51 | s.identityDouble.(*identityservice.UserPass).AddService(service) | ||
51 | 52 | service = identityservice.Service{"swift", "object-store", []identityservice.Endpoint{ep}} | ||
52 | 53 | s.identityDouble.(*identityservice.UserPass).AddService(service) | ||
55 | 54 | case identity.AuthLegacy: | 47 | case identity.AuthLegacy: |
59 | 55 | s.identityDouble = identityservice.NewLegacy() | 48 | legacy := identityservice.NewLegacy() |
60 | 56 | var legacy = s.identityDouble.(*identityservice.Legacy) | 49 | legacy.AddUser(s.cred.User, s.cred.Secrets, s.cred.TenantName) |
58 | 57 | legacy.AddUser(s.cred.User, s.cred.Secrets) | ||
61 | 58 | legacy.SetManagementURL("http://management.test.invalid/url") | 50 | legacy.SetManagementURL("http://management.test.invalid/url") |
62 | 51 | s.service = legacy | ||
63 | 59 | } | 52 | } |
64 | 60 | s.LiveTests.SetUpSuite(c) | 53 | s.LiveTests.SetUpSuite(c) |
65 | 61 | } | 54 | } |
66 | @@ -67,7 +60,7 @@ | |||
67 | 67 | 60 | ||
68 | 68 | func (s *localLiveSuite) SetUpTest(c *C) { | 61 | func (s *localLiveSuite) SetUpTest(c *C) { |
69 | 69 | s.HTTPSuite.SetUpTest(c) | 62 | s.HTTPSuite.SetUpTest(c) |
71 | 70 | s.Mux.Handle("/", s.identityDouble) | 63 | s.service.SetupHTTP(s.Mux) |
72 | 71 | s.LiveTests.SetUpTest(c) | 64 | s.LiveTests.SetUpTest(c) |
73 | 72 | } | 65 | } |
74 | 73 | 66 | ||
75 | 74 | 67 | ||
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-28 18:22:27 +0000 | |||
79 | @@ -15,13 +15,13 @@ | |||
80 | 15 | func (s *LegacyTestSuite) TestAuthAgainstServer(c *C) { | 15 | func (s *LegacyTestSuite) TestAuthAgainstServer(c *C) { |
81 | 16 | service := identityservice.NewLegacy() | 16 | service := identityservice.NewLegacy() |
82 | 17 | s.Mux.Handle("/", service) | 17 | s.Mux.Handle("/", service) |
84 | 18 | token := service.AddUser("joe-user", "secrets") | 18 | userInfo := service.AddUser("joe-user", "secrets", "tenant") |
85 | 19 | service.SetManagementURL("http://management.test.invalid/url") | 19 | service.SetManagementURL("http://management.test.invalid/url") |
86 | 20 | var l Authenticator = &Legacy{} | 20 | var l Authenticator = &Legacy{} |
87 | 21 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "secrets"} | 21 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "secrets"} |
88 | 22 | auth, err := l.Auth(&creds) | 22 | auth, err := l.Auth(&creds) |
89 | 23 | c.Assert(err, IsNil) | 23 | c.Assert(err, IsNil) |
91 | 24 | c.Assert(auth.Token, Equals, token) | 24 | c.Assert(auth.Token, Equals, userInfo.Token) |
92 | 25 | c.Assert( | 25 | c.Assert( |
93 | 26 | auth.ServiceURLs, DeepEquals, | 26 | auth.ServiceURLs, DeepEquals, |
94 | 27 | map[string]string{"compute": "http://management.test.invalid/url/compute", "object-store": "http://management.test.invalid/url/object-store"}) | 27 | 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 | 30 | func (s *LegacyTestSuite) TestBadAuth(c *C) { | 30 | func (s *LegacyTestSuite) TestBadAuth(c *C) { |
97 | 31 | service := identityservice.NewLegacy() | 31 | service := identityservice.NewLegacy() |
98 | 32 | s.Mux.Handle("/", service) | 32 | s.Mux.Handle("/", service) |
100 | 33 | _ = service.AddUser("joe-user", "secrets") | 33 | _ = service.AddUser("joe-user", "secrets", "tenant") |
101 | 34 | var l Authenticator = &Legacy{} | 34 | var l Authenticator = &Legacy{} |
102 | 35 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "bad-secrets"} | 35 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "bad-secrets"} |
103 | 36 | auth, err := l.Auth(&creds) | 36 | auth, err := l.Auth(&creds) |
104 | 37 | 37 | ||
105 | === added file 'identity/live_test.go' | |||
106 | --- identity/live_test.go 1970-01-01 00:00:00 +0000 | |||
107 | +++ identity/live_test.go 2013-01-28 18:22:27 +0000 | |||
108 | @@ -0,0 +1,40 @@ | |||
109 | 1 | package identity_test | ||
110 | 2 | |||
111 | 3 | import ( | ||
112 | 4 | . "launchpad.net/gocheck" | ||
113 | 5 | "launchpad.net/goose/client" | ||
114 | 6 | "launchpad.net/goose/identity" | ||
115 | 7 | ) | ||
116 | 8 | |||
117 | 9 | func registerOpenStackTests(cred *identity.Credentials) { | ||
118 | 10 | Suite(&LiveTests{ | ||
119 | 11 | cred: cred, | ||
120 | 12 | }) | ||
121 | 13 | } | ||
122 | 14 | |||
123 | 15 | type LiveTests struct { | ||
124 | 16 | cred *identity.Credentials | ||
125 | 17 | client client.AuthenticatingClient | ||
126 | 18 | } | ||
127 | 19 | |||
128 | 20 | func (s *LiveTests) SetUpSuite(c *C) { | ||
129 | 21 | s.client = client.NewClient(s.cred, identity.AuthUserPass, nil) | ||
130 | 22 | } | ||
131 | 23 | |||
132 | 24 | func (s *LiveTests) TearDownSuite(c *C) { | ||
133 | 25 | } | ||
134 | 26 | |||
135 | 27 | func (s *LiveTests) SetUpTest(c *C) { | ||
136 | 28 | // noop, called by local test suite. | ||
137 | 29 | } | ||
138 | 30 | |||
139 | 31 | func (s *LiveTests) TearDownTest(c *C) { | ||
140 | 32 | // noop, called by local test suite. | ||
141 | 33 | } | ||
142 | 34 | |||
143 | 35 | func (s *LiveTests) TestAuth(c *C) { | ||
144 | 36 | s.client.Authenticate() | ||
145 | 37 | url, err := s.client.MakeServiceURL("compute", []string{}) | ||
146 | 38 | c.Assert(err, IsNil) | ||
147 | 39 | c.Assert(url[:len(s.cred.URL)], Equals, s.cred.URL) | ||
148 | 40 | } | ||
149 | 0 | 41 | ||
150 | === added file 'identity/local_test.go' | |||
151 | --- identity/local_test.go 1970-01-01 00:00:00 +0000 | |||
152 | +++ identity/local_test.go 2013-01-28 18:22:27 +0000 | |||
153 | @@ -0,0 +1,63 @@ | |||
154 | 1 | package identity_test | ||
155 | 2 | |||
156 | 3 | import ( | ||
157 | 4 | . "launchpad.net/gocheck" | ||
158 | 5 | "launchpad.net/goose/identity" | ||
159 | 6 | "launchpad.net/goose/testservices/openstack" | ||
160 | 7 | "net/http" | ||
161 | 8 | "net/http/httptest" | ||
162 | 9 | ) | ||
163 | 10 | |||
164 | 11 | func registerLocalTests() { | ||
165 | 12 | Suite(&localLiveSuite{}) | ||
166 | 13 | } | ||
167 | 14 | |||
168 | 15 | // localLiveSuite runs tests from LiveTests using a fake | ||
169 | 16 | // nova server that runs within the test process itself. | ||
170 | 17 | type localLiveSuite struct { | ||
171 | 18 | LiveTests | ||
172 | 19 | // The following attributes are for using testing doubles. | ||
173 | 20 | Server *httptest.Server | ||
174 | 21 | Mux *http.ServeMux | ||
175 | 22 | oldHandler http.Handler | ||
176 | 23 | } | ||
177 | 24 | |||
178 | 25 | func (s *localLiveSuite) SetUpSuite(c *C) { | ||
179 | 26 | c.Logf("Using identity and nova service test doubles") | ||
180 | 27 | |||
181 | 28 | // Set up the HTTP server. | ||
182 | 29 | s.Server = httptest.NewServer(nil) | ||
183 | 30 | s.oldHandler = s.Server.Config.Handler | ||
184 | 31 | s.Mux = http.NewServeMux() | ||
185 | 32 | s.Server.Config.Handler = s.Mux | ||
186 | 33 | |||
187 | 34 | // Set up an Openstack service. | ||
188 | 35 | s.cred = &identity.Credentials{ | ||
189 | 36 | URL: s.Server.URL, | ||
190 | 37 | User: "fred", | ||
191 | 38 | Secrets: "secret", | ||
192 | 39 | Region: "some region", | ||
193 | 40 | TenantName: "tenant", | ||
194 | 41 | } | ||
195 | 42 | openstack := openstack.New(s.cred) | ||
196 | 43 | openstack.SetupHTTP(s.Mux) | ||
197 | 44 | |||
198 | 45 | s.LiveTests.SetUpSuite(c) | ||
199 | 46 | } | ||
200 | 47 | |||
201 | 48 | func (s *localLiveSuite) TearDownSuite(c *C) { | ||
202 | 49 | s.LiveTests.TearDownSuite(c) | ||
203 | 50 | s.Mux = nil | ||
204 | 51 | s.Server.Config.Handler = s.oldHandler | ||
205 | 52 | s.Server.Close() | ||
206 | 53 | } | ||
207 | 54 | |||
208 | 55 | func (s *localLiveSuite) SetUpTest(c *C) { | ||
209 | 56 | s.LiveTests.SetUpTest(c) | ||
210 | 57 | } | ||
211 | 58 | |||
212 | 59 | func (s *localLiveSuite) TearDownTest(c *C) { | ||
213 | 60 | s.LiveTests.TearDownTest(c) | ||
214 | 61 | } | ||
215 | 62 | |||
216 | 63 | // Additional tests to be run against the service double only go here. | ||
217 | 0 | 64 | ||
218 | === modified file 'identity/setup_test.go' | |||
219 | --- identity/setup_test.go 2012-11-02 10:59:29 +0000 | |||
220 | +++ identity/setup_test.go 2013-01-28 18:22:27 +0000 | |||
221 | @@ -1,10 +1,22 @@ | |||
223 | 1 | package identity | 1 | package identity_test |
224 | 2 | 2 | ||
225 | 3 | import ( | 3 | import ( |
226 | 4 | "flag" | ||
227 | 4 | . "launchpad.net/gocheck" | 5 | . "launchpad.net/gocheck" |
228 | 6 | "launchpad.net/goose/identity" | ||
229 | 5 | "testing" | 7 | "testing" |
230 | 6 | ) | 8 | ) |
231 | 7 | 9 | ||
232 | 10 | var live = flag.Bool("live", false, "Include live OpenStack (Canonistack) tests") | ||
233 | 11 | |||
234 | 8 | func Test(t *testing.T) { | 12 | func Test(t *testing.T) { |
235 | 13 | if *live { | ||
236 | 14 | cred, err := identity.CompleteCredentialsFromEnv() | ||
237 | 15 | if err != nil { | ||
238 | 16 | t.Fatalf("Error setting up test suite: %s", err.Error()) | ||
239 | 17 | } | ||
240 | 18 | registerOpenStackTests(cred) | ||
241 | 19 | } | ||
242 | 20 | registerLocalTests() | ||
243 | 9 | TestingT(t) | 21 | TestingT(t) |
244 | 10 | } | 22 | } |
245 | 11 | 23 | ||
246 | === modified file 'identity/userpass.go' | |||
247 | --- identity/userpass.go 2012-12-11 04:09:12 +0000 | |||
248 | +++ identity/userpass.go 2013-01-28 18:22:27 +0000 | |||
249 | @@ -4,6 +4,7 @@ | |||
250 | 4 | "fmt" | 4 | "fmt" |
251 | 5 | goosehttp "launchpad.net/goose/http" | 5 | goosehttp "launchpad.net/goose/http" |
252 | 6 | "net/http" | 6 | "net/http" |
253 | 7 | "os" | ||
254 | 7 | ) | 8 | ) |
255 | 8 | 9 | ||
256 | 9 | type passwordCredentials struct { | 10 | type passwordCredentials struct { |
257 | @@ -104,6 +105,9 @@ | |||
258 | 104 | service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...) | 105 | service.Endpoints = append(service.Endpoints[:i], service.Endpoints[i+1:]...) |
259 | 105 | } | 106 | } |
260 | 106 | } | 107 | } |
261 | 108 | if len(service.Endpoints) == 0 { | ||
262 | 109 | fmt.Fprintf(os.Stderr, "Found no endpoints for %v\n", service.Type) | ||
263 | 110 | } | ||
264 | 107 | details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL | 111 | details.ServiceURLs[service.Type] = service.Endpoints[0].PublicURL |
265 | 108 | } | 112 | } |
266 | 109 | 113 | ||
267 | 110 | 114 | ||
268 | === modified file 'identity/userpass_test.go' | |||
269 | --- identity/userpass_test.go 2013-01-14 03:18:37 +0000 | |||
270 | +++ identity/userpass_test.go 2013-01-28 18:22:27 +0000 | |||
271 | @@ -14,12 +14,12 @@ | |||
272 | 14 | 14 | ||
273 | 15 | func (s *UserPassTestSuite) TestAuthAgainstServer(c *C) { | 15 | func (s *UserPassTestSuite) TestAuthAgainstServer(c *C) { |
274 | 16 | service := identityservice.NewUserPass() | 16 | service := identityservice.NewUserPass() |
277 | 17 | s.Mux.Handle("/", service) | 17 | service.SetupHTTP(s.Mux) |
278 | 18 | token := service.AddUser("joe-user", "secrets") | 18 | userInfo := service.AddUser("joe-user", "secrets", "tenant") |
279 | 19 | var l Authenticator = &UserPass{} | 19 | var l Authenticator = &UserPass{} |
281 | 20 | creds := Credentials{User: "joe-user", URL: s.Server.URL, Secrets: "secrets"} | 20 | creds := Credentials{User: "joe-user", URL: s.Server.URL + "/tokens", Secrets: "secrets"} |
282 | 21 | auth, err := l.Auth(&creds) | 21 | auth, err := l.Auth(&creds) |
283 | 22 | c.Assert(err, IsNil) | 22 | c.Assert(err, IsNil) |
286 | 23 | c.Assert(auth.Token, Equals, token) | 23 | c.Assert(auth.Token, Equals, userInfo.Token) |
287 | 24 | // c.Assert(auth.ServiceURLs, DeepEquals, map[string]string{"compute": "http://management.test.invalid/url"}) | 24 | c.Assert(auth.TenantId, Equals, userInfo.TenantId) |
288 | 25 | } | 25 | } |
289 | 26 | 26 | ||
290 | === modified file 'nova/local_test.go' | |||
291 | --- nova/local_test.go 2013-01-21 11:18:33 +0000 | |||
292 | +++ nova/local_test.go 2013-01-28 18:22:27 +0000 | |||
293 | @@ -7,8 +7,7 @@ | |||
294 | 7 | "launchpad.net/goose/errors" | 7 | "launchpad.net/goose/errors" |
295 | 8 | "launchpad.net/goose/identity" | 8 | "launchpad.net/goose/identity" |
296 | 9 | "launchpad.net/goose/nova" | 9 | "launchpad.net/goose/nova" |
299 | 10 | "launchpad.net/goose/testservices/identityservice" | 10 | "launchpad.net/goose/testservices/openstack" |
298 | 11 | "launchpad.net/goose/testservices/novaservice" | ||
300 | 12 | "log" | 11 | "log" |
301 | 13 | "net/http" | 12 | "net/http" |
302 | 14 | "net/http/httptest" | 13 | "net/http/httptest" |
303 | @@ -19,20 +18,14 @@ | |||
304 | 19 | Suite(&localLiveSuite{}) | 18 | Suite(&localLiveSuite{}) |
305 | 20 | } | 19 | } |
306 | 21 | 20 | ||
307 | 22 | const ( | ||
308 | 23 | baseNovaURL = "/V1/1" | ||
309 | 24 | ) | ||
310 | 25 | |||
311 | 26 | // localLiveSuite runs tests from LiveTests using a fake | 21 | // localLiveSuite runs tests from LiveTests using a fake |
312 | 27 | // nova server that runs within the test process itself. | 22 | // nova server that runs within the test process itself. |
313 | 28 | type localLiveSuite struct { | 23 | type localLiveSuite struct { |
314 | 29 | LiveTests | 24 | LiveTests |
315 | 30 | // The following attributes are for using testing doubles. | 25 | // The following attributes are for using testing doubles. |
321 | 31 | Server *httptest.Server | 26 | Server *httptest.Server |
322 | 32 | Mux *http.ServeMux | 27 | Mux *http.ServeMux |
323 | 33 | oldHandler http.Handler | 28 | oldHandler http.Handler |
319 | 34 | identityDouble *identityservice.UserPass | ||
320 | 35 | novaDouble *novaservice.Nova | ||
324 | 36 | } | 29 | } |
325 | 37 | 30 | ||
326 | 38 | func (s *localLiveSuite) SetUpSuite(c *C) { | 31 | func (s *localLiveSuite) SetUpSuite(c *C) { |
327 | @@ -44,29 +37,16 @@ | |||
328 | 44 | s.Mux = http.NewServeMux() | 37 | s.Mux = http.NewServeMux() |
329 | 45 | s.Server.Config.Handler = s.Mux | 38 | s.Server.Config.Handler = s.Mux |
330 | 46 | 39 | ||
331 | 40 | // Set up an Openstack service. | ||
332 | 47 | s.cred = &identity.Credentials{ | 41 | s.cred = &identity.Credentials{ |
345 | 48 | URL: s.Server.URL, | 42 | URL: s.Server.URL, |
346 | 49 | User: "fred", | 43 | User: "fred", |
347 | 50 | Secrets: "secret", | 44 | Secrets: "secret", |
348 | 51 | Region: "some region"} | 45 | Region: "some region", |
349 | 52 | // Create an identity service and register a Nova endpoint. | 46 | TenantName: "tenant", |
338 | 53 | s.identityDouble = identityservice.NewUserPass() | ||
339 | 54 | token := s.identityDouble.AddUser(s.cred.User, s.cred.Secrets) | ||
340 | 55 | ep := identityservice.Endpoint{ | ||
341 | 56 | AdminURL: s.Server.URL + baseNovaURL, | ||
342 | 57 | InternalURL: s.Server.URL + baseNovaURL, | ||
343 | 58 | PublicURL: s.Server.URL + baseNovaURL, | ||
344 | 59 | Region: s.cred.Region, | ||
350 | 60 | } | 47 | } |
360 | 61 | s.Mux.Handle("/tokens", s.identityDouble) | 48 | openstack := openstack.New(s.cred) |
361 | 62 | 49 | openstack.SetupHTTP(s.Mux) | |
353 | 63 | service := identityservice.Service{"nova", "compute", []identityservice.Endpoint{ep}} | ||
354 | 64 | s.identityDouble.AddService(service) | ||
355 | 65 | // Create a nova service at the registered endpoint. | ||
356 | 66 | // TODO: identityservice.UserPass always uses tenantId="1", patch this | ||
357 | 67 | // when that changes. | ||
358 | 68 | s.novaDouble = novaservice.New("localhost", "V1", token, "1") | ||
359 | 69 | s.novaDouble.SetupHTTP(s.Mux) | ||
362 | 70 | 50 | ||
363 | 71 | s.LiveTests.SetUpSuite(c) | 51 | s.LiveTests.SetUpSuite(c) |
364 | 72 | } | 52 | } |
365 | 73 | 53 | ||
366 | === modified file 'swift/local_test.go' | |||
367 | --- swift/local_test.go 2012-12-20 05:47:11 +0000 | |||
368 | +++ swift/local_test.go 2013-01-28 18:22:27 +0000 | |||
369 | @@ -4,19 +4,13 @@ | |||
370 | 4 | . "launchpad.net/gocheck" | 4 | . "launchpad.net/gocheck" |
371 | 5 | "launchpad.net/goose/identity" | 5 | "launchpad.net/goose/identity" |
372 | 6 | "launchpad.net/goose/testing/httpsuite" | 6 | "launchpad.net/goose/testing/httpsuite" |
376 | 7 | "launchpad.net/goose/testservices/identityservice" | 7 | "launchpad.net/goose/testservices/openstack" |
374 | 8 | "launchpad.net/goose/testservices/swiftservice" | ||
375 | 9 | "net/http" | ||
377 | 10 | ) | 8 | ) |
378 | 11 | 9 | ||
379 | 12 | func registerLocalTests() { | 10 | func registerLocalTests() { |
380 | 13 | Suite(&localLiveSuite{}) | 11 | Suite(&localLiveSuite{}) |
381 | 14 | } | 12 | } |
382 | 15 | 13 | ||
383 | 16 | const ( | ||
384 | 17 | baseURL = "/object-store" | ||
385 | 18 | ) | ||
386 | 19 | |||
387 | 20 | // localLiveSuite runs tests from LiveTests using a fake | 14 | // localLiveSuite runs tests from LiveTests using a fake |
388 | 21 | // swift server that runs within the test process itself. | 15 | // swift server that runs within the test process itself. |
389 | 22 | type localLiveSuite struct { | 16 | type localLiveSuite struct { |
390 | @@ -24,32 +18,23 @@ | |||
391 | 24 | LiveTestsPublicContainer | 18 | LiveTestsPublicContainer |
392 | 25 | // The following attributes are for using testing doubles. | 19 | // The following attributes are for using testing doubles. |
393 | 26 | httpsuite.HTTPSuite | 20 | httpsuite.HTTPSuite |
396 | 27 | identityDouble http.Handler | 21 | openstack *openstack.Openstack |
395 | 28 | swiftDouble http.Handler | ||
397 | 29 | } | 22 | } |
398 | 30 | 23 | ||
399 | 31 | func (s *localLiveSuite) SetUpSuite(c *C) { | 24 | func (s *localLiveSuite) SetUpSuite(c *C) { |
400 | 32 | c.Logf("Using identity and swift service test doubles") | 25 | c.Logf("Using identity and swift service test doubles") |
401 | 33 | s.HTTPSuite.SetUpSuite(c) | 26 | s.HTTPSuite.SetUpSuite(c) |
402 | 27 | // Set up an Openstack service. | ||
403 | 34 | s.LiveTests.cred = &identity.Credentials{ | 28 | s.LiveTests.cred = &identity.Credentials{ |
408 | 35 | URL: s.Server.URL, | 29 | URL: s.Server.URL, |
409 | 36 | User: "fred", | 30 | User: "fred", |
410 | 37 | Secrets: "secret", | 31 | Secrets: "secret", |
411 | 38 | Region: "some region"} | 32 | Region: "some region", |
412 | 33 | TenantName: "tenant", | ||
413 | 34 | } | ||
414 | 39 | s.LiveTestsPublicContainer.cred = s.LiveTests.cred | 35 | s.LiveTestsPublicContainer.cred = s.LiveTests.cred |
428 | 40 | // Create an identity service and register a Swift endpoint. | 36 | s.openstack = openstack.New(s.LiveTests.cred) |
429 | 41 | s.identityDouble = identityservice.NewUserPass() | 37 | |
417 | 42 | token := s.identityDouble.(*identityservice.UserPass).AddUser(s.LiveTests.cred.User, s.LiveTests.cred.Secrets) | ||
418 | 43 | ep := identityservice.Endpoint{ | ||
419 | 44 | s.Server.URL + baseURL, //admin | ||
420 | 45 | s.Server.URL + baseURL, //internal | ||
421 | 46 | s.Server.URL + baseURL, //public | ||
422 | 47 | s.LiveTests.cred.Region, | ||
423 | 48 | } | ||
424 | 49 | service := identityservice.Service{"swift", "object-store", []identityservice.Endpoint{ep}} | ||
425 | 50 | s.identityDouble.(*identityservice.UserPass).AddService(service) | ||
426 | 51 | // Create a swift service at the registered endpoint. | ||
427 | 52 | s.swiftDouble = swiftservice.New("localhost", baseURL+"/", token) | ||
430 | 53 | s.LiveTests.SetUpSuite(c) | 38 | s.LiveTests.SetUpSuite(c) |
431 | 54 | s.LiveTestsPublicContainer.SetUpSuite(c) | 39 | s.LiveTestsPublicContainer.SetUpSuite(c) |
432 | 55 | } | 40 | } |
433 | @@ -62,8 +47,7 @@ | |||
434 | 62 | 47 | ||
435 | 63 | func (s *localLiveSuite) SetUpTest(c *C) { | 48 | func (s *localLiveSuite) SetUpTest(c *C) { |
436 | 64 | s.HTTPSuite.SetUpTest(c) | 49 | s.HTTPSuite.SetUpTest(c) |
439 | 65 | s.Mux.Handle(baseURL+"/", s.swiftDouble) | 50 | s.openstack.SetupHTTP(s.Mux) |
438 | 66 | s.Mux.Handle("/", s.identityDouble) | ||
440 | 67 | s.LiveTests.SetUpTest(c) | 51 | s.LiveTests.SetUpTest(c) |
441 | 68 | s.LiveTestsPublicContainer.SetUpTest(c) | 52 | s.LiveTestsPublicContainer.SetUpTest(c) |
442 | 69 | } | 53 | } |
443 | 70 | 54 | ||
444 | === modified file 'test.py' | |||
445 | --- test.py 2013-01-23 10:25:44 +0000 | |||
446 | +++ test.py 2013-01-28 18:22:27 +0000 | |||
447 | @@ -8,6 +8,7 @@ | |||
448 | 8 | KNOWN_LIVE_SUITES = [ | 8 | KNOWN_LIVE_SUITES = [ |
449 | 9 | 'client', | 9 | 'client', |
450 | 10 | 'glance', | 10 | 'glance', |
451 | 11 | 'identity', | ||
452 | 11 | 'nova', | 12 | 'nova', |
453 | 12 | 'swift', | 13 | 'swift', |
454 | 13 | ] | 14 | ] |
455 | 14 | 15 | ||
456 | === added directory 'testservices/cmd' | |||
457 | === renamed file 'testservices/main.go' => 'testservices/cmd/main.go' | |||
458 | --- testservices/main.go 2012-11-11 11:13:52 +0000 | |||
459 | +++ testservices/cmd/main.go 2013-01-28 18:22:27 +0000 | |||
460 | @@ -61,9 +61,10 @@ | |||
461 | 61 | if !ok { | 61 | if !ok { |
462 | 62 | log.Fatalf("No such provider: %s, pick one of: %v", provider, providers()) | 62 | log.Fatalf("No such provider: %s, pick one of: %v", provider, providers()) |
463 | 63 | } | 63 | } |
465 | 64 | http.Handle("/", p) | 64 | mux := http.NewServeMux() |
466 | 65 | p.SetupHTTP(mux) | ||
467 | 65 | for _, u := range users.users { | 66 | for _, u := range users.users { |
469 | 66 | p.AddUser(u.user, u.secret) | 67 | p.AddUser(u.user, u.secret, "tenant") |
470 | 67 | } | 68 | } |
472 | 68 | log.Fatal(http.ListenAndServe(*serveAddr, nil)) | 69 | log.Fatal(http.ListenAndServe(*serveAddr, mux)) |
473 | 69 | } | 70 | } |
474 | 70 | 71 | ||
475 | === modified file 'testservices/identityservice/identityservice.go' | |||
476 | --- testservices/identityservice/identityservice.go 2012-11-11 11:13:52 +0000 | |||
477 | +++ testservices/identityservice/identityservice.go 2013-01-28 18:22:27 +0000 | |||
478 | @@ -1,10 +1,16 @@ | |||
479 | 1 | package identityservice | 1 | package identityservice |
480 | 2 | 2 | ||
484 | 3 | import ( | 3 | import "net/http" |
482 | 4 | "net/http" | ||
483 | 5 | ) | ||
485 | 6 | 4 | ||
486 | 5 | // An IdentityService provides user authentication for an Openstack instance. | ||
487 | 7 | type IdentityService interface { | 6 | type IdentityService interface { |
490 | 8 | AddUser(user, secret string) (token string) | 7 | AddUser(user, secret, tenant string) *UserInfo |
491 | 9 | ServeHTTP(w http.ResponseWriter, r *http.Request) | 8 | FindUser(token string) (*UserInfo, error) |
492 | 9 | RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) | ||
493 | 10 | SetupHTTP(mux *http.ServeMux) | ||
494 | 11 | } | ||
495 | 12 | |||
496 | 13 | // A ServiceProvider is an Openstack module which has service endpoints. | ||
497 | 14 | type ServiceProvider interface { | ||
498 | 15 | Endpoints() []Endpoint | ||
499 | 10 | } | 16 | } |
500 | 11 | 17 | ||
501 | === modified file 'testservices/identityservice/legacy.go' | |||
502 | --- testservices/identityservice/legacy.go 2012-12-21 05:07:39 +0000 | |||
503 | +++ testservices/identityservice/legacy.go 2013-01-28 18:22:27 +0000 | |||
504 | @@ -5,40 +5,48 @@ | |||
505 | 5 | ) | 5 | ) |
506 | 6 | 6 | ||
507 | 7 | type Legacy struct { | 7 | type Legacy struct { |
509 | 8 | tokens map[string]UserInfo | 8 | Users |
510 | 9 | managementURL string | 9 | managementURL string |
511 | 10 | } | 10 | } |
512 | 11 | 11 | ||
513 | 12 | func NewLegacy() *Legacy { | 12 | func NewLegacy() *Legacy { |
514 | 13 | service := &Legacy{} | 13 | service := &Legacy{} |
516 | 14 | service.tokens = make(map[string]UserInfo) | 14 | service.users = make(map[string]UserInfo) |
517 | 15 | service.tenants = make(map[string]string) | ||
518 | 15 | return service | 16 | return service |
519 | 16 | } | 17 | } |
520 | 17 | 18 | ||
521 | 19 | func (lis *Legacy) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { | ||
522 | 20 | // NOOP for legacy identity service. | ||
523 | 21 | } | ||
524 | 22 | |||
525 | 18 | func (lis *Legacy) SetManagementURL(URL string) { | 23 | func (lis *Legacy) SetManagementURL(URL string) { |
526 | 19 | lis.managementURL = URL | 24 | lis.managementURL = URL |
527 | 20 | } | 25 | } |
528 | 21 | 26 | ||
533 | 22 | func (lis *Legacy) AddUser(user, secret string) string { | 27 | // setupHTTP attaches all the needed handlers to provide the HTTP API. |
534 | 23 | token := randomHexToken() | 28 | func (lis *Legacy) SetupHTTP(mux *http.ServeMux) { |
535 | 24 | lis.tokens[user] = UserInfo{secret: secret, token: token} | 29 | mux.Handle("/", lis) |
532 | 25 | return token | ||
536 | 26 | } | 30 | } |
537 | 27 | 31 | ||
538 | 28 | func (lis *Legacy) ServeHTTP(w http.ResponseWriter, r *http.Request) { | 32 | func (lis *Legacy) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
539 | 29 | username := r.Header.Get("X-Auth-User") | 33 | username := r.Header.Get("X-Auth-User") |
541 | 30 | info, ok := lis.tokens[username] | 34 | userInfo, ok := lis.users[username] |
542 | 31 | if !ok { | 35 | if !ok { |
543 | 32 | w.WriteHeader(http.StatusUnauthorized) | 36 | w.WriteHeader(http.StatusUnauthorized) |
544 | 33 | return | 37 | return |
545 | 34 | } | 38 | } |
546 | 35 | auth_key := r.Header.Get("X-Auth-Key") | 39 | auth_key := r.Header.Get("X-Auth-Key") |
548 | 36 | if auth_key != info.secret { | 40 | if auth_key != userInfo.secret { |
549 | 37 | w.WriteHeader(http.StatusUnauthorized) | 41 | w.WriteHeader(http.StatusUnauthorized) |
550 | 38 | return | 42 | return |
551 | 39 | } | 43 | } |
552 | 44 | if userInfo.Token == "" { | ||
553 | 45 | userInfo.Token = randomHexToken() | ||
554 | 46 | lis.users[username] = userInfo | ||
555 | 47 | } | ||
556 | 40 | header := w.Header() | 48 | header := w.Header() |
558 | 41 | header.Set("X-Auth-Token", info.token) | 49 | header.Set("X-Auth-Token", userInfo.Token) |
559 | 42 | header.Set("X-Server-Management-Url", lis.managementURL+"/compute") | 50 | header.Set("X-Server-Management-Url", lis.managementURL+"/compute") |
560 | 43 | header.Set("X-Storage-Url", lis.managementURL+"/object-store") | 51 | header.Set("X-Storage-Url", lis.managementURL+"/object-store") |
561 | 44 | w.WriteHeader(http.StatusNoContent) | 52 | w.WriteHeader(http.StatusNoContent) |
562 | 45 | 53 | ||
563 | === modified file 'testservices/identityservice/legacy_test.go' | |||
564 | --- testservices/identityservice/legacy_test.go 2012-12-21 05:07:39 +0000 | |||
565 | +++ testservices/identityservice/legacy_test.go 2013-01-28 18:22:27 +0000 | |||
566 | @@ -19,9 +19,10 @@ | |||
567 | 19 | // Ensure that it conforms to the interface | 19 | // Ensure that it conforms to the interface |
568 | 20 | var _ IdentityService = identity | 20 | var _ IdentityService = identity |
569 | 21 | identity.SetManagementURL(managementURL) | 21 | identity.SetManagementURL(managementURL) |
571 | 22 | s.Mux.Handle("/", identity) | 22 | identity.SetupHTTP(s.Mux) |
572 | 23 | if user != "" { | 23 | if user != "" { |
574 | 24 | token = identity.AddUser(user, secret) | 24 | userInfo := identity.AddUser(user, secret, "tenant") |
575 | 25 | token = userInfo.Token | ||
576 | 25 | } | 26 | } |
577 | 26 | return | 27 | return |
578 | 27 | } | 28 | } |
579 | 28 | 29 | ||
580 | === modified file 'testservices/identityservice/service_test.go' | |||
581 | --- testservices/identityservice/service_test.go 2012-11-11 15:29:52 +0000 | |||
582 | +++ testservices/identityservice/service_test.go 2013-01-28 18:22:27 +0000 | |||
583 | @@ -17,7 +17,7 @@ | |||
584 | 17 | var _ = Suite(&IdentityServiceSuite{service: NewLegacy()}) | 17 | var _ = Suite(&IdentityServiceSuite{service: NewLegacy()}) |
585 | 18 | 18 | ||
586 | 19 | func (s *IdentityServiceSuite) TestAddUserGivesNewToken(c *C) { | 19 | func (s *IdentityServiceSuite) TestAddUserGivesNewToken(c *C) { |
590 | 20 | token1 := s.service.AddUser("user-1", "password-1") | 20 | userInfo1 := s.service.AddUser("user-1", "password-1", "tenant") |
591 | 21 | token2 := s.service.AddUser("user-2", "password-2") | 21 | userInfo2 := s.service.AddUser("user-2", "password-2", "tenant") |
592 | 22 | c.Assert(token1, Not(Equals), token2) | 22 | c.Assert(userInfo1.Token, Not(Equals), userInfo2.Token) |
593 | 23 | } | 23 | } |
594 | 24 | 24 | ||
595 | === modified file 'testservices/identityservice/userpass.go' | |||
596 | --- testservices/identityservice/userpass.go 2012-12-21 04:10:14 +0000 | |||
597 | +++ testservices/identityservice/userpass.go 2013-01-28 18:22:27 +0000 | |||
598 | @@ -135,25 +135,25 @@ | |||
599 | 135 | }` | 135 | }` |
600 | 136 | 136 | ||
601 | 137 | type UserPass struct { | 137 | type UserPass struct { |
603 | 138 | users map[string]UserInfo | 138 | Users |
604 | 139 | services []Service | 139 | services []Service |
605 | 140 | } | 140 | } |
606 | 141 | 141 | ||
607 | 142 | func NewUserPass() *UserPass { | 142 | func NewUserPass() *UserPass { |
608 | 143 | userpass := &UserPass{ | 143 | userpass := &UserPass{ |
609 | 144 | users: make(map[string]UserInfo), | ||
610 | 145 | services: make([]Service, 0), | 144 | services: make([]Service, 0), |
611 | 146 | } | 145 | } |
612 | 146 | userpass.users = make(map[string]UserInfo) | ||
613 | 147 | userpass.tenants = make(map[string]string) | ||
614 | 147 | return userpass | 148 | return userpass |
615 | 148 | } | 149 | } |
616 | 149 | 150 | ||
621 | 150 | func (u *UserPass) AddUser(user, secret string) string { | 151 | func (u *UserPass) RegisterServiceProvider(name, serviceType string, serviceProvider ServiceProvider) { |
622 | 151 | token := randomHexToken() | 152 | service := Service{name, serviceType, serviceProvider.Endpoints()} |
623 | 152 | u.users[user] = UserInfo{secret: secret, token: token} | 153 | u.addService(service) |
620 | 153 | return token | ||
624 | 154 | } | 154 | } |
625 | 155 | 155 | ||
627 | 156 | func (u *UserPass) AddService(service Service) { | 156 | func (u *UserPass) addService(service Service) { |
628 | 157 | u.services = append(u.services, service) | 157 | u.services = append(u.services, service) |
629 | 158 | } | 158 | } |
630 | 159 | 159 | ||
631 | @@ -189,8 +189,6 @@ | |||
632 | 189 | notJSON = ("Expecting to find application/json in Content-Type header." + | 189 | notJSON = ("Expecting to find application/json in Content-Type header." + |
633 | 190 | " The server could not comply with the request since it is either malformed" + | 190 | " The server could not comply with the request since it is either malformed" + |
634 | 191 | " or otherwise incorrect. The client is assumed to be in error.") | 191 | " or otherwise incorrect. The client is assumed to be in error.") |
635 | 192 | notAuthorized = "The request you have made requires authentication." | ||
636 | 193 | invalidUser = "Invalid user / password" | ||
637 | 194 | ) | 192 | ) |
638 | 195 | 193 | ||
639 | 196 | func (u *UserPass) ServeHTTP(w http.ResponseWriter, r *http.Request) { | 194 | func (u *UserPass) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
640 | @@ -210,13 +208,9 @@ | |||
641 | 210 | return | 208 | return |
642 | 211 | } | 209 | } |
643 | 212 | } | 210 | } |
651 | 213 | userInfo, ok := u.users[req.Auth.PasswordCredentials.Username] | 211 | userInfo, errmsg := u.authenticate(req.Auth.PasswordCredentials.Username, req.Auth.PasswordCredentials.Password) |
652 | 214 | if !ok { | 212 | if errmsg != "" { |
653 | 215 | u.ReturnFailure(w, http.StatusUnauthorized, notAuthorized) | 213 | u.ReturnFailure(w, http.StatusUnauthorized, errmsg) |
647 | 216 | return | ||
648 | 217 | } | ||
649 | 218 | if userInfo.secret != req.Auth.PasswordCredentials.Password { | ||
650 | 219 | u.ReturnFailure(w, http.StatusUnauthorized, invalidUser) | ||
654 | 220 | return | 214 | return |
655 | 221 | } | 215 | } |
656 | 222 | res := AccessResponse{} | 216 | res := AccessResponse{} |
657 | @@ -228,7 +222,9 @@ | |||
658 | 228 | return | 222 | return |
659 | 229 | } | 223 | } |
660 | 230 | res.Access.ServiceCatalog = u.services | 224 | res.Access.ServiceCatalog = u.services |
662 | 231 | res.Access.Token.Id = userInfo.token | 225 | res.Access.Token.Id = userInfo.Token |
663 | 226 | res.Access.Token.Tenant.Id = userInfo.TenantId | ||
664 | 227 | res.Access.User.Id = userInfo.Id | ||
665 | 232 | if content, err := json.Marshal(res); err != nil { | 228 | if content, err := json.Marshal(res); err != nil { |
666 | 233 | u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) | 229 | u.ReturnFailure(w, http.StatusInternalServerError, err.Error()) |
667 | 234 | return | 230 | return |
668 | @@ -239,3 +235,8 @@ | |||
669 | 239 | } | 235 | } |
670 | 240 | panic("All paths should have already returned") | 236 | panic("All paths should have already returned") |
671 | 241 | } | 237 | } |
672 | 238 | |||
673 | 239 | // setupHTTP attaches all the needed handlers to provide the HTTP API. | ||
674 | 240 | func (u *UserPass) SetupHTTP(mux *http.ServeMux) { | ||
675 | 241 | mux.Handle("/tokens", u) | ||
676 | 242 | } | ||
677 | 242 | 243 | ||
678 | === modified file 'testservices/identityservice/userpass_test.go' | |||
679 | --- testservices/identityservice/userpass_test.go 2012-11-16 16:39:16 +0000 | |||
680 | +++ testservices/identityservice/userpass_test.go 2013-01-28 18:22:27 +0000 | |||
681 | @@ -16,30 +16,30 @@ | |||
682 | 16 | 16 | ||
683 | 17 | var _ = Suite(&UserPassSuite{}) | 17 | var _ = Suite(&UserPassSuite{}) |
684 | 18 | 18 | ||
686 | 19 | func makeUserPass(user, secret string) (identity *UserPass, token string) { | 19 | func makeUserPass(user, secret string) (identity *UserPass) { |
687 | 20 | identity = NewUserPass() | 20 | identity = NewUserPass() |
688 | 21 | // Ensure that it conforms to the interface | 21 | // Ensure that it conforms to the interface |
689 | 22 | var _ IdentityService = identity | 22 | var _ IdentityService = identity |
690 | 23 | if user != "" { | 23 | if user != "" { |
692 | 24 | token = identity.AddUser(user, secret) | 24 | identity.AddUser(user, secret, "tenant") |
693 | 25 | } | 25 | } |
694 | 26 | return | 26 | return |
695 | 27 | } | 27 | } |
696 | 28 | 28 | ||
698 | 29 | func (s *UserPassSuite) setupUserPass(user, secret string) (token string) { | 29 | func (s *UserPassSuite) setupUserPass(user, secret string) { |
699 | 30 | var identity *UserPass | 30 | var identity *UserPass |
702 | 31 | identity, token = makeUserPass(user, secret) | 31 | identity = makeUserPass(user, secret) |
703 | 32 | s.Mux.Handle("/", identity) | 32 | identity.SetupHTTP(s.Mux) |
704 | 33 | return | 33 | return |
705 | 34 | } | 34 | } |
706 | 35 | 35 | ||
708 | 36 | func (s *UserPassSuite) setupUserPassWithServices(user, secret string, services []Service) (token string) { | 36 | func (s *UserPassSuite) setupUserPassWithServices(user, secret string, services []Service) { |
709 | 37 | var identity *UserPass | 37 | var identity *UserPass |
711 | 38 | identity, token = makeUserPass(user, secret) | 38 | identity = makeUserPass(user, secret) |
712 | 39 | for _, service := range services { | 39 | for _, service := range services { |
714 | 40 | identity.AddService(service) | 40 | identity.addService(service) |
715 | 41 | } | 41 | } |
717 | 42 | s.Mux.Handle("/", identity) | 42 | identity.SetupHTTP(s.Mux) |
718 | 43 | return | 43 | return |
719 | 44 | } | 44 | } |
720 | 45 | 45 | ||
721 | @@ -56,7 +56,7 @@ | |||
722 | 56 | func userPassAuthRequest(URL, user, key string) (*http.Response, error) { | 56 | func userPassAuthRequest(URL, user, key string) (*http.Response, error) { |
723 | 57 | client := &http.Client{} | 57 | client := &http.Client{} |
724 | 58 | body := strings.NewReader(fmt.Sprintf(authTemplate, user, key)) | 58 | body := strings.NewReader(fmt.Sprintf(authTemplate, user, key)) |
726 | 59 | request, err := http.NewRequest("POST", URL, body) | 59 | request, err := http.NewRequest("POST", URL+"/tokens", body) |
727 | 60 | request.Header.Set("Content-Type", "application/json") | 60 | request.Header.Set("Content-Type", "application/json") |
728 | 61 | if err != nil { | 61 | if err != nil { |
729 | 62 | return nil, err | 62 | return nil, err |
730 | @@ -81,11 +81,10 @@ | |||
731 | 81 | 81 | ||
732 | 82 | func (s *UserPassSuite) TestNotJSON(c *C) { | 82 | func (s *UserPassSuite) TestNotJSON(c *C) { |
733 | 83 | // We do everything in userPassAuthRequest, except set the Content-Type | 83 | // We do everything in userPassAuthRequest, except set the Content-Type |
736 | 84 | token := s.setupUserPass("user", "secret") | 84 | s.setupUserPass("user", "secret") |
735 | 85 | c.Assert(token, NotNil) | ||
737 | 86 | client := &http.Client{} | 85 | client := &http.Client{} |
738 | 87 | body := strings.NewReader(fmt.Sprintf(authTemplate, "user", "secret")) | 86 | body := strings.NewReader(fmt.Sprintf(authTemplate, "user", "secret")) |
740 | 88 | request, err := http.NewRequest("POST", s.Server.URL, body) | 87 | request, err := http.NewRequest("POST", s.Server.URL+"/tokens", body) |
741 | 89 | c.Assert(err, IsNil) | 88 | c.Assert(err, IsNil) |
742 | 90 | res, err := client.Do(request) | 89 | res, err := client.Do(request) |
743 | 91 | defer res.Body.Close() | 90 | defer res.Body.Close() |
744 | @@ -95,8 +94,7 @@ | |||
745 | 95 | 94 | ||
746 | 96 | func (s *UserPassSuite) TestBadJSON(c *C) { | 95 | func (s *UserPassSuite) TestBadJSON(c *C) { |
747 | 97 | // We do everything in userPassAuthRequest, except set the Content-Type | 96 | // We do everything in userPassAuthRequest, except set the Content-Type |
750 | 98 | token := s.setupUserPass("user", "secret") | 97 | s.setupUserPass("user", "secret") |
749 | 99 | c.Assert(token, NotNil) | ||
751 | 100 | res, err := userPassAuthRequest(s.Server.URL, "garbage\"in", "secret") | 98 | res, err := userPassAuthRequest(s.Server.URL, "garbage\"in", "secret") |
752 | 101 | defer res.Body.Close() | 99 | defer res.Body.Close() |
753 | 102 | c.Assert(err, IsNil) | 100 | c.Assert(err, IsNil) |
754 | @@ -104,8 +102,7 @@ | |||
755 | 104 | } | 102 | } |
756 | 105 | 103 | ||
757 | 106 | func (s *UserPassSuite) TestNoSuchUser(c *C) { | 104 | func (s *UserPassSuite) TestNoSuchUser(c *C) { |
760 | 107 | token := s.setupUserPass("user", "secret") | 105 | s.setupUserPass("user", "secret") |
759 | 108 | c.Assert(token, NotNil) | ||
761 | 109 | res, err := userPassAuthRequest(s.Server.URL, "not-user", "secret") | 106 | res, err := userPassAuthRequest(s.Server.URL, "not-user", "secret") |
762 | 110 | defer res.Body.Close() | 107 | defer res.Body.Close() |
763 | 111 | c.Assert(err, IsNil) | 108 | c.Assert(err, IsNil) |
764 | @@ -113,8 +110,7 @@ | |||
765 | 113 | } | 110 | } |
766 | 114 | 111 | ||
767 | 115 | func (s *UserPassSuite) TestBadPassword(c *C) { | 112 | func (s *UserPassSuite) TestBadPassword(c *C) { |
770 | 116 | token := s.setupUserPass("user", "secret") | 113 | s.setupUserPass("user", "secret") |
769 | 117 | c.Assert(token, NotNil) | ||
771 | 118 | res, err := userPassAuthRequest(s.Server.URL, "user", "not-secret") | 114 | res, err := userPassAuthRequest(s.Server.URL, "user", "not-secret") |
772 | 119 | defer res.Body.Close() | 115 | defer res.Body.Close() |
773 | 120 | c.Assert(err, IsNil) | 116 | c.Assert(err, IsNil) |
774 | @@ -123,11 +119,10 @@ | |||
775 | 123 | 119 | ||
776 | 124 | func (s *UserPassSuite) TestValidAuthorization(c *C) { | 120 | func (s *UserPassSuite) TestValidAuthorization(c *C) { |
777 | 125 | compute_url := "http://testing.invalid/compute" | 121 | compute_url := "http://testing.invalid/compute" |
779 | 126 | token := s.setupUserPassWithServices("user", "secret", []Service{ | 122 | s.setupUserPassWithServices("user", "secret", []Service{ |
780 | 127 | {"nova", "compute", []Endpoint{ | 123 | {"nova", "compute", []Endpoint{ |
781 | 128 | {PublicURL: compute_url}, | 124 | {PublicURL: compute_url}, |
782 | 129 | }}}) | 125 | }}}) |
783 | 130 | c.Assert(token, NotNil) | ||
784 | 131 | res, err := userPassAuthRequest(s.Server.URL, "user", "secret") | 126 | res, err := userPassAuthRequest(s.Server.URL, "user", "secret") |
785 | 132 | defer res.Body.Close() | 127 | defer res.Body.Close() |
786 | 133 | c.Assert(err, IsNil) | 128 | c.Assert(err, IsNil) |
787 | @@ -138,7 +133,7 @@ | |||
788 | 138 | var response AccessResponse | 133 | var response AccessResponse |
789 | 139 | err = json.Unmarshal(content, &response) | 134 | err = json.Unmarshal(content, &response) |
790 | 140 | c.Assert(err, IsNil) | 135 | c.Assert(err, IsNil) |
792 | 141 | c.Check(response.Access.Token.Id, Equals, token) | 136 | c.Check(response.Access.Token.Id, NotNil) |
793 | 142 | novaURL := "" | 137 | novaURL := "" |
794 | 143 | for _, service := range response.Access.ServiceCatalog { | 138 | for _, service := range response.Access.ServiceCatalog { |
795 | 144 | if service.Type == "compute" { | 139 | if service.Type == "compute" { |
796 | 145 | 140 | ||
797 | === added file 'testservices/identityservice/users.go' | |||
798 | --- testservices/identityservice/users.go 1970-01-01 00:00:00 +0000 | |||
799 | +++ testservices/identityservice/users.go 2013-01-28 18:22:27 +0000 | |||
800 | @@ -0,0 +1,63 @@ | |||
801 | 1 | package identityservice | ||
802 | 2 | |||
803 | 3 | import ( | ||
804 | 4 | "fmt" | ||
805 | 5 | "strconv" | ||
806 | 6 | ) | ||
807 | 7 | |||
808 | 8 | type Users struct { | ||
809 | 9 | nextUserId int | ||
810 | 10 | nextTenantId int | ||
811 | 11 | users map[string]UserInfo | ||
812 | 12 | tenants map[string]string | ||
813 | 13 | } | ||
814 | 14 | |||
815 | 15 | func (u *Users) addTenant(tenant string) string { | ||
816 | 16 | for id, tenantName := range u.tenants { | ||
817 | 17 | if tenant == tenantName { | ||
818 | 18 | return id | ||
819 | 19 | } | ||
820 | 20 | } | ||
821 | 21 | u.nextTenantId++ | ||
822 | 22 | id := strconv.Itoa(u.nextTenantId) | ||
823 | 23 | u.tenants[id] = tenant | ||
824 | 24 | return id | ||
825 | 25 | } | ||
826 | 26 | |||
827 | 27 | func (u *Users) AddUser(user, secret, tenant string) *UserInfo { | ||
828 | 28 | tenantId := u.addTenant(tenant) | ||
829 | 29 | u.nextUserId++ | ||
830 | 30 | userInfo := &UserInfo{secret: secret, Id: strconv.Itoa(u.nextUserId), TenantId: tenantId} | ||
831 | 31 | u.users[user] = *userInfo | ||
832 | 32 | userInfo, _ = u.authenticate(user, secret) | ||
833 | 33 | return userInfo | ||
834 | 34 | } | ||
835 | 35 | |||
836 | 36 | func (u *Users) FindUser(token string) (*UserInfo, error) { | ||
837 | 37 | for _, userInfo := range u.users { | ||
838 | 38 | if userInfo.Token == token { | ||
839 | 39 | return &userInfo, nil | ||
840 | 40 | } | ||
841 | 41 | } | ||
842 | 42 | return nil, fmt.Errorf("No user with token %v exists", token) | ||
843 | 43 | } | ||
844 | 44 | |||
845 | 45 | const ( | ||
846 | 46 | notAuthorized = "The request you have made requires authentication." | ||
847 | 47 | invalidUser = "Invalid user / password" | ||
848 | 48 | ) | ||
849 | 49 | |||
850 | 50 | func (u *Users) authenticate(username, password string) (*UserInfo, string) { | ||
851 | 51 | userInfo, ok := u.users[username] | ||
852 | 52 | if !ok { | ||
853 | 53 | return nil, notAuthorized | ||
854 | 54 | } | ||
855 | 55 | if userInfo.secret != password { | ||
856 | 56 | return nil, invalidUser | ||
857 | 57 | } | ||
858 | 58 | if userInfo.Token == "" { | ||
859 | 59 | userInfo.Token = randomHexToken() | ||
860 | 60 | u.users[username] = userInfo | ||
861 | 61 | } | ||
862 | 62 | return &userInfo, "" | ||
863 | 63 | } | ||
864 | 0 | 64 | ||
865 | === modified file 'testservices/identityservice/util.go' | |||
866 | --- testservices/identityservice/util.go 2012-11-23 03:05:16 +0000 | |||
867 | +++ testservices/identityservice/util.go 2013-01-28 18:22:27 +0000 | |||
868 | @@ -7,8 +7,10 @@ | |||
869 | 7 | ) | 7 | ) |
870 | 8 | 8 | ||
871 | 9 | type UserInfo struct { | 9 | type UserInfo struct { |
874 | 10 | secret string | 10 | Id string |
875 | 11 | token string | 11 | TenantId string |
876 | 12 | Token string | ||
877 | 13 | secret string | ||
878 | 12 | } | 14 | } |
879 | 13 | 15 | ||
880 | 14 | // Generate a bit of random hex data for | 16 | // Generate a bit of random hex data for |
881 | 15 | 17 | ||
882 | === modified file 'testservices/novaservice/service.go' | |||
883 | --- testservices/novaservice/service.go 2013-01-21 23:57:31 +0000 | |||
884 | +++ testservices/novaservice/service.go 2013-01-28 18:22:27 +0000 | |||
885 | @@ -5,12 +5,19 @@ | |||
886 | 5 | import ( | 5 | import ( |
887 | 6 | "fmt" | 6 | "fmt" |
888 | 7 | "launchpad.net/goose/nova" | 7 | "launchpad.net/goose/nova" |
889 | 8 | "launchpad.net/goose/testservices" | ||
890 | 9 | "launchpad.net/goose/testservices/identityservice" | ||
891 | 10 | "net/url" | ||
892 | 8 | "strings" | 11 | "strings" |
893 | 9 | ) | 12 | ) |
894 | 10 | 13 | ||
895 | 14 | var _ testservices.HttpService = (*Nova)(nil) | ||
896 | 15 | var _ identityservice.ServiceProvider = (*Nova)(nil) | ||
897 | 16 | |||
898 | 11 | // Nova implements a OpenStack Nova testing service and | 17 | // Nova implements a OpenStack Nova testing service and |
899 | 12 | // contains the service double's internal state. | 18 | // contains the service double's internal state. |
900 | 13 | type Nova struct { | 19 | type Nova struct { |
901 | 20 | testservices.ServiceInstance | ||
902 | 14 | flavors map[string]nova.FlavorDetail | 21 | flavors map[string]nova.FlavorDetail |
903 | 15 | servers map[string]nova.ServerDetail | 22 | servers map[string]nova.ServerDetail |
904 | 16 | groups map[int]nova.SecurityGroup | 23 | groups map[int]nova.SecurityGroup |
905 | @@ -18,11 +25,6 @@ | |||
906 | 18 | floatingIPs map[int]nova.FloatingIP | 25 | floatingIPs map[int]nova.FloatingIP |
907 | 19 | serverGroups map[string][]int | 26 | serverGroups map[string][]int |
908 | 20 | serverIPs map[string][]int | 27 | serverIPs map[string][]int |
909 | 21 | hostname string | ||
910 | 22 | versionPath string | ||
911 | 23 | token string | ||
912 | 24 | tenantId string | ||
913 | 25 | userId string | ||
914 | 26 | nextGroupId int | 28 | nextGroupId int |
915 | 27 | nextRuleId int | 29 | nextRuleId int |
916 | 28 | nextIPId int | 30 | nextIPId int |
917 | @@ -31,17 +33,35 @@ | |||
918 | 31 | 33 | ||
919 | 32 | // endpoint returns either a versioned or non-versioned service | 34 | // endpoint returns either a versioned or non-versioned service |
920 | 33 | // endpoint URL from the given path. | 35 | // endpoint URL from the given path. |
923 | 34 | func (n *Nova) endpoint(version bool, path string) string { | 36 | func (n *Nova) endpointURL(version bool, path string) string { |
924 | 35 | ep := "http://" + n.hostname | 37 | ep := "http://" + n.Hostname |
925 | 36 | if version { | 38 | if version { |
929 | 37 | ep += n.versionPath + "/" | 39 | ep += n.VersionPath + "/" |
930 | 38 | } | 40 | } |
931 | 39 | ep += n.tenantId + "/" + strings.TrimLeft(path, "/") | 41 | ep += n.TenantId |
932 | 42 | if path != "" { | ||
933 | 43 | ep += "/" + strings.TrimLeft(path, "/") | ||
934 | 44 | } | ||
935 | 40 | return ep | 45 | return ep |
936 | 41 | } | 46 | } |
937 | 42 | 47 | ||
938 | 48 | func (n *Nova) Endpoints() []identityservice.Endpoint { | ||
939 | 49 | ep := identityservice.Endpoint{ | ||
940 | 50 | AdminURL: n.endpointURL(true, ""), | ||
941 | 51 | InternalURL: n.endpointURL(true, ""), | ||
942 | 52 | PublicURL: n.endpointURL(true, ""), | ||
943 | 53 | Region: n.Region, | ||
944 | 54 | } | ||
945 | 55 | return []identityservice.Endpoint{ep} | ||
946 | 56 | } | ||
947 | 57 | |||
948 | 43 | // New creates an instance of the Nova object, given the parameters. | 58 | // New creates an instance of the Nova object, given the parameters. |
950 | 44 | func New(hostname, versionPath, token, tenantId string) *Nova { | 59 | func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Nova { |
951 | 60 | url, err := url.Parse(hostURL) | ||
952 | 61 | if err != nil { | ||
953 | 62 | panic(err) | ||
954 | 63 | } | ||
955 | 64 | hostname := url.Host | ||
956 | 45 | if !strings.HasSuffix(hostname, "/") { | 65 | if !strings.HasSuffix(hostname, "/") { |
957 | 46 | hostname += "/" | 66 | hostname += "/" |
958 | 47 | } | 67 | } |
959 | @@ -62,17 +82,19 @@ | |||
960 | 62 | floatingIPs: make(map[int]nova.FloatingIP), | 82 | floatingIPs: make(map[int]nova.FloatingIP), |
961 | 63 | serverGroups: make(map[string][]int), | 83 | serverGroups: make(map[string][]int), |
962 | 64 | serverIPs: make(map[string][]int), | 84 | serverIPs: make(map[string][]int), |
963 | 65 | hostname: hostname, | ||
964 | 66 | versionPath: versionPath, | ||
965 | 67 | token: token, | ||
966 | 68 | tenantId: tenantId, | ||
967 | 69 | // TODO(wallyworld): Identity service double currently hard codes all user ids to "14". This should be fixed | ||
968 | 70 | // in the identity service but the fix will result in an API change which will break juju-core. So for now | ||
969 | 71 | // we will also hard code it here too. | ||
970 | 72 | userId: "14", | ||
971 | 73 | // The following attribute controls whether rate limit responses are sent back to the caller. | 85 | // The following attribute controls whether rate limit responses are sent back to the caller. |
972 | 74 | // This is switched off when we want to ensure the client eventually gets a proper response. | 86 | // This is switched off when we want to ensure the client eventually gets a proper response. |
973 | 75 | sendFakeRateLimitResponse: true, | 87 | sendFakeRateLimitResponse: true, |
974 | 88 | ServiceInstance: testservices.ServiceInstance{ | ||
975 | 89 | IdentityService: identityService, | ||
976 | 90 | Hostname: hostname, | ||
977 | 91 | VersionPath: versionPath, | ||
978 | 92 | TenantId: tenantId, | ||
979 | 93 | Region: region, | ||
980 | 94 | }, | ||
981 | 95 | } | ||
982 | 96 | if identityService != nil { | ||
983 | 97 | identityService.RegisterServiceProvider("nova", "compute", nova) | ||
984 | 76 | } | 98 | } |
985 | 77 | for i, flavor := range defaultFlavors { | 99 | for i, flavor := range defaultFlavors { |
986 | 78 | nova.buildFlavorLinks(&flavor) | 100 | nova.buildFlavorLinks(&flavor) |
987 | @@ -97,8 +119,8 @@ | |||
988 | 97 | func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) { | 119 | func (n *Nova) buildFlavorLinks(flavor *nova.FlavorDetail) { |
989 | 98 | url := "/flavors/" + flavor.Id | 120 | url := "/flavors/" + flavor.Id |
990 | 99 | flavor.Links = []nova.Link{ | 121 | flavor.Links = []nova.Link{ |
993 | 100 | nova.Link{Href: n.endpoint(true, url), Rel: "self"}, | 122 | nova.Link{Href: n.endpointURL(true, url), Rel: "self"}, |
994 | 101 | nova.Link{Href: n.endpoint(false, url), Rel: "bookmark"}, | 123 | nova.Link{Href: n.endpointURL(false, url), Rel: "bookmark"}, |
995 | 102 | } | 124 | } |
996 | 103 | } | 125 | } |
997 | 104 | 126 | ||
998 | @@ -170,8 +192,8 @@ | |||
999 | 170 | func (n *Nova) buildServerLinks(server *nova.ServerDetail) { | 192 | func (n *Nova) buildServerLinks(server *nova.ServerDetail) { |
1000 | 171 | url := "/servers/" + server.Id | 193 | url := "/servers/" + server.Id |
1001 | 172 | server.Links = []nova.Link{ | 194 | server.Links = []nova.Link{ |
1004 | 173 | nova.Link{Href: n.endpoint(true, url), Rel: "self"}, | 195 | nova.Link{Href: n.endpointURL(true, url), Rel: "self"}, |
1005 | 174 | nova.Link{Href: n.endpoint(false, url), Rel: "bookmark"}, | 196 | nova.Link{Href: n.endpointURL(false, url), Rel: "bookmark"}, |
1006 | 175 | } | 197 | } |
1007 | 176 | } | 198 | } |
1008 | 177 | 199 | ||
1009 | @@ -280,7 +302,7 @@ | |||
1010 | 280 | if _, err := n.securityGroup(group.Id); err == nil { | 302 | if _, err := n.securityGroup(group.Id); err == nil { |
1011 | 281 | return fmt.Errorf("a security group with id %d already exists", group.Id) | 303 | return fmt.Errorf("a security group with id %d already exists", group.Id) |
1012 | 282 | } | 304 | } |
1014 | 283 | group.TenantId = n.tenantId | 305 | group.TenantId = n.TenantId |
1015 | 284 | if group.Rules == nil { | 306 | if group.Rules == nil { |
1016 | 285 | group.Rules = []nova.SecurityGroupRule{} | 307 | group.Rules = []nova.SecurityGroupRule{} |
1017 | 286 | } | 308 | } |
1018 | 287 | 309 | ||
1019 | === modified file 'testservices/novaservice/service_http.go' | |||
1020 | --- testservices/novaservice/service_http.go 2013-01-21 12:54:46 +0000 | |||
1021 | +++ testservices/novaservice/service_http.go 2013-01-28 18:22:27 +0000 | |||
1022 | @@ -9,6 +9,7 @@ | |||
1023 | 9 | "io" | 9 | "io" |
1024 | 10 | "io/ioutil" | 10 | "io/ioutil" |
1025 | 11 | "launchpad.net/goose/nova" | 11 | "launchpad.net/goose/nova" |
1026 | 12 | "launchpad.net/goose/testservices/identityservice" | ||
1027 | 12 | "net/http" | 13 | "net/http" |
1028 | 13 | "path" | 14 | "path" |
1029 | 14 | "strconv" | 15 | "strconv" |
1030 | @@ -228,7 +229,7 @@ | |||
1031 | 228 | body := e.body | 229 | body := e.body |
1032 | 229 | if body != "" { | 230 | if body != "" { |
1033 | 230 | if e.nova != nil { | 231 | if e.nova != nil { |
1035 | 231 | body = strings.Replace(body, "$ENDPOINT$", e.nova.endpoint(true, "/"), -1) | 232 | body = strings.Replace(body, "$ENDPOINT$", e.nova.endpointURL(true, "/"), -1) |
1036 | 232 | } | 233 | } |
1037 | 233 | body = strings.Replace(body, "$URL$", url, -1) | 234 | body = strings.Replace(body, "$URL$", url, -1) |
1038 | 234 | body = strings.Replace(body, "$ERROR$", e.Error(), -1) | 235 | body = strings.Replace(body, "$ERROR$", e.Error(), -1) |
1039 | @@ -264,10 +265,15 @@ | |||
1040 | 264 | method func(n *Nova, w http.ResponseWriter, r *http.Request) error | 265 | method func(n *Nova, w http.ResponseWriter, r *http.Request) error |
1041 | 265 | } | 266 | } |
1042 | 266 | 267 | ||
1043 | 268 | func userInfo(i identityservice.IdentityService, r *http.Request) (*identityservice.UserInfo, error) { | ||
1044 | 269 | return i.FindUser(r.Header.Get(authToken)) | ||
1045 | 270 | } | ||
1046 | 271 | |||
1047 | 267 | func (h *novaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | 272 | func (h *novaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
1048 | 268 | path := r.URL.Path | 273 | path := r.URL.Path |
1049 | 269 | // handle invalid X-Auth-Token header | 274 | // handle invalid X-Auth-Token header |
1051 | 270 | if r.Header.Get(authToken) != h.n.token { | 275 | _, err := userInfo(h.n.IdentityService, r) |
1052 | 276 | if err != nil { | ||
1053 | 271 | errUnauthorized.ServeHTTP(w, r) | 277 | errUnauthorized.ServeHTTP(w, r) |
1054 | 272 | return | 278 | return |
1055 | 273 | } | 279 | } |
1056 | @@ -276,7 +282,7 @@ | |||
1057 | 276 | errNotFound.ServeHTTP(w, r) | 282 | errNotFound.ServeHTTP(w, r) |
1058 | 277 | return | 283 | return |
1059 | 278 | } | 284 | } |
1061 | 279 | err := h.method(h.n, w, r) | 285 | err = h.method(h.n, w, r) |
1062 | 280 | if err == nil { | 286 | if err == nil { |
1063 | 281 | return | 287 | return |
1064 | 282 | } | 288 | } |
1065 | @@ -541,7 +547,7 @@ | |||
1066 | 541 | continue | 547 | continue |
1067 | 542 | } | 548 | } |
1068 | 543 | if sg, err := n.securityGroupByName(groupName); err != nil { | 549 | if sg, err := n.securityGroupByName(groupName); err != nil { |
1070 | 544 | return noGroupError(groupName, n.tenantId) | 550 | return noGroupError(groupName, n.TenantId) |
1071 | 545 | } else { | 551 | } else { |
1072 | 546 | groups = append(groups, sg.Id) | 552 | groups = append(groups, sg.Id) |
1073 | 547 | } | 553 | } |
1074 | @@ -553,11 +559,12 @@ | |||
1075 | 553 | flavorEnt := nova.Entity{Id: flavor.Id, Links: flavor.Links} | 559 | flavorEnt := nova.Entity{Id: flavor.Id, Links: flavor.Links} |
1076 | 554 | image := nova.Entity{Id: req.Server.ImageRef} | 560 | image := nova.Entity{Id: req.Server.ImageRef} |
1077 | 555 | timestr := time.Now().Format(time.RFC3339) | 561 | timestr := time.Now().Format(time.RFC3339) |
1078 | 562 | userInfo, _ := userInfo(n.IdentityService, r) | ||
1079 | 556 | server := nova.ServerDetail{ | 563 | server := nova.ServerDetail{ |
1080 | 557 | Id: id, | 564 | Id: id, |
1081 | 558 | Name: req.Server.Name, | 565 | Name: req.Server.Name, |
1084 | 559 | TenantId: n.tenantId, | 566 | TenantId: n.TenantId, |
1085 | 560 | UserId: n.userId, | 567 | UserId: userInfo.Id, |
1086 | 561 | HostId: "1", | 568 | HostId: "1", |
1087 | 562 | Image: image, | 569 | Image: image, |
1088 | 563 | Flavor: flavorEnt, | 570 | Flavor: flavorEnt, |
1089 | @@ -787,7 +794,7 @@ | |||
1090 | 787 | Id: nextId, | 794 | Id: nextId, |
1091 | 788 | Name: req.Group.Name, | 795 | Name: req.Group.Name, |
1092 | 789 | Description: req.Group.Description, | 796 | Description: req.Group.Description, |
1094 | 790 | TenantId: n.tenantId, | 797 | TenantId: n.TenantId, |
1095 | 791 | }) | 798 | }) |
1096 | 792 | if err != nil { | 799 | if err != nil { |
1097 | 793 | return err | 800 | return err |
1098 | @@ -970,8 +977,8 @@ | |||
1099 | 970 | "/$v/$t/os-floating-ips": n.handler((*Nova).handleFloatingIPs), | 977 | "/$v/$t/os-floating-ips": n.handler((*Nova).handleFloatingIPs), |
1100 | 971 | } | 978 | } |
1101 | 972 | for path, h := range handlers { | 979 | for path, h := range handlers { |
1104 | 973 | path = strings.Replace(path, "$v", n.versionPath, 1) | 980 | path = strings.Replace(path, "$v", n.VersionPath, 1) |
1105 | 974 | path = strings.Replace(path, "$t", n.tenantId, 1) | 981 | path = strings.Replace(path, "$t", n.TenantId, 1) |
1106 | 975 | if !strings.HasSuffix(path, "/") { | 982 | if !strings.HasSuffix(path, "/") { |
1107 | 976 | mux.Handle(path+"/", h) | 983 | mux.Handle(path+"/", h) |
1108 | 977 | } | 984 | } |
1109 | 978 | 985 | ||
1110 | === modified file 'testservices/novaservice/service_http_test.go' | |||
1111 | --- testservices/novaservice/service_http_test.go 2013-01-21 11:19:10 +0000 | |||
1112 | +++ testservices/novaservice/service_http_test.go 2013-01-28 18:22:27 +0000 | |||
1113 | @@ -10,8 +10,8 @@ | |||
1114 | 10 | . "launchpad.net/gocheck" | 10 | . "launchpad.net/gocheck" |
1115 | 11 | "launchpad.net/goose/nova" | 11 | "launchpad.net/goose/nova" |
1116 | 12 | "launchpad.net/goose/testing/httpsuite" | 12 | "launchpad.net/goose/testing/httpsuite" |
1117 | 13 | "launchpad.net/goose/testservices/identityservice" | ||
1118 | 13 | "net/http" | 14 | "net/http" |
1119 | 14 | "net/url" | ||
1120 | 15 | "strconv" | 15 | "strconv" |
1121 | 16 | "strings" | 16 | "strings" |
1122 | 17 | ) | 17 | ) |
1123 | @@ -19,14 +19,17 @@ | |||
1124 | 19 | type NovaHTTPSuite struct { | 19 | type NovaHTTPSuite struct { |
1125 | 20 | httpsuite.HTTPSuite | 20 | httpsuite.HTTPSuite |
1126 | 21 | service *Nova | 21 | service *Nova |
1127 | 22 | token string | ||
1128 | 22 | } | 23 | } |
1129 | 23 | 24 | ||
1130 | 24 | var _ = Suite(&NovaHTTPSuite{}) | 25 | var _ = Suite(&NovaHTTPSuite{}) |
1131 | 25 | 26 | ||
1132 | 26 | func (s *NovaHTTPSuite) SetUpSuite(c *C) { | 27 | func (s *NovaHTTPSuite) SetUpSuite(c *C) { |
1133 | 27 | s.HTTPSuite.SetUpSuite(c) | 28 | s.HTTPSuite.SetUpSuite(c) |
1136 | 28 | url, _ := url.Parse(s.Server.URL) | 29 | identityDouble := identityservice.NewUserPass() |
1137 | 29 | s.service = New(url.Host, versionPath, token, tenantId) | 30 | userInfo := identityDouble.AddUser("fred", "secret", "tenant") |
1138 | 31 | s.token = userInfo.Token | ||
1139 | 32 | s.service = New(s.Server.URL, versionPath, userInfo.TenantId, region, identityDouble) | ||
1140 | 30 | } | 33 | } |
1141 | 31 | 34 | ||
1142 | 32 | func (s *NovaHTTPSuite) TearDownSuite(c *C) { | 35 | func (s *NovaHTTPSuite) TearDownSuite(c *C) { |
1143 | @@ -68,8 +71,8 @@ | |||
1144 | 68 | // sendRequest constructs an HTTP request from the parameters and | 71 | // sendRequest constructs an HTTP request from the parameters and |
1145 | 69 | // sends it, returning the response or an error. | 72 | // sends it, returning the response or an error. |
1146 | 70 | func (s *NovaHTTPSuite) sendRequest(method, url string, body []byte, headers http.Header) (*http.Response, error) { | 73 | func (s *NovaHTTPSuite) sendRequest(method, url string, body []byte, headers http.Header) (*http.Response, error) { |
1149 | 71 | if !strings.HasPrefix(url, "http") { //s.service.hostname) { | 74 | if !strings.HasPrefix(url, "http") { |
1150 | 72 | url = "http://" + s.service.hostname + strings.TrimLeft(url, "/") | 75 | url = "http://" + s.service.Hostname + strings.TrimLeft(url, "/") |
1151 | 73 | } | 76 | } |
1152 | 74 | req, err := http.NewRequest(method, url, bytes.NewReader(body)) | 77 | req, err := http.NewRequest(method, url, bytes.NewReader(body)) |
1153 | 75 | if err != nil { | 78 | if err != nil { |
1154 | @@ -91,8 +94,8 @@ | |||
1155 | 91 | if headers == nil { | 94 | if headers == nil { |
1156 | 92 | headers = make(http.Header) | 95 | headers = make(http.Header) |
1157 | 93 | } | 96 | } |
1160 | 94 | headers.Set(authToken, s.service.token) | 97 | headers.Set(authToken, s.token) |
1161 | 95 | url := s.service.endpoint(true, path) | 98 | url := s.service.endpointURL(true, path) |
1162 | 96 | return s.sendRequest(method, url, body, headers) | 99 | return s.sendRequest(method, url, body, headers) |
1163 | 97 | } | 100 | } |
1164 | 98 | 101 | ||
1165 | @@ -114,339 +117,345 @@ | |||
1166 | 114 | return h | 117 | return h |
1167 | 115 | } | 118 | } |
1168 | 116 | 119 | ||
1171 | 117 | // simpleTests defines a simple request without a body and expected response. | 120 | // SimpleTest defines a simple request without a body and expected response. |
1172 | 118 | var simpleTests = []struct { | 121 | type SimpleTest struct { |
1173 | 119 | unauth bool | 122 | unauth bool |
1174 | 120 | method string | 123 | method string |
1175 | 121 | url string | 124 | url string |
1176 | 122 | headers http.Header | 125 | headers http.Header |
1177 | 123 | expect *errorResponse | 126 | expect *errorResponse |
1496 | 124 | }{ | 127 | } |
1497 | 125 | { | 128 | |
1498 | 126 | unauth: true, | 129 | func (s *NovaHTTPSuite) simpleTests() []SimpleTest { |
1499 | 127 | method: "GET", | 130 | var simpleTests = []SimpleTest{ |
1500 | 128 | url: "/any", | 131 | { |
1501 | 129 | headers: make(http.Header), | 132 | unauth: true, |
1502 | 130 | expect: errUnauthorized, | 133 | method: "GET", |
1503 | 131 | }, | 134 | url: "/any", |
1504 | 132 | { | 135 | headers: make(http.Header), |
1505 | 133 | unauth: true, | 136 | expect: errUnauthorized, |
1506 | 134 | method: "POST", | 137 | }, |
1507 | 135 | url: "/any", | 138 | { |
1508 | 136 | headers: setHeader(authToken, "phony"), | 139 | unauth: true, |
1509 | 137 | expect: errUnauthorized, | 140 | method: "POST", |
1510 | 138 | }, | 141 | url: "/any", |
1511 | 139 | { | 142 | headers: setHeader(authToken, "phony"), |
1512 | 140 | unauth: true, | 143 | expect: errUnauthorized, |
1513 | 141 | method: "GET", | 144 | }, |
1514 | 142 | url: "/", | 145 | { |
1515 | 143 | headers: setHeader(authToken, token), | 146 | unauth: true, |
1516 | 144 | expect: errNoVersion, | 147 | method: "GET", |
1517 | 145 | }, | 148 | url: "/", |
1518 | 146 | { | 149 | headers: setHeader(authToken, s.token), |
1519 | 147 | unauth: true, | 150 | expect: errNoVersion, |
1520 | 148 | method: "GET", | 151 | }, |
1521 | 149 | url: "/any", | 152 | { |
1522 | 150 | headers: setHeader(authToken, token), | 153 | unauth: true, |
1523 | 151 | expect: errMultipleChoices, | 154 | method: "GET", |
1524 | 152 | }, | 155 | url: "/any", |
1525 | 153 | { | 156 | headers: setHeader(authToken, s.token), |
1526 | 154 | unauth: true, | 157 | expect: errMultipleChoices, |
1527 | 155 | method: "POST", | 158 | }, |
1528 | 156 | url: "/any/unknown/one", | 159 | { |
1529 | 157 | headers: setHeader(authToken, token), | 160 | unauth: true, |
1530 | 158 | expect: errMultipleChoices, | 161 | method: "POST", |
1531 | 159 | }, | 162 | url: "/any/unknown/one", |
1532 | 160 | { | 163 | headers: setHeader(authToken, s.token), |
1533 | 161 | method: "POST", | 164 | expect: errMultipleChoices, |
1534 | 162 | url: "/any/unknown/one", | 165 | }, |
1535 | 163 | expect: errNotFound, | 166 | { |
1536 | 164 | }, | 167 | method: "POST", |
1537 | 165 | { | 168 | url: "/any/unknown/one", |
1538 | 166 | unauth: true, | 169 | expect: errNotFound, |
1539 | 167 | method: "GET", | 170 | }, |
1540 | 168 | url: versionPath + "/phony_token", | 171 | { |
1541 | 169 | headers: setHeader(authToken, token), | 172 | unauth: true, |
1542 | 170 | expect: errBadRequest, | 173 | method: "GET", |
1543 | 171 | }, | 174 | url: versionPath + "/phony_token", |
1544 | 172 | { | 175 | headers: setHeader(authToken, s.token), |
1545 | 173 | method: "GET", | 176 | expect: errBadRequest, |
1546 | 174 | url: "/flavors/", | 177 | }, |
1547 | 175 | expect: errNotFound, | 178 | { |
1548 | 176 | }, | 179 | method: "GET", |
1549 | 177 | { | 180 | url: "/flavors/", |
1550 | 178 | method: "GET", | 181 | expect: errNotFound, |
1551 | 179 | url: "/flavors/invalid", | 182 | }, |
1552 | 180 | expect: errNotFound, | 183 | { |
1553 | 181 | }, | 184 | method: "GET", |
1554 | 182 | { | 185 | url: "/flavors/invalid", |
1555 | 183 | method: "POST", | 186 | expect: errNotFound, |
1556 | 184 | url: "/flavors", | 187 | }, |
1557 | 185 | expect: errBadRequest2, | 188 | { |
1558 | 186 | }, | 189 | method: "POST", |
1559 | 187 | { | 190 | url: "/flavors", |
1560 | 188 | method: "POST", | 191 | expect: errBadRequest2, |
1561 | 189 | url: "/flavors/invalid", | 192 | }, |
1562 | 190 | expect: errNotFound, | 193 | { |
1563 | 191 | }, | 194 | method: "POST", |
1564 | 192 | { | 195 | url: "/flavors/invalid", |
1565 | 193 | method: "PUT", | 196 | expect: errNotFound, |
1566 | 194 | url: "/flavors", | 197 | }, |
1567 | 195 | expect: errNotFound, | 198 | { |
1568 | 196 | }, | 199 | method: "PUT", |
1569 | 197 | { | 200 | url: "/flavors", |
1570 | 198 | method: "PUT", | 201 | expect: errNotFound, |
1571 | 199 | url: "/flavors/invalid", | 202 | }, |
1572 | 200 | expect: errNotFoundJSON, | 203 | { |
1573 | 201 | }, | 204 | method: "PUT", |
1574 | 202 | { | 205 | url: "/flavors/invalid", |
1575 | 203 | method: "DELETE", | 206 | expect: errNotFoundJSON, |
1576 | 204 | url: "/flavors", | 207 | }, |
1577 | 205 | expect: errNotFound, | 208 | { |
1578 | 206 | }, | 209 | method: "DELETE", |
1579 | 207 | { | 210 | url: "/flavors", |
1580 | 208 | method: "DELETE", | 211 | expect: errNotFound, |
1581 | 209 | url: "/flavors/invalid", | 212 | }, |
1582 | 210 | expect: errForbidden, | 213 | { |
1583 | 211 | }, | 214 | method: "DELETE", |
1584 | 212 | { | 215 | url: "/flavors/invalid", |
1585 | 213 | method: "GET", | 216 | expect: errForbidden, |
1586 | 214 | url: "/flavors/detail/invalid", | 217 | }, |
1587 | 215 | expect: errNotFound, | 218 | { |
1588 | 216 | }, | 219 | method: "GET", |
1589 | 217 | { | 220 | url: "/flavors/detail/invalid", |
1590 | 218 | method: "POST", | 221 | expect: errNotFound, |
1591 | 219 | url: "/flavors/detail", | 222 | }, |
1592 | 220 | expect: errNotFound, | 223 | { |
1593 | 221 | }, | 224 | method: "POST", |
1594 | 222 | { | 225 | url: "/flavors/detail", |
1595 | 223 | method: "POST", | 226 | expect: errNotFound, |
1596 | 224 | url: "/flavors/detail/invalid", | 227 | }, |
1597 | 225 | expect: errNotFound, | 228 | { |
1598 | 226 | }, | 229 | method: "POST", |
1599 | 227 | { | 230 | url: "/flavors/detail/invalid", |
1600 | 228 | method: "PUT", | 231 | expect: errNotFound, |
1601 | 229 | url: "/flavors/detail", | 232 | }, |
1602 | 230 | expect: errNotFoundJSON, | 233 | { |
1603 | 231 | }, | 234 | method: "PUT", |
1604 | 232 | { | 235 | url: "/flavors/detail", |
1605 | 233 | method: "PUT", | 236 | expect: errNotFoundJSON, |
1606 | 234 | url: "/flavors/detail/invalid", | 237 | }, |
1607 | 235 | expect: errNotFound, | 238 | { |
1608 | 236 | }, | 239 | method: "PUT", |
1609 | 237 | { | 240 | url: "/flavors/detail/invalid", |
1610 | 238 | method: "DELETE", | 241 | expect: errNotFound, |
1611 | 239 | url: "/flavors/detail", | 242 | }, |
1612 | 240 | expect: errForbidden, | 243 | { |
1613 | 241 | }, | 244 | method: "DELETE", |
1614 | 242 | { | 245 | url: "/flavors/detail", |
1615 | 243 | method: "DELETE", | 246 | expect: errForbidden, |
1616 | 244 | url: "/flavors/detail/invalid", | 247 | }, |
1617 | 245 | expect: errNotFound, | 248 | { |
1618 | 246 | }, | 249 | method: "DELETE", |
1619 | 247 | { | 250 | url: "/flavors/detail/invalid", |
1620 | 248 | method: "GET", | 251 | expect: errNotFound, |
1621 | 249 | url: "/servers/invalid", | 252 | }, |
1622 | 250 | expect: errNotFoundJSON, | 253 | { |
1623 | 251 | }, | 254 | method: "GET", |
1624 | 252 | { | 255 | url: "/servers/invalid", |
1625 | 253 | method: "POST", | 256 | expect: errNotFoundJSON, |
1626 | 254 | url: "/servers", | 257 | }, |
1627 | 255 | expect: errBadRequest2, | 258 | { |
1628 | 256 | }, | 259 | method: "POST", |
1629 | 257 | { | 260 | url: "/servers", |
1630 | 258 | method: "POST", | 261 | expect: errBadRequest2, |
1631 | 259 | url: "/servers/invalid", | 262 | }, |
1632 | 260 | expect: errNotFound, | 263 | { |
1633 | 261 | }, | 264 | method: "POST", |
1634 | 262 | { | 265 | url: "/servers/invalid", |
1635 | 263 | method: "PUT", | 266 | expect: errNotFound, |
1636 | 264 | url: "/servers", | 267 | }, |
1637 | 265 | expect: errNotFound, | 268 | { |
1638 | 266 | }, | 269 | method: "PUT", |
1639 | 267 | { | 270 | url: "/servers", |
1640 | 268 | method: "PUT", | 271 | expect: errNotFound, |
1641 | 269 | url: "/servers/invalid", | 272 | }, |
1642 | 270 | expect: errBadRequest2, | 273 | { |
1643 | 271 | }, | 274 | method: "PUT", |
1644 | 272 | { | 275 | url: "/servers/invalid", |
1645 | 273 | method: "DELETE", | 276 | expect: errBadRequest2, |
1646 | 274 | url: "/servers", | 277 | }, |
1647 | 275 | expect: errNotFound, | 278 | { |
1648 | 276 | }, | 279 | method: "DELETE", |
1649 | 277 | { | 280 | url: "/servers", |
1650 | 278 | method: "DELETE", | 281 | expect: errNotFound, |
1651 | 279 | url: "/servers/invalid", | 282 | }, |
1652 | 280 | expect: errNotFoundJSON, | 283 | { |
1653 | 281 | }, | 284 | method: "DELETE", |
1654 | 282 | { | 285 | url: "/servers/invalid", |
1655 | 283 | method: "GET", | 286 | expect: errNotFoundJSON, |
1656 | 284 | url: "/servers/detail/invalid", | 287 | }, |
1657 | 285 | expect: errNotFound, | 288 | { |
1658 | 286 | }, | 289 | method: "GET", |
1659 | 287 | { | 290 | url: "/servers/detail/invalid", |
1660 | 288 | method: "POST", | 291 | expect: errNotFound, |
1661 | 289 | url: "/servers/detail", | 292 | }, |
1662 | 290 | expect: errNotFound, | 293 | { |
1663 | 291 | }, | 294 | method: "POST", |
1664 | 292 | { | 295 | url: "/servers/detail", |
1665 | 293 | method: "POST", | 296 | expect: errNotFound, |
1666 | 294 | url: "/servers/detail/invalid", | 297 | }, |
1667 | 295 | expect: errNotFound, | 298 | { |
1668 | 296 | }, | 299 | method: "POST", |
1669 | 297 | { | 300 | url: "/servers/detail/invalid", |
1670 | 298 | method: "PUT", | 301 | expect: errNotFound, |
1671 | 299 | url: "/servers/detail", | 302 | }, |
1672 | 300 | expect: errBadRequest2, | 303 | { |
1673 | 301 | }, | 304 | method: "PUT", |
1674 | 302 | { | 305 | url: "/servers/detail", |
1675 | 303 | method: "PUT", | 306 | expect: errBadRequest2, |
1676 | 304 | url: "/servers/detail/invalid", | 307 | }, |
1677 | 305 | expect: errNotFound, | 308 | { |
1678 | 306 | }, | 309 | method: "PUT", |
1679 | 307 | { | 310 | url: "/servers/detail/invalid", |
1680 | 308 | method: "DELETE", | 311 | expect: errNotFound, |
1681 | 309 | url: "/servers/detail", | 312 | }, |
1682 | 310 | expect: errNotFoundJSON, | 313 | { |
1683 | 311 | }, | 314 | method: "DELETE", |
1684 | 312 | { | 315 | url: "/servers/detail", |
1685 | 313 | method: "DELETE", | 316 | expect: errNotFoundJSON, |
1686 | 314 | url: "/servers/detail/invalid", | 317 | }, |
1687 | 315 | expect: errNotFound, | 318 | { |
1688 | 316 | }, | 319 | method: "DELETE", |
1689 | 317 | { | 320 | url: "/servers/detail/invalid", |
1690 | 318 | method: "GET", | 321 | expect: errNotFound, |
1691 | 319 | url: "/os-security-groups/invalid", | 322 | }, |
1692 | 320 | expect: errBadRequestSG, | 323 | { |
1693 | 321 | }, | 324 | method: "GET", |
1694 | 322 | { | 325 | url: "/os-security-groups/invalid", |
1695 | 323 | method: "GET", | 326 | expect: errBadRequestSG, |
1696 | 324 | url: "/os-security-groups/42", | 327 | }, |
1697 | 325 | expect: errNotFoundJSONSG, | 328 | { |
1698 | 326 | }, | 329 | method: "GET", |
1699 | 327 | { | 330 | url: "/os-security-groups/42", |
1700 | 328 | method: "POST", | 331 | expect: errNotFoundJSONSG, |
1701 | 329 | url: "/os-security-groups", | 332 | }, |
1702 | 330 | expect: errBadRequest2, | 333 | { |
1703 | 331 | }, | 334 | method: "POST", |
1704 | 332 | { | 335 | url: "/os-security-groups", |
1705 | 333 | method: "POST", | 336 | expect: errBadRequest2, |
1706 | 334 | url: "/os-security-groups/invalid", | 337 | }, |
1707 | 335 | expect: errNotFound, | 338 | { |
1708 | 336 | }, | 339 | method: "POST", |
1709 | 337 | { | 340 | url: "/os-security-groups/invalid", |
1710 | 338 | method: "PUT", | 341 | expect: errNotFound, |
1711 | 339 | url: "/os-security-groups", | 342 | }, |
1712 | 340 | expect: errNotFound, | 343 | { |
1713 | 341 | }, | 344 | method: "PUT", |
1714 | 342 | { | 345 | url: "/os-security-groups", |
1715 | 343 | method: "PUT", | 346 | expect: errNotFound, |
1716 | 344 | url: "/os-security-groups/invalid", | 347 | }, |
1717 | 345 | expect: errNotFoundJSON, | 348 | { |
1718 | 346 | }, | 349 | method: "PUT", |
1719 | 347 | { | 350 | url: "/os-security-groups/invalid", |
1720 | 348 | method: "DELETE", | 351 | expect: errNotFoundJSON, |
1721 | 349 | url: "/os-security-groups", | 352 | }, |
1722 | 350 | expect: errNotFound, | 353 | { |
1723 | 351 | }, | 354 | method: "DELETE", |
1724 | 352 | { | 355 | url: "/os-security-groups", |
1725 | 353 | method: "DELETE", | 356 | expect: errNotFound, |
1726 | 354 | url: "/os-security-groups/invalid", | 357 | }, |
1727 | 355 | expect: errBadRequestSG, | 358 | { |
1728 | 356 | }, | 359 | method: "DELETE", |
1729 | 357 | { | 360 | url: "/os-security-groups/invalid", |
1730 | 358 | method: "DELETE", | 361 | expect: errBadRequestSG, |
1731 | 359 | url: "/os-security-groups/42", | 362 | }, |
1732 | 360 | expect: errNotFoundJSONSG, | 363 | { |
1733 | 361 | }, | 364 | method: "DELETE", |
1734 | 362 | { | 365 | url: "/os-security-groups/42", |
1735 | 363 | method: "GET", | 366 | expect: errNotFoundJSONSG, |
1736 | 364 | url: "/os-security-group-rules", | 367 | }, |
1737 | 365 | expect: errNotFoundJSON, | 368 | { |
1738 | 366 | }, | 369 | method: "GET", |
1739 | 367 | { | 370 | url: "/os-security-group-rules", |
1740 | 368 | method: "GET", | 371 | expect: errNotFoundJSON, |
1741 | 369 | url: "/os-security-group-rules/invalid", | 372 | }, |
1742 | 370 | expect: errNotFoundJSON, | 373 | { |
1743 | 371 | }, | 374 | method: "GET", |
1744 | 372 | { | 375 | url: "/os-security-group-rules/invalid", |
1745 | 373 | method: "GET", | 376 | expect: errNotFoundJSON, |
1746 | 374 | url: "/os-security-group-rules/42", | 377 | }, |
1747 | 375 | expect: errNotFoundJSON, | 378 | { |
1748 | 376 | }, | 379 | method: "GET", |
1749 | 377 | { | 380 | url: "/os-security-group-rules/42", |
1750 | 378 | method: "POST", | 381 | expect: errNotFoundJSON, |
1751 | 379 | url: "/os-security-group-rules", | 382 | }, |
1752 | 380 | expect: errBadRequest2, | 383 | { |
1753 | 381 | }, | 384 | method: "POST", |
1754 | 382 | { | 385 | url: "/os-security-group-rules", |
1755 | 383 | method: "POST", | 386 | expect: errBadRequest2, |
1756 | 384 | url: "/os-security-group-rules/invalid", | 387 | }, |
1757 | 385 | expect: errNotFound, | 388 | { |
1758 | 386 | }, | 389 | method: "POST", |
1759 | 387 | { | 390 | url: "/os-security-group-rules/invalid", |
1760 | 388 | method: "PUT", | 391 | expect: errNotFound, |
1761 | 389 | url: "/os-security-group-rules", | 392 | }, |
1762 | 390 | expect: errNotFound, | 393 | { |
1763 | 391 | }, | 394 | method: "PUT", |
1764 | 392 | { | 395 | url: "/os-security-group-rules", |
1765 | 393 | method: "PUT", | 396 | expect: errNotFound, |
1766 | 394 | url: "/os-security-group-rules/invalid", | 397 | }, |
1767 | 395 | expect: errNotFoundJSON, | 398 | { |
1768 | 396 | }, | 399 | method: "PUT", |
1769 | 397 | { | 400 | url: "/os-security-group-rules/invalid", |
1770 | 398 | method: "DELETE", | 401 | expect: errNotFoundJSON, |
1771 | 399 | url: "/os-security-group-rules", | 402 | }, |
1772 | 400 | expect: errNotFound, | 403 | { |
1773 | 401 | }, | 404 | method: "DELETE", |
1774 | 402 | { | 405 | url: "/os-security-group-rules", |
1775 | 403 | method: "DELETE", | 406 | expect: errNotFound, |
1776 | 404 | url: "/os-security-group-rules/invalid", | 407 | }, |
1777 | 405 | expect: errBadRequestSG, // sic; should've been rule-specific | 408 | { |
1778 | 406 | }, | 409 | method: "DELETE", |
1779 | 407 | { | 410 | url: "/os-security-group-rules/invalid", |
1780 | 408 | method: "DELETE", | 411 | expect: errBadRequestSG, // sic; should've been rule-specific |
1781 | 409 | url: "/os-security-group-rules/42", | 412 | }, |
1782 | 410 | expect: errNotFoundJSONSGR, | 413 | { |
1783 | 411 | }, | 414 | method: "DELETE", |
1784 | 412 | { | 415 | url: "/os-security-group-rules/42", |
1785 | 413 | method: "GET", | 416 | expect: errNotFoundJSONSGR, |
1786 | 414 | url: "/os-floating-ips/42", | 417 | }, |
1787 | 415 | expect: errNotFoundJSON, | 418 | { |
1788 | 416 | }, | 419 | method: "GET", |
1789 | 417 | { | 420 | url: "/os-floating-ips/42", |
1790 | 418 | method: "POST", | 421 | expect: errNotFoundJSON, |
1791 | 419 | url: "/os-floating-ips/invalid", | 422 | }, |
1792 | 420 | expect: errNotFound, | 423 | { |
1793 | 421 | }, | 424 | method: "POST", |
1794 | 422 | { | 425 | url: "/os-floating-ips/invalid", |
1795 | 423 | method: "PUT", | 426 | expect: errNotFound, |
1796 | 424 | url: "/os-floating-ips", | 427 | }, |
1797 | 425 | expect: errNotFound, | 428 | { |
1798 | 426 | }, | 429 | method: "PUT", |
1799 | 427 | { | 430 | url: "/os-floating-ips", |
1800 | 428 | method: "PUT", | 431 | expect: errNotFound, |
1801 | 429 | url: "/os-floating-ips/invalid", | 432 | }, |
1802 | 430 | expect: errNotFoundJSON, | 433 | { |
1803 | 431 | }, | 434 | method: "PUT", |
1804 | 432 | { | 435 | url: "/os-floating-ips/invalid", |
1805 | 433 | method: "DELETE", | 436 | expect: errNotFoundJSON, |
1806 | 434 | url: "/os-floating-ips", | 437 | }, |
1807 | 435 | expect: errNotFound, | 438 | { |
1808 | 436 | }, | 439 | method: "DELETE", |
1809 | 437 | { | 440 | url: "/os-floating-ips", |
1810 | 438 | method: "DELETE", | 441 | expect: errNotFound, |
1811 | 439 | url: "/os-floating-ips/invalid", | 442 | }, |
1812 | 440 | expect: errNotFoundJSON, | 443 | { |
1813 | 441 | }, | 444 | method: "DELETE", |
1814 | 445 | url: "/os-floating-ips/invalid", | ||
1815 | 446 | expect: errNotFoundJSON, | ||
1816 | 447 | }, | ||
1817 | 448 | } | ||
1818 | 449 | return simpleTests | ||
1819 | 442 | } | 450 | } |
1820 | 443 | 451 | ||
1821 | 444 | func (s *NovaHTTPSuite) TestSimpleRequestTests(c *C) { | 452 | func (s *NovaHTTPSuite) TestSimpleRequestTests(c *C) { |
1822 | 453 | simpleTests := s.simpleTests() | ||
1823 | 445 | for i, t := range simpleTests { | 454 | for i, t := range simpleTests { |
1824 | 446 | c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.code) | 455 | c.Logf("#%d. %s %s -> %d", i, t.method, t.url, t.expect.code) |
1825 | 447 | if t.headers == nil { | 456 | if t.headers == nil { |
1826 | 448 | t.headers = make(http.Header) | 457 | t.headers = make(http.Header) |
1828 | 449 | t.headers.Set(authToken, s.service.token) | 458 | t.headers.Set(authToken, s.token) |
1829 | 450 | } | 459 | } |
1830 | 451 | var ( | 460 | var ( |
1831 | 452 | resp *http.Response | 461 | resp *http.Response |
1832 | @@ -780,13 +789,13 @@ | |||
1833 | 780 | { | 789 | { |
1834 | 781 | Id: 1, | 790 | Id: 1, |
1835 | 782 | Name: "group 1", | 791 | Name: "group 1", |
1837 | 783 | TenantId: s.service.tenantId, | 792 | TenantId: s.service.TenantId, |
1838 | 784 | Rules: []nova.SecurityGroupRule{}, | 793 | Rules: []nova.SecurityGroupRule{}, |
1839 | 785 | }, | 794 | }, |
1840 | 786 | { | 795 | { |
1841 | 787 | Id: 2, | 796 | Id: 2, |
1842 | 788 | Name: "group 2", | 797 | Name: "group 2", |
1844 | 789 | TenantId: s.service.tenantId, | 798 | TenantId: s.service.TenantId, |
1845 | 790 | Rules: []nova.SecurityGroupRule{}, | 799 | Rules: []nova.SecurityGroupRule{}, |
1846 | 791 | }, | 800 | }, |
1847 | 792 | } | 801 | } |
1848 | @@ -816,7 +825,7 @@ | |||
1849 | 816 | Id: 1, | 825 | Id: 1, |
1850 | 817 | Name: "group 1", | 826 | Name: "group 1", |
1851 | 818 | Description: "desc", | 827 | Description: "desc", |
1853 | 819 | TenantId: s.service.tenantId, | 828 | TenantId: s.service.TenantId, |
1854 | 820 | Rules: []nova.SecurityGroupRule{}, | 829 | Rules: []nova.SecurityGroupRule{}, |
1855 | 821 | } | 830 | } |
1856 | 822 | _, err := s.service.securityGroup(group.Id) | 831 | _, err := s.service.securityGroup(group.Id) |
1857 | @@ -890,7 +899,7 @@ | |||
1858 | 890 | ParentGroupId: group2.Id, | 899 | ParentGroupId: group2.Id, |
1859 | 891 | Group: nova.SecurityGroupRef{ | 900 | Group: nova.SecurityGroupRef{ |
1860 | 892 | Name: group1.Name, | 901 | Name: group1.Name, |
1862 | 893 | TenantId: s.service.tenantId, | 902 | TenantId: s.service.TenantId, |
1863 | 894 | }, | 903 | }, |
1864 | 895 | } | 904 | } |
1865 | 896 | ok := s.service.hasSecurityGroupRule(group1.Id, rule1.Id) | 905 | ok := s.service.hasSecurityGroupRule(group1.Id, rule1.Id) |
1866 | @@ -990,13 +999,13 @@ | |||
1867 | 990 | { | 999 | { |
1868 | 991 | Id: 1, | 1000 | Id: 1, |
1869 | 992 | Name: "group1", | 1001 | Name: "group1", |
1871 | 993 | TenantId: s.service.tenantId, | 1002 | TenantId: s.service.TenantId, |
1872 | 994 | Rules: []nova.SecurityGroupRule{}, | 1003 | Rules: []nova.SecurityGroupRule{}, |
1873 | 995 | }, | 1004 | }, |
1874 | 996 | { | 1005 | { |
1875 | 997 | Id: 2, | 1006 | Id: 2, |
1876 | 998 | Name: "group2", | 1007 | Name: "group2", |
1878 | 999 | TenantId: s.service.tenantId, | 1008 | TenantId: s.service.TenantId, |
1879 | 1000 | Rules: []nova.SecurityGroupRule{}, | 1009 | Rules: []nova.SecurityGroupRule{}, |
1880 | 1001 | }, | 1010 | }, |
1881 | 1002 | } | 1011 | } |
1882 | 1003 | 1012 | ||
1883 | === modified file 'testservices/novaservice/service_test.go' | |||
1884 | --- testservices/novaservice/service_test.go 2013-01-21 11:18:33 +0000 | |||
1885 | +++ testservices/novaservice/service_test.go 2013-01-28 18:22:27 +0000 | |||
1886 | @@ -14,15 +14,14 @@ | |||
1887 | 14 | 14 | ||
1888 | 15 | const ( | 15 | const ( |
1889 | 16 | versionPath = "v2" | 16 | versionPath = "v2" |
1893 | 17 | token = "token" | 17 | hostname = "http://example.com" |
1894 | 18 | hostname = "example.com" | 18 | region = "region" |
1892 | 19 | tenantId = "tenant_id" | ||
1895 | 20 | ) | 19 | ) |
1896 | 21 | 20 | ||
1897 | 22 | var _ = Suite(&NovaSuite{}) | 21 | var _ = Suite(&NovaSuite{}) |
1898 | 23 | 22 | ||
1899 | 24 | func (s *NovaSuite) SetUpSuite(c *C) { | 23 | func (s *NovaSuite) SetUpSuite(c *C) { |
1901 | 25 | s.service = New(hostname, versionPath, token, tenantId) | 24 | s.service = New(hostname, versionPath, "tenant", region, nil) |
1902 | 26 | } | 25 | } |
1903 | 27 | 26 | ||
1904 | 28 | func (s *NovaSuite) ensureNoFlavor(c *C, flavor nova.FlavorDetail) { | 27 | func (s *NovaSuite) ensureNoFlavor(c *C, flavor nova.FlavorDetail) { |
1905 | @@ -118,8 +117,8 @@ | |||
1906 | 118 | fl, _ := s.service.flavor(flavor.Id) | 117 | fl, _ := s.service.flavor(flavor.Id) |
1907 | 119 | url := "/flavors/" + flavor.Id | 118 | url := "/flavors/" + flavor.Id |
1908 | 120 | links := []nova.Link{ | 119 | links := []nova.Link{ |
1911 | 121 | nova.Link{Href: s.service.endpoint(true, url), Rel: "self"}, | 120 | nova.Link{Href: s.service.endpointURL(true, url), Rel: "self"}, |
1912 | 122 | nova.Link{Href: s.service.endpoint(false, url), Rel: "bookmark"}, | 121 | nova.Link{Href: s.service.endpointURL(false, url), Rel: "bookmark"}, |
1913 | 123 | } | 122 | } |
1914 | 124 | c.Assert(fl.Links, DeepEquals, links) | 123 | c.Assert(fl.Links, DeepEquals, links) |
1915 | 125 | } | 124 | } |
1916 | @@ -214,8 +213,8 @@ | |||
1917 | 214 | sr, _ := s.service.server(server.Id) | 213 | sr, _ := s.service.server(server.Id) |
1918 | 215 | url := "/servers/" + server.Id | 214 | url := "/servers/" + server.Id |
1919 | 216 | links := []nova.Link{ | 215 | links := []nova.Link{ |
1922 | 217 | {Href: s.service.endpoint(true, url), Rel: "self"}, | 216 | {Href: s.service.endpointURL(true, url), Rel: "self"}, |
1923 | 218 | {Href: s.service.endpoint(false, url), Rel: "bookmark"}, | 217 | {Href: s.service.endpointURL(false, url), Rel: "bookmark"}, |
1924 | 219 | } | 218 | } |
1925 | 220 | c.Assert(sr.Links, DeepEquals, links) | 219 | c.Assert(sr.Links, DeepEquals, links) |
1926 | 221 | } | 220 | } |
1927 | @@ -456,7 +455,7 @@ | |||
1928 | 456 | group := nova.SecurityGroup{ | 455 | group := nova.SecurityGroup{ |
1929 | 457 | Id: 1, | 456 | Id: 1, |
1930 | 458 | Name: "test", | 457 | Name: "test", |
1932 | 459 | TenantId: s.service.tenantId, | 458 | TenantId: s.service.TenantId, |
1933 | 460 | Rules: []nova.SecurityGroupRule{ | 459 | Rules: []nova.SecurityGroupRule{ |
1934 | 461 | {Id: 10, ParentGroupId: 1}, | 460 | {Id: 10, ParentGroupId: 1}, |
1935 | 462 | {Id: 20, ParentGroupId: 1}, | 461 | {Id: 20, ParentGroupId: 1}, |
1936 | @@ -492,13 +491,13 @@ | |||
1937 | 492 | { | 491 | { |
1938 | 493 | Id: 1, | 492 | Id: 1, |
1939 | 494 | Name: "one", | 493 | Name: "one", |
1941 | 495 | TenantId: s.service.tenantId, | 494 | TenantId: s.service.TenantId, |
1942 | 496 | Rules: []nova.SecurityGroupRule{}, | 495 | Rules: []nova.SecurityGroupRule{}, |
1943 | 497 | }, | 496 | }, |
1944 | 498 | { | 497 | { |
1945 | 499 | Id: 2, | 498 | Id: 2, |
1946 | 500 | Name: "two", | 499 | Name: "two", |
1948 | 501 | TenantId: s.service.tenantId, | 500 | TenantId: s.service.TenantId, |
1949 | 502 | Rules: []nova.SecurityGroupRule{}, | 501 | Rules: []nova.SecurityGroupRule{}, |
1950 | 503 | }, | 502 | }, |
1951 | 504 | } | 503 | } |
1952 | @@ -514,7 +513,7 @@ | |||
1953 | 514 | func (s *NovaSuite) TestGetSecurityGroup(c *C) { | 513 | func (s *NovaSuite) TestGetSecurityGroup(c *C) { |
1954 | 515 | group := nova.SecurityGroup{ | 514 | group := nova.SecurityGroup{ |
1955 | 516 | Id: 42, | 515 | Id: 42, |
1957 | 517 | TenantId: s.service.tenantId, | 516 | TenantId: s.service.TenantId, |
1958 | 518 | Name: "group", | 517 | Name: "group", |
1959 | 519 | Description: "desc", | 518 | Description: "desc", |
1960 | 520 | Rules: []nova.SecurityGroupRule{}, | 519 | Rules: []nova.SecurityGroupRule{}, |
1961 | @@ -529,7 +528,7 @@ | |||
1962 | 529 | group := nova.SecurityGroup{ | 528 | group := nova.SecurityGroup{ |
1963 | 530 | Id: 1, | 529 | Id: 1, |
1964 | 531 | Name: "test", | 530 | Name: "test", |
1966 | 532 | TenantId: s.service.tenantId, | 531 | TenantId: s.service.TenantId, |
1967 | 533 | Rules: []nova.SecurityGroupRule{}, | 532 | Rules: []nova.SecurityGroupRule{}, |
1968 | 534 | } | 533 | } |
1969 | 535 | s.ensureNoGroup(c, group) | 534 | s.ensureNoGroup(c, group) |
1970 | @@ -600,7 +599,7 @@ | |||
1971 | 600 | } | 599 | } |
1972 | 601 | 600 | ||
1973 | 602 | func (s *NovaSuite) TestAddGetGroupSecurityGroupRule(c *C) { | 601 | func (s *NovaSuite) TestAddGetGroupSecurityGroupRule(c *C) { |
1975 | 603 | srcGroup := nova.SecurityGroup{Id: 1, Name: "source", TenantId: s.service.tenantId} | 602 | srcGroup := nova.SecurityGroup{Id: 1, Name: "source", TenantId: s.service.TenantId} |
1976 | 604 | tgtGroup := nova.SecurityGroup{Id: 2, Name: "target"} | 603 | tgtGroup := nova.SecurityGroup{Id: 2, Name: "target"} |
1977 | 605 | s.createGroup(c, srcGroup) | 604 | s.createGroup(c, srcGroup) |
1978 | 606 | defer s.deleteGroup(c, srcGroup) | 605 | defer s.deleteGroup(c, srcGroup) |
1979 | @@ -696,7 +695,7 @@ | |||
1980 | 696 | group := nova.SecurityGroup{ | 695 | group := nova.SecurityGroup{ |
1981 | 697 | Id: 1, | 696 | Id: 1, |
1982 | 698 | Name: "test", | 697 | Name: "test", |
1984 | 699 | TenantId: s.service.tenantId, | 698 | TenantId: s.service.TenantId, |
1985 | 700 | } | 699 | } |
1986 | 701 | s.createGroup(c, group) | 700 | s.createGroup(c, group) |
1987 | 702 | defer s.deleteGroup(c, group) | 701 | defer s.deleteGroup(c, group) |
1988 | @@ -800,13 +799,13 @@ | |||
1989 | 800 | { | 799 | { |
1990 | 801 | Id: 1, | 800 | Id: 1, |
1991 | 802 | Name: "gr1", | 801 | Name: "gr1", |
1993 | 803 | TenantId: s.service.tenantId, | 802 | TenantId: s.service.TenantId, |
1994 | 804 | Rules: []nova.SecurityGroupRule{}, | 803 | Rules: []nova.SecurityGroupRule{}, |
1995 | 805 | }, | 804 | }, |
1996 | 806 | { | 805 | { |
1997 | 807 | Id: 2, | 806 | Id: 2, |
1998 | 808 | Name: "gr2", | 807 | Name: "gr2", |
2000 | 809 | TenantId: s.service.tenantId, | 808 | TenantId: s.service.TenantId, |
2001 | 810 | Rules: []nova.SecurityGroupRule{}, | 809 | Rules: []nova.SecurityGroupRule{}, |
2002 | 811 | }, | 810 | }, |
2003 | 812 | } | 811 | } |
2004 | 813 | 812 | ||
2005 | === added directory 'testservices/openstack' | |||
2006 | === added file 'testservices/openstack/openstack.go' | |||
2007 | --- testservices/openstack/openstack.go 1970-01-01 00:00:00 +0000 | |||
2008 | +++ testservices/openstack/openstack.go 2013-01-28 18:22:27 +0000 | |||
2009 | @@ -0,0 +1,38 @@ | |||
2010 | 1 | package openstack | ||
2011 | 2 | |||
2012 | 3 | import ( | ||
2013 | 4 | "launchpad.net/goose/identity" | ||
2014 | 5 | "launchpad.net/goose/testservices/identityservice" | ||
2015 | 6 | "launchpad.net/goose/testservices/novaservice" | ||
2016 | 7 | "launchpad.net/goose/testservices/swiftservice" | ||
2017 | 8 | "net/http" | ||
2018 | 9 | ) | ||
2019 | 10 | |||
2020 | 11 | // Openstack provides an Openstack service double implementation. | ||
2021 | 12 | type Openstack struct { | ||
2022 | 13 | identity identityservice.IdentityService | ||
2023 | 14 | nova *novaservice.Nova | ||
2024 | 15 | swift *swiftservice.Swift | ||
2025 | 16 | } | ||
2026 | 17 | |||
2027 | 18 | // New creates an instance of a full Openstack service double. | ||
2028 | 19 | // An initial user with the specified credentials is registered with the identity service. | ||
2029 | 20 | func New(cred *identity.Credentials) *Openstack { | ||
2030 | 21 | openstack := Openstack{ | ||
2031 | 22 | identity: identityservice.NewUserPass(), | ||
2032 | 23 | } | ||
2033 | 24 | userInfo := openstack.identity.AddUser(cred.User, cred.Secrets, cred.TenantName) | ||
2034 | 25 | if cred.TenantName == "" { | ||
2035 | 26 | panic("Openstack service double requires a tenant to be specified.") | ||
2036 | 27 | } | ||
2037 | 28 | openstack.nova = novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, openstack.identity) | ||
2038 | 29 | openstack.swift = swiftservice.New(cred.URL, "v1", userInfo.TenantId, cred.Region, openstack.identity) | ||
2039 | 30 | return &openstack | ||
2040 | 31 | } | ||
2041 | 32 | |||
2042 | 33 | // setupHTTP attaches all the needed handlers to provide the HTTP API for the Openstack service.. | ||
2043 | 34 | func (openstack *Openstack) SetupHTTP(mux *http.ServeMux) { | ||
2044 | 35 | openstack.identity.SetupHTTP(mux) | ||
2045 | 36 | openstack.nova.SetupHTTP(mux) | ||
2046 | 37 | openstack.swift.SetupHTTP(mux) | ||
2047 | 38 | } | ||
2048 | 0 | 39 | ||
2049 | === added file 'testservices/service.go' | |||
2050 | --- testservices/service.go 1970-01-01 00:00:00 +0000 | |||
2051 | +++ testservices/service.go 2013-01-28 18:22:27 +0000 | |||
2052 | @@ -0,0 +1,21 @@ | |||
2053 | 1 | package testservices | ||
2054 | 2 | |||
2055 | 3 | import ( | ||
2056 | 4 | "launchpad.net/goose/testservices/identityservice" | ||
2057 | 5 | "net/http" | ||
2058 | 6 | ) | ||
2059 | 7 | |||
2060 | 8 | // An HttpService provides the HTTP API for a service double. | ||
2061 | 9 | type HttpService interface { | ||
2062 | 10 | SetupHTTP(mux *http.ServeMux) | ||
2063 | 11 | } | ||
2064 | 12 | |||
2065 | 13 | // A ServiceInstance is an Openstack module, one of nova, swift, glance. | ||
2066 | 14 | type ServiceInstance struct { | ||
2067 | 15 | identityservice.ServiceProvider | ||
2068 | 16 | IdentityService identityservice.IdentityService | ||
2069 | 17 | Hostname string | ||
2070 | 18 | VersionPath string | ||
2071 | 19 | TenantId string | ||
2072 | 20 | Region string | ||
2073 | 21 | } | ||
2074 | 0 | 22 | ||
2075 | === modified file 'testservices/swiftservice/service.go' | |||
2076 | --- testservices/swiftservice/service.go 2012-12-17 01:30:20 +0000 | |||
2077 | +++ testservices/swiftservice/service.go 2013-01-28 18:22:27 +0000 | |||
2078 | @@ -5,29 +5,67 @@ | |||
2079 | 5 | import ( | 5 | import ( |
2080 | 6 | "fmt" | 6 | "fmt" |
2081 | 7 | "launchpad.net/goose/swift" | 7 | "launchpad.net/goose/swift" |
2082 | 8 | "launchpad.net/goose/testservices" | ||
2083 | 9 | "launchpad.net/goose/testservices/identityservice" | ||
2084 | 10 | "net/url" | ||
2085 | 11 | "strings" | ||
2086 | 8 | "time" | 12 | "time" |
2087 | 9 | ) | 13 | ) |
2088 | 10 | 14 | ||
2089 | 11 | type object map[string][]byte | 15 | type object map[string][]byte |
2090 | 12 | 16 | ||
2091 | 17 | var _ testservices.HttpService = (*Swift)(nil) | ||
2092 | 18 | var _ identityservice.ServiceProvider = (*Swift)(nil) | ||
2093 | 19 | |||
2094 | 13 | type Swift struct { | 20 | type Swift struct { |
2095 | 21 | testservices.ServiceInstance | ||
2096 | 14 | containers map[string]object | 22 | containers map[string]object |
2097 | 15 | hostname string | ||
2098 | 16 | baseURL string | ||
2099 | 17 | token string | ||
2100 | 18 | } | 23 | } |
2101 | 19 | 24 | ||
2102 | 20 | // New creates an instance of the Swift object, given the parameters. | 25 | // New creates an instance of the Swift object, given the parameters. |
2104 | 21 | func New(hostname, baseURL, token string) *Swift { | 26 | func New(hostURL, versionPath, tenantId, region string, identityService identityservice.IdentityService) *Swift { |
2105 | 27 | url, err := url.Parse(hostURL) | ||
2106 | 28 | if err != nil { | ||
2107 | 29 | panic(err) | ||
2108 | 30 | } | ||
2109 | 31 | hostname := url.Host | ||
2110 | 32 | if !strings.HasSuffix(hostname, "/") { | ||
2111 | 33 | hostname += "/" | ||
2112 | 34 | } | ||
2113 | 22 | swift := &Swift{ | 35 | swift := &Swift{ |
2114 | 23 | containers: make(map[string]object), | 36 | containers: make(map[string]object), |
2118 | 24 | hostname: hostname, | 37 | ServiceInstance: testservices.ServiceInstance{ |
2119 | 25 | baseURL: baseURL, | 38 | IdentityService: identityService, |
2120 | 26 | token: token, | 39 | Hostname: hostname, |
2121 | 40 | VersionPath: versionPath, | ||
2122 | 41 | TenantId: tenantId, | ||
2123 | 42 | Region: region, | ||
2124 | 43 | }, | ||
2125 | 44 | } | ||
2126 | 45 | if identityService != nil { | ||
2127 | 46 | identityService.RegisterServiceProvider("swift", "object-store", swift) | ||
2128 | 27 | } | 47 | } |
2129 | 28 | return swift | 48 | return swift |
2130 | 29 | } | 49 | } |
2131 | 30 | 50 | ||
2132 | 51 | func (s *Swift) endpointURL(path string) string { | ||
2133 | 52 | ep := "http://" + s.Hostname + s.VersionPath + "/" + s.TenantId | ||
2134 | 53 | if path != "" { | ||
2135 | 54 | ep += "/" + strings.TrimLeft(path, "/") | ||
2136 | 55 | } | ||
2137 | 56 | return ep | ||
2138 | 57 | } | ||
2139 | 58 | |||
2140 | 59 | func (s *Swift) Endpoints() []identityservice.Endpoint { | ||
2141 | 60 | ep := identityservice.Endpoint{ | ||
2142 | 61 | AdminURL: s.endpointURL(""), | ||
2143 | 62 | InternalURL: s.endpointURL(""), | ||
2144 | 63 | PublicURL: s.endpointURL(""), | ||
2145 | 64 | Region: s.Region, | ||
2146 | 65 | } | ||
2147 | 66 | return []identityservice.Endpoint{ep} | ||
2148 | 67 | } | ||
2149 | 68 | |||
2150 | 31 | // HasContainer verifies the given container exists or not. | 69 | // HasContainer verifies the given container exists or not. |
2151 | 32 | func (s *Swift) HasContainer(name string) bool { | 70 | func (s *Swift) HasContainer(name string) bool { |
2152 | 33 | _, ok := s.containers[name] | 71 | _, ok := s.containers[name] |
2153 | @@ -119,5 +157,5 @@ | |||
2154 | 119 | if _, err := s.GetObject(container, object); err != nil { | 157 | if _, err := s.GetObject(container, object); err != nil { |
2155 | 120 | return "", err | 158 | return "", err |
2156 | 121 | } | 159 | } |
2158 | 122 | return fmt.Sprintf("%s%s%s/%s", s.hostname, s.baseURL, container, object), nil | 160 | return s.endpointURL(fmt.Sprintf("%s/%s", container, object)), nil |
2159 | 123 | } | 161 | } |
2160 | 124 | 162 | ||
2161 | === modified file 'testservices/swiftservice/service_http.go' | |||
2162 | --- testservices/swiftservice/service_http.go 2013-01-21 11:18:33 +0000 | |||
2163 | +++ testservices/swiftservice/service_http.go 2013-01-28 18:22:27 +0000 | |||
2164 | @@ -4,6 +4,7 @@ | |||
2165 | 4 | 4 | ||
2166 | 5 | import ( | 5 | import ( |
2167 | 6 | "encoding/json" | 6 | "encoding/json" |
2168 | 7 | "fmt" | ||
2169 | 7 | "io/ioutil" | 8 | "io/ioutil" |
2170 | 8 | "net/http" | 9 | "net/http" |
2171 | 9 | "strings" | 10 | "strings" |
2172 | @@ -143,16 +144,15 @@ | |||
2173 | 143 | // For public containers, the token is not required to access the files. For now, if the request | 144 | // For public containers, the token is not required to access the files. For now, if the request |
2174 | 144 | // does not provide a token, we will let it through and assume a public container is being accessed. | 145 | // does not provide a token, we will let it through and assume a public container is being accessed. |
2175 | 145 | token := r.Header.Get("X-Auth-Token") | 146 | token := r.Header.Get("X-Auth-Token") |
2177 | 146 | if token != "" && token != s.token { | 147 | _, err := s.IdentityService.FindUser(token) |
2178 | 148 | if token != "" && err != nil { | ||
2179 | 147 | w.WriteHeader(http.StatusUnauthorized) | 149 | w.WriteHeader(http.StatusUnauthorized) |
2180 | 148 | return | 150 | return |
2181 | 149 | } | 151 | } |
2188 | 150 | path := r.URL.Path | 152 | path := strings.TrimRight(r.URL.Path, "/") |
2189 | 151 | if path[:len(s.baseURL)] == s.baseURL { | 153 | path = strings.Trim(path, "/") |
2190 | 152 | path = path[len(s.baseURL):] | 154 | parts := strings.SplitN(path, "/", 4) |
2191 | 153 | } | 155 | parts = parts[2:] |
2186 | 154 | path = strings.TrimRight(path, "/") | ||
2187 | 155 | parts := strings.SplitN(path, "/", 2) | ||
2192 | 156 | if len(parts) == 1 { | 156 | if len(parts) == 1 { |
2193 | 157 | container := parts[0] | 157 | container := parts[0] |
2194 | 158 | s.handleContainers(container, w, r) | 158 | s.handleContainers(container, w, r) |
2195 | @@ -164,3 +164,9 @@ | |||
2196 | 164 | panic("not implemented request: " + r.URL.Path) | 164 | panic("not implemented request: " + r.URL.Path) |
2197 | 165 | } | 165 | } |
2198 | 166 | } | 166 | } |
2199 | 167 | |||
2200 | 168 | // setupHTTP attaches all the needed handlers to provide the HTTP API. | ||
2201 | 169 | func (s *Swift) SetupHTTP(mux *http.ServeMux) { | ||
2202 | 170 | path := fmt.Sprintf("/%s/%s/", s.VersionPath, s.TenantId) | ||
2203 | 171 | mux.Handle(path, s) | ||
2204 | 172 | } | ||
2205 | 167 | 173 | ||
2206 | === modified file 'testservices/swiftservice/service_http_test.go' | |||
2207 | --- testservices/swiftservice/service_http_test.go 2012-12-20 05:47:11 +0000 | |||
2208 | +++ testservices/swiftservice/service_http_test.go 2013-01-28 18:22:27 +0000 | |||
2209 | @@ -9,24 +9,29 @@ | |||
2210 | 9 | . "launchpad.net/gocheck" | 9 | . "launchpad.net/gocheck" |
2211 | 10 | "launchpad.net/goose/swift" | 10 | "launchpad.net/goose/swift" |
2212 | 11 | "launchpad.net/goose/testing/httpsuite" | 11 | "launchpad.net/goose/testing/httpsuite" |
2213 | 12 | "launchpad.net/goose/testservices/identityservice" | ||
2214 | 12 | "net/http" | 13 | "net/http" |
2215 | 13 | ) | 14 | ) |
2216 | 14 | 15 | ||
2217 | 15 | type SwiftHTTPSuite struct { | 16 | type SwiftHTTPSuite struct { |
2218 | 16 | httpsuite.HTTPSuite | 17 | httpsuite.HTTPSuite |
2220 | 17 | service SwiftService | 18 | service *Swift |
2221 | 19 | token string | ||
2222 | 18 | } | 20 | } |
2223 | 19 | 21 | ||
2224 | 20 | var _ = Suite(&SwiftHTTPSuite{}) | 22 | var _ = Suite(&SwiftHTTPSuite{}) |
2225 | 21 | 23 | ||
2226 | 22 | func (s *SwiftHTTPSuite) SetUpSuite(c *C) { | 24 | func (s *SwiftHTTPSuite) SetUpSuite(c *C) { |
2227 | 23 | s.HTTPSuite.SetUpSuite(c) | 25 | s.HTTPSuite.SetUpSuite(c) |
2229 | 24 | s.service = New(s.Server.URL, baseURL, token) | 26 | identityDouble := identityservice.NewUserPass() |
2230 | 27 | s.service = New(s.Server.URL, versionPath, tenantId, region, identityDouble) | ||
2231 | 28 | userInfo := identityDouble.AddUser("fred", "secret", "tenant") | ||
2232 | 29 | s.token = userInfo.Token | ||
2233 | 25 | } | 30 | } |
2234 | 26 | 31 | ||
2235 | 27 | func (s *SwiftHTTPSuite) SetUpTest(c *C) { | 32 | func (s *SwiftHTTPSuite) SetUpTest(c *C) { |
2236 | 28 | s.HTTPSuite.SetUpTest(c) | 33 | s.HTTPSuite.SetUpTest(c) |
2238 | 29 | s.Mux.Handle(baseURL, s.service) | 34 | s.service.SetupHTTP(s.Mux) |
2239 | 30 | } | 35 | } |
2240 | 31 | 36 | ||
2241 | 32 | func (s *SwiftHTTPSuite) TearDownTest(c *C) { | 37 | func (s *SwiftHTTPSuite) TearDownTest(c *C) { |
2242 | @@ -41,15 +46,15 @@ | |||
2243 | 41 | expectedStatusCode int) (resp *http.Response) { | 46 | expectedStatusCode int) (resp *http.Response) { |
2244 | 42 | var req *http.Request | 47 | var req *http.Request |
2245 | 43 | var err error | 48 | var err error |
2247 | 44 | url := s.Server.URL + baseURL + path | 49 | url := s.service.endpointURL(path) |
2248 | 45 | if body != nil { | 50 | if body != nil { |
2249 | 46 | req, err = http.NewRequest(method, url, bytes.NewBuffer(body)) | 51 | req, err = http.NewRequest(method, url, bytes.NewBuffer(body)) |
2250 | 47 | } else { | 52 | } else { |
2251 | 48 | req, err = http.NewRequest(method, url, nil) | 53 | req, err = http.NewRequest(method, url, nil) |
2252 | 49 | } | 54 | } |
2253 | 50 | c.Assert(err, IsNil) | 55 | c.Assert(err, IsNil) |
2256 | 51 | if token != "" { | 56 | if s.token != "" { |
2257 | 52 | req.Header.Add("X-Auth-Token", token) | 57 | req.Header.Add("X-Auth-Token", s.token) |
2258 | 53 | } | 58 | } |
2259 | 54 | client := &http.Client{} | 59 | client := &http.Client{} |
2260 | 55 | resp, err = client.Do(req) | 60 | resp, err = client.Do(req) |
2261 | @@ -250,16 +255,16 @@ | |||
2262 | 250 | } | 255 | } |
2263 | 251 | 256 | ||
2264 | 252 | func (s *SwiftHTTPSuite) TestUnauthorizedFails(c *C) { | 257 | func (s *SwiftHTTPSuite) TestUnauthorizedFails(c *C) { |
2266 | 253 | oldtoken := token | 258 | oldtoken := s.token |
2267 | 254 | defer func() { | 259 | defer func() { |
2269 | 255 | token = oldtoken | 260 | s.token = oldtoken |
2270 | 256 | }() | 261 | }() |
2271 | 257 | // TODO(wallyworld) - until ACLs are supported, empty tokens are assumed to be used when | 262 | // TODO(wallyworld) - until ACLs are supported, empty tokens are assumed to be used when |
2272 | 258 | // we need to access a public container. | 263 | // we need to access a public container. |
2273 | 259 | // token = "" | 264 | // token = "" |
2274 | 260 | // s.sendRequest(c, "GET", "test", nil, http.StatusUnauthorized) | 265 | // s.sendRequest(c, "GET", "test", nil, http.StatusUnauthorized) |
2275 | 261 | 266 | ||
2277 | 262 | token = "invalid" | 267 | s.token = "invalid" |
2278 | 263 | s.sendRequest(c, "PUT", "test", nil, http.StatusUnauthorized) | 268 | s.sendRequest(c, "PUT", "test", nil, http.StatusUnauthorized) |
2279 | 264 | 269 | ||
2280 | 265 | s.sendRequest(c, "DELETE", "test", nil, http.StatusUnauthorized) | 270 | s.sendRequest(c, "DELETE", "test", nil, http.StatusUnauthorized) |
2281 | 266 | 271 | ||
2282 | === modified file 'testservices/swiftservice/service_test.go' | |||
2283 | --- testservices/swiftservice/service_test.go 2012-12-17 01:30:20 +0000 | |||
2284 | +++ testservices/swiftservice/service_test.go 2013-01-28 18:22:27 +0000 | |||
2285 | @@ -3,21 +3,23 @@ | |||
2286 | 3 | package swiftservice | 3 | package swiftservice |
2287 | 4 | 4 | ||
2288 | 5 | import ( | 5 | import ( |
2289 | 6 | "fmt" | ||
2290 | 6 | . "launchpad.net/gocheck" | 7 | . "launchpad.net/gocheck" |
2291 | 7 | ) | 8 | ) |
2292 | 8 | 9 | ||
2293 | 9 | type SwiftServiceSuite struct { | 10 | type SwiftServiceSuite struct { |
2295 | 10 | service SwiftService | 11 | service *Swift |
2296 | 11 | } | 12 | } |
2297 | 12 | 13 | ||
2301 | 13 | var baseURL = "/v1/AUTH_tenant/" | 14 | var region = "region" // not really used here |
2302 | 14 | var token = "token" | 15 | var hostname = "http://localhost" // not really used here |
2303 | 15 | var hostname = "localhost" // not really used here | 16 | var versionPath = "v2" // not really used here |
2304 | 17 | var tenantId = "tenant" // not really used here | ||
2305 | 16 | 18 | ||
2306 | 17 | var _ = Suite(&SwiftServiceSuite{}) | 19 | var _ = Suite(&SwiftServiceSuite{}) |
2307 | 18 | 20 | ||
2308 | 19 | func (s *SwiftServiceSuite) SetUpSuite(c *C) { | 21 | func (s *SwiftServiceSuite) SetUpSuite(c *C) { |
2310 | 20 | s.service = New(hostname, baseURL, token) | 22 | s.service = New(hostname, versionPath, tenantId, region, nil) |
2311 | 21 | } | 23 | } |
2312 | 22 | 24 | ||
2313 | 23 | func (s *SwiftServiceSuite) TestAddHasRemoveContainer(c *C) { | 25 | func (s *SwiftServiceSuite) TestAddHasRemoveContainer(c *C) { |
2314 | @@ -76,9 +78,8 @@ | |||
2315 | 76 | err = s.service.AddObject("test", "obj", data) | 78 | err = s.service.AddObject("test", "obj", data) |
2316 | 77 | c.Assert(err, IsNil) | 79 | c.Assert(err, IsNil) |
2317 | 78 | url, err := s.service.GetURL("test", "obj") | 80 | url, err := s.service.GetURL("test", "obj") |
2318 | 79 | path := baseURL + "test/obj" | ||
2319 | 80 | c.Assert(err, IsNil) | 81 | c.Assert(err, IsNil) |
2321 | 81 | c.Assert(url, Equals, hostname+path) | 82 | c.Assert(url, Equals, fmt.Sprintf("%s/%s/%s/test/obj", hostname, versionPath, tenantId)) |
2322 | 82 | err = s.service.RemoveContainer("test") | 83 | err = s.service.RemoveContainer("test") |
2323 | 83 | c.Assert(err, IsNil) | 84 | c.Assert(err, IsNil) |
2324 | 84 | ok = s.service.HasContainer("test") | 85 | ok = s.service.HasContainer("test") |
2325 | 85 | 86 | ||
2326 | === removed file 'testservices/swiftservice/swiftservice.go' | |||
2327 | --- testservices/swiftservice/swiftservice.go 2012-12-17 01:30:20 +0000 | |||
2328 | +++ testservices/swiftservice/swiftservice.go 1970-01-01 00:00:00 +0000 | |||
2329 | @@ -1,40 +0,0 @@ | |||
2330 | 1 | // Swift double testing service - mimics OpenStack Swift object | ||
2331 | 2 | // storage service for testing goose against close-to-live API. | ||
2332 | 3 | |||
2333 | 4 | package swiftservice | ||
2334 | 5 | |||
2335 | 6 | import ( | ||
2336 | 7 | "launchpad.net/goose/swift" | ||
2337 | 8 | "net/http" | ||
2338 | 9 | ) | ||
2339 | 10 | |||
2340 | 11 | // SwiftService presents an direct-API to manipulate the internal | ||
2341 | 12 | // state, as well as an HTTP API double for OpenStack Swift. | ||
2342 | 13 | type SwiftService interface { | ||
2343 | 14 | // AddContainer creates a new container with the given name. | ||
2344 | 15 | AddContainer(name string) error | ||
2345 | 16 | |||
2346 | 17 | // AddObject creates a new named object in an existing container. | ||
2347 | 18 | AddObject(container, name string, data []byte) error | ||
2348 | 19 | |||
2349 | 20 | // HasContainer verifies the given container exists or not. | ||
2350 | 21 | HasContainer(name string) bool | ||
2351 | 22 | |||
2352 | 23 | // ListContainer lists the objects in the given container. | ||
2353 | 24 | ListContainer(name string) ([]swift.ContainerContents, error) | ||
2354 | 25 | |||
2355 | 26 | // GetObject retrieves a given object's data from its container. | ||
2356 | 27 | GetObject(container, name string) ([]byte, error) | ||
2357 | 28 | |||
2358 | 29 | // RemoveContainer deletes an existing named container. | ||
2359 | 30 | RemoveContainer(name string) error | ||
2360 | 31 | |||
2361 | 32 | // RemoveObject deletes an existing named object, from its container. | ||
2362 | 33 | RemoveObject(container, name string) error | ||
2363 | 34 | |||
2364 | 35 | // GetURL returns the named object's full public URL to get its data. | ||
2365 | 36 | GetURL(container, object string) (string, error) | ||
2366 | 37 | |||
2367 | 38 | // ServeHTTP is the main entry point in the HTTP request processing. | ||
2368 | 39 | ServeHTTP(w http.ResponseWriter, r *http.Request) | ||
2369 | 40 | } | ||
2370 | 41 | 0 | ||
2371 | === added directory 'tools' | |||
2372 | === added directory 'tools/secgroup-delete-all' | |||
2373 | === added file 'tools/secgroup-delete-all/main.go' | |||
2374 | --- tools/secgroup-delete-all/main.go 1970-01-01 00:00:00 +0000 | |||
2375 | +++ tools/secgroup-delete-all/main.go 2013-01-28 18:22:27 +0000 | |||
2376 | @@ -0,0 +1,73 @@ | |||
2377 | 1 | package main | ||
2378 | 2 | |||
2379 | 3 | import ( | ||
2380 | 4 | "fmt" | ||
2381 | 5 | "io" | ||
2382 | 6 | "launchpad.net/gnuflag" | ||
2383 | 7 | "launchpad.net/goose/client" | ||
2384 | 8 | "launchpad.net/goose/identity" | ||
2385 | 9 | "launchpad.net/goose/nova" | ||
2386 | 10 | "os" | ||
2387 | 11 | ) | ||
2388 | 12 | |||
2389 | 13 | // DeleteAll destroys all security groups except the default | ||
2390 | 14 | func DeleteAll(w io.Writer, osn *nova.Client) (err error) { | ||
2391 | 15 | groups, err := osn.ListSecurityGroups() | ||
2392 | 16 | if err != nil { | ||
2393 | 17 | return err | ||
2394 | 18 | } | ||
2395 | 19 | deleted := 0 | ||
2396 | 20 | failed := 0 | ||
2397 | 21 | for _, group := range groups { | ||
2398 | 22 | if group.Name != "default" { | ||
2399 | 23 | err := osn.DeleteSecurityGroup(group.Id) | ||
2400 | 24 | if err != nil { | ||
2401 | 25 | failed++ | ||
2402 | 26 | } else { | ||
2403 | 27 | deleted++ | ||
2404 | 28 | } | ||
2405 | 29 | } | ||
2406 | 30 | } | ||
2407 | 31 | if deleted != 0 { | ||
2408 | 32 | fmt.Fprintf(w, "%d security groups deleted.\n", deleted) | ||
2409 | 33 | } else if failed == 0 { | ||
2410 | 34 | fmt.Fprint(w, "No security groups to delete.\n") | ||
2411 | 35 | } | ||
2412 | 36 | if failed != 0 { | ||
2413 | 37 | fmt.Fprintf(w, "%d security groups could not be deleted.\n", failed) | ||
2414 | 38 | } | ||
2415 | 39 | return nil | ||
2416 | 40 | } | ||
2417 | 41 | |||
2418 | 42 | func createNovaClient(authMode identity.AuthMethod) (osn *nova.Client, err error) { | ||
2419 | 43 | creds, err := identity.CompleteCredentialsFromEnv() | ||
2420 | 44 | if err != nil { | ||
2421 | 45 | return nil, err | ||
2422 | 46 | } | ||
2423 | 47 | osc := client.NewClient(creds, authMode, nil) | ||
2424 | 48 | return nova.New(osc), nil | ||
2425 | 49 | } | ||
2426 | 50 | |||
2427 | 51 | var authModeFlag = gnuflag.String("auth-mode", "userpass", "type of authentication to use") | ||
2428 | 52 | |||
2429 | 53 | var authModes = map[string]identity.AuthMethod{ | ||
2430 | 54 | "userpass": identity.AuthUserPass, | ||
2431 | 55 | "legacy": identity.AuthLegacy, | ||
2432 | 56 | } | ||
2433 | 57 | |||
2434 | 58 | func main() { | ||
2435 | 59 | gnuflag.Parse(true) | ||
2436 | 60 | mode, ok := authModes[*authModeFlag] | ||
2437 | 61 | if !ok { | ||
2438 | 62 | fmt.Fprintf(os.Stderr, "error: no such auth-mode %q\n", *authModeFlag) | ||
2439 | 63 | os.Exit(1) | ||
2440 | 64 | } | ||
2441 | 65 | novaclient, err := createNovaClient(mode) | ||
2442 | 66 | if err == nil { | ||
2443 | 67 | err = DeleteAll(os.Stdout, novaclient) | ||
2444 | 68 | } | ||
2445 | 69 | if err != nil { | ||
2446 | 70 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | ||
2447 | 71 | os.Exit(1) | ||
2448 | 72 | } | ||
2449 | 73 | } | ||
2450 | 0 | 74 | ||
2451 | === added file 'tools/secgroup-delete-all/main_test.go' | |||
2452 | --- tools/secgroup-delete-all/main_test.go 1970-01-01 00:00:00 +0000 | |||
2453 | +++ tools/secgroup-delete-all/main_test.go 2013-01-28 18:22:27 +0000 | |||
2454 | @@ -0,0 +1,71 @@ | |||
2455 | 1 | package main_test | ||
2456 | 2 | |||
2457 | 3 | import ( | ||
2458 | 4 | "bytes" | ||
2459 | 5 | . "launchpad.net/gocheck" | ||
2460 | 6 | "launchpad.net/goose/client" | ||
2461 | 7 | "launchpad.net/goose/identity" | ||
2462 | 8 | "launchpad.net/goose/nova" | ||
2463 | 9 | "launchpad.net/goose/testing/httpsuite" | ||
2464 | 10 | "launchpad.net/goose/testservices/openstack" | ||
2465 | 11 | tool "launchpad.net/goose/tools/secgroup-delete-all" | ||
2466 | 12 | "testing" | ||
2467 | 13 | ) | ||
2468 | 14 | |||
2469 | 15 | func Test(t *testing.T) { | ||
2470 | 16 | TestingT(t) | ||
2471 | 17 | } | ||
2472 | 18 | |||
2473 | 19 | const ( | ||
2474 | 20 | username = "auser" | ||
2475 | 21 | password = "apass" | ||
2476 | 22 | region = "aregion" | ||
2477 | 23 | tenant = "1" | ||
2478 | 24 | ) | ||
2479 | 25 | |||
2480 | 26 | type ToolSuite struct { | ||
2481 | 27 | httpsuite.HTTPSuite | ||
2482 | 28 | creds *identity.Credentials | ||
2483 | 29 | } | ||
2484 | 30 | |||
2485 | 31 | var _ = Suite(&ToolSuite{}) | ||
2486 | 32 | |||
2487 | 33 | // GZ 2013-01-21: Should require EnvSuite for this, but clashes with HTTPSuite | ||
2488 | 34 | func createNovaClient(creds *identity.Credentials) *nova.Client { | ||
2489 | 35 | osc := client.NewClient(creds, identity.AuthUserPass, nil) | ||
2490 | 36 | return nova.New(osc) | ||
2491 | 37 | } | ||
2492 | 38 | |||
2493 | 39 | func (s *ToolSuite) makeServices(c *C) *nova.Client { | ||
2494 | 40 | creds := &identity.Credentials{ | ||
2495 | 41 | URL: s.Server.URL, | ||
2496 | 42 | User: username, | ||
2497 | 43 | Secrets: password, | ||
2498 | 44 | Region: region, | ||
2499 | 45 | TenantName: tenant, | ||
2500 | 46 | } | ||
2501 | 47 | openstack := openstack.New(creds) | ||
2502 | 48 | openstack.SetupHTTP(s.Mux) | ||
2503 | 49 | return createNovaClient(creds) | ||
2504 | 50 | } | ||
2505 | 51 | |||
2506 | 52 | func (s *ToolSuite) TestNoGroups(c *C) { | ||
2507 | 53 | nova := s.makeServices(c) | ||
2508 | 54 | var buf bytes.Buffer | ||
2509 | 55 | err := tool.DeleteAll(&buf, nova) | ||
2510 | 56 | c.Assert(err, IsNil) | ||
2511 | 57 | c.Assert(string(buf.Bytes()), Equals, "No security groups to delete.\n") | ||
2512 | 58 | } | ||
2513 | 59 | |||
2514 | 60 | func (s *ToolSuite) TestTwoGroups(c *C) { | ||
2515 | 61 | nova := s.makeServices(c) | ||
2516 | 62 | nova.CreateSecurityGroup("group-a", "A group") | ||
2517 | 63 | nova.CreateSecurityGroup("group-b", "Another group") | ||
2518 | 64 | var buf bytes.Buffer | ||
2519 | 65 | err := tool.DeleteAll(&buf, nova) | ||
2520 | 66 | c.Assert(err, IsNil) | ||
2521 | 67 | c.Assert(string(buf.Bytes()), Equals, "2 security groups deleted.\n") | ||
2522 | 68 | } | ||
2523 | 69 | |||
2524 | 70 | // GZ 2013-01-21: Should also test undeleteable groups, but can't induce | ||
2525 | 71 | // novaservice errors currently. |
The attempt to merge lp:goose into lp:~goose-bot/goose/trunk failed. Below is the output from the failed tests.
Setting GOPATH to: /home/tarmac/trees sys.argv[ 1:]) setup(opts) tarmac_ repository( ) tarmac_ repository tion.apply( False) python2. 7/dist- packages/ bzrlib/ reconfigure. py", line 351, in apply find_repository () python2. 7/dist- packages/ bzrlib/ bzrdir. py", line 574, in find_repository NoRepositoryPre sent(self) errors. NoRepositoryPre sent: No repository present: "bzr+ssh: //bazaar. launchpad. net/~goose- bot/goose/ "
Reconfiguring to use a shared repository
Traceback (most recent call last):
File "test.py", line 172, in <module>
main(
File "test.py", line 157, in main
tarmac_
File "test.py", line 82, in tarmac_setup
create_
File "test.py", line 58, in create_
reconfigura
File "/usr/lib/
new_repo = up_bzrdir.
File "/usr/lib/
raise errors.
bzrlib.