OLD | NEW |
| (Empty) |
1 # Copyright 2010 Google Inc. | |
2 # | |
3 # Permission is hereby granted, free of charge, to any person obtaining a | |
4 # copy of this software and associated documentation files (the | |
5 # "Software"), to deal in the Software without restriction, including | |
6 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
7 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
8 # persons to whom the Software is furnished to do so, subject to the fol- | |
9 # lowing conditions: | |
10 # | |
11 # The above copyright notice and this permission notice shall be included | |
12 # in all copies or substantial portions of the Software. | |
13 # | |
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
20 # IN THE SOFTWARE. | |
21 | |
22 """ | |
23 Provides basic mocks of core storage service classes, for unit testing: | |
24 ACL, Key, Bucket, Connection, and StorageUri. We implement a subset of | |
25 the interfaces defined in the real boto classes, but don't handle most | |
26 of the optional params (which we indicate with the constant "NOT_IMPL"). | |
27 """ | |
28 | |
29 import copy | |
30 import boto | |
31 | |
32 NOT_IMPL = None | |
33 | |
34 | |
35 class MockAcl(object): | |
36 | |
37 def __init__(self, parent=NOT_IMPL): | |
38 pass | |
39 | |
40 def startElement(self, name, attrs, connection): | |
41 pass | |
42 | |
43 def endElement(self, name, value, connection): | |
44 pass | |
45 | |
46 def to_xml(self): | |
47 return '<mock_ACL_XML/>' | |
48 | |
49 | |
50 class MockKey(object): | |
51 | |
52 def __init__(self, bucket=None, name=None): | |
53 self.bucket = bucket | |
54 self.name = name | |
55 self.data = None | |
56 self.size = None | |
57 self.content_encoding = None | |
58 self.content_type = None | |
59 self.last_modified = 'Wed, 06 Oct 2010 05:11:54 GMT' | |
60 | |
61 def get_contents_as_string(self, headers=NOT_IMPL, | |
62 cb=NOT_IMPL, num_cb=NOT_IMPL, | |
63 torrent=NOT_IMPL, | |
64 version_id=NOT_IMPL): | |
65 return self.data | |
66 | |
67 def get_contents_to_file(self, fp, headers=NOT_IMPL, | |
68 cb=NOT_IMPL, num_cb=NOT_IMPL, | |
69 torrent=NOT_IMPL, | |
70 version_id=NOT_IMPL, | |
71 res_download_handler=NOT_IMPL): | |
72 fp.write(self.data) | |
73 | |
74 def get_file(self, fp, headers=NOT_IMPL, cb=NOT_IMPL, num_cb=NOT_IMPL, | |
75 torrent=NOT_IMPL, version_id=NOT_IMPL, | |
76 override_num_retries=NOT_IMPL): | |
77 fp.write(self.data) | |
78 | |
79 def _handle_headers(self, headers): | |
80 if not headers: | |
81 return | |
82 if 'Content-Encoding' in headers: | |
83 self.content_encoding = headers['Content-Encoding'] | |
84 if 'Content-Type' in headers: | |
85 self.content_type = headers['Content-Type'] | |
86 | |
87 def open_read(self, headers=NOT_IMPL, query_args=NOT_IMPL, | |
88 override_num_retries=NOT_IMPL): | |
89 pass | |
90 | |
91 def set_contents_from_file(self, fp, headers=None, replace=NOT_IMPL, | |
92 cb=NOT_IMPL, num_cb=NOT_IMPL, | |
93 policy=NOT_IMPL, md5=NOT_IMPL, | |
94 res_upload_handler=NOT_IMPL): | |
95 self.data = fp.readlines() | |
96 self.size = len(self.data) | |
97 self._handle_headers(headers) | |
98 | |
99 def set_contents_from_string(self, s, headers=NOT_IMPL, replace=NOT_IMPL, | |
100 cb=NOT_IMPL, num_cb=NOT_IMPL, policy=NOT_IMPL, | |
101 md5=NOT_IMPL, reduced_redundancy=NOT_IMPL): | |
102 self.data = copy.copy(s) | |
103 self.size = len(s) | |
104 self._handle_headers(headers) | |
105 | |
106 | |
107 class MockBucket(object): | |
108 | |
109 def __init__(self, connection=NOT_IMPL, name=None, key_class=NOT_IMPL): | |
110 self.name = name | |
111 self.keys = {} | |
112 self.acls = {name: MockAcl()} | |
113 | |
114 def copy_key(self, new_key_name, src_bucket_name, | |
115 src_key_name, metadata=NOT_IMPL, src_version_id=NOT_IMPL, | |
116 storage_class=NOT_IMPL, preserve_acl=NOT_IMPL): | |
117 new_key = self.new_key(key_name=new_key_name) | |
118 src_key = mock_connection.get_bucket( | |
119 src_bucket_name).get_key(src_key_name) | |
120 new_key.data = copy.copy(src_key.data) | |
121 new_key.size = len(new_key.data) | |
122 | |
123 def get_acl(self, key_name='', headers=NOT_IMPL, version_id=NOT_IMPL): | |
124 if key_name: | |
125 # Return ACL for the key. | |
126 return self.acls[key_name] | |
127 else: | |
128 # Return ACL for the bucket. | |
129 return self.acls[self.name] | |
130 | |
131 def new_key(self, key_name=None): | |
132 mock_key = MockKey(self, key_name) | |
133 self.keys[key_name] = mock_key | |
134 self.acls[key_name] = MockAcl() | |
135 return mock_key | |
136 | |
137 def delete_key(self, key_name, headers=NOT_IMPL, | |
138 version_id=NOT_IMPL, mfa_token=NOT_IMPL): | |
139 if key_name not in self.keys: | |
140 raise boto.exception.StorageResponseError(404, 'Not Found') | |
141 del self.keys[key_name] | |
142 | |
143 def get_all_keys(self, headers=NOT_IMPL): | |
144 return self.keys.itervalues() | |
145 | |
146 def get_key(self, key_name, headers=NOT_IMPL, version_id=NOT_IMPL): | |
147 # Emulate behavior of boto when get_key called with non-existent key. | |
148 if key_name not in self.keys: | |
149 return None | |
150 return self.keys[key_name] | |
151 | |
152 def list(self, prefix='', delimiter=NOT_IMPL, marker=NOT_IMPL, | |
153 headers=NOT_IMPL): | |
154 # Return list instead of using a generator so we don't get | |
155 # 'dictionary changed size during iteration' error when performing | |
156 # deletions while iterating (e.g., during test cleanup). | |
157 result = [] | |
158 for k in self.keys.itervalues(): | |
159 if not prefix: | |
160 result.append(k) | |
161 elif k.name.startswith(prefix): | |
162 result.append(k) | |
163 return result | |
164 | |
165 def set_acl(self, acl_or_str, key_name='', headers=NOT_IMPL, | |
166 version_id=NOT_IMPL): | |
167 # We only handle setting ACL XML here; if you pass a canned ACL | |
168 # the get_acl call will just return that string name. | |
169 if key_name: | |
170 # Set ACL for the key. | |
171 self.acls[key_name] = acl_or_str | |
172 else: | |
173 # Set ACL for the bucket. | |
174 self.acls[self.name] = acl_or_str | |
175 | |
176 | |
177 class MockConnection(object): | |
178 | |
179 def __init__(self, aws_access_key_id=NOT_IMPL, | |
180 aws_secret_access_key=NOT_IMPL, is_secure=NOT_IMPL, | |
181 port=NOT_IMPL, proxy=NOT_IMPL, proxy_port=NOT_IMPL, | |
182 proxy_user=NOT_IMPL, proxy_pass=NOT_IMPL, | |
183 host=NOT_IMPL, debug=NOT_IMPL, | |
184 https_connection_factory=NOT_IMPL, | |
185 calling_format=NOT_IMPL, | |
186 path=NOT_IMPL, provider=NOT_IMPL, | |
187 bucket_class=NOT_IMPL): | |
188 self.buckets = {} | |
189 | |
190 def create_bucket(self, bucket_name, headers=NOT_IMPL, location=NOT_IMPL, | |
191 policy=NOT_IMPL): | |
192 if bucket_name in self.buckets: | |
193 raise boto.exception.StorageCreateError( | |
194 409, 'BucketAlreadyOwnedByYou', 'bucket already exists') | |
195 mock_bucket = MockBucket(name=bucket_name) | |
196 self.buckets[bucket_name] = mock_bucket | |
197 return mock_bucket | |
198 | |
199 def delete_bucket(self, bucket, headers=NOT_IMPL): | |
200 if bucket not in self.buckets: | |
201 raise boto.exception.StorageResponseError(404, 'NoSuchBucket', | |
202 'no such bucket') | |
203 del self.buckets[bucket] | |
204 | |
205 def get_bucket(self, bucket_name, validate=NOT_IMPL, headers=NOT_IMPL): | |
206 if bucket_name not in self.buckets: | |
207 raise boto.exception.StorageResponseError(404, 'NoSuchBucket', | |
208 'Not Found') | |
209 return self.buckets[bucket_name] | |
210 | |
211 def get_all_buckets(self, headers=NOT_IMPL): | |
212 return self.buckets.itervalues() | |
213 | |
214 | |
215 # We only mock a single provider/connection. | |
216 mock_connection = MockConnection() | |
217 | |
218 | |
219 class MockBucketStorageUri(object): | |
220 | |
221 def __init__(self, scheme, bucket_name=None, object_name=None, | |
222 debug=NOT_IMPL): | |
223 self.scheme = scheme | |
224 self.bucket_name = bucket_name | |
225 self.object_name = object_name | |
226 if self.bucket_name and self.object_name: | |
227 self.uri = ('%s://%s/%s' % (self.scheme, self.bucket_name, | |
228 self.object_name)) | |
229 elif self.bucket_name: | |
230 self.uri = ('%s://%s/' % (self.scheme, self.bucket_name)) | |
231 else: | |
232 self.uri = ('%s://' % self.scheme) | |
233 | |
234 def __repr__(self): | |
235 """Returns string representation of URI.""" | |
236 return self.uri | |
237 | |
238 def acl_class(self): | |
239 return MockAcl | |
240 | |
241 def canned_acls(self): | |
242 return boto.provider.Provider('aws').canned_acls | |
243 | |
244 def clone_replace_name(self, new_name): | |
245 return MockBucketStorageUri(self.scheme, self.bucket_name, new_name) | |
246 | |
247 def connect(self, access_key_id=NOT_IMPL, secret_access_key=NOT_IMPL): | |
248 return mock_connection | |
249 | |
250 def create_bucket(self, headers=NOT_IMPL, location=NOT_IMPL, | |
251 policy=NOT_IMPL): | |
252 return self.connect().create_bucket(self.bucket_name) | |
253 | |
254 def delete_bucket(self, headers=NOT_IMPL): | |
255 return self.connect().delete_bucket(self.bucket_name) | |
256 | |
257 def delete_key(self, validate=NOT_IMPL, headers=NOT_IMPL, | |
258 version_id=NOT_IMPL, mfa_token=NOT_IMPL): | |
259 self.get_bucket().delete_key(self.object_name) | |
260 | |
261 def equals(self, uri): | |
262 return self.uri == uri.uri | |
263 | |
264 def get_acl(self, validate=NOT_IMPL, headers=NOT_IMPL, version_id=NOT_IMPL): | |
265 return self.get_bucket().get_acl(self.object_name) | |
266 | |
267 def get_all_buckets(self, headers=NOT_IMPL): | |
268 return self.connect().get_all_buckets() | |
269 | |
270 def get_all_keys(self, validate=NOT_IMPL, headers=NOT_IMPL): | |
271 return self.get_bucket().get_all_keys(self) | |
272 | |
273 def get_bucket(self, validate=NOT_IMPL, headers=NOT_IMPL): | |
274 return self.connect().get_bucket(self.bucket_name) | |
275 | |
276 def get_key(self, validate=NOT_IMPL, headers=NOT_IMPL, | |
277 version_id=NOT_IMPL): | |
278 return self.get_bucket().get_key(self.object_name) | |
279 | |
280 def is_file_uri(self): | |
281 return False | |
282 | |
283 def is_cloud_uri(self): | |
284 return True | |
285 | |
286 def names_container(self): | |
287 return not self.object_name | |
288 | |
289 def names_singleton(self): | |
290 return self.object_name | |
291 | |
292 def new_key(self, validate=NOT_IMPL, headers=NOT_IMPL): | |
293 bucket = self.get_bucket() | |
294 return bucket.new_key(self.object_name) | |
295 | |
296 def set_acl(self, acl_or_str, key_name='', validate=NOT_IMPL, | |
297 headers=NOT_IMPL, version_id=NOT_IMPL): | |
298 self.get_bucket().set_acl(acl_or_str, key_name) | |
OLD | NEW |