Merge lp:~oubiwann/txaws/486365-get-bucket into lp:txaws
- 486365-get-bucket
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merge reported by: | Duncan McGreggor | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~oubiwann/txaws/486365-get-bucket | ||||
Merge into: | lp:txaws | ||||
Prerequisite: | lp:~oubiwann/txaws/486363-no-content-fix | ||||
Diff against target: |
2773 lines (+1302/-458) 29 files modified
LICENSE (+10/-5) README (+6/-0) bin/txaws-create-bucket (+42/-0) bin/txaws-delete-bucket (+42/-0) bin/txaws-delete-object (+46/-0) bin/txaws-get-bucket (+46/-0) bin/txaws-get-object (+46/-0) bin/txaws-head-object (+47/-0) bin/txaws-list-buckets (+43/-0) bin/txaws-put-object (+56/-0) txaws/client/base.py (+41/-0) txaws/client/tests/test_client.py (+41/-2) txaws/ec2/client.py (+70/-85) txaws/ec2/exception.py (+4/-108) txaws/ec2/tests/test_client.py (+132/-87) txaws/ec2/tests/test_exception.py (+2/-129) txaws/exception.py (+113/-0) txaws/meta.py (+10/-0) txaws/s3/client.py (+68/-7) txaws/s3/exception.py (+21/-0) txaws/s3/model.py (+38/-3) txaws/s3/tests/test_client.py (+47/-2) txaws/s3/tests/test_exception.py (+62/-0) txaws/script.py (+42/-0) txaws/service.py (+12/-9) txaws/testing/payload.py (+64/-19) txaws/tests/test_exception.py (+114/-0) txaws/tests/test_service.py (+7/-2) txaws/util.py (+30/-0) |
||||
To merge this branch: | bzr merge lp:~oubiwann/txaws/486365-get-bucket | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins | Approve | ||
Review via email: mp+15343@code.launchpad.net |
This proposal supersedes a proposal from 2009-11-23.
Commit message
Description of the change
Duncan McGreggor (oubiwann) wrote : Posted in a previous version of this proposal | # |
Duncan McGreggor (oubiwann) wrote : Posted in a previous version of this proposal | # |
Landscape trunk has been tested against this branch.
Robert Collins (lifeless) wrote : | # |
I've reviewed what I see on this web page: https:/
I don't know if that is the whole branch or just an incremental bit; I think it might be the lot.
2024 +author = "txAWS Deelopers"
There is a bunch of duplication in the example scripts. I'd like to see that removed. Perhaps:
- give them a if __name__ == guard
- move the error/return etc callback support into script.py
Lastly, the default options in util/script.py include a bucket - is that relevant for all aws services? If not, lets factor that into two layers - truely global options and options for a given service.
e.g
options = all_options()
s3_options(
Duncan McGreggor (oubiwann) wrote : | # |
> I've reviewed what I see on this web page:
> https:/
>
> I don't know if that is the whole branch or just an incremental bit; I think
> it might be the lot.
>
> 2024 +author = "txAWS Deelopers"
Fixed -- thanks!
> There is a bunch of duplication in the example scripts. I'd like to see that
> removed. Perhaps:
> - give them a if __name__ == guard
> - move the error/return etc callback support into script.py
I responded to this in the scripts merge proposal:
https:/
> Lastly, the default options in util/script.py include a bucket - is that
> relevant for all aws services? If not, lets factor that into two layers -
> truely global options and options for a given service.
> e.g
> options = all_options()
> s3_options(options)
Yup, this is definitely planned for. However, I wanted to wait until there were some non-s3 script, before splitting out the code. I'll add a note to the ec2 scripts ticket (bug #484857).
Preview Diff
1 | === modified file 'LICENSE' | |||
2 | --- LICENSE 2008-07-06 22:51:54 +0000 | |||
3 | +++ LICENSE 2009-11-28 01:15:24 +0000 | |||
4 | @@ -1,3 +1,8 @@ | |||
5 | 1 | Copyright (C) 2008 Tristan Seligmann <mithrandi@mithrandi.net> | ||
6 | 2 | Copyright (C) 2009 Robert Collins <robertc@robertcollins.net> | ||
7 | 3 | Copyright (C) 2009 Canonical Ltd | ||
8 | 4 | Copyright (C) 2009 Duncan McGreggor <oubiwann@adytum.us> | ||
9 | 5 | |||
10 | 1 | Permission is hereby granted, free of charge, to any person obtaining | 6 | Permission is hereby granted, free of charge, to any person obtaining |
11 | 2 | a copy of this software and associated documentation files (the | 7 | a copy of this software and associated documentation files (the |
12 | 3 | "Software"), to deal in the Software without restriction, including | 8 | "Software"), to deal in the Software without restriction, including |
13 | @@ -11,8 +16,8 @@ | |||
14 | 11 | 16 | ||
15 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
16 | 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
22 | 14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
23 | 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
24 | 16 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
25 | 17 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
26 | 18 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
27 | 19 | 24 | ||
28 | === modified file 'README' | |||
29 | --- README 2009-08-19 20:55:49 +0000 | |||
30 | +++ README 2009-11-28 01:15:24 +0000 | |||
31 | @@ -14,3 +14,9 @@ | |||
32 | 14 | * The txaws python package. (No installer at the moment) | 14 | * The txaws python package. (No installer at the moment) |
33 | 15 | 15 | ||
34 | 16 | * bin/aws-status, a GUI status program for aws resources. | 16 | * bin/aws-status, a GUI status program for aws resources. |
35 | 17 | |||
36 | 18 | License | ||
37 | 19 | ------- | ||
38 | 20 | |||
39 | 21 | txAWS is open source software, MIT License. See the LICENSE file for more | ||
40 | 22 | details. | ||
41 | 17 | 23 | ||
42 | === added file 'bin/txaws-create-bucket' | |||
43 | --- bin/txaws-create-bucket 1970-01-01 00:00:00 +0000 | |||
44 | +++ bin/txaws-create-bucket 2009-11-28 01:15:24 +0000 | |||
45 | @@ -0,0 +1,42 @@ | |||
46 | 1 | #!/usr/bin/env python | ||
47 | 2 | """ | ||
48 | 3 | %prog [options] | ||
49 | 4 | """ | ||
50 | 5 | |||
51 | 6 | import sys | ||
52 | 7 | |||
53 | 8 | from txaws.credentials import AWSCredentials | ||
54 | 9 | from txaws.script import parse_options | ||
55 | 10 | from txaws.service import AWSServiceRegion | ||
56 | 11 | from txaws.util import reactor | ||
57 | 12 | |||
58 | 13 | |||
59 | 14 | def printResults(results): | ||
60 | 15 | return 0 | ||
61 | 16 | |||
62 | 17 | |||
63 | 18 | def printError(error): | ||
64 | 19 | print error.value | ||
65 | 20 | return 1 | ||
66 | 21 | |||
67 | 22 | |||
68 | 23 | def finish(return_code): | ||
69 | 24 | reactor.stop(exitStatus=return_code) | ||
70 | 25 | |||
71 | 26 | |||
72 | 27 | options, args = parse_options(__doc__.strip()) | ||
73 | 28 | if options.bucket is None: | ||
74 | 29 | print "Error Message: A bucket name is required." | ||
75 | 30 | sys.exit(1) | ||
76 | 31 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
77 | 32 | region = AWSServiceRegion( | ||
78 | 33 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
79 | 34 | client = region.get_s3_client() | ||
80 | 35 | |||
81 | 36 | d = client.create_bucket(options.bucket) | ||
82 | 37 | d.addCallback(printResults) | ||
83 | 38 | d.addErrback(printError) | ||
84 | 39 | d.addCallback(finish) | ||
85 | 40 | # We use a custom reactor so that we can return the exit status from | ||
86 | 41 | # reactor.run(). | ||
87 | 42 | sys.exit(reactor.run()) | ||
88 | 0 | 43 | ||
89 | === added file 'bin/txaws-delete-bucket' | |||
90 | --- bin/txaws-delete-bucket 1970-01-01 00:00:00 +0000 | |||
91 | +++ bin/txaws-delete-bucket 2009-11-28 01:15:24 +0000 | |||
92 | @@ -0,0 +1,42 @@ | |||
93 | 1 | #!/usr/bin/env python | ||
94 | 2 | """ | ||
95 | 3 | %prog [options] | ||
96 | 4 | """ | ||
97 | 5 | |||
98 | 6 | import sys | ||
99 | 7 | |||
100 | 8 | from txaws.credentials import AWSCredentials | ||
101 | 9 | from txaws.script import parse_options | ||
102 | 10 | from txaws.service import AWSServiceRegion | ||
103 | 11 | from txaws.util import reactor | ||
104 | 12 | |||
105 | 13 | |||
106 | 14 | def printResults(results): | ||
107 | 15 | return 0 | ||
108 | 16 | |||
109 | 17 | |||
110 | 18 | def printError(error): | ||
111 | 19 | print error.value | ||
112 | 20 | return 1 | ||
113 | 21 | |||
114 | 22 | |||
115 | 23 | def finish(return_code): | ||
116 | 24 | reactor.stop(exitStatus=return_code) | ||
117 | 25 | |||
118 | 26 | |||
119 | 27 | options, args = parse_options(__doc__.strip()) | ||
120 | 28 | if options.bucket is None: | ||
121 | 29 | print "Error Message: A bucket name is required." | ||
122 | 30 | sys.exit(1) | ||
123 | 31 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
124 | 32 | region = AWSServiceRegion( | ||
125 | 33 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
126 | 34 | client = region.get_s3_client() | ||
127 | 35 | |||
128 | 36 | d = client.delete_bucket(options.bucket) | ||
129 | 37 | d.addCallback(printResults) | ||
130 | 38 | d.addErrback(printError) | ||
131 | 39 | d.addCallback(finish) | ||
132 | 40 | # We use a custom reactor so that we can return the exit status from | ||
133 | 41 | # reactor.run(). | ||
134 | 42 | sys.exit(reactor.run()) | ||
135 | 0 | 43 | ||
136 | === added file 'bin/txaws-delete-object' | |||
137 | --- bin/txaws-delete-object 1970-01-01 00:00:00 +0000 | |||
138 | +++ bin/txaws-delete-object 2009-11-28 01:15:24 +0000 | |||
139 | @@ -0,0 +1,46 @@ | |||
140 | 1 | #!/usr/bin/env python | ||
141 | 2 | """ | ||
142 | 3 | %prog [options] | ||
143 | 4 | """ | ||
144 | 5 | |||
145 | 6 | import sys | ||
146 | 7 | |||
147 | 8 | from txaws.credentials import AWSCredentials | ||
148 | 9 | from txaws.script import parse_options | ||
149 | 10 | from txaws.service import AWSServiceRegion | ||
150 | 11 | from txaws.util import reactor | ||
151 | 12 | |||
152 | 13 | |||
153 | 14 | def printResults(results): | ||
154 | 15 | print results | ||
155 | 16 | return 0 | ||
156 | 17 | |||
157 | 18 | |||
158 | 19 | def printError(error): | ||
159 | 20 | print error.value | ||
160 | 21 | return 1 | ||
161 | 22 | |||
162 | 23 | |||
163 | 24 | def finish(return_code): | ||
164 | 25 | reactor.stop(exitStatus=return_code) | ||
165 | 26 | |||
166 | 27 | |||
167 | 28 | options, args = parse_options(__doc__.strip()) | ||
168 | 29 | if options.bucket is None: | ||
169 | 30 | print "Error Message: A bucket name is required." | ||
170 | 31 | sys.exit(1) | ||
171 | 32 | if options.object_name is None: | ||
172 | 33 | print "Error Message: An object name is required." | ||
173 | 34 | sys.exit(1) | ||
174 | 35 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
175 | 36 | region = AWSServiceRegion( | ||
176 | 37 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
177 | 38 | client = region.get_s3_client() | ||
178 | 39 | |||
179 | 40 | d = client.delete_object(options.bucket, options.object_name) | ||
180 | 41 | d.addCallback(printResults) | ||
181 | 42 | d.addErrback(printError) | ||
182 | 43 | d.addCallback(finish) | ||
183 | 44 | # We use a custom reactor so that we can return the exit status from | ||
184 | 45 | # reactor.run(). | ||
185 | 46 | sys.exit(reactor.run()) | ||
186 | 0 | 47 | ||
187 | === added file 'bin/txaws-get-bucket' | |||
188 | --- bin/txaws-get-bucket 1970-01-01 00:00:00 +0000 | |||
189 | +++ bin/txaws-get-bucket 2009-11-28 01:15:24 +0000 | |||
190 | @@ -0,0 +1,46 @@ | |||
191 | 1 | #!/usr/bin/env python | ||
192 | 2 | """ | ||
193 | 3 | %prog [options] | ||
194 | 4 | """ | ||
195 | 5 | |||
196 | 6 | import sys | ||
197 | 7 | |||
198 | 8 | from txaws.credentials import AWSCredentials | ||
199 | 9 | from txaws.script import parse_options | ||
200 | 10 | from txaws.service import AWSServiceRegion | ||
201 | 11 | from txaws.util import reactor | ||
202 | 12 | |||
203 | 13 | |||
204 | 14 | def printResults(listing, bucket): | ||
205 | 15 | print "Contents of '%s' bucket:" % bucket | ||
206 | 16 | for item in listing.contents: | ||
207 | 17 | print "\t%s (last modified on %s)" % (item.key, item.modification_date) | ||
208 | 18 | print "Total items: %s\n" % len(listing.contents) | ||
209 | 19 | return 0 | ||
210 | 20 | |||
211 | 21 | |||
212 | 22 | def printError(error): | ||
213 | 23 | print error.value | ||
214 | 24 | return 1 | ||
215 | 25 | |||
216 | 26 | |||
217 | 27 | def finish(return_code): | ||
218 | 28 | reactor.stop(exitStatus=return_code) | ||
219 | 29 | |||
220 | 30 | |||
221 | 31 | options, args = parse_options(__doc__.strip()) | ||
222 | 32 | if options.bucket is None: | ||
223 | 33 | print "Error Message: A bucket name is required." | ||
224 | 34 | sys.exit(1) | ||
225 | 35 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
226 | 36 | region = AWSServiceRegion( | ||
227 | 37 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
228 | 38 | client = region.get_s3_client() | ||
229 | 39 | |||
230 | 40 | d = client.get_bucket(options.bucket) | ||
231 | 41 | d.addCallback(printResults, options.bucket) | ||
232 | 42 | d.addErrback(printError) | ||
233 | 43 | d.addCallback(finish) | ||
234 | 44 | # We use a custom reactor so that we can return the exit status from | ||
235 | 45 | # reactor.run(). | ||
236 | 46 | sys.exit(reactor.run()) | ||
237 | 0 | 47 | ||
238 | === added file 'bin/txaws-get-object' | |||
239 | --- bin/txaws-get-object 1970-01-01 00:00:00 +0000 | |||
240 | +++ bin/txaws-get-object 2009-11-28 01:15:24 +0000 | |||
241 | @@ -0,0 +1,46 @@ | |||
242 | 1 | #!/usr/bin/env python | ||
243 | 2 | """ | ||
244 | 3 | %prog [options] | ||
245 | 4 | """ | ||
246 | 5 | |||
247 | 6 | import sys | ||
248 | 7 | |||
249 | 8 | from txaws.credentials import AWSCredentials | ||
250 | 9 | from txaws.script import parse_options | ||
251 | 10 | from txaws.service import AWSServiceRegion | ||
252 | 11 | from txaws.util import reactor | ||
253 | 12 | |||
254 | 13 | |||
255 | 14 | def printResults(results): | ||
256 | 15 | print results | ||
257 | 16 | return 0 | ||
258 | 17 | |||
259 | 18 | |||
260 | 19 | def printError(error): | ||
261 | 20 | print error.value | ||
262 | 21 | return 1 | ||
263 | 22 | |||
264 | 23 | |||
265 | 24 | def finish(return_code): | ||
266 | 25 | reactor.stop(exitStatus=return_code) | ||
267 | 26 | |||
268 | 27 | |||
269 | 28 | options, args = parse_options(__doc__.strip()) | ||
270 | 29 | if options.bucket is None: | ||
271 | 30 | print "Error Message: A bucket name is required." | ||
272 | 31 | sys.exit(1) | ||
273 | 32 | if options.object_name is None: | ||
274 | 33 | print "Error Message: An object name is required." | ||
275 | 34 | sys.exit(1) | ||
276 | 35 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
277 | 36 | region = AWSServiceRegion( | ||
278 | 37 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
279 | 38 | client = region.get_s3_client() | ||
280 | 39 | |||
281 | 40 | d = client.get_object(options.bucket, options.object_name) | ||
282 | 41 | d.addCallback(printResults) | ||
283 | 42 | d.addErrback(printError) | ||
284 | 43 | d.addCallback(finish) | ||
285 | 44 | # We use a custom reactor so that we can return the exit status from | ||
286 | 45 | # reactor.run(). | ||
287 | 46 | sys.exit(reactor.run()) | ||
288 | 0 | 47 | ||
289 | === added file 'bin/txaws-head-object' | |||
290 | --- bin/txaws-head-object 1970-01-01 00:00:00 +0000 | |||
291 | +++ bin/txaws-head-object 2009-11-28 01:15:24 +0000 | |||
292 | @@ -0,0 +1,47 @@ | |||
293 | 1 | #!/usr/bin/env python | ||
294 | 2 | """ | ||
295 | 3 | %prog [options] | ||
296 | 4 | """ | ||
297 | 5 | |||
298 | 6 | import sys | ||
299 | 7 | from pprint import pprint | ||
300 | 8 | |||
301 | 9 | from txaws.credentials import AWSCredentials | ||
302 | 10 | from txaws.script import parse_options | ||
303 | 11 | from txaws.service import AWSServiceRegion | ||
304 | 12 | from txaws.util import reactor | ||
305 | 13 | |||
306 | 14 | |||
307 | 15 | def printResults(results): | ||
308 | 16 | pprint(results) | ||
309 | 17 | return 0 | ||
310 | 18 | |||
311 | 19 | |||
312 | 20 | def printError(error): | ||
313 | 21 | print error.value | ||
314 | 22 | return 1 | ||
315 | 23 | |||
316 | 24 | |||
317 | 25 | def finish(return_code): | ||
318 | 26 | reactor.stop(exitStatus=return_code) | ||
319 | 27 | |||
320 | 28 | |||
321 | 29 | options, args = parse_options(__doc__.strip()) | ||
322 | 30 | if options.bucket is None: | ||
323 | 31 | print "Error Message: A bucket name is required." | ||
324 | 32 | sys.exit(1) | ||
325 | 33 | if options.object_name is None: | ||
326 | 34 | print "Error Message: An object name is required." | ||
327 | 35 | sys.exit(1) | ||
328 | 36 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
329 | 37 | region = AWSServiceRegion( | ||
330 | 38 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
331 | 39 | client = region.get_s3_client() | ||
332 | 40 | |||
333 | 41 | d = client.head_object(options.bucket, options.object_name) | ||
334 | 42 | d.addCallback(printResults) | ||
335 | 43 | d.addErrback(printError) | ||
336 | 44 | d.addCallback(finish) | ||
337 | 45 | # We use a custom reactor so that we can return the exit status from | ||
338 | 46 | # reactor.run(). | ||
339 | 47 | sys.exit(reactor.run()) | ||
340 | 0 | 48 | ||
341 | === added file 'bin/txaws-list-buckets' | |||
342 | --- bin/txaws-list-buckets 1970-01-01 00:00:00 +0000 | |||
343 | +++ bin/txaws-list-buckets 2009-11-28 01:15:24 +0000 | |||
344 | @@ -0,0 +1,43 @@ | |||
345 | 1 | #!/usr/bin/env python | ||
346 | 2 | """ | ||
347 | 3 | %prog [options] | ||
348 | 4 | """ | ||
349 | 5 | |||
350 | 6 | import sys | ||
351 | 7 | |||
352 | 8 | from txaws.credentials import AWSCredentials | ||
353 | 9 | from txaws.script import parse_options | ||
354 | 10 | from txaws.service import AWSServiceRegion | ||
355 | 11 | from txaws.util import reactor | ||
356 | 12 | |||
357 | 13 | |||
358 | 14 | def printResults(results): | ||
359 | 15 | print "\nBuckets:" | ||
360 | 16 | for bucket in results: | ||
361 | 17 | print "\t%s (created on %s)" % (bucket.name, bucket.creation_date) | ||
362 | 18 | print "Total buckets: %s\n" % len(list(results)) | ||
363 | 19 | return 0 | ||
364 | 20 | |||
365 | 21 | |||
366 | 22 | def printError(error): | ||
367 | 23 | print error.value | ||
368 | 24 | return 1 | ||
369 | 25 | |||
370 | 26 | |||
371 | 27 | def finish(return_code): | ||
372 | 28 | reactor.stop(exitStatus=return_code) | ||
373 | 29 | |||
374 | 30 | |||
375 | 31 | options, args = parse_options(__doc__.strip()) | ||
376 | 32 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
377 | 33 | region = AWSServiceRegion( | ||
378 | 34 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
379 | 35 | client = region.get_s3_client() | ||
380 | 36 | |||
381 | 37 | d = client.list_buckets() | ||
382 | 38 | d.addCallback(printResults) | ||
383 | 39 | d.addErrback(printError) | ||
384 | 40 | d.addCallback(finish) | ||
385 | 41 | # We use a custom reactor so that we can return the exit status from | ||
386 | 42 | # reactor.run(). | ||
387 | 43 | sys.exit(reactor.run()) | ||
388 | 0 | 44 | ||
389 | === added file 'bin/txaws-put-object' | |||
390 | --- bin/txaws-put-object 1970-01-01 00:00:00 +0000 | |||
391 | +++ bin/txaws-put-object 2009-11-28 01:15:24 +0000 | |||
392 | @@ -0,0 +1,56 @@ | |||
393 | 1 | #!/usr/bin/env python | ||
394 | 2 | """ | ||
395 | 3 | %prog [options] | ||
396 | 4 | """ | ||
397 | 5 | |||
398 | 6 | import os | ||
399 | 7 | import sys | ||
400 | 8 | |||
401 | 9 | from txaws.credentials import AWSCredentials | ||
402 | 10 | from txaws.script import parse_options | ||
403 | 11 | from txaws.service import AWSServiceRegion | ||
404 | 12 | from txaws.util import reactor | ||
405 | 13 | |||
406 | 14 | |||
407 | 15 | def printResults(results): | ||
408 | 16 | return 0 | ||
409 | 17 | |||
410 | 18 | |||
411 | 19 | def printError(error): | ||
412 | 20 | print error.value | ||
413 | 21 | return 1 | ||
414 | 22 | |||
415 | 23 | |||
416 | 24 | def finish(return_code): | ||
417 | 25 | reactor.stop(exitStatus=return_code) | ||
418 | 26 | |||
419 | 27 | |||
420 | 28 | options, args = parse_options(__doc__.strip()) | ||
421 | 29 | if options.bucket is None: | ||
422 | 30 | print "Error Message: A bucket name is required." | ||
423 | 31 | sys.exit(1) | ||
424 | 32 | filename = options.object_filename | ||
425 | 33 | if filename: | ||
426 | 34 | options.object_name = os.path.basename(filename) | ||
427 | 35 | try: | ||
428 | 36 | options.object_data = open(filename).read() | ||
429 | 37 | except Exception, error: | ||
430 | 38 | print error | ||
431 | 39 | sys.exit(1) | ||
432 | 40 | elif options.object_name is None: | ||
433 | 41 | print "Error Message: An object name is required." | ||
434 | 42 | sys.exit(1) | ||
435 | 43 | creds = AWSCredentials(options.access_key, options.secret_key) | ||
436 | 44 | region = AWSServiceRegion( | ||
437 | 45 | creds=creds, region=options.region, s3_endpoint=options.url) | ||
438 | 46 | client = region.get_s3_client() | ||
439 | 47 | |||
440 | 48 | d = client.put_object( | ||
441 | 49 | options.bucket, options.object_name, options.object_data, | ||
442 | 50 | options.content_type) | ||
443 | 51 | d.addCallback(printResults) | ||
444 | 52 | d.addErrback(printError) | ||
445 | 53 | d.addCallback(finish) | ||
446 | 54 | # We use a custom reactor so that we can return the exit status from | ||
447 | 55 | # reactor.run(). | ||
448 | 56 | sys.exit(reactor.run()) | ||
449 | 0 | 57 | ||
450 | === modified file 'txaws/client/base.py' | |||
451 | --- txaws/client/base.py 2009-11-28 01:15:24 +0000 | |||
452 | +++ txaws/client/base.py 2009-11-28 01:15:24 +0000 | |||
453 | @@ -1,11 +1,52 @@ | |||
454 | 1 | from xml.parsers.expat import ExpatError | ||
455 | 2 | |||
456 | 1 | from twisted.internet import reactor, ssl | 3 | from twisted.internet import reactor, ssl |
457 | 4 | from twisted.web import http | ||
458 | 2 | from twisted.web.client import HTTPClientFactory | 5 | from twisted.web.client import HTTPClientFactory |
459 | 6 | from twisted.web.error import Error as TwistedWebError | ||
460 | 3 | 7 | ||
461 | 4 | from txaws.util import parse | 8 | from txaws.util import parse |
462 | 5 | from txaws.credentials import AWSCredentials | 9 | from txaws.credentials import AWSCredentials |
463 | 10 | from txaws.exception import AWSResponseParseError | ||
464 | 6 | from txaws.service import AWSServiceEndpoint | 11 | from txaws.service import AWSServiceEndpoint |
465 | 7 | 12 | ||
466 | 8 | 13 | ||
467 | 14 | def error_wrapper(error, errorClass): | ||
468 | 15 | """ | ||
469 | 16 | We want to see all error messages from cloud services. Amazon's EC2 says | ||
470 | 17 | that their errors are accompanied either by a 400-series or 500-series HTTP | ||
471 | 18 | response code. As such, the first thing we want to do is check to see if | ||
472 | 19 | the error is in that range. If it is, we then need to see if the error | ||
473 | 20 | message is an EC2 one. | ||
474 | 21 | |||
475 | 22 | In the event that an error is not a Twisted web error nor an EC2 one, the | ||
476 | 23 | original exception is raised. | ||
477 | 24 | """ | ||
478 | 25 | http_status = 0 | ||
479 | 26 | if error.check(TwistedWebError): | ||
480 | 27 | xml_payload = error.value.response | ||
481 | 28 | if error.value.status: | ||
482 | 29 | http_status = int(error.value.status) | ||
483 | 30 | else: | ||
484 | 31 | error.raiseException() | ||
485 | 32 | if http_status >= 400: | ||
486 | 33 | if not xml_payload: | ||
487 | 34 | error.raiseException() | ||
488 | 35 | try: | ||
489 | 36 | fallback_error = errorClass( | ||
490 | 37 | xml_payload, error.value.status, error.value.message, | ||
491 | 38 | error.value.response) | ||
492 | 39 | except (ExpatError, AWSResponseParseError): | ||
493 | 40 | error_message = http.RESPONSES.get(http_status) | ||
494 | 41 | fallback_error = TwistedWebError( | ||
495 | 42 | http_status, error_message, error.value.response) | ||
496 | 43 | raise fallback_error | ||
497 | 44 | elif 200 <= http_status < 300: | ||
498 | 45 | return str(error.value) | ||
499 | 46 | else: | ||
500 | 47 | error.raiseException() | ||
501 | 48 | |||
502 | 49 | |||
503 | 9 | class BaseClient(object): | 50 | class BaseClient(object): |
504 | 10 | """Create an AWS client. | 51 | """Create an AWS client. |
505 | 11 | 52 | ||
506 | 12 | 53 | ||
507 | === modified file 'txaws/client/tests/test_client.py' | |||
508 | --- txaws/client/tests/test_client.py 2009-11-28 01:15:24 +0000 | |||
509 | +++ txaws/client/tests/test_client.py 2009-11-28 01:15:24 +0000 | |||
510 | @@ -1,16 +1,55 @@ | |||
511 | 1 | import os | 1 | import os |
512 | 2 | 2 | ||
513 | 3 | from twisted.internet import reactor | 3 | from twisted.internet import reactor |
514 | 4 | from twisted.internet.error import ConnectionRefusedError | ||
515 | 4 | from twisted.protocols.policies import WrappingFactory | 5 | from twisted.protocols.policies import WrappingFactory |
516 | 5 | from twisted.python import log | 6 | from twisted.python import log |
517 | 6 | from twisted.python.filepath import FilePath | 7 | from twisted.python.filepath import FilePath |
518 | 8 | from twisted.python.failure import Failure | ||
519 | 9 | from twisted.web import server, static | ||
520 | 7 | from twisted.web.client import HTTPClientFactory | 10 | from twisted.web.client import HTTPClientFactory |
522 | 8 | from twisted.web import server, static | 11 | from twisted.web.error import Error as TwistedWebError |
523 | 9 | 12 | ||
525 | 10 | from txaws.client.base import BaseClient, BaseQuery | 13 | from txaws.client.base import BaseClient, BaseQuery, error_wrapper |
526 | 11 | from txaws.testing.base import TXAWSTestCase | 14 | from txaws.testing.base import TXAWSTestCase |
527 | 12 | 15 | ||
528 | 13 | 16 | ||
529 | 17 | class ErrorWrapperTestCase(TXAWSTestCase): | ||
530 | 18 | |||
531 | 19 | def test_204_no_content(self): | ||
532 | 20 | failure = Failure(TwistedWebError(204, "No content")) | ||
533 | 21 | wrapped = error_wrapper(failure, None) | ||
534 | 22 | self.assertEquals(wrapped, "204 No content") | ||
535 | 23 | |||
536 | 24 | def test_302_found(self): | ||
537 | 25 | # XXX I'm not sure we want to raise for 300s... | ||
538 | 26 | failure = Failure(TwistedWebError(302, "found")) | ||
539 | 27 | error = self.assertRaises( | ||
540 | 28 | Exception, error_wrapper, failure, None) | ||
541 | 29 | self.assertEquals(failure.type, type(error)) | ||
542 | 30 | self.assertTrue(isinstance(error, TwistedWebError)) | ||
543 | 31 | self.assertEquals(str(error), "302 found") | ||
544 | 32 | |||
545 | 33 | def test_500(self): | ||
546 | 34 | failure = Failure(TwistedWebError(500, "internal error")) | ||
547 | 35 | error = self.assertRaises( | ||
548 | 36 | Exception, error_wrapper, failure, None) | ||
549 | 37 | self.assertTrue(isinstance(error, TwistedWebError)) | ||
550 | 38 | self.assertEquals(str(error), "500 internal error") | ||
551 | 39 | |||
552 | 40 | def test_timeout_error(self): | ||
553 | 41 | failure = Failure(Exception("timeout")) | ||
554 | 42 | error = self.assertRaises(Exception, error_wrapper, failure, None) | ||
555 | 43 | self.assertTrue(isinstance(error, Exception)) | ||
556 | 44 | self.assertEquals(error.message, "timeout") | ||
557 | 45 | |||
558 | 46 | def test_connection_error(self): | ||
559 | 47 | failure = Failure(ConnectionRefusedError("timeout")) | ||
560 | 48 | error = self.assertRaises( | ||
561 | 49 | Exception, error_wrapper, failure, ConnectionRefusedError) | ||
562 | 50 | self.assertTrue(isinstance(error, ConnectionRefusedError)) | ||
563 | 51 | |||
564 | 52 | |||
565 | 14 | class BaseClientTestCase(TXAWSTestCase): | 53 | class BaseClientTestCase(TXAWSTestCase): |
566 | 15 | 54 | ||
567 | 16 | def test_creation(self): | 55 | def test_creation(self): |
568 | 17 | 56 | ||
569 | === modified file 'txaws/ec2/client.py' | |||
570 | --- txaws/ec2/client.py 2009-11-28 01:15:24 +0000 | |||
571 | +++ txaws/ec2/client.py 2009-11-28 01:15:24 +0000 | |||
572 | @@ -8,16 +8,11 @@ | |||
573 | 8 | from datetime import datetime | 8 | from datetime import datetime |
574 | 9 | from urllib import quote | 9 | from urllib import quote |
575 | 10 | from base64 import b64encode | 10 | from base64 import b64encode |
576 | 11 | from xml.parsers.expat import ExpatError | ||
577 | 12 | |||
578 | 13 | from twisted.web import http | ||
579 | 14 | from twisted.web.error import Error as TwistedWebError | ||
580 | 15 | 11 | ||
581 | 16 | from txaws import version | 12 | from txaws import version |
583 | 17 | from txaws.client.base import BaseClient, BaseQuery | 13 | from txaws.client.base import BaseClient, BaseQuery, error_wrapper |
584 | 18 | from txaws.ec2 import model | 14 | from txaws.ec2 import model |
585 | 19 | from txaws.ec2.exception import EC2Error | 15 | from txaws.ec2.exception import EC2Error |
586 | 20 | from txaws.exception import AWSResponseParseError | ||
587 | 21 | from txaws.util import iso8601time, XML | 16 | from txaws.util import iso8601time, XML |
588 | 22 | 17 | ||
589 | 23 | 18 | ||
590 | @@ -25,34 +20,7 @@ | |||
591 | 25 | 20 | ||
592 | 26 | 21 | ||
593 | 27 | def ec2_error_wrapper(error): | 22 | def ec2_error_wrapper(error): |
622 | 28 | """ | 23 | error_wrapper(error, EC2Error) |
595 | 29 | We want to see all error messages from cloud services. Amazon's EC2 says | ||
596 | 30 | that their errors are accompanied either by a 400-series or 500-series HTTP | ||
597 | 31 | response code. As such, the first thing we want to do is check to see if | ||
598 | 32 | the error is in that range. If it is, we then need to see if the error | ||
599 | 33 | message is an EC2 one. | ||
600 | 34 | |||
601 | 35 | In the event that an error is not a Twisted web error nor an EC2 one, the | ||
602 | 36 | original exception is raised. | ||
603 | 37 | """ | ||
604 | 38 | http_status = 0 | ||
605 | 39 | if error.check(TwistedWebError): | ||
606 | 40 | xml_payload = error.value.response | ||
607 | 41 | if error.value.status: | ||
608 | 42 | http_status = int(error.value.status) | ||
609 | 43 | else: | ||
610 | 44 | error.raiseException() | ||
611 | 45 | if http_status >= 400: | ||
612 | 46 | try: | ||
613 | 47 | fallback_error = EC2Error(xml_payload, error.value.status, | ||
614 | 48 | error.value.message, error.value.response) | ||
615 | 49 | except (ExpatError, AWSResponseParseError): | ||
616 | 50 | error_message = http.RESPONSES.get(http_status) | ||
617 | 51 | fallback_error = TwistedWebError(http_status, error_message, | ||
618 | 52 | error.value.response) | ||
619 | 53 | raise fallback_error | ||
620 | 54 | else: | ||
621 | 55 | error.raiseException() | ||
623 | 56 | 24 | ||
624 | 57 | 25 | ||
625 | 58 | class EC2Client(BaseClient): | 26 | class EC2Client(BaseClient): |
626 | @@ -60,16 +28,17 @@ | |||
627 | 60 | 28 | ||
628 | 61 | def __init__(self, creds=None, endpoint=None, query_factory=None): | 29 | def __init__(self, creds=None, endpoint=None, query_factory=None): |
629 | 62 | if query_factory is None: | 30 | if query_factory is None: |
631 | 63 | self.query_factory = Query | 31 | query_factory = Query |
632 | 64 | super(EC2Client, self).__init__(creds, endpoint, query_factory) | 32 | super(EC2Client, self).__init__(creds, endpoint, query_factory) |
633 | 65 | 33 | ||
634 | 66 | def describe_instances(self, *instance_ids): | 34 | def describe_instances(self, *instance_ids): |
635 | 67 | """Describe current instances.""" | 35 | """Describe current instances.""" |
637 | 68 | instanceset = {} | 36 | instances= {} |
638 | 69 | for pos, instance_id in enumerate(instance_ids): | 37 | for pos, instance_id in enumerate(instance_ids): |
642 | 70 | instanceset["InstanceId.%d" % (pos + 1)] = instance_id | 38 | instances["InstanceId.%d" % (pos + 1)] = instance_id |
643 | 71 | query = self.query_factory("DescribeInstances", self.creds, | 39 | query = self.query_factory( |
644 | 72 | self.endpoint, instanceset) | 40 | action="DescribeInstances", creds=self.creds, |
645 | 41 | endpoint=self.endpoint, other_params=instances) | ||
646 | 73 | d = query.submit() | 42 | d = query.submit() |
647 | 74 | return d.addCallback(self._parse_describe_instances) | 43 | return d.addCallback(self._parse_describe_instances) |
648 | 75 | 44 | ||
649 | @@ -164,7 +133,8 @@ | |||
650 | 164 | if ramdisk_id is not None: | 133 | if ramdisk_id is not None: |
651 | 165 | params["RamdiskId"] = ramdisk_id | 134 | params["RamdiskId"] = ramdisk_id |
652 | 166 | query = self.query_factory( | 135 | query = self.query_factory( |
654 | 167 | "RunInstances", self.creds, self.endpoint, params) | 136 | action="RunInstances", creds=self.creds, endpoint=self.endpoint, |
655 | 137 | other_params=params) | ||
656 | 168 | d = query.submit() | 138 | d = query.submit() |
657 | 169 | return d.addCallback(self._parse_run_instances) | 139 | return d.addCallback(self._parse_run_instances) |
658 | 170 | 140 | ||
659 | @@ -195,11 +165,12 @@ | |||
660 | 195 | @return: A deferred which on success gives an iterable of | 165 | @return: A deferred which on success gives an iterable of |
661 | 196 | (id, old-state, new-state) tuples. | 166 | (id, old-state, new-state) tuples. |
662 | 197 | """ | 167 | """ |
664 | 198 | instanceset = {} | 168 | instances = {} |
665 | 199 | for pos, instance_id in enumerate(instance_ids): | 169 | for pos, instance_id in enumerate(instance_ids): |
667 | 200 | instanceset["InstanceId.%d" % (pos+1)] = instance_id | 170 | instances["InstanceId.%d" % (pos+1)] = instance_id |
668 | 201 | query = self.query_factory( | 171 | query = self.query_factory( |
670 | 202 | "TerminateInstances", self.creds, self.endpoint, instanceset) | 172 | action="TerminateInstances", creds=self.creds, |
671 | 173 | endpoint=self.endpoint, other_params=instances) | ||
672 | 203 | d = query.submit() | 174 | d = query.submit() |
673 | 204 | return d.addCallback(self._parse_terminate_instances) | 175 | return d.addCallback(self._parse_terminate_instances) |
674 | 205 | 176 | ||
675 | @@ -224,12 +195,13 @@ | |||
676 | 224 | @return: A C{Deferred} that will fire with a list of L{SecurityGroup}s | 195 | @return: A C{Deferred} that will fire with a list of L{SecurityGroup}s |
677 | 225 | retrieved from the cloud. | 196 | retrieved from the cloud. |
678 | 226 | """ | 197 | """ |
680 | 227 | group_names = None | 198 | group_names = {} |
681 | 228 | if names: | 199 | if names: |
682 | 229 | group_names = dict([("GroupName.%d" % (i+1), name) | 200 | group_names = dict([("GroupName.%d" % (i+1), name) |
683 | 230 | for i, name in enumerate(names)]) | 201 | for i, name in enumerate(names)]) |
686 | 231 | query = self.query_factory("DescribeSecurityGroups", self.creds, | 202 | query = self.query_factory( |
687 | 232 | self.endpoint, group_names) | 203 | action="DescribeSecurityGroups", creds=self.creds, |
688 | 204 | endpoint=self.endpoint, other_params=group_names) | ||
689 | 233 | d = query.submit() | 205 | d = query.submit() |
690 | 234 | return d.addCallback(self._parse_describe_security_groups) | 206 | return d.addCallback(self._parse_describe_security_groups) |
691 | 235 | 207 | ||
692 | @@ -282,8 +254,9 @@ | |||
693 | 282 | success of the operation. | 254 | success of the operation. |
694 | 283 | """ | 255 | """ |
695 | 284 | parameters = {"GroupName": name, "GroupDescription": description} | 256 | parameters = {"GroupName": name, "GroupDescription": description} |
698 | 285 | query = self.query_factory("CreateSecurityGroup", self.creds, | 257 | query = self.query_factory( |
699 | 286 | self.endpoint, parameters) | 258 | action="CreateSecurityGroup", creds=self.creds, |
700 | 259 | endpoint=self.endpoint, other_params=parameters) | ||
701 | 287 | d = query.submit() | 260 | d = query.submit() |
702 | 288 | return d.addCallback(self._parse_truth_return) | 261 | return d.addCallback(self._parse_truth_return) |
703 | 289 | 262 | ||
704 | @@ -298,8 +271,9 @@ | |||
705 | 298 | success of the operation. | 271 | success of the operation. |
706 | 299 | """ | 272 | """ |
707 | 300 | parameter = {"GroupName": name} | 273 | parameter = {"GroupName": name} |
710 | 301 | query = self.query_factory("DeleteSecurityGroup", self.creds, | 274 | query = self.query_factory( |
711 | 302 | self.endpoint, parameter) | 275 | action="DeleteSecurityGroup", creds=self.creds, |
712 | 276 | endpoint=self.endpoint, other_params=parameter) | ||
713 | 303 | d = query.submit() | 277 | d = query.submit() |
714 | 304 | return d.addCallback(self._parse_truth_return) | 278 | return d.addCallback(self._parse_truth_return) |
715 | 305 | 279 | ||
716 | @@ -354,8 +328,9 @@ | |||
717 | 354 | "all the ip parameters.") | 328 | "all the ip parameters.") |
718 | 355 | raise ValueError(msg) | 329 | raise ValueError(msg) |
719 | 356 | parameters["GroupName"] = group_name | 330 | parameters["GroupName"] = group_name |
722 | 357 | query = self.query_factory("AuthorizeSecurityGroupIngress", self.creds, | 331 | query = self.query_factory( |
723 | 358 | self.endpoint, parameters) | 332 | action="AuthorizeSecurityGroupIngress", creds=self.creds, |
724 | 333 | endpoint=self.endpoint, other_params=parameters) | ||
725 | 359 | d = query.submit() | 334 | d = query.submit() |
726 | 360 | return d.addCallback(self._parse_truth_return) | 335 | return d.addCallback(self._parse_truth_return) |
727 | 361 | 336 | ||
728 | @@ -438,8 +413,9 @@ | |||
729 | 438 | "all the ip parameters.") | 413 | "all the ip parameters.") |
730 | 439 | raise ValueError(msg) | 414 | raise ValueError(msg) |
731 | 440 | parameters["GroupName"] = group_name | 415 | parameters["GroupName"] = group_name |
734 | 441 | query = self.query_factory("RevokeSecurityGroupIngress", self.creds, | 416 | query = self.query_factory( |
735 | 442 | self.endpoint, parameters) | 417 | action="RevokeSecurityGroupIngress", creds=self.creds, |
736 | 418 | endpoint=self.endpoint, other_params=parameters) | ||
737 | 443 | d = query.submit() | 419 | d = query.submit() |
738 | 444 | return d.addCallback(self._parse_truth_return) | 420 | return d.addCallback(self._parse_truth_return) |
739 | 445 | 421 | ||
740 | @@ -477,7 +453,8 @@ | |||
741 | 477 | for pos, volume_id in enumerate(volume_ids): | 453 | for pos, volume_id in enumerate(volume_ids): |
742 | 478 | volumeset["VolumeId.%d" % (pos + 1)] = volume_id | 454 | volumeset["VolumeId.%d" % (pos + 1)] = volume_id |
743 | 479 | query = self.query_factory( | 455 | query = self.query_factory( |
745 | 480 | "DescribeVolumes", self.creds, self.endpoint, volumeset) | 456 | action="DescribeVolumes", creds=self.creds, endpoint=self.endpoint, |
746 | 457 | other_params=volumeset) | ||
747 | 481 | d = query.submit() | 458 | d = query.submit() |
748 | 482 | return d.addCallback(self._parse_describe_volumes) | 459 | return d.addCallback(self._parse_describe_volumes) |
749 | 483 | 460 | ||
750 | @@ -520,7 +497,8 @@ | |||
751 | 520 | if snapshot_id is not None: | 497 | if snapshot_id is not None: |
752 | 521 | params["SnapshotId"] = snapshot_id | 498 | params["SnapshotId"] = snapshot_id |
753 | 522 | query = self.query_factory( | 499 | query = self.query_factory( |
755 | 523 | "CreateVolume", self.creds, self.endpoint, params) | 500 | action="CreateVolume", creds=self.creds, endpoint=self.endpoint, |
756 | 501 | other_params=params) | ||
757 | 524 | d = query.submit() | 502 | d = query.submit() |
758 | 525 | return d.addCallback(self._parse_create_volume) | 503 | return d.addCallback(self._parse_create_volume) |
759 | 526 | 504 | ||
760 | @@ -541,7 +519,8 @@ | |||
761 | 541 | 519 | ||
762 | 542 | def delete_volume(self, volume_id): | 520 | def delete_volume(self, volume_id): |
763 | 543 | query = self.query_factory( | 521 | query = self.query_factory( |
765 | 544 | "DeleteVolume", self.creds, self.endpoint, {"VolumeId": volume_id}) | 522 | action="DeleteVolume", creds=self.creds, endpoint=self.endpoint, |
766 | 523 | other_params={"VolumeId": volume_id}) | ||
767 | 545 | d = query.submit() | 524 | d = query.submit() |
768 | 546 | return d.addCallback(self._parse_truth_return) | 525 | return d.addCallback(self._parse_truth_return) |
769 | 547 | 526 | ||
770 | @@ -551,7 +530,8 @@ | |||
771 | 551 | for pos, snapshot_id in enumerate(snapshot_ids): | 530 | for pos, snapshot_id in enumerate(snapshot_ids): |
772 | 552 | snapshot_set["SnapshotId.%d" % (pos + 1)] = snapshot_id | 531 | snapshot_set["SnapshotId.%d" % (pos + 1)] = snapshot_id |
773 | 553 | query = self.query_factory( | 532 | query = self.query_factory( |
775 | 554 | "DescribeSnapshots", self.creds, self.endpoint, snapshot_set) | 533 | action="DescribeSnapshots", creds=self.creds, |
776 | 534 | endpoint=self.endpoint, other_params=snapshot_set) | ||
777 | 555 | d = query.submit() | 535 | d = query.submit() |
778 | 556 | return d.addCallback(self._parse_snapshots) | 536 | return d.addCallback(self._parse_snapshots) |
779 | 557 | 537 | ||
780 | @@ -575,8 +555,8 @@ | |||
781 | 575 | def create_snapshot(self, volume_id): | 555 | def create_snapshot(self, volume_id): |
782 | 576 | """Create a new snapshot of an existing volume.""" | 556 | """Create a new snapshot of an existing volume.""" |
783 | 577 | query = self.query_factory( | 557 | query = self.query_factory( |
786 | 578 | "CreateSnapshot", self.creds, self.endpoint, | 558 | action="CreateSnapshot", creds=self.creds, endpoint=self.endpoint, |
787 | 579 | {"VolumeId": volume_id}) | 559 | other_params={"VolumeId": volume_id}) |
788 | 580 | d = query.submit() | 560 | d = query.submit() |
789 | 581 | return d.addCallback(self._parse_create_snapshot) | 561 | return d.addCallback(self._parse_create_snapshot) |
790 | 582 | 562 | ||
791 | @@ -596,17 +576,17 @@ | |||
792 | 596 | def delete_snapshot(self, snapshot_id): | 576 | def delete_snapshot(self, snapshot_id): |
793 | 597 | """Remove a previously created snapshot.""" | 577 | """Remove a previously created snapshot.""" |
794 | 598 | query = self.query_factory( | 578 | query = self.query_factory( |
797 | 599 | "DeleteSnapshot", self.creds, self.endpoint, | 579 | action="DeleteSnapshot", creds=self.creds, endpoint=self.endpoint, |
798 | 600 | {"SnapshotId": snapshot_id}) | 580 | other_params={"SnapshotId": snapshot_id}) |
799 | 601 | d = query.submit() | 581 | d = query.submit() |
800 | 602 | return d.addCallback(self._parse_truth_return) | 582 | return d.addCallback(self._parse_truth_return) |
801 | 603 | 583 | ||
802 | 604 | def attach_volume(self, volume_id, instance_id, device): | 584 | def attach_volume(self, volume_id, instance_id, device): |
803 | 605 | """Attach the given volume to the specified instance at C{device}.""" | 585 | """Attach the given volume to the specified instance at C{device}.""" |
804 | 606 | query = self.query_factory( | 586 | query = self.query_factory( |
808 | 607 | "AttachVolume", self.creds, self.endpoint, | 587 | action="AttachVolume", creds=self.creds, endpoint=self.endpoint, |
809 | 608 | {"VolumeId": volume_id, "InstanceId": instance_id, | 588 | other_params={"VolumeId": volume_id, "InstanceId": instance_id, |
810 | 609 | "Device": device}) | 589 | "Device": device}) |
811 | 610 | d = query.submit() | 590 | d = query.submit() |
812 | 611 | return d.addCallback(self._parse_attach_volume) | 591 | return d.addCallback(self._parse_attach_volume) |
813 | 612 | 592 | ||
814 | @@ -620,11 +600,12 @@ | |||
815 | 620 | 600 | ||
816 | 621 | def describe_keypairs(self, *keypair_names): | 601 | def describe_keypairs(self, *keypair_names): |
817 | 622 | """Returns information about key pairs available.""" | 602 | """Returns information about key pairs available.""" |
821 | 623 | keypair_set = {} | 603 | keypairs = {} |
822 | 624 | for pos, keypair_name in enumerate(keypair_names): | 604 | for index, keypair_name in enumerate(keypair_names): |
823 | 625 | keypair_set["KeyPair.%d" % (pos + 1)] = keypair_name | 605 | keypairs["KeyPair.%d" % (index + 1)] = keypair_name |
824 | 626 | query = self.query_factory( | 606 | query = self.query_factory( |
826 | 627 | "DescribeKeyPairs", self.creds, self.endpoint, keypair_set) | 607 | action="DescribeKeyPairs", creds=self.creds, |
827 | 608 | endpoint=self.endpoint, other_params=keypairs) | ||
828 | 628 | d = query.submit() | 609 | d = query.submit() |
829 | 629 | return d.addCallback(self._parse_describe_keypairs) | 610 | return d.addCallback(self._parse_describe_keypairs) |
830 | 630 | 611 | ||
831 | @@ -646,8 +627,8 @@ | |||
832 | 646 | used to reference the created key pair when launching new instances. | 627 | used to reference the created key pair when launching new instances. |
833 | 647 | """ | 628 | """ |
834 | 648 | query = self.query_factory( | 629 | query = self.query_factory( |
837 | 649 | "CreateKeyPair", self.creds, self.endpoint, | 630 | action="CreateKeyPair", creds=self.creds, endpoint=self.endpoint, |
838 | 650 | {"KeyName": keypair_name}) | 631 | other_params={"KeyName": keypair_name}) |
839 | 651 | d = query.submit() | 632 | d = query.submit() |
840 | 652 | return d.addCallback(self._parse_create_keypair) | 633 | return d.addCallback(self._parse_create_keypair) |
841 | 653 | 634 | ||
842 | @@ -661,8 +642,8 @@ | |||
843 | 661 | def delete_keypair(self, keypair_name): | 642 | def delete_keypair(self, keypair_name): |
844 | 662 | """Delete a given keypair.""" | 643 | """Delete a given keypair.""" |
845 | 663 | query = self.query_factory( | 644 | query = self.query_factory( |
848 | 664 | "DeleteKeyPair", self.creds, self.endpoint, | 645 | action="DeleteKeyPair", creds=self.creds, endpoint=self.endpoint, |
849 | 665 | {"KeyName": keypair_name}) | 646 | other_params={"KeyName": keypair_name}) |
850 | 666 | d = query.submit() | 647 | d = query.submit() |
851 | 667 | return d.addCallback(self._parse_truth_return) | 648 | return d.addCallback(self._parse_truth_return) |
852 | 668 | 649 | ||
853 | @@ -673,8 +654,10 @@ | |||
854 | 673 | 654 | ||
855 | 674 | @return: the IP address allocated. | 655 | @return: the IP address allocated. |
856 | 675 | """ | 656 | """ |
857 | 657 | # XXX remove empty other_params | ||
858 | 676 | query = self.query_factory( | 658 | query = self.query_factory( |
860 | 677 | "AllocateAddress", self.creds, self.endpoint, {}) | 659 | action="AllocateAddress", creds=self.creds, endpoint=self.endpoint, |
861 | 660 | other_params={}) | ||
862 | 678 | d = query.submit() | 661 | d = query.submit() |
863 | 679 | return d.addCallback(self._parse_allocate_address) | 662 | return d.addCallback(self._parse_allocate_address) |
864 | 680 | 663 | ||
865 | @@ -689,8 +672,8 @@ | |||
866 | 689 | @return: C{True} if the operation succeeded. | 672 | @return: C{True} if the operation succeeded. |
867 | 690 | """ | 673 | """ |
868 | 691 | query = self.query_factory( | 674 | query = self.query_factory( |
871 | 692 | "ReleaseAddress", self.creds, self.endpoint, | 675 | action="ReleaseAddress", creds=self.creds, endpoint=self.endpoint, |
872 | 693 | {"PublicIp": address}) | 676 | other_params={"PublicIp": address}) |
873 | 694 | d = query.submit() | 677 | d = query.submit() |
874 | 695 | return d.addCallback(self._parse_truth_return) | 678 | return d.addCallback(self._parse_truth_return) |
875 | 696 | 679 | ||
876 | @@ -702,8 +685,9 @@ | |||
877 | 702 | @return: C{True} if the operation succeeded. | 685 | @return: C{True} if the operation succeeded. |
878 | 703 | """ | 686 | """ |
879 | 704 | query = self.query_factory( | 687 | query = self.query_factory( |
882 | 705 | "AssociateAddress", self.creds, self.endpoint, | 688 | action="AssociateAddress", creds=self.creds, |
883 | 706 | {"InstanceId": instance_id, "PublicIp": address}) | 689 | endpoint=self.endpoint, |
884 | 690 | other_params={"InstanceId": instance_id, "PublicIp": address}) | ||
885 | 707 | d = query.submit() | 691 | d = query.submit() |
886 | 708 | return d.addCallback(self._parse_truth_return) | 692 | return d.addCallback(self._parse_truth_return) |
887 | 709 | 693 | ||
888 | @@ -714,8 +698,8 @@ | |||
889 | 714 | called several times without error. | 698 | called several times without error. |
890 | 715 | """ | 699 | """ |
891 | 716 | query = self.query_factory( | 700 | query = self.query_factory( |
894 | 717 | "DisassociateAddress", self.creds, self.endpoint, | 701 | action="DisassociateAddress", creds=self.creds, |
895 | 718 | {"PublicIp": address}) | 702 | endpoint=self.endpoint, other_params={"PublicIp": address}) |
896 | 719 | d = query.submit() | 703 | d = query.submit() |
897 | 720 | return d.addCallback(self._parse_truth_return) | 704 | return d.addCallback(self._parse_truth_return) |
898 | 721 | 705 | ||
899 | @@ -732,7 +716,8 @@ | |||
900 | 732 | for pos, address in enumerate(addresses): | 716 | for pos, address in enumerate(addresses): |
901 | 733 | address_set["PublicIp.%d" % (pos + 1)] = address | 717 | address_set["PublicIp.%d" % (pos + 1)] = address |
902 | 734 | query = self.query_factory( | 718 | query = self.query_factory( |
904 | 735 | "DescribeAddresses", self.creds, self.endpoint, address_set) | 719 | action="DescribeAddresses", creds=self.creds, |
905 | 720 | endpoint=self.endpoint, other_params=address_set) | ||
906 | 736 | d = query.submit() | 721 | d = query.submit() |
907 | 737 | return d.addCallback(self._parse_describe_addresses) | 722 | return d.addCallback(self._parse_describe_addresses) |
908 | 738 | 723 | ||
909 | @@ -750,8 +735,9 @@ | |||
910 | 750 | if names: | 735 | if names: |
911 | 751 | zone_names = dict([("ZoneName.%d" % (i+1), name) | 736 | zone_names = dict([("ZoneName.%d" % (i+1), name) |
912 | 752 | for i, name in enumerate(names)]) | 737 | for i, name in enumerate(names)]) |
915 | 753 | query = self.query_factory("DescribeAvailabilityZones", self.creds, | 738 | query = self.query_factory( |
916 | 754 | self.endpoint, zone_names) | 739 | action="DescribeAvailabilityZones", creds=self.creds, |
917 | 740 | endpoint=self.endpoint, other_params=zone_names) | ||
918 | 755 | d = query.submit() | 741 | d = query.submit() |
919 | 756 | return d.addCallback(self._parse_describe_availability_zones) | 742 | return d.addCallback(self._parse_describe_availability_zones) |
920 | 757 | 743 | ||
921 | @@ -830,5 +816,4 @@ | |||
922 | 830 | url = "%s?%s" % (self.endpoint.get_uri(), | 816 | url = "%s?%s" % (self.endpoint.get_uri(), |
923 | 831 | self.get_canonical_query_params()) | 817 | self.get_canonical_query_params()) |
924 | 832 | d = self.get_page(url, method=self.endpoint.method) | 818 | d = self.get_page(url, method=self.endpoint.method) |
927 | 833 | d.addErrback(ec2_error_wrapper) | 819 | return d.addErrback(ec2_error_wrapper) |
926 | 834 | return d | ||
928 | 835 | 820 | ||
929 | === modified file 'txaws/ec2/exception.py' | |||
930 | --- txaws/ec2/exception.py 2009-11-28 01:15:24 +0000 | |||
931 | +++ txaws/ec2/exception.py 2009-11-28 01:15:24 +0000 | |||
932 | @@ -1,39 +1,14 @@ | |||
933 | 1 | # Copyright (c) 2009 Canonical Ltd <duncan.mcgreggor@canonical.com> | 1 | # Copyright (c) 2009 Canonical Ltd <duncan.mcgreggor@canonical.com> |
934 | 2 | # Licenced under the txaws licence available at /LICENSE in the txaws source. | 2 | # Licenced under the txaws licence available at /LICENSE in the txaws source. |
935 | 3 | 3 | ||
938 | 4 | from txaws.exception import AWSError, AWSResponseParseError | 4 | from txaws.exception import AWSError |
937 | 5 | from txaws.util import XML | ||
939 | 6 | 5 | ||
940 | 7 | 6 | ||
941 | 8 | class EC2Error(AWSError): | 7 | class EC2Error(AWSError): |
942 | 9 | """ | 8 | """ |
943 | 10 | A error class providing custom methods on EC2 errors. | 9 | A error class providing custom methods on EC2 errors. |
944 | 11 | """ | 10 | """ |
970 | 12 | def __init__(self, xml_bytes, status=None, message=None, response=None): | 11 | def _set_400_error(self, tree): |
946 | 13 | super(AWSError, self).__init__(status, message, response) | ||
947 | 14 | if not xml_bytes: | ||
948 | 15 | raise ValueError("XML cannot be empty.") | ||
949 | 16 | self.original = xml_bytes | ||
950 | 17 | self.errors = [] | ||
951 | 18 | self.request_id = "" | ||
952 | 19 | self.host_id = "" | ||
953 | 20 | self.parse() | ||
954 | 21 | |||
955 | 22 | def __str__(self): | ||
956 | 23 | return self._get_error_message_string() | ||
957 | 24 | |||
958 | 25 | def __repr__(self): | ||
959 | 26 | return "<%s object with %s>" % ( | ||
960 | 27 | self.__class__.__name__, self._get_error_code_string()) | ||
961 | 28 | |||
962 | 29 | def _set_request_id(self, tree): | ||
963 | 30 | request_id_node = tree.find(".//RequestID") | ||
964 | 31 | if hasattr(request_id_node, "text"): | ||
965 | 32 | text = request_id_node.text | ||
966 | 33 | if text: | ||
967 | 34 | self.request_id = text | ||
968 | 35 | |||
969 | 36 | def _set_400_errors(self, tree): | ||
971 | 37 | errors_node = tree.find(".//Errors") | 12 | errors_node = tree.find(".//Errors") |
972 | 38 | if errors_node: | 13 | if errors_node: |
973 | 39 | for error in errors_node: | 14 | for error in errors_node: |
974 | @@ -41,84 +16,5 @@ | |||
975 | 41 | if data: | 16 | if data: |
976 | 42 | self.errors.append(data) | 17 | self.errors.append(data) |
977 | 43 | 18 | ||
1059 | 44 | def _set_host_id(self, tree): | 19 | |
1060 | 45 | host_id = tree.find(".//HostID") | 20 | |
980 | 46 | if hasattr(host_id, "text"): | ||
981 | 47 | text = host_id.text | ||
982 | 48 | if text: | ||
983 | 49 | self.host_id = text | ||
984 | 50 | |||
985 | 51 | def _set_500_error(self, tree): | ||
986 | 52 | self._set_request_id(tree) | ||
987 | 53 | self._set_host_id(tree) | ||
988 | 54 | data = self._node_to_dict(tree) | ||
989 | 55 | if data: | ||
990 | 56 | self.errors.append(data) | ||
991 | 57 | |||
992 | 58 | def _get_error_code_string(self): | ||
993 | 59 | count = len(self.errors) | ||
994 | 60 | error_code = self.get_error_codes() | ||
995 | 61 | if count > 1: | ||
996 | 62 | return "Error count: %s" % error_code | ||
997 | 63 | else: | ||
998 | 64 | return "Error code: %s" % error_code | ||
999 | 65 | |||
1000 | 66 | def _get_error_message_string(self): | ||
1001 | 67 | count = len(self.errors) | ||
1002 | 68 | error_message = self.get_error_messages() | ||
1003 | 69 | if count > 1: | ||
1004 | 70 | return "%s." % error_message | ||
1005 | 71 | else: | ||
1006 | 72 | return "Error Message: %s" % error_message | ||
1007 | 73 | |||
1008 | 74 | def _node_to_dict(self, node): | ||
1009 | 75 | data = {} | ||
1010 | 76 | for child in node: | ||
1011 | 77 | if child.tag and child.text: | ||
1012 | 78 | data[child.tag] = child.text | ||
1013 | 79 | return data | ||
1014 | 80 | |||
1015 | 81 | def _check_for_html(self, tree): | ||
1016 | 82 | if tree.tag == "html": | ||
1017 | 83 | message = "Could not parse HTML in the response." | ||
1018 | 84 | raise AWSResponseParseError(message) | ||
1019 | 85 | |||
1020 | 86 | def parse(self, xml_bytes=""): | ||
1021 | 87 | if not xml_bytes: | ||
1022 | 88 | xml_bytes = self.original | ||
1023 | 89 | self.original = xml_bytes | ||
1024 | 90 | tree = XML(xml_bytes.strip()) | ||
1025 | 91 | self._check_for_html(tree) | ||
1026 | 92 | self._set_request_id(tree) | ||
1027 | 93 | if self.status: | ||
1028 | 94 | status = int(self.status) | ||
1029 | 95 | else: | ||
1030 | 96 | status = 400 | ||
1031 | 97 | if status >= 500: | ||
1032 | 98 | self._set_500_error(tree) | ||
1033 | 99 | else: | ||
1034 | 100 | self._set_400_errors(tree) | ||
1035 | 101 | |||
1036 | 102 | def has_error(self, errorString): | ||
1037 | 103 | for error in self.errors: | ||
1038 | 104 | if errorString in error.values(): | ||
1039 | 105 | return True | ||
1040 | 106 | return False | ||
1041 | 107 | |||
1042 | 108 | def get_error_codes(self): | ||
1043 | 109 | count = len(self.errors) | ||
1044 | 110 | if count > 1: | ||
1045 | 111 | return count | ||
1046 | 112 | elif count == 0: | ||
1047 | 113 | return | ||
1048 | 114 | else: | ||
1049 | 115 | return self.errors[0]["Code"] | ||
1050 | 116 | |||
1051 | 117 | def get_error_messages(self): | ||
1052 | 118 | count = len(self.errors) | ||
1053 | 119 | if count > 1: | ||
1054 | 120 | return "Multiple EC2 Errors" | ||
1055 | 121 | elif count == 0: | ||
1056 | 122 | return "Empty error list" | ||
1057 | 123 | else: | ||
1058 | 124 | return self.errors[0]["Message"] | ||
1061 | 125 | 21 | ||
1062 | === modified file 'txaws/ec2/tests/test_client.py' | |||
1063 | --- txaws/ec2/tests/test_client.py 2009-11-28 01:15:24 +0000 | |||
1064 | +++ txaws/ec2/tests/test_client.py 2009-11-28 01:15:24 +0000 | |||
1065 | @@ -76,13 +76,14 @@ | |||
1066 | 76 | 76 | ||
1067 | 77 | class StubQuery(object): | 77 | class StubQuery(object): |
1068 | 78 | 78 | ||
1070 | 79 | def __init__(stub, action, creds, endpoint, other_params): | 79 | def __init__(stub, action="", creds=None, endpoint=None, |
1071 | 80 | other_params={}): | ||
1072 | 80 | self.assertEqual(action, "DescribeAvailabilityZones") | 81 | self.assertEqual(action, "DescribeAvailabilityZones") |
1073 | 81 | self.assertEqual(creds.access_key, "foo") | 82 | self.assertEqual(creds.access_key, "foo") |
1074 | 82 | self.assertEqual(creds.secret_key, "bar") | 83 | self.assertEqual(creds.secret_key, "bar") |
1075 | 83 | self.assertEqual( | 84 | self.assertEqual( |
1078 | 84 | {"ZoneName.1": "us-east-1a"}, | 85 | other_params, |
1079 | 85 | other_params) | 86 | {"ZoneName.1": "us-east-1a"}) |
1080 | 86 | 87 | ||
1081 | 87 | def submit(self): | 88 | def submit(self): |
1082 | 88 | return succeed( | 89 | return succeed( |
1083 | @@ -105,7 +106,8 @@ | |||
1084 | 105 | 106 | ||
1085 | 106 | class StubQuery(object): | 107 | class StubQuery(object): |
1086 | 107 | 108 | ||
1088 | 108 | def __init__(stub, action, creds, endpoint, other_params): | 109 | def __init__(stub, action="", creds=None, endpoint=None, |
1089 | 110 | other_params={}): | ||
1090 | 109 | self.assertEqual(action, "DescribeAvailabilityZones") | 111 | self.assertEqual(action, "DescribeAvailabilityZones") |
1091 | 110 | self.assertEqual(creds.access_key, "foo") | 112 | self.assertEqual(creds.access_key, "foo") |
1092 | 111 | self.assertEqual(creds.secret_key, "bar") | 113 | self.assertEqual(creds.secret_key, "bar") |
1093 | @@ -199,11 +201,12 @@ | |||
1094 | 199 | 201 | ||
1095 | 200 | class StubQuery(object): | 202 | class StubQuery(object): |
1096 | 201 | 203 | ||
1098 | 202 | def __init__(stub, action, creds, endpoint, params): | 204 | def __init__(stub, action="", creds=None, endpoint=None, |
1099 | 205 | other_params={}): | ||
1100 | 203 | self.assertEqual(action, "DescribeInstances") | 206 | self.assertEqual(action, "DescribeInstances") |
1101 | 204 | self.assertEqual(creds.access_key, "foo") | 207 | self.assertEqual(creds.access_key, "foo") |
1102 | 205 | self.assertEqual(creds.secret_key, "bar") | 208 | self.assertEqual(creds.secret_key, "bar") |
1104 | 206 | self.assertEquals(params, {}) | 209 | self.assertEquals(other_params, {}) |
1105 | 207 | 210 | ||
1106 | 208 | def submit(self): | 211 | def submit(self): |
1107 | 209 | return succeed(payload.sample_describe_instances_result) | 212 | return succeed(payload.sample_describe_instances_result) |
1108 | @@ -218,11 +221,12 @@ | |||
1109 | 218 | 221 | ||
1110 | 219 | class StubQuery(object): | 222 | class StubQuery(object): |
1111 | 220 | 223 | ||
1113 | 221 | def __init__(stub, action, creds, endpoint, params): | 224 | def __init__(stub, action="", creds=None, endpoint=None, |
1114 | 225 | other_params={}): | ||
1115 | 222 | self.assertEqual(action, "DescribeInstances") | 226 | self.assertEqual(action, "DescribeInstances") |
1116 | 223 | self.assertEqual(creds.access_key, "foo") | 227 | self.assertEqual(creds.access_key, "foo") |
1117 | 224 | self.assertEqual(creds.secret_key, "bar") | 228 | self.assertEqual(creds.secret_key, "bar") |
1119 | 225 | self.assertEquals(params, {}) | 229 | self.assertEquals(other_params, {}) |
1120 | 226 | 230 | ||
1121 | 227 | def submit(self): | 231 | def submit(self): |
1122 | 228 | return succeed( | 232 | return succeed( |
1123 | @@ -238,12 +242,13 @@ | |||
1124 | 238 | 242 | ||
1125 | 239 | class StubQuery(object): | 243 | class StubQuery(object): |
1126 | 240 | 244 | ||
1128 | 241 | def __init__(stub, action, creds, endpoint, params): | 245 | def __init__(stub, action="", creds=None, endpoint=None, |
1129 | 246 | other_params={}): | ||
1130 | 242 | self.assertEqual(action, "DescribeInstances") | 247 | self.assertEqual(action, "DescribeInstances") |
1131 | 243 | self.assertEqual(creds.access_key, "foo") | 248 | self.assertEqual(creds.access_key, "foo") |
1132 | 244 | self.assertEqual(creds.secret_key, "bar") | 249 | self.assertEqual(creds.secret_key, "bar") |
1133 | 245 | self.assertEquals( | 250 | self.assertEquals( |
1135 | 246 | params, | 251 | other_params, |
1136 | 247 | {"InstanceId.1": "i-16546401", | 252 | {"InstanceId.1": "i-16546401", |
1137 | 248 | "InstanceId.2": "i-49873415"}) | 253 | "InstanceId.2": "i-49873415"}) |
1138 | 249 | 254 | ||
1139 | @@ -261,13 +266,14 @@ | |||
1140 | 261 | 266 | ||
1141 | 262 | class StubQuery(object): | 267 | class StubQuery(object): |
1142 | 263 | 268 | ||
1144 | 264 | def __init__(stub, action, creds, endpoint, other_params): | 269 | def __init__(stub, action="", creds=None, endpoint=None, |
1145 | 270 | other_params={}): | ||
1146 | 265 | self.assertEqual(action, "TerminateInstances") | 271 | self.assertEqual(action, "TerminateInstances") |
1147 | 266 | self.assertEqual(creds.access_key, "foo") | 272 | self.assertEqual(creds.access_key, "foo") |
1148 | 267 | self.assertEqual(creds.secret_key, "bar") | 273 | self.assertEqual(creds.secret_key, "bar") |
1149 | 268 | self.assertEqual( | 274 | self.assertEqual( |
1152 | 269 | {"InstanceId.1": "i-1234", "InstanceId.2": "i-5678"}, | 275 | other_params, |
1153 | 270 | other_params) | 276 | {"InstanceId.1": "i-1234", "InstanceId.2": "i-5678"}) |
1154 | 271 | 277 | ||
1155 | 272 | def submit(self): | 278 | def submit(self): |
1156 | 273 | return succeed(payload.sample_terminate_instances_result) | 279 | return succeed(payload.sample_terminate_instances_result) |
1157 | @@ -313,12 +319,13 @@ | |||
1158 | 313 | 319 | ||
1159 | 314 | class StubQuery(object): | 320 | class StubQuery(object): |
1160 | 315 | 321 | ||
1162 | 316 | def __init__(stub, action, creds, endpoint, params): | 322 | def __init__(stub, action="", creds=None, endpoint=None, |
1163 | 323 | other_params={}): | ||
1164 | 317 | self.assertEqual(action, "RunInstances") | 324 | self.assertEqual(action, "RunInstances") |
1165 | 318 | self.assertEqual(creds.access_key, "foo") | 325 | self.assertEqual(creds.access_key, "foo") |
1166 | 319 | self.assertEqual(creds.secret_key, "bar") | 326 | self.assertEqual(creds.secret_key, "bar") |
1167 | 320 | self.assertEquals( | 327 | self.assertEquals( |
1169 | 321 | params, | 328 | other_params, |
1170 | 322 | {"ImageId": "ami-1234", "MaxCount": "2", "MinCount": "1", | 329 | {"ImageId": "ami-1234", "MaxCount": "2", "MinCount": "1", |
1171 | 323 | "SecurityGroup.1": u"group1", "KeyName": u"default", | 330 | "SecurityGroup.1": u"group1", "KeyName": u"default", |
1172 | 324 | "UserData": "Zm9v", "InstanceType": u"m1.small", | 331 | "UserData": "Zm9v", "InstanceType": u"m1.small", |
1173 | @@ -348,11 +355,12 @@ | |||
1174 | 348 | """ | 355 | """ |
1175 | 349 | class StubQuery(object): | 356 | class StubQuery(object): |
1176 | 350 | 357 | ||
1178 | 351 | def __init__(stub, action, creds, endpoint, other_params=None): | 358 | def __init__(stub, action="", creds=None, endpoint=None, |
1179 | 359 | other_params={}): | ||
1180 | 352 | self.assertEqual(action, "DescribeSecurityGroups") | 360 | self.assertEqual(action, "DescribeSecurityGroups") |
1181 | 353 | self.assertEqual(creds.access_key, "foo") | 361 | self.assertEqual(creds.access_key, "foo") |
1182 | 354 | self.assertEqual(creds.secret_key, "bar") | 362 | self.assertEqual(creds.secret_key, "bar") |
1184 | 355 | self.assertEqual(other_params, None) | 363 | self.assertEqual(other_params, {}) |
1185 | 356 | 364 | ||
1186 | 357 | def submit(self): | 365 | def submit(self): |
1187 | 358 | return succeed(payload.sample_describe_security_groups_result) | 366 | return succeed(payload.sample_describe_security_groups_result) |
1188 | @@ -382,11 +390,12 @@ | |||
1189 | 382 | """ | 390 | """ |
1190 | 383 | class StubQuery(object): | 391 | class StubQuery(object): |
1191 | 384 | 392 | ||
1193 | 385 | def __init__(stub, action, creds, endpoint, other_params=None): | 393 | def __init__(stub, action="", creds=None, endpoint=None, |
1194 | 394 | other_params={}): | ||
1195 | 386 | self.assertEqual(action, "DescribeSecurityGroups") | 395 | self.assertEqual(action, "DescribeSecurityGroups") |
1196 | 387 | self.assertEqual(creds.access_key, "foo") | 396 | self.assertEqual(creds.access_key, "foo") |
1197 | 388 | self.assertEqual(creds.secret_key, "bar") | 397 | self.assertEqual(creds.secret_key, "bar") |
1199 | 389 | self.assertEqual(other_params, None) | 398 | self.assertEqual(other_params, {}) |
1200 | 390 | 399 | ||
1201 | 391 | def submit(self): | 400 | def submit(self): |
1202 | 392 | return succeed( | 401 | return succeed( |
1203 | @@ -431,7 +440,8 @@ | |||
1204 | 431 | """ | 440 | """ |
1205 | 432 | class StubQuery(object): | 441 | class StubQuery(object): |
1206 | 433 | 442 | ||
1208 | 434 | def __init__(stub, action, creds, endpoint, other_params=None): | 443 | def __init__(stub, action="", creds=None, endpoint=None, |
1209 | 444 | other_params={}): | ||
1210 | 435 | self.assertEqual(action, "DescribeSecurityGroups") | 445 | self.assertEqual(action, "DescribeSecurityGroups") |
1211 | 436 | self.assertEqual(creds.access_key, "foo") | 446 | self.assertEqual(creds.access_key, "foo") |
1212 | 437 | self.assertEqual(creds.secret_key, "bar") | 447 | self.assertEqual(creds.secret_key, "bar") |
1213 | @@ -457,7 +467,8 @@ | |||
1214 | 457 | """ | 467 | """ |
1215 | 458 | class StubQuery(object): | 468 | class StubQuery(object): |
1216 | 459 | 469 | ||
1218 | 460 | def __init__(stub, action, creds, endpoint, other_params=None): | 470 | def __init__(stub, action="", creds=None, endpoint=None, |
1219 | 471 | other_params={}): | ||
1220 | 461 | self.assertEqual(action, "CreateSecurityGroup") | 472 | self.assertEqual(action, "CreateSecurityGroup") |
1221 | 462 | self.assertEqual(creds.access_key, "foo") | 473 | self.assertEqual(creds.access_key, "foo") |
1222 | 463 | self.assertEqual(creds.secret_key, "bar") | 474 | self.assertEqual(creds.secret_key, "bar") |
1223 | @@ -484,7 +495,8 @@ | |||
1224 | 484 | """ | 495 | """ |
1225 | 485 | class StubQuery(object): | 496 | class StubQuery(object): |
1226 | 486 | 497 | ||
1228 | 487 | def __init__(stub, action, creds, endpoint, other_params=None): | 498 | def __init__(stub, action="", creds=None, endpoint=None, |
1229 | 499 | other_params={}): | ||
1230 | 488 | self.assertEqual(action, "DeleteSecurityGroup") | 500 | self.assertEqual(action, "DeleteSecurityGroup") |
1231 | 489 | self.assertEqual(creds.access_key, "foo") | 501 | self.assertEqual(creds.access_key, "foo") |
1232 | 490 | self.assertEqual(creds.secret_key, "bar") | 502 | self.assertEqual(creds.secret_key, "bar") |
1233 | @@ -508,7 +520,8 @@ | |||
1234 | 508 | """ | 520 | """ |
1235 | 509 | class StubQuery(object): | 521 | class StubQuery(object): |
1236 | 510 | 522 | ||
1238 | 511 | def __init__(stub, action, creds, endpoint, other_params=None): | 523 | def __init__(stub, action="", creds=None, endpoint=None, |
1239 | 524 | other_params={}): | ||
1240 | 512 | self.assertEqual(action, "DeleteSecurityGroup") | 525 | self.assertEqual(action, "DeleteSecurityGroup") |
1241 | 513 | self.assertEqual(creds.access_key, "foo") | 526 | self.assertEqual(creds.access_key, "foo") |
1242 | 514 | self.assertEqual(creds.secret_key, "bar") | 527 | self.assertEqual(creds.secret_key, "bar") |
1243 | @@ -542,7 +555,8 @@ | |||
1244 | 542 | """ | 555 | """ |
1245 | 543 | class StubQuery(object): | 556 | class StubQuery(object): |
1246 | 544 | 557 | ||
1248 | 545 | def __init__(stub, action, creds, endpoint, other_params=None): | 558 | def __init__(stub, action="", creds=None, endpoint=None, |
1249 | 559 | other_params={}): | ||
1250 | 546 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") | 560 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") |
1251 | 547 | self.assertEqual(creds.access_key, "foo") | 561 | self.assertEqual(creds.access_key, "foo") |
1252 | 548 | self.assertEqual(creds.secret_key, "bar") | 562 | self.assertEqual(creds.secret_key, "bar") |
1253 | @@ -572,7 +586,8 @@ | |||
1254 | 572 | """ | 586 | """ |
1255 | 573 | class StubQuery(object): | 587 | class StubQuery(object): |
1256 | 574 | 588 | ||
1258 | 575 | def __init__(stub, action, creds, endpoint, other_params=None): | 589 | def __init__(stub, action="", creds=None, endpoint=None, |
1259 | 590 | other_params={}): | ||
1260 | 576 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") | 591 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") |
1261 | 577 | self.assertEqual(creds.access_key, "foo") | 592 | self.assertEqual(creds.access_key, "foo") |
1262 | 578 | self.assertEqual(creds.secret_key, "bar") | 593 | self.assertEqual(creds.secret_key, "bar") |
1263 | @@ -622,7 +637,8 @@ | |||
1264 | 622 | """ | 637 | """ |
1265 | 623 | class StubQuery(object): | 638 | class StubQuery(object): |
1266 | 624 | 639 | ||
1268 | 625 | def __init__(stub, action, creds, endpoint, other_params=None): | 640 | def __init__(stub, action="", creds=None, endpoint=None, |
1269 | 641 | other_params={}): | ||
1270 | 626 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") | 642 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") |
1271 | 627 | self.assertEqual(creds.access_key, "foo") | 643 | self.assertEqual(creds.access_key, "foo") |
1272 | 628 | self.assertEqual(creds.secret_key, "bar") | 644 | self.assertEqual(creds.secret_key, "bar") |
1273 | @@ -650,7 +666,8 @@ | |||
1274 | 650 | """ | 666 | """ |
1275 | 651 | class StubQuery(object): | 667 | class StubQuery(object): |
1276 | 652 | 668 | ||
1278 | 653 | def __init__(stub, action, creds, endpoint, other_params=None): | 669 | def __init__(stub, action="", creds=None, endpoint=None, |
1279 | 670 | other_params={}): | ||
1280 | 654 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") | 671 | self.assertEqual(action, "AuthorizeSecurityGroupIngress") |
1281 | 655 | self.assertEqual(creds.access_key, "foo") | 672 | self.assertEqual(creds.access_key, "foo") |
1282 | 656 | self.assertEqual(creds.secret_key, "bar") | 673 | self.assertEqual(creds.secret_key, "bar") |
1283 | @@ -680,7 +697,8 @@ | |||
1284 | 680 | """ | 697 | """ |
1285 | 681 | class StubQuery(object): | 698 | class StubQuery(object): |
1286 | 682 | 699 | ||
1288 | 683 | def __init__(stub, action, creds, endpoint, other_params=None): | 700 | def __init__(stub, action="", creds=None, endpoint=None, |
1289 | 701 | other_params={}): | ||
1290 | 684 | self.assertEqual(action, "RevokeSecurityGroupIngress") | 702 | self.assertEqual(action, "RevokeSecurityGroupIngress") |
1291 | 685 | self.assertEqual(creds.access_key, "foo") | 703 | self.assertEqual(creds.access_key, "foo") |
1292 | 686 | self.assertEqual(creds.secret_key, "bar") | 704 | self.assertEqual(creds.secret_key, "bar") |
1293 | @@ -710,7 +728,8 @@ | |||
1294 | 710 | """ | 728 | """ |
1295 | 711 | class StubQuery(object): | 729 | class StubQuery(object): |
1296 | 712 | 730 | ||
1298 | 713 | def __init__(stub, action, creds, endpoint, other_params=None): | 731 | def __init__(stub, action="", creds=None, endpoint=None, |
1299 | 732 | other_params={}): | ||
1300 | 714 | self.assertEqual(action, "RevokeSecurityGroupIngress") | 733 | self.assertEqual(action, "RevokeSecurityGroupIngress") |
1301 | 715 | self.assertEqual(creds.access_key, "foo") | 734 | self.assertEqual(creds.access_key, "foo") |
1302 | 716 | self.assertEqual(creds.secret_key, "bar") | 735 | self.assertEqual(creds.secret_key, "bar") |
1303 | @@ -760,7 +779,8 @@ | |||
1304 | 760 | """ | 779 | """ |
1305 | 761 | class StubQuery(object): | 780 | class StubQuery(object): |
1306 | 762 | 781 | ||
1308 | 763 | def __init__(stub, action, creds, endpoint, other_params=None): | 782 | def __init__(stub, action="", creds=None, endpoint=None, |
1309 | 783 | other_params={}): | ||
1310 | 764 | self.assertEqual(action, "RevokeSecurityGroupIngress") | 784 | self.assertEqual(action, "RevokeSecurityGroupIngress") |
1311 | 765 | self.assertEqual(creds.access_key, "foo") | 785 | self.assertEqual(creds.access_key, "foo") |
1312 | 766 | self.assertEqual(creds.secret_key, "bar") | 786 | self.assertEqual(creds.secret_key, "bar") |
1313 | @@ -788,7 +808,8 @@ | |||
1314 | 788 | """ | 808 | """ |
1315 | 789 | class StubQuery(object): | 809 | class StubQuery(object): |
1316 | 790 | 810 | ||
1318 | 791 | def __init__(stub, action, creds, endpoint, other_params=None): | 811 | def __init__(stub, action="", creds=None, endpoint=None, |
1319 | 812 | other_params={}): | ||
1320 | 792 | self.assertEqual(action, "RevokeSecurityGroupIngress") | 813 | self.assertEqual(action, "RevokeSecurityGroupIngress") |
1321 | 793 | self.assertEqual(creds.access_key, "foo") | 814 | self.assertEqual(creds.access_key, "foo") |
1322 | 794 | self.assertEqual(creds.secret_key, "bar") | 815 | self.assertEqual(creds.secret_key, "bar") |
1323 | @@ -838,11 +859,12 @@ | |||
1324 | 838 | 859 | ||
1325 | 839 | class StubQuery(object): | 860 | class StubQuery(object): |
1326 | 840 | 861 | ||
1328 | 841 | def __init__(stub, action, creds, endpoint, params): | 862 | def __init__(stub, action="", creds=None, endpoint=None, |
1329 | 863 | other_params={}): | ||
1330 | 842 | self.assertEqual(action, "DescribeVolumes") | 864 | self.assertEqual(action, "DescribeVolumes") |
1331 | 843 | self.assertEqual(self.creds, creds) | 865 | self.assertEqual(self.creds, creds) |
1332 | 844 | self.assertEqual(self.endpoint, endpoint) | 866 | self.assertEqual(self.endpoint, endpoint) |
1334 | 845 | self.assertEquals(params, {}) | 867 | self.assertEquals(other_params, {}) |
1335 | 846 | 868 | ||
1336 | 847 | def submit(self): | 869 | def submit(self): |
1337 | 848 | return succeed(payload.sample_describe_volumes_result) | 870 | return succeed(payload.sample_describe_volumes_result) |
1338 | @@ -857,12 +879,13 @@ | |||
1339 | 857 | 879 | ||
1340 | 858 | class StubQuery(object): | 880 | class StubQuery(object): |
1341 | 859 | 881 | ||
1343 | 860 | def __init__(stub, action, creds, endpoint, params): | 882 | def __init__(stub, action="", creds=None, endpoint=None, |
1344 | 883 | other_params={}): | ||
1345 | 861 | self.assertEqual(action, "DescribeVolumes") | 884 | self.assertEqual(action, "DescribeVolumes") |
1346 | 862 | self.assertEqual(self.creds, creds) | 885 | self.assertEqual(self.creds, creds) |
1347 | 863 | self.assertEqual(self.endpoint, endpoint) | 886 | self.assertEqual(self.endpoint, endpoint) |
1348 | 864 | self.assertEquals( | 887 | self.assertEquals( |
1350 | 865 | params, | 888 | other_params, |
1351 | 866 | {"VolumeId.1": "vol-4282672b"}) | 889 | {"VolumeId.1": "vol-4282672b"}) |
1352 | 867 | 890 | ||
1353 | 868 | def submit(self): | 891 | def submit(self): |
1354 | @@ -888,11 +911,12 @@ | |||
1355 | 888 | 911 | ||
1356 | 889 | class StubQuery(object): | 912 | class StubQuery(object): |
1357 | 890 | 913 | ||
1359 | 891 | def __init__(stub, action, creds, endpoint, params): | 914 | def __init__(stub, action="", creds=None, endpoint=None, |
1360 | 915 | other_params={}): | ||
1361 | 892 | self.assertEqual(action, "DescribeSnapshots") | 916 | self.assertEqual(action, "DescribeSnapshots") |
1362 | 893 | self.assertEqual(self.creds, creds) | 917 | self.assertEqual(self.creds, creds) |
1363 | 894 | self.assertEqual(self.endpoint, endpoint) | 918 | self.assertEqual(self.endpoint, endpoint) |
1365 | 895 | self.assertEquals(params, {}) | 919 | self.assertEquals(other_params, {}) |
1366 | 896 | 920 | ||
1367 | 897 | def submit(self): | 921 | def submit(self): |
1368 | 898 | return succeed(payload.sample_describe_snapshots_result) | 922 | return succeed(payload.sample_describe_snapshots_result) |
1369 | @@ -907,12 +931,13 @@ | |||
1370 | 907 | 931 | ||
1371 | 908 | class StubQuery(object): | 932 | class StubQuery(object): |
1372 | 909 | 933 | ||
1374 | 910 | def __init__(stub, action, creds, endpoint, params): | 934 | def __init__(stub, action="", creds=None, endpoint=None, |
1375 | 935 | other_params={}): | ||
1376 | 911 | self.assertEqual(action, "DescribeSnapshots") | 936 | self.assertEqual(action, "DescribeSnapshots") |
1377 | 912 | self.assertEqual(self.creds, creds) | 937 | self.assertEqual(self.creds, creds) |
1378 | 913 | self.assertEqual(self.endpoint, endpoint) | 938 | self.assertEqual(self.endpoint, endpoint) |
1379 | 914 | self.assertEquals( | 939 | self.assertEquals( |
1381 | 915 | params, | 940 | other_params, |
1382 | 916 | {"SnapshotId.1": "snap-78a54011"}) | 941 | {"SnapshotId.1": "snap-78a54011"}) |
1383 | 917 | 942 | ||
1384 | 918 | def submit(self): | 943 | def submit(self): |
1385 | @@ -928,13 +953,14 @@ | |||
1386 | 928 | 953 | ||
1387 | 929 | class StubQuery(object): | 954 | class StubQuery(object): |
1388 | 930 | 955 | ||
1390 | 931 | def __init__(stub, action, creds, endpoint, params): | 956 | def __init__(stub, action="", creds=None, endpoint=None, |
1391 | 957 | other_params={}): | ||
1392 | 932 | self.assertEqual(action, "CreateVolume") | 958 | self.assertEqual(action, "CreateVolume") |
1393 | 933 | self.assertEqual(self.creds, creds) | 959 | self.assertEqual(self.creds, creds) |
1394 | 934 | self.assertEqual(self.endpoint, endpoint) | 960 | self.assertEqual(self.endpoint, endpoint) |
1395 | 935 | self.assertEqual( | 961 | self.assertEqual( |
1398 | 936 | {"AvailabilityZone": "us-east-1", "Size": "800"}, | 962 | other_params, |
1399 | 937 | params) | 963 | {"AvailabilityZone": "us-east-1", "Size": "800"}) |
1400 | 938 | 964 | ||
1401 | 939 | def submit(self): | 965 | def submit(self): |
1402 | 940 | return succeed(payload.sample_create_volume_result) | 966 | return succeed(payload.sample_create_volume_result) |
1403 | @@ -956,14 +982,15 @@ | |||
1404 | 956 | 982 | ||
1405 | 957 | class StubQuery(object): | 983 | class StubQuery(object): |
1406 | 958 | 984 | ||
1408 | 959 | def __init__(stub, action, creds, endpoint, params): | 985 | def __init__(stub, action="", creds=None, endpoint=None, |
1409 | 986 | other_params={}): | ||
1410 | 960 | self.assertEqual(action, "CreateVolume") | 987 | self.assertEqual(action, "CreateVolume") |
1411 | 961 | self.assertEqual(self.creds, creds) | 988 | self.assertEqual(self.creds, creds) |
1412 | 962 | self.assertEqual(self.endpoint, endpoint) | 989 | self.assertEqual(self.endpoint, endpoint) |
1413 | 963 | self.assertEqual( | 990 | self.assertEqual( |
1414 | 991 | other_params, | ||
1415 | 964 | {"AvailabilityZone": "us-east-1", | 992 | {"AvailabilityZone": "us-east-1", |
1418 | 965 | "SnapshotId": "snap-12345678"}, | 993 | "SnapshotId": "snap-12345678"}) |
1417 | 966 | params) | ||
1419 | 967 | 994 | ||
1420 | 968 | def submit(self): | 995 | def submit(self): |
1421 | 969 | return succeed(payload.sample_create_volume_result) | 996 | return succeed(payload.sample_create_volume_result) |
1422 | @@ -999,13 +1026,14 @@ | |||
1423 | 999 | 1026 | ||
1424 | 1000 | class StubQuery(object): | 1027 | class StubQuery(object): |
1425 | 1001 | 1028 | ||
1427 | 1002 | def __init__(stub, action, creds, endpoint, params): | 1029 | def __init__(stub, action="", creds=None, endpoint=None, |
1428 | 1030 | other_params={}): | ||
1429 | 1003 | self.assertEqual(action, "DeleteVolume") | 1031 | self.assertEqual(action, "DeleteVolume") |
1430 | 1004 | self.assertEqual(self.creds, creds) | 1032 | self.assertEqual(self.creds, creds) |
1431 | 1005 | self.assertEqual(self.endpoint, endpoint) | 1033 | self.assertEqual(self.endpoint, endpoint) |
1432 | 1006 | self.assertEqual( | 1034 | self.assertEqual( |
1435 | 1007 | {"VolumeId": "vol-4282672b"}, | 1035 | other_params, |
1436 | 1008 | params) | 1036 | {"VolumeId": "vol-4282672b"}) |
1437 | 1009 | 1037 | ||
1438 | 1010 | def submit(self): | 1038 | def submit(self): |
1439 | 1011 | return succeed(payload.sample_delete_volume_result) | 1039 | return succeed(payload.sample_delete_volume_result) |
1440 | @@ -1020,13 +1048,14 @@ | |||
1441 | 1020 | 1048 | ||
1442 | 1021 | class StubQuery(object): | 1049 | class StubQuery(object): |
1443 | 1022 | 1050 | ||
1445 | 1023 | def __init__(stub, action, creds, endpoint, params): | 1051 | def __init__(stub, action="", creds=None, endpoint=None, |
1446 | 1052 | other_params={}): | ||
1447 | 1024 | self.assertEqual(action, "CreateSnapshot") | 1053 | self.assertEqual(action, "CreateSnapshot") |
1448 | 1025 | self.assertEqual(self.creds, creds) | 1054 | self.assertEqual(self.creds, creds) |
1449 | 1026 | self.assertEqual(self.endpoint, endpoint) | 1055 | self.assertEqual(self.endpoint, endpoint) |
1450 | 1027 | self.assertEqual( | 1056 | self.assertEqual( |
1453 | 1028 | {"VolumeId": "vol-4d826724"}, | 1057 | other_params, |
1454 | 1029 | params) | 1058 | {"VolumeId": "vol-4d826724"}) |
1455 | 1030 | 1059 | ||
1456 | 1031 | def submit(self): | 1060 | def submit(self): |
1457 | 1032 | return succeed(payload.sample_create_snapshot_result) | 1061 | return succeed(payload.sample_create_snapshot_result) |
1458 | @@ -1049,13 +1078,14 @@ | |||
1459 | 1049 | 1078 | ||
1460 | 1050 | class StubQuery(object): | 1079 | class StubQuery(object): |
1461 | 1051 | 1080 | ||
1463 | 1052 | def __init__(stub, action, creds, endpoint, params): | 1081 | def __init__(stub, action="", creds=None, endpoint=None, |
1464 | 1082 | other_params={}): | ||
1465 | 1053 | self.assertEqual(action, "DeleteSnapshot") | 1083 | self.assertEqual(action, "DeleteSnapshot") |
1466 | 1054 | self.assertEqual(self.creds, creds) | 1084 | self.assertEqual(self.creds, creds) |
1467 | 1055 | self.assertEqual(self.endpoint, endpoint) | 1085 | self.assertEqual(self.endpoint, endpoint) |
1468 | 1056 | self.assertEqual( | 1086 | self.assertEqual( |
1471 | 1057 | {"SnapshotId": "snap-78a54011"}, | 1087 | other_params, |
1472 | 1058 | params) | 1088 | {"SnapshotId": "snap-78a54011"}) |
1473 | 1059 | 1089 | ||
1474 | 1060 | def submit(self): | 1090 | def submit(self): |
1475 | 1061 | return succeed(payload.sample_delete_snapshot_result) | 1091 | return succeed(payload.sample_delete_snapshot_result) |
1476 | @@ -1070,14 +1100,15 @@ | |||
1477 | 1070 | 1100 | ||
1478 | 1071 | class StubQuery(object): | 1101 | class StubQuery(object): |
1479 | 1072 | 1102 | ||
1481 | 1073 | def __init__(stub, action, creds, endpoint, params): | 1103 | def __init__(stub, action="", creds=None, endpoint=None, |
1482 | 1104 | other_params={}): | ||
1483 | 1074 | self.assertEqual(action, "AttachVolume") | 1105 | self.assertEqual(action, "AttachVolume") |
1484 | 1075 | self.assertEqual(self.creds, creds) | 1106 | self.assertEqual(self.creds, creds) |
1485 | 1076 | self.assertEqual(self.endpoint, endpoint) | 1107 | self.assertEqual(self.endpoint, endpoint) |
1486 | 1077 | self.assertEqual( | 1108 | self.assertEqual( |
1487 | 1109 | other_params, | ||
1488 | 1078 | {"VolumeId": "vol-4d826724", "InstanceId": "i-6058a509", | 1110 | {"VolumeId": "vol-4d826724", "InstanceId": "i-6058a509", |
1491 | 1079 | "Device": "/dev/sdh"}, | 1111 | "Device": "/dev/sdh"}) |
1490 | 1080 | params) | ||
1492 | 1081 | 1112 | ||
1493 | 1082 | def submit(self): | 1113 | def submit(self): |
1494 | 1083 | return succeed(payload.sample_attach_volume_result) | 1114 | return succeed(payload.sample_attach_volume_result) |
1495 | @@ -1106,10 +1137,11 @@ | |||
1496 | 1106 | 1137 | ||
1497 | 1107 | class StubQuery(object): | 1138 | class StubQuery(object): |
1498 | 1108 | 1139 | ||
1500 | 1109 | def __init__(stub, action, creds, endpoint, params): | 1140 | def __init__(stub, action="", creds=None, endpoint=None, |
1501 | 1141 | other_params={}): | ||
1502 | 1110 | self.assertEqual(action, "DescribeKeyPairs") | 1142 | self.assertEqual(action, "DescribeKeyPairs") |
1503 | 1111 | self.assertEqual("foo", creds) | 1143 | self.assertEqual("foo", creds) |
1505 | 1112 | self.assertEquals(params, {}) | 1144 | self.assertEquals(other_params, {}) |
1506 | 1113 | 1145 | ||
1507 | 1114 | def submit(self): | 1146 | def submit(self): |
1508 | 1115 | return succeed(payload.sample_single_describe_keypairs_result) | 1147 | return succeed(payload.sample_single_describe_keypairs_result) |
1509 | @@ -1134,10 +1166,12 @@ | |||
1510 | 1134 | "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:70") | 1166 | "1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:70") |
1511 | 1135 | 1167 | ||
1512 | 1136 | class StubQuery(object): | 1168 | class StubQuery(object): |
1514 | 1137 | def __init__(stub, action, creds, endpoint, params): | 1169 | |
1515 | 1170 | def __init__(stub, action="", creds=None, endpoint=None, | ||
1516 | 1171 | other_params={}): | ||
1517 | 1138 | self.assertEqual(action, "DescribeKeyPairs") | 1172 | self.assertEqual(action, "DescribeKeyPairs") |
1518 | 1139 | self.assertEqual("foo", creds) | 1173 | self.assertEqual("foo", creds) |
1520 | 1140 | self.assertEquals(params, {}) | 1174 | self.assertEquals(other_params, {}) |
1521 | 1141 | 1175 | ||
1522 | 1142 | def submit(self): | 1176 | def submit(self): |
1523 | 1143 | return succeed( | 1177 | return succeed( |
1524 | @@ -1152,11 +1186,12 @@ | |||
1525 | 1152 | 1186 | ||
1526 | 1153 | class StubQuery(object): | 1187 | class StubQuery(object): |
1527 | 1154 | 1188 | ||
1529 | 1155 | def __init__(stub, action, creds, endpoint, params): | 1189 | def __init__(stub, action="", creds=None, endpoint=None, |
1530 | 1190 | other_params={}): | ||
1531 | 1156 | self.assertEqual(action, "DescribeKeyPairs") | 1191 | self.assertEqual(action, "DescribeKeyPairs") |
1532 | 1157 | self.assertEqual("foo", creds) | 1192 | self.assertEqual("foo", creds) |
1533 | 1158 | self.assertEquals( | 1193 | self.assertEquals( |
1535 | 1159 | params, | 1194 | other_params, |
1536 | 1160 | {"KeyPair.1": "gsg-keypair"}) | 1195 | {"KeyPair.1": "gsg-keypair"}) |
1537 | 1161 | 1196 | ||
1538 | 1162 | def submit(self): | 1197 | def submit(self): |
1539 | @@ -1182,11 +1217,12 @@ | |||
1540 | 1182 | 1217 | ||
1541 | 1183 | class StubQuery(object): | 1218 | class StubQuery(object): |
1542 | 1184 | 1219 | ||
1544 | 1185 | def __init__(stub, action, creds, endpoint, params): | 1220 | def __init__(stub, action="", creds=None, endpoint=None, |
1545 | 1221 | other_params={}): | ||
1546 | 1186 | self.assertEqual(action, "CreateKeyPair") | 1222 | self.assertEqual(action, "CreateKeyPair") |
1547 | 1187 | self.assertEqual("foo", creds) | 1223 | self.assertEqual("foo", creds) |
1548 | 1188 | self.assertEquals( | 1224 | self.assertEquals( |
1550 | 1189 | params, | 1225 | other_params, |
1551 | 1190 | {"KeyName": "example-key-name"}) | 1226 | {"KeyName": "example-key-name"}) |
1552 | 1191 | 1227 | ||
1553 | 1192 | def submit(self): | 1228 | def submit(self): |
1554 | @@ -1201,12 +1237,13 @@ | |||
1555 | 1201 | 1237 | ||
1556 | 1202 | class StubQuery(object): | 1238 | class StubQuery(object): |
1557 | 1203 | 1239 | ||
1559 | 1204 | def __init__(stub, action, creds, endpoint, params): | 1240 | def __init__(stub, action="", creds=None, endpoint=None, |
1560 | 1241 | other_params={}): | ||
1561 | 1205 | self.assertEqual(action, "DeleteKeyPair") | 1242 | self.assertEqual(action, "DeleteKeyPair") |
1562 | 1206 | self.assertEqual("foo", creds) | 1243 | self.assertEqual("foo", creds) |
1563 | 1207 | self.assertEqual("http:///", endpoint.get_uri()) | 1244 | self.assertEqual("http:///", endpoint.get_uri()) |
1564 | 1208 | self.assertEquals( | 1245 | self.assertEquals( |
1566 | 1209 | params, | 1246 | other_params, |
1567 | 1210 | {"KeyName": "example-key-name"}) | 1247 | {"KeyName": "example-key-name"}) |
1568 | 1211 | 1248 | ||
1569 | 1212 | def submit(self): | 1249 | def submit(self): |
1570 | @@ -1221,12 +1258,13 @@ | |||
1571 | 1221 | 1258 | ||
1572 | 1222 | class StubQuery(object): | 1259 | class StubQuery(object): |
1573 | 1223 | 1260 | ||
1575 | 1224 | def __init__(stub, action, creds, endpoint, params): | 1261 | def __init__(stub, action="", creds=None, endpoint=None, |
1576 | 1262 | other_params={}): | ||
1577 | 1225 | self.assertEqual(action, "DeleteKeyPair") | 1263 | self.assertEqual(action, "DeleteKeyPair") |
1578 | 1226 | self.assertEqual("foo", creds) | 1264 | self.assertEqual("foo", creds) |
1579 | 1227 | self.assertEqual("http:///", endpoint.get_uri()) | 1265 | self.assertEqual("http:///", endpoint.get_uri()) |
1580 | 1228 | self.assertEquals( | 1266 | self.assertEquals( |
1582 | 1229 | params, | 1267 | other_params, |
1583 | 1230 | {"KeyName": "example-key-name"}) | 1268 | {"KeyName": "example-key-name"}) |
1584 | 1231 | 1269 | ||
1585 | 1232 | def submit(self): | 1270 | def submit(self): |
1586 | @@ -1241,12 +1279,13 @@ | |||
1587 | 1241 | 1279 | ||
1588 | 1242 | class StubQuery(object): | 1280 | class StubQuery(object): |
1589 | 1243 | 1281 | ||
1591 | 1244 | def __init__(stub, action, creds, endpoint, params): | 1282 | def __init__(stub, action="", creds=None, endpoint=None, |
1592 | 1283 | other_params={}): | ||
1593 | 1245 | self.assertEqual(action, "DeleteKeyPair") | 1284 | self.assertEqual(action, "DeleteKeyPair") |
1594 | 1246 | self.assertEqual("foo", creds) | 1285 | self.assertEqual("foo", creds) |
1595 | 1247 | self.assertEqual("http:///", endpoint.get_uri()) | 1286 | self.assertEqual("http:///", endpoint.get_uri()) |
1596 | 1248 | self.assertEquals( | 1287 | self.assertEquals( |
1598 | 1249 | params, | 1288 | other_params, |
1599 | 1250 | {"KeyName": "example-key-name"}) | 1289 | {"KeyName": "example-key-name"}) |
1600 | 1251 | 1290 | ||
1601 | 1252 | def submit(self): | 1291 | def submit(self): |
1602 | @@ -1373,12 +1412,12 @@ | |||
1603 | 1373 | self.assertTrue("Timestamp" in query.params) | 1412 | self.assertTrue("Timestamp" in query.params) |
1604 | 1374 | del query.params["Timestamp"] | 1413 | del query.params["Timestamp"] |
1605 | 1375 | self.assertEqual( | 1414 | self.assertEqual( |
1606 | 1415 | query.params, | ||
1607 | 1376 | {"AWSAccessKeyId": "foo", | 1416 | {"AWSAccessKeyId": "foo", |
1608 | 1377 | "Action": "DescribeInstances", | 1417 | "Action": "DescribeInstances", |
1609 | 1378 | "SignatureMethod": "HmacSHA256", | 1418 | "SignatureMethod": "HmacSHA256", |
1610 | 1379 | "SignatureVersion": "2", | 1419 | "SignatureVersion": "2", |
1613 | 1380 | "Version": "2008-12-01"}, | 1420 | "Version": "2008-12-01"}) |
1612 | 1381 | query.params) | ||
1614 | 1382 | 1421 | ||
1615 | 1383 | def test_init_other_args_are_params(self): | 1422 | def test_init_other_args_are_params(self): |
1616 | 1384 | query = client.Query( | 1423 | query = client.Query( |
1617 | @@ -1386,14 +1425,14 @@ | |||
1618 | 1386 | endpoint=self.endpoint, other_params={"InstanceId.0": "12345"}, | 1425 | endpoint=self.endpoint, other_params={"InstanceId.0": "12345"}, |
1619 | 1387 | time_tuple=(2007,11,12,13,14,15,0,0,0)) | 1426 | time_tuple=(2007,11,12,13,14,15,0,0,0)) |
1620 | 1388 | self.assertEqual( | 1427 | self.assertEqual( |
1621 | 1428 | query.params, | ||
1622 | 1389 | {"AWSAccessKeyId": "foo", | 1429 | {"AWSAccessKeyId": "foo", |
1623 | 1390 | "Action": "DescribeInstances", | 1430 | "Action": "DescribeInstances", |
1624 | 1391 | "InstanceId.0": "12345", | 1431 | "InstanceId.0": "12345", |
1625 | 1392 | "SignatureMethod": "HmacSHA256", | 1432 | "SignatureMethod": "HmacSHA256", |
1626 | 1393 | "SignatureVersion": "2", | 1433 | "SignatureVersion": "2", |
1627 | 1394 | "Timestamp": "2007-11-12T13:14:15Z", | 1434 | "Timestamp": "2007-11-12T13:14:15Z", |
1630 | 1395 | "Version": "2008-12-01"}, | 1435 | "Version": "2008-12-01"}) |
1629 | 1396 | query.params) | ||
1631 | 1397 | 1436 | ||
1632 | 1398 | def test_sorted_params(self): | 1437 | def test_sorted_params(self): |
1633 | 1399 | query = client.Query( | 1438 | query = client.Query( |
1634 | @@ -1604,11 +1643,12 @@ | |||
1635 | 1604 | 1643 | ||
1636 | 1605 | class StubQuery(object): | 1644 | class StubQuery(object): |
1637 | 1606 | 1645 | ||
1639 | 1607 | def __init__(stub, action, creds, endpoint, params): | 1646 | def __init__(stub, action="", creds=None, endpoint=None, |
1640 | 1647 | other_params={}): | ||
1641 | 1608 | self.assertEqual(action, "DescribeAddresses") | 1648 | self.assertEqual(action, "DescribeAddresses") |
1642 | 1609 | self.assertEqual(self.creds, creds) | 1649 | self.assertEqual(self.creds, creds) |
1643 | 1610 | self.assertEqual(self.endpoint, endpoint) | 1650 | self.assertEqual(self.endpoint, endpoint) |
1645 | 1611 | self.assertEquals(params, {}) | 1651 | self.assertEquals(other_params, {}) |
1646 | 1612 | 1652 | ||
1647 | 1613 | def submit(self): | 1653 | def submit(self): |
1648 | 1614 | return succeed(payload.sample_describe_addresses_result) | 1654 | return succeed(payload.sample_describe_addresses_result) |
1649 | @@ -1625,12 +1665,13 @@ | |||
1650 | 1625 | 1665 | ||
1651 | 1626 | class StubQuery(object): | 1666 | class StubQuery(object): |
1652 | 1627 | 1667 | ||
1654 | 1628 | def __init__(stub, action, creds, endpoint, params): | 1668 | def __init__(stub, action="", creds=None, endpoint=None, |
1655 | 1669 | other_params={}): | ||
1656 | 1629 | self.assertEqual(action, "DescribeAddresses") | 1670 | self.assertEqual(action, "DescribeAddresses") |
1657 | 1630 | self.assertEqual(self.creds, creds) | 1671 | self.assertEqual(self.creds, creds) |
1658 | 1631 | self.assertEqual(self.endpoint, endpoint) | 1672 | self.assertEqual(self.endpoint, endpoint) |
1659 | 1632 | self.assertEquals( | 1673 | self.assertEquals( |
1661 | 1633 | params, | 1674 | other_params, |
1662 | 1634 | {"PublicIp.1": "67.202.55.255"}) | 1675 | {"PublicIp.1": "67.202.55.255"}) |
1663 | 1635 | 1676 | ||
1664 | 1636 | def submit(self): | 1677 | def submit(self): |
1665 | @@ -1648,12 +1689,13 @@ | |||
1666 | 1648 | 1689 | ||
1667 | 1649 | class StubQuery(object): | 1690 | class StubQuery(object): |
1668 | 1650 | 1691 | ||
1670 | 1651 | def __init__(stub, action, creds, endpoint, params): | 1692 | def __init__(stub, action="", creds=None, endpoint=None, |
1671 | 1693 | other_params={}): | ||
1672 | 1652 | self.assertEqual(action, "AssociateAddress") | 1694 | self.assertEqual(action, "AssociateAddress") |
1673 | 1653 | self.assertEqual(self.creds, creds) | 1695 | self.assertEqual(self.creds, creds) |
1674 | 1654 | self.assertEqual(self.endpoint, endpoint) | 1696 | self.assertEqual(self.endpoint, endpoint) |
1675 | 1655 | self.assertEquals( | 1697 | self.assertEquals( |
1677 | 1656 | params, | 1698 | other_params, |
1678 | 1657 | {"InstanceId": "i-28a64341", "PublicIp": "67.202.55.255"}) | 1699 | {"InstanceId": "i-28a64341", "PublicIp": "67.202.55.255"}) |
1679 | 1658 | 1700 | ||
1680 | 1659 | def submit(self): | 1701 | def submit(self): |
1681 | @@ -1669,11 +1711,12 @@ | |||
1682 | 1669 | 1711 | ||
1683 | 1670 | class StubQuery(object): | 1712 | class StubQuery(object): |
1684 | 1671 | 1713 | ||
1686 | 1672 | def __init__(stub, action, creds, endpoint, params): | 1714 | def __init__(stub, action="", creds=None, endpoint=None, |
1687 | 1715 | other_params={}): | ||
1688 | 1673 | self.assertEqual(action, "AllocateAddress") | 1716 | self.assertEqual(action, "AllocateAddress") |
1689 | 1674 | self.assertEqual(self.creds, creds) | 1717 | self.assertEqual(self.creds, creds) |
1690 | 1675 | self.assertEqual(self.endpoint, endpoint) | 1718 | self.assertEqual(self.endpoint, endpoint) |
1692 | 1676 | self.assertEquals(params, {}) | 1719 | self.assertEquals(other_params, {}) |
1693 | 1677 | 1720 | ||
1694 | 1678 | def submit(self): | 1721 | def submit(self): |
1695 | 1679 | return succeed(payload.sample_allocate_address_result) | 1722 | return succeed(payload.sample_allocate_address_result) |
1696 | @@ -1688,11 +1731,12 @@ | |||
1697 | 1688 | 1731 | ||
1698 | 1689 | class StubQuery(object): | 1732 | class StubQuery(object): |
1699 | 1690 | 1733 | ||
1701 | 1691 | def __init__(stub, action, creds, endpoint, params): | 1734 | def __init__(stub, action="", creds=None, endpoint=None, |
1702 | 1735 | other_params={}): | ||
1703 | 1692 | self.assertEqual(action, "ReleaseAddress") | 1736 | self.assertEqual(action, "ReleaseAddress") |
1704 | 1693 | self.assertEqual(self.creds, creds) | 1737 | self.assertEqual(self.creds, creds) |
1705 | 1694 | self.assertEqual(self.endpoint, endpoint) | 1738 | self.assertEqual(self.endpoint, endpoint) |
1707 | 1695 | self.assertEquals(params, {"PublicIp": "67.202.55.255"}) | 1739 | self.assertEquals(other_params, {"PublicIp": "67.202.55.255"}) |
1708 | 1696 | 1740 | ||
1709 | 1697 | def submit(self): | 1741 | def submit(self): |
1710 | 1698 | return succeed(payload.sample_release_address_result) | 1742 | return succeed(payload.sample_release_address_result) |
1711 | @@ -1707,11 +1751,12 @@ | |||
1712 | 1707 | 1751 | ||
1713 | 1708 | class StubQuery(object): | 1752 | class StubQuery(object): |
1714 | 1709 | 1753 | ||
1716 | 1710 | def __init__(stub, action, creds, endpoint, params): | 1754 | def __init__(stub, action="", creds=None, endpoint=None, |
1717 | 1755 | other_params={}): | ||
1718 | 1711 | self.assertEqual(action, "DisassociateAddress") | 1756 | self.assertEqual(action, "DisassociateAddress") |
1719 | 1712 | self.assertEqual(self.creds, creds) | 1757 | self.assertEqual(self.creds, creds) |
1720 | 1713 | self.assertEqual(self.endpoint, endpoint) | 1758 | self.assertEqual(self.endpoint, endpoint) |
1722 | 1714 | self.assertEquals(params, {"PublicIp": "67.202.55.255"}) | 1759 | self.assertEquals(other_params, {"PublicIp": "67.202.55.255"}) |
1723 | 1715 | 1760 | ||
1724 | 1716 | def submit(self): | 1761 | def submit(self): |
1725 | 1717 | return succeed(payload.sample_disassociate_address_result) | 1762 | return succeed(payload.sample_disassociate_address_result) |
1726 | 1718 | 1763 | ||
1727 | === modified file 'txaws/ec2/tests/test_exception.py' | |||
1728 | --- txaws/ec2/tests/test_exception.py 2009-11-28 01:15:24 +0000 | |||
1729 | +++ txaws/ec2/tests/test_exception.py 2009-11-28 01:15:24 +0000 | |||
1730 | @@ -4,7 +4,6 @@ | |||
1731 | 4 | from twisted.trial.unittest import TestCase | 4 | from twisted.trial.unittest import TestCase |
1732 | 5 | 5 | ||
1733 | 6 | from txaws.ec2.exception import EC2Error | 6 | from txaws.ec2.exception import EC2Error |
1734 | 7 | from txaws.exception import AWSResponseParseError | ||
1735 | 8 | from txaws.testing import payload | 7 | from txaws.testing import payload |
1736 | 9 | from txaws.util import XML | 8 | from txaws.util import XML |
1737 | 10 | 9 | ||
1738 | @@ -14,77 +13,14 @@ | |||
1739 | 14 | 13 | ||
1740 | 15 | class EC2ErrorTestCase(TestCase): | 14 | class EC2ErrorTestCase(TestCase): |
1741 | 16 | 15 | ||
1763 | 17 | def test_creation(self): | 16 | def test_set_400_error(self): |
1743 | 18 | error = EC2Error("<dummy1 />", 400, "Not Found", "<dummy2 />") | ||
1744 | 19 | self.assertEquals(error.status, 400) | ||
1745 | 20 | self.assertEquals(error.response, "<dummy2 />") | ||
1746 | 21 | self.assertEquals(error.original, "<dummy1 />") | ||
1747 | 22 | self.assertEquals(error.errors, []) | ||
1748 | 23 | self.assertEquals(error.request_id, "") | ||
1749 | 24 | |||
1750 | 25 | def test_node_to_dict(self): | ||
1751 | 26 | xml = "<parent><child1>text1</child1><child2>text2</child2></parent>" | ||
1752 | 27 | error = EC2Error("<dummy />") | ||
1753 | 28 | data = error._node_to_dict(XML(xml)) | ||
1754 | 29 | self.assertEquals(data, {"child1": "text1", "child2": "text2"}) | ||
1755 | 30 | |||
1756 | 31 | def test_set_request_id(self): | ||
1757 | 32 | xml = "<a><b /><RequestID>%s</RequestID></a>" % REQUEST_ID | ||
1758 | 33 | error = EC2Error("<dummy />") | ||
1759 | 34 | error._set_request_id(XML(xml)) | ||
1760 | 35 | self.assertEquals(error.request_id, REQUEST_ID) | ||
1761 | 36 | |||
1762 | 37 | def test_set_400_errors(self): | ||
1764 | 38 | errorsXML = "<Error><Code>1</Code><Message>2</Message></Error>" | 17 | errorsXML = "<Error><Code>1</Code><Message>2</Message></Error>" |
1765 | 39 | xml = "<a><Errors>%s</Errors><b /></a>" % errorsXML | 18 | xml = "<a><Errors>%s</Errors><b /></a>" % errorsXML |
1766 | 40 | error = EC2Error("<dummy />") | 19 | error = EC2Error("<dummy />") |
1768 | 41 | error._set_400_errors(XML(xml)) | 20 | error._set_400_error(XML(xml)) |
1769 | 42 | self.assertEquals(error.errors[0]["Code"], "1") | 21 | self.assertEquals(error.errors[0]["Code"], "1") |
1770 | 43 | self.assertEquals(error.errors[0]["Message"], "2") | 22 | self.assertEquals(error.errors[0]["Message"], "2") |
1771 | 44 | 23 | ||
1772 | 45 | def test_set_host_id(self): | ||
1773 | 46 | host_id = "ASD@#FDG$E%FG" | ||
1774 | 47 | xml = "<a><b /><HostID>%s</HostID></a>" % host_id | ||
1775 | 48 | error = EC2Error("<dummy />") | ||
1776 | 49 | error._set_host_id(XML(xml)) | ||
1777 | 50 | self.assertEquals(error.host_id, host_id) | ||
1778 | 51 | |||
1779 | 52 | def test_set_500_error(self): | ||
1780 | 53 | xml = "<Error><Code>500</Code><Message>Oops</Message></Error>" | ||
1781 | 54 | error = EC2Error("<dummy />") | ||
1782 | 55 | error._set_500_error(XML(xml)) | ||
1783 | 56 | self.assertEquals(error.errors[0]["Code"], "500") | ||
1784 | 57 | self.assertEquals(error.errors[0]["Message"], "Oops") | ||
1785 | 58 | |||
1786 | 59 | def test_set_empty_errors(self): | ||
1787 | 60 | xml = "<a><Errors /><b /></a>" | ||
1788 | 61 | error = EC2Error("<dummy />") | ||
1789 | 62 | error._set_400_errors(XML(xml)) | ||
1790 | 63 | self.assertEquals(error.errors, []) | ||
1791 | 64 | |||
1792 | 65 | def test_set_empty_error(self): | ||
1793 | 66 | xml = "<a><Errors><Error /><Error /></Errors><b /></a>" | ||
1794 | 67 | error = EC2Error("<dummy />") | ||
1795 | 68 | error._set_400_errors(XML(xml)) | ||
1796 | 69 | self.assertEquals(error.errors, []) | ||
1797 | 70 | |||
1798 | 71 | def test_parse_without_xml(self): | ||
1799 | 72 | xml = "<dummy />" | ||
1800 | 73 | error = EC2Error(xml) | ||
1801 | 74 | error.parse() | ||
1802 | 75 | self.assertEquals(error.original, xml) | ||
1803 | 76 | |||
1804 | 77 | def test_parse_with_xml(self): | ||
1805 | 78 | xml1 = "<dummy1 />" | ||
1806 | 79 | xml2 = "<dummy2 />" | ||
1807 | 80 | error = EC2Error(xml1) | ||
1808 | 81 | error.parse(xml2) | ||
1809 | 82 | self.assertEquals(error.original, xml2) | ||
1810 | 83 | |||
1811 | 84 | def test_parse_html(self): | ||
1812 | 85 | xml = "<html><body>a page</body></html>" | ||
1813 | 86 | self.assertRaises(AWSResponseParseError, EC2Error, xml) | ||
1814 | 87 | |||
1815 | 88 | def test_has_error(self): | 24 | def test_has_error(self): |
1816 | 89 | errorsXML = "<Error><Code>Code1</Code><Message>2</Message></Error>" | 25 | errorsXML = "<Error><Code>Code1</Code><Message>2</Message></Error>" |
1817 | 90 | xml = "<a><Errors>%s</Errors><b /></a>" % errorsXML | 26 | xml = "<a><Errors>%s</Errors><b /></a>" % errorsXML |
1818 | @@ -99,69 +35,6 @@ | |||
1819 | 99 | error = EC2Error(payload.sample_ec2_error_messages) | 35 | error = EC2Error(payload.sample_ec2_error_messages) |
1820 | 100 | self.assertEquals(len(error.errors), 2) | 36 | self.assertEquals(len(error.errors), 2) |
1821 | 101 | 37 | ||
1822 | 102 | def test_empty_xml(self): | ||
1823 | 103 | self.assertRaises(ValueError, EC2Error, "") | ||
1824 | 104 | |||
1825 | 105 | def test_no_request_id(self): | ||
1826 | 106 | errors = "<Errors><Error><Code /><Message /></Error></Errors>" | ||
1827 | 107 | xml = "<Response>%s<RequestID /></Response>" % errors | ||
1828 | 108 | error = EC2Error(xml) | ||
1829 | 109 | self.assertEquals(error.request_id, "") | ||
1830 | 110 | |||
1831 | 111 | def test_no_request_id_node(self): | ||
1832 | 112 | errors = "<Errors><Error><Code /><Message /></Error></Errors>" | ||
1833 | 113 | xml = "<Response>%s</Response>" % errors | ||
1834 | 114 | error = EC2Error(xml) | ||
1835 | 115 | self.assertEquals(error.request_id, "") | ||
1836 | 116 | |||
1837 | 117 | def test_no_errors_node(self): | ||
1838 | 118 | xml = "<Response><RequestID /></Response>" | ||
1839 | 119 | error = EC2Error(xml) | ||
1840 | 120 | self.assertEquals(error.errors, []) | ||
1841 | 121 | |||
1842 | 122 | def test_no_error_node(self): | ||
1843 | 123 | xml = "<Response><Errors /><RequestID /></Response>" | ||
1844 | 124 | error = EC2Error(xml) | ||
1845 | 125 | self.assertEquals(error.errors, []) | ||
1846 | 126 | |||
1847 | 127 | def test_no_error_code_node(self): | ||
1848 | 128 | errors = "<Errors><Error><Message /></Error></Errors>" | ||
1849 | 129 | xml = "<Response>%s<RequestID /></Response>" % errors | ||
1850 | 130 | error = EC2Error(xml) | ||
1851 | 131 | self.assertEquals(error.errors, []) | ||
1852 | 132 | |||
1853 | 133 | def test_no_error_message_node(self): | ||
1854 | 134 | errors = "<Errors><Error><Code /></Error></Errors>" | ||
1855 | 135 | xml = "<Response>%s<RequestID /></Response>" % errors | ||
1856 | 136 | error = EC2Error(xml) | ||
1857 | 137 | self.assertEquals(error.errors, []) | ||
1858 | 138 | |||
1859 | 139 | def test_single_get_error_codes(self): | ||
1860 | 140 | error = EC2Error(payload.sample_ec2_error_message) | ||
1861 | 141 | self.assertEquals(error.get_error_codes(), "Error.Code") | ||
1862 | 142 | |||
1863 | 143 | def test_multiple_get_error_codes(self): | ||
1864 | 144 | error = EC2Error(payload.sample_ec2_error_messages) | ||
1865 | 145 | self.assertEquals(error.get_error_codes(), 2) | ||
1866 | 146 | |||
1867 | 147 | def test_zero_get_error_codes(self): | ||
1868 | 148 | xml = "<Response><RequestID /></Response>" | ||
1869 | 149 | error = EC2Error(xml) | ||
1870 | 150 | self.assertEquals(error.get_error_codes(), None) | ||
1871 | 151 | |||
1872 | 152 | def test_single_get_error_messages(self): | ||
1873 | 153 | error = EC2Error(payload.sample_ec2_error_message) | ||
1874 | 154 | self.assertEquals(error.get_error_messages(), "Message for Error.Code") | ||
1875 | 155 | |||
1876 | 156 | def test_multiple_get_error_messages(self): | ||
1877 | 157 | error = EC2Error(payload.sample_ec2_error_messages) | ||
1878 | 158 | self.assertEquals(error.get_error_messages(), "Multiple EC2 Errors") | ||
1879 | 159 | |||
1880 | 160 | def test_zero_get_error_messages(self): | ||
1881 | 161 | xml = "<Response><RequestID /></Response>" | ||
1882 | 162 | error = EC2Error(xml) | ||
1883 | 163 | self.assertEquals(error.get_error_messages(), "Empty error list") | ||
1884 | 164 | |||
1885 | 165 | def test_single_error_str(self): | 38 | def test_single_error_str(self): |
1886 | 166 | error = EC2Error(payload.sample_ec2_error_message) | 39 | error = EC2Error(payload.sample_ec2_error_message) |
1887 | 167 | self.assertEquals(str(error), "Error Message: Message for Error.Code") | 40 | self.assertEquals(str(error), "Error Message: Message for Error.Code") |
1888 | 168 | 41 | ||
1889 | === modified file 'txaws/exception.py' | |||
1890 | --- txaws/exception.py 2009-10-28 17:43:23 +0000 | |||
1891 | +++ txaws/exception.py 2009-11-28 01:15:24 +0000 | |||
1892 | @@ -3,11 +3,124 @@ | |||
1893 | 3 | 3 | ||
1894 | 4 | from twisted.web.error import Error | 4 | from twisted.web.error import Error |
1895 | 5 | 5 | ||
1896 | 6 | from txaws.util import XML | ||
1897 | 7 | |||
1898 | 6 | 8 | ||
1899 | 7 | class AWSError(Error): | 9 | class AWSError(Error): |
1900 | 8 | """ | 10 | """ |
1901 | 9 | A base class for txAWS errors. | 11 | A base class for txAWS errors. |
1902 | 10 | """ | 12 | """ |
1903 | 13 | def __init__(self, xml_bytes, status=None, message=None, response=None): | ||
1904 | 14 | super(AWSError, self).__init__(status, message, response) | ||
1905 | 15 | if not xml_bytes: | ||
1906 | 16 | raise ValueError("XML cannot be empty.") | ||
1907 | 17 | self.original = xml_bytes | ||
1908 | 18 | self.errors = [] | ||
1909 | 19 | self.request_id = "" | ||
1910 | 20 | self.host_id = "" | ||
1911 | 21 | self.parse() | ||
1912 | 22 | |||
1913 | 23 | def __str__(self): | ||
1914 | 24 | return self._get_error_message_string() | ||
1915 | 25 | |||
1916 | 26 | def __repr__(self): | ||
1917 | 27 | return "<%s object with %s>" % ( | ||
1918 | 28 | self.__class__.__name__, self._get_error_code_string()) | ||
1919 | 29 | |||
1920 | 30 | def _set_request_id(self, tree): | ||
1921 | 31 | request_id_node = tree.find(".//RequestID") | ||
1922 | 32 | if hasattr(request_id_node, "text"): | ||
1923 | 33 | text = request_id_node.text | ||
1924 | 34 | if text: | ||
1925 | 35 | self.request_id = text | ||
1926 | 36 | |||
1927 | 37 | def _set_host_id(self, tree): | ||
1928 | 38 | host_id = tree.find(".//HostID") | ||
1929 | 39 | if hasattr(host_id, "text"): | ||
1930 | 40 | text = host_id.text | ||
1931 | 41 | if text: | ||
1932 | 42 | self.host_id = text | ||
1933 | 43 | |||
1934 | 44 | def _get_error_code_string(self): | ||
1935 | 45 | count = len(self.errors) | ||
1936 | 46 | error_code = self.get_error_codes() | ||
1937 | 47 | if count > 1: | ||
1938 | 48 | return "Error count: %s" % error_code | ||
1939 | 49 | else: | ||
1940 | 50 | return "Error code: %s" % error_code | ||
1941 | 51 | |||
1942 | 52 | def _get_error_message_string(self): | ||
1943 | 53 | count = len(self.errors) | ||
1944 | 54 | error_message = self.get_error_messages() | ||
1945 | 55 | if count > 1: | ||
1946 | 56 | return "%s." % error_message | ||
1947 | 57 | else: | ||
1948 | 58 | return "Error Message: %s" % error_message | ||
1949 | 59 | |||
1950 | 60 | def _node_to_dict(self, node): | ||
1951 | 61 | data = {} | ||
1952 | 62 | for child in node: | ||
1953 | 63 | if child.tag and child.text: | ||
1954 | 64 | data[child.tag] = child.text | ||
1955 | 65 | return data | ||
1956 | 66 | |||
1957 | 67 | def _check_for_html(self, tree): | ||
1958 | 68 | if tree.tag == "html": | ||
1959 | 69 | message = "Could not parse HTML in the response." | ||
1960 | 70 | raise AWSResponseParseError(message) | ||
1961 | 71 | |||
1962 | 72 | def _set_400_error(self, tree): | ||
1963 | 73 | """ | ||
1964 | 74 | This method needs to be implemented by subclasses. | ||
1965 | 75 | """ | ||
1966 | 76 | |||
1967 | 77 | def _set_500_error(self, tree): | ||
1968 | 78 | self._set_request_id(tree) | ||
1969 | 79 | self._set_host_id(tree) | ||
1970 | 80 | data = self._node_to_dict(tree) | ||
1971 | 81 | if data: | ||
1972 | 82 | self.errors.append(data) | ||
1973 | 83 | |||
1974 | 84 | def parse(self, xml_bytes=""): | ||
1975 | 85 | if not xml_bytes: | ||
1976 | 86 | xml_bytes = self.original | ||
1977 | 87 | self.original = xml_bytes | ||
1978 | 88 | tree = XML(xml_bytes.strip()) | ||
1979 | 89 | self._check_for_html(tree) | ||
1980 | 90 | self._set_request_id(tree) | ||
1981 | 91 | if self.status: | ||
1982 | 92 | status = int(self.status) | ||
1983 | 93 | else: | ||
1984 | 94 | status = 400 | ||
1985 | 95 | if status >= 500: | ||
1986 | 96 | self._set_500_error(tree) | ||
1987 | 97 | else: | ||
1988 | 98 | self._set_400_error(tree) | ||
1989 | 99 | |||
1990 | 100 | def has_error(self, errorString): | ||
1991 | 101 | for error in self.errors: | ||
1992 | 102 | if errorString in error.values(): | ||
1993 | 103 | return True | ||
1994 | 104 | return False | ||
1995 | 105 | |||
1996 | 106 | def get_error_codes(self): | ||
1997 | 107 | count = len(self.errors) | ||
1998 | 108 | if count > 1: | ||
1999 | 109 | return count | ||
2000 | 110 | elif count == 0: | ||
2001 | 111 | return | ||
2002 | 112 | else: | ||
2003 | 113 | return self.errors[0]["Code"] | ||
2004 | 114 | |||
2005 | 115 | def get_error_messages(self): | ||
2006 | 116 | count = len(self.errors) | ||
2007 | 117 | if count > 1: | ||
2008 | 118 | return "Multiple EC2 Errors" | ||
2009 | 119 | elif count == 0: | ||
2010 | 120 | return "Empty error list" | ||
2011 | 121 | else: | ||
2012 | 122 | return self.errors[0]["Message"] | ||
2013 | 123 | |||
2014 | 11 | 124 | ||
2015 | 12 | 125 | ||
2016 | 13 | class AWSResponseParseError(Exception): | 126 | class AWSResponseParseError(Exception): |
2017 | 14 | 127 | ||
2018 | === added file 'txaws/meta.py' | |||
2019 | --- txaws/meta.py 1970-01-01 00:00:00 +0000 | |||
2020 | +++ txaws/meta.py 2009-11-28 01:15:24 +0000 | |||
2021 | @@ -0,0 +1,10 @@ | |||
2022 | 1 | display_name = "txAWS" | ||
2023 | 2 | library_name = "txaws" | ||
2024 | 3 | author = "txAWS Deelopers" | ||
2025 | 4 | author_email = "txaws-dev@lists.launchpad.net" | ||
2026 | 5 | license = "MIT" | ||
2027 | 6 | url = "http://launchpad.net/txaws" | ||
2028 | 7 | description = """ | ||
2029 | 8 | Twisted-based Asynchronous Libraries for Amazon Web Services | ||
2030 | 9 | """ | ||
2031 | 10 | |||
2032 | 0 | 11 | ||
2033 | === modified file 'txaws/s3/client.py' | |||
2034 | --- txaws/s3/client.py 2009-11-28 01:15:24 +0000 | |||
2035 | +++ txaws/s3/client.py 2009-11-28 01:15:24 +0000 | |||
2036 | @@ -17,12 +17,17 @@ | |||
2037 | 17 | 17 | ||
2038 | 18 | from epsilon.extime import Time | 18 | from epsilon.extime import Time |
2039 | 19 | 19 | ||
2041 | 20 | from txaws.client.base import BaseClient, BaseQuery | 20 | from txaws.client.base import BaseClient, BaseQuery, error_wrapper |
2042 | 21 | from txaws.s3 import model | 21 | from txaws.s3 import model |
2043 | 22 | from txaws.s3.exception import S3Error | ||
2044 | 22 | from txaws.service import AWSServiceEndpoint, S3_ENDPOINT | 23 | from txaws.service import AWSServiceEndpoint, S3_ENDPOINT |
2045 | 23 | from txaws.util import XML, calculate_md5 | 24 | from txaws.util import XML, calculate_md5 |
2046 | 24 | 25 | ||
2047 | 25 | 26 | ||
2048 | 27 | def s3_error_wrapper(error): | ||
2049 | 28 | error_wrapper(error, S3Error) | ||
2050 | 29 | |||
2051 | 30 | |||
2052 | 26 | class URLContext(object): | 31 | class URLContext(object): |
2053 | 27 | """ | 32 | """ |
2054 | 28 | The hosts and the paths that form an S3 endpoint change depending upon the | 33 | The hosts and the paths that form an S3 endpoint change depending upon the |
2055 | @@ -129,6 +134,49 @@ | |||
2056 | 129 | url_context = BucketURLContext(self.endpoint, bucket) | 134 | url_context = BucketURLContext(self.endpoint, bucket) |
2057 | 130 | return query.submit(url_context) | 135 | return query.submit(url_context) |
2058 | 131 | 136 | ||
2059 | 137 | def get_bucket(self, bucket): | ||
2060 | 138 | """ | ||
2061 | 139 | Get a list of all the objects in a bucket. | ||
2062 | 140 | """ | ||
2063 | 141 | query = self.query_factory( | ||
2064 | 142 | action="GET", creds=self.creds, endpoint=self.endpoint, | ||
2065 | 143 | bucket=bucket) | ||
2066 | 144 | url_context = BucketURLContext(self.endpoint, bucket) | ||
2067 | 145 | d = query.submit(url_context) | ||
2068 | 146 | return d.addCallback(self._parse_get_bucket) | ||
2069 | 147 | |||
2070 | 148 | def _parse_get_bucket(self, xml_bytes): | ||
2071 | 149 | root = XML(xml_bytes) | ||
2072 | 150 | name = root.findtext("Name") | ||
2073 | 151 | prefix = root.findtext("Prefix") | ||
2074 | 152 | marker = root.findtext("Marker") | ||
2075 | 153 | max_keys = root.findtext("MaxKeys") | ||
2076 | 154 | is_truncated = root.findtext("IsTruncated") | ||
2077 | 155 | contents = [] | ||
2078 | 156 | |||
2079 | 157 | for content_data in root.findall("Contents"): | ||
2080 | 158 | key = content_data.findtext("Key") | ||
2081 | 159 | date_text = content_data.findtext("LastModified") | ||
2082 | 160 | modification_date = Time.fromISO8601TimeAndDate( | ||
2083 | 161 | date_text).asDatetime() | ||
2084 | 162 | etag = content_data.findtext("ETag") | ||
2085 | 163 | size = content_data.findtext("Size") | ||
2086 | 164 | storage_class = content_data.findtext("StorageClass") | ||
2087 | 165 | owner_id = content_data.findtext("Owner/ID") | ||
2088 | 166 | owner_display_name = content_data.findtext("Owner/DisplayName") | ||
2089 | 167 | owner = model.ItemOwner(owner_id, owner_display_name) | ||
2090 | 168 | content_item = model.BucketItem( | ||
2091 | 169 | key, modification_date, etag, size, storage_class, owner) | ||
2092 | 170 | contents.append(content_item) | ||
2093 | 171 | |||
2094 | 172 | common_prefixes = [] | ||
2095 | 173 | for prefix_data in root.findall("CommonPrefixes"): | ||
2096 | 174 | common_prefixes.append(prefix_data.text) | ||
2097 | 175 | |||
2098 | 176 | return model.BucketListing( | ||
2099 | 177 | name, prefix, marker, max_keys, is_truncated, contents, | ||
2100 | 178 | common_prefixes) | ||
2101 | 179 | |||
2102 | 132 | def put_object(self, bucket, object_name, data, content_type=None, | 180 | def put_object(self, bucket, object_name, data, content_type=None, |
2103 | 133 | metadata={}): | 181 | metadata={}): |
2104 | 134 | """ | 182 | """ |
2105 | @@ -190,6 +238,10 @@ | |||
2106 | 190 | self.endpoint.set_method(self.action) | 238 | self.endpoint.set_method(self.action) |
2107 | 191 | 239 | ||
2108 | 192 | def set_content_type(self): | 240 | def set_content_type(self): |
2109 | 241 | """ | ||
2110 | 242 | Set the content type based on the file extension used in the object | ||
2111 | 243 | name. | ||
2112 | 244 | """ | ||
2113 | 193 | if self.object_name and not self.content_type: | 245 | if self.object_name and not self.content_type: |
2114 | 194 | # XXX nothing is currently done with the encoding... we may | 246 | # XXX nothing is currently done with the encoding... we may |
2115 | 195 | # need to in the future | 247 | # need to in the future |
2116 | @@ -197,6 +249,9 @@ | |||
2117 | 197 | self.object_name, strict=False) | 249 | self.object_name, strict=False) |
2118 | 198 | 250 | ||
2119 | 199 | def get_headers(self): | 251 | def get_headers(self): |
2120 | 252 | """ | ||
2121 | 253 | Build the list of headers needed in order to perform S3 operations. | ||
2122 | 254 | """ | ||
2123 | 200 | headers = {"Content-Length": len(self.data), | 255 | headers = {"Content-Length": len(self.data), |
2124 | 201 | "Content-MD5": calculate_md5(self.data), | 256 | "Content-MD5": calculate_md5(self.data), |
2125 | 202 | "Date": self.date} | 257 | "Date": self.date} |
2126 | @@ -214,6 +269,9 @@ | |||
2127 | 214 | return headers | 269 | return headers |
2128 | 215 | 270 | ||
2129 | 216 | def get_canonicalized_amz_headers(self, headers): | 271 | def get_canonicalized_amz_headers(self, headers): |
2130 | 272 | """ | ||
2131 | 273 | Get the headers defined by Amazon S3. | ||
2132 | 274 | """ | ||
2133 | 217 | headers = [ | 275 | headers = [ |
2134 | 218 | (name.lower(), value) for name, value in headers.iteritems() | 276 | (name.lower(), value) for name, value in headers.iteritems() |
2135 | 219 | if name.lower().startswith("x-amz-")] | 277 | if name.lower().startswith("x-amz-")] |
2136 | @@ -224,6 +282,9 @@ | |||
2137 | 224 | return "".join("%s:%s\n" % (name, value) for name, value in headers) | 282 | return "".join("%s:%s\n" % (name, value) for name, value in headers) |
2138 | 225 | 283 | ||
2139 | 226 | def get_canonicalized_resource(self): | 284 | def get_canonicalized_resource(self): |
2140 | 285 | """ | ||
2141 | 286 | Get an S3 resource path. | ||
2142 | 287 | """ | ||
2143 | 227 | resource = "/" | 288 | resource = "/" |
2144 | 228 | if self.bucket: | 289 | if self.bucket: |
2145 | 229 | resource += self.bucket | 290 | resource += self.bucket |
2146 | @@ -232,7 +293,7 @@ | |||
2147 | 232 | return resource | 293 | return resource |
2148 | 233 | 294 | ||
2149 | 234 | def sign(self, headers): | 295 | def sign(self, headers): |
2151 | 235 | 296 | """Sign this query using its built in credentials.""" | |
2152 | 236 | text = (self.action + "\n" + | 297 | text = (self.action + "\n" + |
2153 | 237 | headers.get("Content-MD5", "") + "\n" + | 298 | headers.get("Content-MD5", "") + "\n" + |
2154 | 238 | headers.get("Content-Type", "") + "\n" + | 299 | headers.get("Content-Type", "") + "\n" + |
2155 | @@ -242,14 +303,14 @@ | |||
2156 | 242 | return self.creds.sign(text, hash_type="sha1") | 303 | return self.creds.sign(text, hash_type="sha1") |
2157 | 243 | 304 | ||
2158 | 244 | def submit(self, url_context=None): | 305 | def submit(self, url_context=None): |
2159 | 306 | """Submit this query. | ||
2160 | 307 | |||
2161 | 308 | @return: A deferred from get_page | ||
2162 | 309 | """ | ||
2163 | 245 | if not url_context: | 310 | if not url_context: |
2164 | 246 | url_context = URLContext( | 311 | url_context = URLContext( |
2165 | 247 | self.endpoint, self.bucket, self.object_name) | 312 | self.endpoint, self.bucket, self.object_name) |
2166 | 248 | d = self.get_page( | 313 | d = self.get_page( |
2167 | 249 | url_context.get_url(), method=self.action, postdata=self.data, | 314 | url_context.get_url(), method=self.action, postdata=self.data, |
2168 | 250 | headers=self.get_headers()) | 315 | headers=self.get_headers()) |
2174 | 251 | # XXX - we need an error wrapper like we have for ec2... but let's | 316 | return d.addErrback(s3_error_wrapper) |
2170 | 252 | # wait until the new error-wrapper branch has landed, and possibly | ||
2171 | 253 | # generalize a base class for all clients. | ||
2172 | 254 | #d.addErrback(s3_error_wrapper) | ||
2173 | 255 | return d | ||
2175 | 256 | 317 | ||
2176 | === added file 'txaws/s3/exception.py' | |||
2177 | --- txaws/s3/exception.py 1970-01-01 00:00:00 +0000 | |||
2178 | +++ txaws/s3/exception.py 2009-11-28 01:15:24 +0000 | |||
2179 | @@ -0,0 +1,21 @@ | |||
2180 | 1 | # Copyright (c) 2009 Canonical Ltd <duncan.mcgreggor@canonical.com> | ||
2181 | 2 | # Licenced under the txaws licence available at /LICENSE in the txaws source. | ||
2182 | 3 | |||
2183 | 4 | from txaws.exception import AWSError | ||
2184 | 5 | |||
2185 | 6 | |||
2186 | 7 | class S3Error(AWSError): | ||
2187 | 8 | """ | ||
2188 | 9 | A error class providing custom methods on S3 errors. | ||
2189 | 10 | """ | ||
2190 | 11 | def _set_400_error(self, tree): | ||
2191 | 12 | if tree.tag.lower() == "error": | ||
2192 | 13 | data = self._node_to_dict(tree) | ||
2193 | 14 | if data: | ||
2194 | 15 | self.errors.append(data) | ||
2195 | 16 | |||
2196 | 17 | def get_error_code(self, *args, **kwargs): | ||
2197 | 18 | return super(S3Error, self).get_error_codes(*args, **kwargs) | ||
2198 | 19 | |||
2199 | 20 | def get_error_message(self, *args, **kwargs): | ||
2200 | 21 | return super(S3Error, self).get_error_messages(*args, **kwargs) | ||
2201 | 0 | 22 | ||
2202 | === modified file 'txaws/s3/model.py' | |||
2203 | --- txaws/s3/model.py 2009-11-28 01:15:24 +0000 | |||
2204 | +++ txaws/s3/model.py 2009-11-28 01:15:24 +0000 | |||
2205 | @@ -1,11 +1,47 @@ | |||
2206 | 1 | class Bucket(object): | 1 | class Bucket(object): |
2209 | 2 | """An Amazon S3 storage bucket.""" | 2 | """ |
2210 | 3 | 3 | An Amazon S3 storage bucket. | |
2211 | 4 | """ | ||
2212 | 4 | def __init__(self, name, creation_date): | 5 | def __init__(self, name, creation_date): |
2213 | 5 | self.name = name | 6 | self.name = name |
2214 | 6 | self.creation_date = creation_date | 7 | self.creation_date = creation_date |
2215 | 7 | 8 | ||
2216 | 8 | 9 | ||
2217 | 10 | class ItemOwner(object): | ||
2218 | 11 | """ | ||
2219 | 12 | The owner of a content item. | ||
2220 | 13 | """ | ||
2221 | 14 | def __init__(self, id, display_name): | ||
2222 | 15 | self.id = id | ||
2223 | 16 | self.display_name = display_name | ||
2224 | 17 | |||
2225 | 18 | |||
2226 | 19 | class BucketItem(object): | ||
2227 | 20 | """ | ||
2228 | 21 | The contents of an Amazon S3 bucket. | ||
2229 | 22 | """ | ||
2230 | 23 | def __init__(self, key, modification_date, etag, size, storage_class, | ||
2231 | 24 | owner=None): | ||
2232 | 25 | self.key = key | ||
2233 | 26 | self.modification_date = modification_date | ||
2234 | 27 | self.etag = etag | ||
2235 | 28 | self.size = size | ||
2236 | 29 | self.storage_class = storage_class | ||
2237 | 30 | self.owner = owner | ||
2238 | 31 | |||
2239 | 32 | |||
2240 | 33 | class BucketListing(object): | ||
2241 | 34 | def __init__(self, name, prefix, marker, max_keys, is_truncated, | ||
2242 | 35 | contents=None, common_prefixes=None): | ||
2243 | 36 | self.name = name | ||
2244 | 37 | self.prefix = prefix | ||
2245 | 38 | self.marker = marker | ||
2246 | 39 | self.max_keys = max_keys | ||
2247 | 40 | self.is_truncated = is_truncated | ||
2248 | 41 | self.contents = contents | ||
2249 | 42 | self.common_prefixes = common_prefixes | ||
2250 | 43 | |||
2251 | 44 | |||
2252 | 9 | class FileChunk(object): | 45 | class FileChunk(object): |
2253 | 10 | """ | 46 | """ |
2254 | 11 | An Amazon S3 file chunk. | 47 | An Amazon S3 file chunk. |
2255 | @@ -13,4 +49,3 @@ | |||
2256 | 13 | S3 returns file chunks, 10 MB at a time, until the entire file is returned. | 49 | S3 returns file chunks, 10 MB at a time, until the entire file is returned. |
2257 | 14 | These chunks need to be assembled once they are all returned. | 50 | These chunks need to be assembled once they are all returned. |
2258 | 15 | """ | 51 | """ |
2259 | 16 | |||
2260 | 17 | 52 | ||
2261 | === modified file 'txaws/s3/tests/test_client.py' | |||
2262 | --- txaws/s3/tests/test_client.py 2009-11-28 01:15:24 +0000 | |||
2263 | +++ txaws/s3/tests/test_client.py 2009-11-28 01:15:24 +0000 | |||
2264 | @@ -129,6 +129,51 @@ | |||
2265 | 129 | s3 = client.S3Client(creds, query_factory=StubQuery) | 129 | s3 = client.S3Client(creds, query_factory=StubQuery) |
2266 | 130 | return s3.create_bucket("mybucket") | 130 | return s3.create_bucket("mybucket") |
2267 | 131 | 131 | ||
2268 | 132 | def test_get_bucket(self): | ||
2269 | 133 | |||
2270 | 134 | class StubQuery(client.Query): | ||
2271 | 135 | |||
2272 | 136 | def __init__(query, action, creds, endpoint, bucket=None): | ||
2273 | 137 | super(StubQuery, query).__init__( | ||
2274 | 138 | action=action, creds=creds, bucket=bucket) | ||
2275 | 139 | self.assertEquals(action, "GET") | ||
2276 | 140 | self.assertEqual(creds.access_key, "foo") | ||
2277 | 141 | self.assertEqual(creds.secret_key, "bar") | ||
2278 | 142 | self.assertEqual(query.bucket, "mybucket") | ||
2279 | 143 | self.assertEqual(query.object_name, None) | ||
2280 | 144 | self.assertEqual(query.data, "") | ||
2281 | 145 | self.assertEqual(query.metadata, {}) | ||
2282 | 146 | |||
2283 | 147 | def submit(query, url_context=None): | ||
2284 | 148 | return succeed(payload.sample_get_bucket_result) | ||
2285 | 149 | |||
2286 | 150 | def check_results(listing): | ||
2287 | 151 | self.assertEquals(listing.name, "mybucket") | ||
2288 | 152 | self.assertEquals(listing.prefix, "N") | ||
2289 | 153 | self.assertEquals(listing.marker, "Ned") | ||
2290 | 154 | self.assertEquals(listing.max_keys, "40") | ||
2291 | 155 | self.assertEquals(listing.is_truncated, "false") | ||
2292 | 156 | self.assertEquals(len(listing.contents), 2) | ||
2293 | 157 | content1 = listing.contents[0] | ||
2294 | 158 | self.assertEquals(content1.key, "Nelson") | ||
2295 | 159 | self.assertEquals( | ||
2296 | 160 | content1.modification_date.timetuple(), | ||
2297 | 161 | (2006, 1, 1, 12, 0, 0, 6, 1, 0)) | ||
2298 | 162 | self.assertEquals( | ||
2299 | 163 | content1.etag, '"828ef3fdfa96f00ad9f27c383fc9ac7f"') | ||
2300 | 164 | self.assertEquals(content1.size, "5") | ||
2301 | 165 | self.assertEquals(content1.storage_class, "STANDARD") | ||
2302 | 166 | owner = content1.owner | ||
2303 | 167 | self.assertEquals( | ||
2304 | 168 | owner.id, | ||
2305 | 169 | "bcaf1ffd86f41caff1a493dc2ad8c2c281e37522a640e161ca5fb16fd081034f") | ||
2306 | 170 | self.assertEquals(owner.display_name, "webfile") | ||
2307 | 171 | |||
2308 | 172 | creds = AWSCredentials("foo", "bar") | ||
2309 | 173 | s3 = client.S3Client(creds, query_factory=StubQuery) | ||
2310 | 174 | d = s3.get_bucket("mybucket") | ||
2311 | 175 | return d.addCallback(check_results) | ||
2312 | 176 | |||
2313 | 132 | def test_delete_bucket(self): | 177 | def test_delete_bucket(self): |
2314 | 133 | 178 | ||
2315 | 134 | class StubQuery(client.Query): | 179 | class StubQuery(client.Query): |
2316 | @@ -344,7 +389,7 @@ | |||
2317 | 344 | query = client.Query(action="PUT", creds=self.creds) | 389 | query = client.Query(action="PUT", creds=self.creds) |
2318 | 345 | signed = query.sign({}) | 390 | signed = query.sign({}) |
2319 | 346 | self.assertEquals(signed, "H6UJCNHizzXZCGPl7wM6nL6tQdo=") | 391 | self.assertEquals(signed, "H6UJCNHizzXZCGPl7wM6nL6tQdo=") |
2321 | 347 | 392 | ||
2322 | 348 | def test_object_query(self): | 393 | def test_object_query(self): |
2323 | 349 | """ | 394 | """ |
2324 | 350 | Test that a request addressing an object is created correctly. | 395 | Test that a request addressing an object is created correctly. |
2325 | @@ -421,7 +466,7 @@ | |||
2326 | 421 | 466 | ||
2327 | 422 | headers = query.get_headers() | 467 | headers = query.get_headers() |
2328 | 423 | self.assertEqual( | 468 | self.assertEqual( |
2330 | 424 | headers["Authorization"], | 469 | headers["Authorization"], |
2331 | 425 | "AWS fookeyid:TESTINGSIG=") | 470 | "AWS fookeyid:TESTINGSIG=") |
2332 | 426 | 471 | ||
2333 | 427 | 472 | ||
2334 | 428 | 473 | ||
2335 | === added file 'txaws/s3/tests/test_exception.py' | |||
2336 | --- txaws/s3/tests/test_exception.py 1970-01-01 00:00:00 +0000 | |||
2337 | +++ txaws/s3/tests/test_exception.py 2009-11-28 01:15:24 +0000 | |||
2338 | @@ -0,0 +1,62 @@ | |||
2339 | 1 | # Copyright (c) 2009 Canonical Ltd <duncan.mcgreggor@canonical.com> | ||
2340 | 2 | # Licenced under the txaws licence available at /LICENSE in the txaws source. | ||
2341 | 3 | |||
2342 | 4 | from twisted.trial.unittest import TestCase | ||
2343 | 5 | |||
2344 | 6 | from txaws.s3.exception import S3Error | ||
2345 | 7 | from txaws.testing import payload | ||
2346 | 8 | from txaws.util import XML | ||
2347 | 9 | |||
2348 | 10 | |||
2349 | 11 | REQUEST_ID = "0ef9fc37-6230-4d81-b2e6-1b36277d4247" | ||
2350 | 12 | |||
2351 | 13 | |||
2352 | 14 | class S3ErrorTestCase(TestCase): | ||
2353 | 15 | |||
2354 | 16 | def test_set_400_error(self): | ||
2355 | 17 | xml = "<Error><Code>1</Code><Message>2</Message></Error>" | ||
2356 | 18 | error = S3Error("<dummy />") | ||
2357 | 19 | error._set_400_error(XML(xml)) | ||
2358 | 20 | self.assertEquals(error.errors[0]["Code"], "1") | ||
2359 | 21 | self.assertEquals(error.errors[0]["Message"], "2") | ||
2360 | 22 | |||
2361 | 23 | def test_get_error_code(self): | ||
2362 | 24 | error = S3Error(payload.sample_s3_invalid_access_key_result) | ||
2363 | 25 | self.assertEquals(error.get_error_code(), "InvalidAccessKeyId") | ||
2364 | 26 | |||
2365 | 27 | def test_get_error_message(self): | ||
2366 | 28 | error = S3Error(payload.sample_s3_invalid_access_key_result) | ||
2367 | 29 | self.assertEquals( | ||
2368 | 30 | error.get_error_message(), | ||
2369 | 31 | ("The AWS Access Key Id you provided does not exist in our " | ||
2370 | 32 | "records.")) | ||
2371 | 33 | |||
2372 | 34 | def test_error_count(self): | ||
2373 | 35 | error = S3Error(payload.sample_s3_invalid_access_key_result) | ||
2374 | 36 | self.assertEquals(len(error.errors), 1) | ||
2375 | 37 | |||
2376 | 38 | def test_error_repr(self): | ||
2377 | 39 | error = S3Error(payload.sample_s3_invalid_access_key_result) | ||
2378 | 40 | self.assertEquals( | ||
2379 | 41 | repr(error), | ||
2380 | 42 | "<S3Error object with Error code: InvalidAccessKeyId>") | ||
2381 | 43 | |||
2382 | 44 | def test_signature_mismatch_result(self): | ||
2383 | 45 | error = S3Error(payload.sample_s3_signature_mismatch) | ||
2384 | 46 | self.assertEquals( | ||
2385 | 47 | error.get_error_messages(), | ||
2386 | 48 | ("The request signature we calculated does not match the " | ||
2387 | 49 | "signature you provided. Check your key and signing method.")) | ||
2388 | 50 | |||
2389 | 51 | def test_invalid_access_key_result(self): | ||
2390 | 52 | error = S3Error(payload.sample_s3_invalid_access_key_result) | ||
2391 | 53 | self.assertEquals( | ||
2392 | 54 | error.get_error_messages(), | ||
2393 | 55 | ("The AWS Access Key Id you provided does not exist in our " | ||
2394 | 56 | "records.")) | ||
2395 | 57 | |||
2396 | 58 | def test_internal_error_result(self): | ||
2397 | 59 | error = S3Error(payload.sample_server_internal_error_result) | ||
2398 | 60 | self.assertEquals( | ||
2399 | 61 | error.get_error_messages(), | ||
2400 | 62 | "We encountered an internal error. Please try again.") | ||
2401 | 0 | 63 | ||
2402 | === added file 'txaws/script.py' | |||
2403 | --- txaws/script.py 1970-01-01 00:00:00 +0000 | |||
2404 | +++ txaws/script.py 2009-11-28 01:15:24 +0000 | |||
2405 | @@ -0,0 +1,42 @@ | |||
2406 | 1 | from optparse import OptionParser | ||
2407 | 2 | |||
2408 | 3 | from txaws import meta | ||
2409 | 4 | from txaws import version | ||
2410 | 5 | |||
2411 | 6 | |||
2412 | 7 | # XXX Once we start adding script that require conflicting options, we'll need | ||
2413 | 8 | # multiple parsers and option dispatching... | ||
2414 | 9 | def parse_options(usage): | ||
2415 | 10 | parser = OptionParser(usage, version="%s %s" % ( | ||
2416 | 11 | meta.display_name, version.txaws)) | ||
2417 | 12 | parser.add_option( | ||
2418 | 13 | "-a", "--access-key", dest="access_key", help="access key ID") | ||
2419 | 14 | parser.add_option( | ||
2420 | 15 | "-s", "--secret-key", dest="secret_key", help="access secret key") | ||
2421 | 16 | parser.add_option( | ||
2422 | 17 | "-r", "--region", dest="region", help="US or EU (valid for AWS only)") | ||
2423 | 18 | parser.add_option( | ||
2424 | 19 | "-U", "--url", dest="url", help="service URL/endpoint") | ||
2425 | 20 | parser.add_option( | ||
2426 | 21 | "-b", "--bucket", dest="bucket", help="name of the bucket") | ||
2427 | 22 | parser.add_option( | ||
2428 | 23 | "-o", "--object-name", dest="object_name", help="name of the object") | ||
2429 | 24 | parser.add_option( | ||
2430 | 25 | "-d", "--object-data", dest="object_data", | ||
2431 | 26 | help="content data of the object") | ||
2432 | 27 | parser.add_option( | ||
2433 | 28 | "--object-file", dest="object_filename", | ||
2434 | 29 | help=("the path to the file that will be saved as an object; if " | ||
2435 | 30 | "provided, the --object-name and --object-data options are " | ||
2436 | 31 | "not necessary")) | ||
2437 | 32 | parser.add_option( | ||
2438 | 33 | "-c", "--content-type", dest="content_type", | ||
2439 | 34 | help="content type of the object") | ||
2440 | 35 | options, args = parser.parse_args() | ||
2441 | 36 | if not (options.access_key and options.secret_key): | ||
2442 | 37 | parser.error( | ||
2443 | 38 | "both the access key ID and the secret key must be supplied") | ||
2444 | 39 | region = options.region | ||
2445 | 40 | if region and region.upper() not in ["US", "EU"]: | ||
2446 | 41 | parser.error("region must be one of 'US' or 'EU'") | ||
2447 | 42 | return (options, args) | ||
2448 | 0 | 43 | ||
2449 | === modified file 'txaws/service.py' | |||
2450 | --- txaws/service.py 2009-11-28 01:15:24 +0000 | |||
2451 | +++ txaws/service.py 2009-11-28 01:15:24 +0000 | |||
2452 | @@ -77,19 +77,22 @@ | |||
2453 | 77 | """ | 77 | """ |
2454 | 78 | # XXX update unit test to check for both ec2 and s3 endpoints | 78 | # XXX update unit test to check for both ec2 and s3 endpoints |
2455 | 79 | def __init__(self, creds=None, access_key="", secret_key="", | 79 | def __init__(self, creds=None, access_key="", secret_key="", |
2457 | 80 | region=REGION_US, ec2_endpoint="", s3_endpoint=""): | 80 | region=REGION_US, uri="", ec2_uri="", s3_uri=""): |
2458 | 81 | if not creds: | 81 | if not creds: |
2459 | 82 | creds = AWSCredentials(access_key, secret_key) | 82 | creds = AWSCredentials(access_key, secret_key) |
2460 | 83 | self.creds = creds | 83 | self.creds = creds |
2467 | 84 | if not ec2_endpoint and region == REGION_US: | 84 | # Provide backwards compatibility for the "uri" parameter. |
2468 | 85 | ec2_endpoint = EC2_ENDPOINT_US | 85 | if uri and not ec2_uri: |
2469 | 86 | elif not ec2_endpoint and region == REGION_EU: | 86 | ec2_uri = uri |
2470 | 87 | ec2_endpoint = EC2_ENDPOINT_EU | 87 | if not ec2_uri and region == REGION_US: |
2471 | 88 | if not s3_endpoint: | 88 | ec2_uri = EC2_ENDPOINT_US |
2472 | 89 | s3_endpoint = S3_ENDPOINT | 89 | elif not ec2_uri and region == REGION_EU: |
2473 | 90 | ec2_uri = EC2_ENDPOINT_EU | ||
2474 | 91 | if not s3_uri: | ||
2475 | 92 | s3_uri = S3_ENDPOINT | ||
2476 | 90 | self._clients = {} | 93 | self._clients = {} |
2479 | 91 | self.ec2_endpoint = AWSServiceEndpoint(uri=ec2_endpoint) | 94 | self.ec2_endpoint = AWSServiceEndpoint(uri=ec2_uri) |
2480 | 92 | self.s3_endpoint = AWSServiceEndpoint(uri=s3_endpoint) | 95 | self.s3_endpoint = AWSServiceEndpoint(uri=s3_uri) |
2481 | 93 | 96 | ||
2482 | 94 | def get_client(self, cls, purge_cache=False, *args, **kwds): | 97 | def get_client(self, cls, purge_cache=False, *args, **kwds): |
2483 | 95 | """ | 98 | """ |
2484 | 96 | 99 | ||
2485 | === modified file 'txaws/testing/payload.py' | |||
2486 | --- txaws/testing/payload.py 2009-11-28 01:15:24 +0000 | |||
2487 | +++ txaws/testing/payload.py 2009-11-28 01:15:24 +0000 | |||
2488 | @@ -656,6 +656,31 @@ | |||
2489 | 656 | """ | 656 | """ |
2490 | 657 | 657 | ||
2491 | 658 | 658 | ||
2492 | 659 | sample_restricted_resource_result = """\ | ||
2493 | 660 | <?xml version="1.0"?> | ||
2494 | 661 | <Response> | ||
2495 | 662 | <Errors> | ||
2496 | 663 | <Error> | ||
2497 | 664 | <Code>AuthFailure</Code> | ||
2498 | 665 | <Message>Unauthorized attempt to access restricted resource</Message> | ||
2499 | 666 | </Error> | ||
2500 | 667 | </Errors> | ||
2501 | 668 | <RequestID>a99e832e-e6e0-416a-9a35-81798ea521b4</RequestID> | ||
2502 | 669 | </Response> | ||
2503 | 670 | """ | ||
2504 | 671 | |||
2505 | 672 | |||
2506 | 673 | sample_server_internal_error_result = """\ | ||
2507 | 674 | <?xml version="1.0" encoding="UTF-8"?> | ||
2508 | 675 | <Error> | ||
2509 | 676 | <Code>InternalError</Code> | ||
2510 | 677 | <Message>We encountered an internal error. Please try again.</Message> | ||
2511 | 678 | <RequestID>A2A7E5395E27DFBB</RequestID> | ||
2512 | 679 | <HostID>f691zulHNsUqonsZkjhILnvWwD3ZnmOM4ObM1wXTc6xuS3GzPmjArp8QC/sGsn6K</HostID> | ||
2513 | 680 | </Error> | ||
2514 | 681 | """ | ||
2515 | 682 | |||
2516 | 683 | |||
2517 | 659 | sample_list_buckets_result = """\ | 684 | sample_list_buckets_result = """\ |
2518 | 660 | <?xml version="1.0" encoding="UTF-8"?> | 685 | <?xml version="1.0" encoding="UTF-8"?> |
2519 | 661 | <ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/%s/"> | 686 | <ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/%s/"> |
2520 | @@ -677,6 +702,39 @@ | |||
2521 | 677 | """ % (version.s3_api,) | 702 | """ % (version.s3_api,) |
2522 | 678 | 703 | ||
2523 | 679 | 704 | ||
2524 | 705 | sample_get_bucket_result = """\ | ||
2525 | 706 | <?xml version="1.0" encoding="UTF-8"?> | ||
2526 | 707 | <ListBucketResult xmlns="http://s3.amazonaws.com/doc/%s/"> | ||
2527 | 708 | <Name>mybucket</Name> | ||
2528 | 709 | <Prefix>N</Prefix> | ||
2529 | 710 | <Marker>Ned</Marker> | ||
2530 | 711 | <MaxKeys>40</MaxKeys> | ||
2531 | 712 | <IsTruncated>false</IsTruncated> | ||
2532 | 713 | <Contents> | ||
2533 | 714 | <Key>Nelson</Key> | ||
2534 | 715 | <LastModified>2006-01-01T12:00:00.000Z</LastModified> | ||
2535 | 716 | <ETag>"828ef3fdfa96f00ad9f27c383fc9ac7f"</ETag> | ||
2536 | 717 | <Size>5</Size> | ||
2537 | 718 | <StorageClass>STANDARD</StorageClass> | ||
2538 | 719 | <Owner> | ||
2539 | 720 | <ID>bcaf1ffd86f41caff1a493dc2ad8c2c281e37522a640e161ca5fb16fd081034f</ID> | ||
2540 | 721 | <DisplayName>webfile</DisplayName> | ||
2541 | 722 | </Owner> | ||
2542 | 723 | </Contents> | ||
2543 | 724 | <Contents> | ||
2544 | 725 | <Key>Neo</Key> | ||
2545 | 726 | <LastModified>2006-01-01T12:00:00.000Z</LastModified> | ||
2546 | 727 | <ETag>"828ef3fdfa96f00ad9f27c383fc9ac7f"</ETag> | ||
2547 | 728 | <Size>4</Size> | ||
2548 | 729 | <StorageClass>STANDARD</StorageClass> | ||
2549 | 730 | <Owner> | ||
2550 | 731 | <ID>bcaf1ffd86f41caff1a493dc2ad8c2c281e37522a640e161ca5fb16fd081034f</ID> | ||
2551 | 732 | <DisplayName>webfile</DisplayName> | ||
2552 | 733 | </Owner> | ||
2553 | 734 | </Contents> | ||
2554 | 735 | </ListBucketResult> | ||
2555 | 736 | """ % (version.s3_api,) | ||
2556 | 737 | |||
2557 | 680 | sample_s3_signature_mismatch = """\ | 738 | sample_s3_signature_mismatch = """\ |
2558 | 681 | <?xml version="1.0" encoding="UTF-8"?> | 739 | <?xml version="1.0" encoding="UTF-8"?> |
2559 | 682 | <Error> | 740 | <Error> |
2560 | @@ -692,26 +750,13 @@ | |||
2561 | 692 | """ | 750 | """ |
2562 | 693 | 751 | ||
2563 | 694 | 752 | ||
2565 | 695 | sample_server_internal_error_result = """\ | 753 | sample_s3_invalid_access_key_result = """\ |
2566 | 696 | <?xml version="1.0" encoding="UTF-8"?> | 754 | <?xml version="1.0" encoding="UTF-8"?> |
2567 | 697 | <Error> | 755 | <Error> |
2572 | 698 | <Code>InternalError</Code> | 756 | <Code>InvalidAccessKeyId</Code> |
2573 | 699 | <Message>We encountered an internal error. Please try again.</Message> | 757 | <Message>The AWS Access Key Id you provided does not exist in our records.</Message> |
2574 | 700 | <RequestID>A2A7E5395E27DFBB</RequestID> | 758 | <RequestId>0223AD81A94821CE</RequestId> |
2575 | 701 | <HostID>f691zulHNsUqonsZkjhILnvWwD3ZnmOM4ObM1wXTc6xuS3GzPmjArp8QC/sGsn6K</HostID> | 759 | <HostId>HAw5g9P1VkN8ztgLKFTK20CY5LmCfTwXcSths1O7UQV6NuJx2P4tmFnpuOsziwOE</HostId> |
2576 | 760 | <AWSAccessKeyId>SOMEKEYID</AWSAccessKeyId> | ||
2577 | 702 | </Error> | 761 | </Error> |
2578 | 703 | """ | 762 | """ |
2579 | 704 | |||
2580 | 705 | |||
2581 | 706 | sample_restricted_resource_result = """\ | ||
2582 | 707 | <?xml version="1.0"?> | ||
2583 | 708 | <Response> | ||
2584 | 709 | <Errors> | ||
2585 | 710 | <Error> | ||
2586 | 711 | <Code>AuthFailure</Code> | ||
2587 | 712 | <Message>Unauthorized attempt to access restricted resource</Message> | ||
2588 | 713 | </Error> | ||
2589 | 714 | </Errors> | ||
2590 | 715 | <RequestID>a99e832e-e6e0-416a-9a35-81798ea521b4</RequestID> | ||
2591 | 716 | </Response> | ||
2592 | 717 | """ | ||
2593 | 718 | 763 | ||
2594 | === added file 'txaws/tests/test_exception.py' | |||
2595 | --- txaws/tests/test_exception.py 1970-01-01 00:00:00 +0000 | |||
2596 | +++ txaws/tests/test_exception.py 2009-11-28 01:15:24 +0000 | |||
2597 | @@ -0,0 +1,114 @@ | |||
2598 | 1 | # Copyright (c) 2009 Canonical Ltd <duncan.mcgreggor@canonical.com> | ||
2599 | 2 | # Licenced under the txaws licence available at /LICENSE in the txaws source. | ||
2600 | 3 | |||
2601 | 4 | from twisted.trial.unittest import TestCase | ||
2602 | 5 | |||
2603 | 6 | from txaws.exception import AWSError | ||
2604 | 7 | from txaws.exception import AWSResponseParseError | ||
2605 | 8 | from txaws.util import XML | ||
2606 | 9 | |||
2607 | 10 | |||
2608 | 11 | REQUEST_ID = "0ef9fc37-6230-4d81-b2e6-1b36277d4247" | ||
2609 | 12 | |||
2610 | 13 | |||
2611 | 14 | class AWSErrorTestCase(TestCase): | ||
2612 | 15 | |||
2613 | 16 | def test_creation(self): | ||
2614 | 17 | error = AWSError("<dummy1 />", 500, "Server Error", "<dummy2 />") | ||
2615 | 18 | self.assertEquals(error.status, 500) | ||
2616 | 19 | self.assertEquals(error.response, "<dummy2 />") | ||
2617 | 20 | self.assertEquals(error.original, "<dummy1 />") | ||
2618 | 21 | self.assertEquals(error.errors, []) | ||
2619 | 22 | self.assertEquals(error.request_id, "") | ||
2620 | 23 | |||
2621 | 24 | def test_node_to_dict(self): | ||
2622 | 25 | xml = "<parent><child1>text1</child1><child2>text2</child2></parent>" | ||
2623 | 26 | error = AWSError("<dummy />") | ||
2624 | 27 | data = error._node_to_dict(XML(xml)) | ||
2625 | 28 | self.assertEquals(data, {"child1": "text1", "child2": "text2"}) | ||
2626 | 29 | |||
2627 | 30 | def test_set_request_id(self): | ||
2628 | 31 | xml = "<a><b /><RequestID>%s</RequestID></a>" % REQUEST_ID | ||
2629 | 32 | error = AWSError("<dummy />") | ||
2630 | 33 | error._set_request_id(XML(xml)) | ||
2631 | 34 | self.assertEquals(error.request_id, REQUEST_ID) | ||
2632 | 35 | |||
2633 | 36 | def test_set_host_id(self): | ||
2634 | 37 | host_id = "ASD@#FDG$E%FG" | ||
2635 | 38 | xml = "<a><b /><HostID>%s</HostID></a>" % host_id | ||
2636 | 39 | error = AWSError("<dummy />") | ||
2637 | 40 | error._set_host_id(XML(xml)) | ||
2638 | 41 | self.assertEquals(error.host_id, host_id) | ||
2639 | 42 | |||
2640 | 43 | def test_set_empty_errors(self): | ||
2641 | 44 | xml = "<a><Errors /><b /></a>" | ||
2642 | 45 | error = AWSError("<dummy />") | ||
2643 | 46 | error._set_500_error(XML(xml)) | ||
2644 | 47 | self.assertEquals(error.errors, []) | ||
2645 | 48 | |||
2646 | 49 | def test_set_empty_error(self): | ||
2647 | 50 | xml = "<a><Errors><Error /><Error /></Errors><b /></a>" | ||
2648 | 51 | error = AWSError("<dummy />") | ||
2649 | 52 | error._set_500_error(XML(xml)) | ||
2650 | 53 | self.assertEquals(error.errors, []) | ||
2651 | 54 | |||
2652 | 55 | def test_parse_without_xml(self): | ||
2653 | 56 | xml = "<dummy />" | ||
2654 | 57 | error = AWSError(xml) | ||
2655 | 58 | error.parse() | ||
2656 | 59 | self.assertEquals(error.original, xml) | ||
2657 | 60 | |||
2658 | 61 | def test_parse_with_xml(self): | ||
2659 | 62 | xml1 = "<dummy1 />" | ||
2660 | 63 | xml2 = "<dummy2 />" | ||
2661 | 64 | error = AWSError(xml1) | ||
2662 | 65 | error.parse(xml2) | ||
2663 | 66 | self.assertEquals(error.original, xml2) | ||
2664 | 67 | |||
2665 | 68 | def test_parse_html(self): | ||
2666 | 69 | xml = "<html><body>a page</body></html>" | ||
2667 | 70 | self.assertRaises(AWSResponseParseError, AWSError, xml) | ||
2668 | 71 | |||
2669 | 72 | def test_empty_xml(self): | ||
2670 | 73 | self.assertRaises(ValueError, AWSError, "") | ||
2671 | 74 | |||
2672 | 75 | def test_no_request_id(self): | ||
2673 | 76 | errors = "<Errors><Error><Code /><Message /></Error></Errors>" | ||
2674 | 77 | xml = "<Response>%s<RequestID /></Response>" % errors | ||
2675 | 78 | error = AWSError(xml) | ||
2676 | 79 | self.assertEquals(error.request_id, "") | ||
2677 | 80 | |||
2678 | 81 | def test_no_request_id_node(self): | ||
2679 | 82 | errors = "<Errors><Error><Code /><Message /></Error></Errors>" | ||
2680 | 83 | xml = "<Response>%s</Response>" % errors | ||
2681 | 84 | error = AWSError(xml) | ||
2682 | 85 | self.assertEquals(error.request_id, "") | ||
2683 | 86 | |||
2684 | 87 | def test_no_errors_node(self): | ||
2685 | 88 | xml = "<Response><RequestID /></Response>" | ||
2686 | 89 | error = AWSError(xml) | ||
2687 | 90 | self.assertEquals(error.errors, []) | ||
2688 | 91 | |||
2689 | 92 | def test_no_error_node(self): | ||
2690 | 93 | xml = "<Response><Errors /><RequestID /></Response>" | ||
2691 | 94 | error = AWSError(xml) | ||
2692 | 95 | self.assertEquals(error.errors, []) | ||
2693 | 96 | |||
2694 | 97 | def test_no_error_code_node(self): | ||
2695 | 98 | errors = "<Errors><Error><Message /></Error></Errors>" | ||
2696 | 99 | xml = "<Response>%s<RequestID /></Response>" % errors | ||
2697 | 100 | error = AWSError(xml) | ||
2698 | 101 | self.assertEquals(error.errors, []) | ||
2699 | 102 | |||
2700 | 103 | def test_no_error_message_node(self): | ||
2701 | 104 | errors = "<Errors><Error><Code /></Error></Errors>" | ||
2702 | 105 | xml = "<Response>%s<RequestID /></Response>" % errors | ||
2703 | 106 | error = AWSError(xml) | ||
2704 | 107 | self.assertEquals(error.errors, []) | ||
2705 | 108 | |||
2706 | 109 | def test_set_500_error(self): | ||
2707 | 110 | xml = "<Error><Code>500</Code><Message>Oops</Message></Error>" | ||
2708 | 111 | error = AWSError("<dummy />") | ||
2709 | 112 | error._set_500_error(XML(xml)) | ||
2710 | 113 | self.assertEquals(error.errors[0]["Code"], "500") | ||
2711 | 114 | self.assertEquals(error.errors[0]["Message"], "Oops") | ||
2712 | 0 | 115 | ||
2713 | === modified file 'txaws/tests/test_service.py' | |||
2714 | --- txaws/tests/test_service.py 2009-11-28 01:15:24 +0000 | |||
2715 | +++ txaws/tests/test_service.py 2009-11-28 01:15:24 +0000 | |||
2716 | @@ -97,12 +97,17 @@ | |||
2717 | 97 | 97 | ||
2718 | 98 | def test_creation_with_uri(self): | 98 | def test_creation_with_uri(self): |
2719 | 99 | region = AWSServiceRegion( | 99 | region = AWSServiceRegion( |
2721 | 100 | creds=self.creds, ec2_endpoint="http://foo/bar") | 100 | creds=self.creds, ec2_uri="http://foo/bar") |
2722 | 101 | self.assertEquals(region.ec2_endpoint.get_uri(), "http://foo/bar") | ||
2723 | 102 | |||
2724 | 103 | def test_creation_with_uri_backwards_compatible(self): | ||
2725 | 104 | region = AWSServiceRegion( | ||
2726 | 105 | creds=self.creds, uri="http://foo/bar") | ||
2727 | 101 | self.assertEquals(region.ec2_endpoint.get_uri(), "http://foo/bar") | 106 | self.assertEquals(region.ec2_endpoint.get_uri(), "http://foo/bar") |
2728 | 102 | 107 | ||
2729 | 103 | def test_creation_with_uri_and_region(self): | 108 | def test_creation_with_uri_and_region(self): |
2730 | 104 | region = AWSServiceRegion( | 109 | region = AWSServiceRegion( |
2732 | 105 | creds=self.creds, region=REGION_EU, ec2_endpoint="http://foo/bar") | 110 | creds=self.creds, region=REGION_EU, ec2_uri="http://foo/bar") |
2733 | 106 | self.assertEquals(region.ec2_endpoint.get_uri(), "http://foo/bar") | 111 | self.assertEquals(region.ec2_endpoint.get_uri(), "http://foo/bar") |
2734 | 107 | 112 | ||
2735 | 108 | def test_creation_with_region_override(self): | 113 | def test_creation_with_region_override(self): |
2736 | 109 | 114 | ||
2737 | === modified file 'txaws/util.py' | |||
2738 | --- txaws/util.py 2009-11-28 01:15:24 +0000 | |||
2739 | +++ txaws/util.py 2009-11-28 01:15:24 +0000 | |||
2740 | @@ -94,3 +94,33 @@ | |||
2741 | 94 | if path == "": | 94 | if path == "": |
2742 | 95 | path = "/" | 95 | path = "/" |
2743 | 96 | return (str(scheme), str(host), port, str(path)) | 96 | return (str(scheme), str(host), port, str(path)) |
2744 | 97 | |||
2745 | 98 | |||
2746 | 99 | def get_exitcode_reactor(): | ||
2747 | 100 | """ | ||
2748 | 101 | This is only neccesary until a fix like the one outlined here is | ||
2749 | 102 | implemented for Twisted: | ||
2750 | 103 | http://twistedmatrix.com/trac/ticket/2182 | ||
2751 | 104 | """ | ||
2752 | 105 | from twisted.internet.main import installReactor | ||
2753 | 106 | from twisted.internet.selectreactor import SelectReactor | ||
2754 | 107 | |||
2755 | 108 | class ExitCodeReactor(SelectReactor): | ||
2756 | 109 | |||
2757 | 110 | def stop(self, exitStatus=0): | ||
2758 | 111 | super(ExitCodeReactor, self).stop() | ||
2759 | 112 | self.exitStatus = exitStatus | ||
2760 | 113 | |||
2761 | 114 | def run(self, *args, **kwargs): | ||
2762 | 115 | super(ExitCodeReactor, self).run(*args, **kwargs) | ||
2763 | 116 | return self.exitStatus | ||
2764 | 117 | |||
2765 | 118 | reactor = ExitCodeReactor() | ||
2766 | 119 | installReactor(reactor) | ||
2767 | 120 | return reactor | ||
2768 | 121 | |||
2769 | 122 | |||
2770 | 123 | try: | ||
2771 | 124 | reactor = get_exitcode_reactor() | ||
2772 | 125 | except: | ||
2773 | 126 | from twisted.internet import reactor |
This branch adds support for getting a list of objects in a bucket (it includes a script for this as well).
Depends on lp:~oubiwann/txaws/486363-no-content-fix