Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/boto/gs/bucket.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
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 import re | |
23 import urllib | |
24 import xml.sax | |
25 | |
26 import boto | |
27 from boto import handler | |
28 from boto.resultset import ResultSet | |
29 from boto.exception import GSResponseError | |
30 from boto.exception import InvalidAclError | |
31 from boto.gs.acl import ACL, CannedACLStrings | |
32 from boto.gs.acl import SupportedPermissions as GSPermissions | |
33 from boto.gs.bucketlistresultset import VersionedBucketListResultSet | |
34 from boto.gs.cors import Cors | |
35 from boto.gs.encryptionconfig import EncryptionConfig | |
36 from boto.gs.lifecycle import LifecycleConfig | |
37 from boto.gs.key import Key as GSKey | |
38 from boto.s3.acl import Policy | |
39 from boto.s3.bucket import Bucket as S3Bucket | |
40 from boto.utils import get_utf8_value | |
41 from boto.compat import six | |
42 | |
43 # constants for http query args | |
44 DEF_OBJ_ACL = 'defaultObjectAcl' | |
45 STANDARD_ACL = 'acl' | |
46 CORS_ARG = 'cors' | |
47 ENCRYPTION_CONFIG_ARG = 'encryptionConfig' | |
48 LIFECYCLE_ARG = 'lifecycle' | |
49 STORAGE_CLASS_ARG='storageClass' | |
50 ERROR_DETAILS_REGEX = re.compile(r'<Details>(?P<details>.*)</Details>') | |
51 | |
52 class Bucket(S3Bucket): | |
53 """Represents a Google Cloud Storage bucket.""" | |
54 | |
55 BillingBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' | |
56 '<BillingConfiguration>' | |
57 '<RequesterPays>%s</RequesterPays>' | |
58 '</BillingConfiguration>') | |
59 EncryptionConfigBody = ( | |
60 '<?xml version="1.0" encoding="UTF-8"?>\n' | |
61 '<EncryptionConfiguration>%s</EncryptionConfiguration>') | |
62 EncryptionConfigDefaultKeyNameFragment = ( | |
63 '<DefaultKmsKeyName>%s</DefaultKmsKeyName>') | |
64 StorageClassBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' | |
65 '<StorageClass>%s</StorageClass>') | |
66 VersioningBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' | |
67 '<VersioningConfiguration>' | |
68 '<Status>%s</Status>' | |
69 '</VersioningConfiguration>') | |
70 WebsiteBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' | |
71 '<WebsiteConfiguration>%s%s</WebsiteConfiguration>') | |
72 WebsiteMainPageFragment = '<MainPageSuffix>%s</MainPageSuffix>' | |
73 WebsiteErrorFragment = '<NotFoundPage>%s</NotFoundPage>' | |
74 | |
75 def __init__(self, connection=None, name=None, key_class=GSKey): | |
76 super(Bucket, self).__init__(connection, name, key_class) | |
77 | |
78 def startElement(self, name, attrs, connection): | |
79 return None | |
80 | |
81 def endElement(self, name, value, connection): | |
82 if name == 'Name': | |
83 self.name = value | |
84 elif name == 'CreationDate': | |
85 self.creation_date = value | |
86 else: | |
87 setattr(self, name, value) | |
88 | |
89 def get_key(self, key_name, headers=None, version_id=None, | |
90 response_headers=None, generation=None): | |
91 """Returns a Key instance for an object in this bucket. | |
92 | |
93 Note that this method uses a HEAD request to check for the existence of | |
94 the key. | |
95 | |
96 :type key_name: string | |
97 :param key_name: The name of the key to retrieve | |
98 | |
99 :type response_headers: dict | |
100 :param response_headers: A dictionary containing HTTP | |
101 headers/values that will override any headers associated | |
102 with the stored object in the response. See | |
103 http://goo.gl/06N3b for details. | |
104 | |
105 :type version_id: string | |
106 :param version_id: Unused in this subclass. | |
107 | |
108 :type generation: int | |
109 :param generation: A specific generation number to fetch the key at. If | |
110 not specified, the latest generation is fetched. | |
111 | |
112 :rtype: :class:`boto.gs.key.Key` | |
113 :returns: A Key object from this bucket. | |
114 """ | |
115 query_args_l = [] | |
116 if generation: | |
117 query_args_l.append('generation=%s' % generation) | |
118 if response_headers: | |
119 for rk, rv in six.iteritems(response_headers): | |
120 query_args_l.append('%s=%s' % (rk, urllib.quote(rv))) | |
121 try: | |
122 key, resp = self._get_key_internal(key_name, headers, | |
123 query_args_l=query_args_l) | |
124 except GSResponseError as e: | |
125 if e.status == 403 and 'Forbidden' in e.reason: | |
126 # If we failed getting an object, let the user know which object | |
127 # failed rather than just returning a generic 403. | |
128 e.reason = ("Access denied to 'gs://%s/%s'." % | |
129 (self.name, key_name)) | |
130 raise | |
131 return key | |
132 | |
133 def copy_key(self, new_key_name, src_bucket_name, src_key_name, | |
134 metadata=None, src_version_id=None, storage_class='STANDARD', | |
135 preserve_acl=False, encrypt_key=False, headers=None, | |
136 query_args=None, src_generation=None): | |
137 """Create a new key in the bucket by copying an existing key. | |
138 | |
139 :type new_key_name: string | |
140 :param new_key_name: The name of the new key | |
141 | |
142 :type src_bucket_name: string | |
143 :param src_bucket_name: The name of the source bucket | |
144 | |
145 :type src_key_name: string | |
146 :param src_key_name: The name of the source key | |
147 | |
148 :type src_generation: int | |
149 :param src_generation: The generation number of the source key to copy. | |
150 If not specified, the latest generation is copied. | |
151 | |
152 :type metadata: dict | |
153 :param metadata: Metadata to be associated with new key. If | |
154 metadata is supplied, it will replace the metadata of the | |
155 source key being copied. If no metadata is supplied, the | |
156 source key's metadata will be copied to the new key. | |
157 | |
158 :type version_id: string | |
159 :param version_id: Unused in this subclass. | |
160 | |
161 :type storage_class: string | |
162 :param storage_class: The storage class of the new key. By | |
163 default, the new key will use the standard storage class. | |
164 Possible values are: STANDARD | DURABLE_REDUCED_AVAILABILITY | |
165 | |
166 :type preserve_acl: bool | |
167 :param preserve_acl: If True, the ACL from the source key will | |
168 be copied to the destination key. If False, the | |
169 destination key will have the default ACL. Note that | |
170 preserving the ACL in the new key object will require two | |
171 additional API calls to GCS, one to retrieve the current | |
172 ACL and one to set that ACL on the new object. If you | |
173 don't care about the ACL (or if you have a default ACL set | |
174 on the bucket), a value of False will be significantly more | |
175 efficient. | |
176 | |
177 :type encrypt_key: bool | |
178 :param encrypt_key: Included for compatibility with S3. This argument is | |
179 ignored. | |
180 | |
181 :type headers: dict | |
182 :param headers: A dictionary of header name/value pairs. | |
183 | |
184 :type query_args: string | |
185 :param query_args: A string of additional querystring arguments | |
186 to append to the request | |
187 | |
188 :rtype: :class:`boto.gs.key.Key` | |
189 :returns: An instance of the newly created key object | |
190 """ | |
191 if src_generation: | |
192 headers = headers or {} | |
193 headers['x-goog-copy-source-generation'] = str(src_generation) | |
194 return super(Bucket, self).copy_key( | |
195 new_key_name, src_bucket_name, src_key_name, metadata=metadata, | |
196 storage_class=storage_class, preserve_acl=preserve_acl, | |
197 encrypt_key=encrypt_key, headers=headers, query_args=query_args) | |
198 | |
199 def list_versions(self, prefix='', delimiter='', marker='', | |
200 generation_marker='', headers=None): | |
201 """ | |
202 List versioned objects within a bucket. This returns an | |
203 instance of an VersionedBucketListResultSet that automatically | |
204 handles all of the result paging, etc. from GCS. You just need | |
205 to keep iterating until there are no more results. Called | |
206 with no arguments, this will return an iterator object across | |
207 all keys within the bucket. | |
208 | |
209 :type prefix: string | |
210 :param prefix: allows you to limit the listing to a particular | |
211 prefix. For example, if you call the method with | |
212 prefix='/foo/' then the iterator will only cycle through | |
213 the keys that begin with the string '/foo/'. | |
214 | |
215 :type delimiter: string | |
216 :param delimiter: can be used in conjunction with the prefix | |
217 to allow you to organize and browse your keys | |
218 hierarchically. See: | |
219 https://developers.google.com/storage/docs/reference-headers#delimiter | |
220 for more details. | |
221 | |
222 :type marker: string | |
223 :param marker: The "marker" of where you are in the result set | |
224 | |
225 :type generation_marker: string | |
226 :param generation_marker: The "generation marker" of where you are in | |
227 the result set. | |
228 | |
229 :type headers: dict | |
230 :param headers: A dictionary of header name/value pairs. | |
231 | |
232 :rtype: | |
233 :class:`boto.gs.bucketlistresultset.VersionedBucketListResultSet` | |
234 :return: an instance of a BucketListResultSet that handles paging, etc. | |
235 """ | |
236 return VersionedBucketListResultSet(self, prefix, delimiter, | |
237 marker, generation_marker, | |
238 headers) | |
239 | |
240 def validate_get_all_versions_params(self, params): | |
241 """ | |
242 See documentation in boto/s3/bucket.py. | |
243 """ | |
244 self.validate_kwarg_names(params, | |
245 ['version_id_marker', 'delimiter', 'marker', | |
246 'generation_marker', 'prefix', 'max_keys']) | |
247 | |
248 def delete_key(self, key_name, headers=None, version_id=None, | |
249 mfa_token=None, generation=None): | |
250 """ | |
251 Deletes a key from the bucket. | |
252 | |
253 :type key_name: string | |
254 :param key_name: The key name to delete | |
255 | |
256 :type headers: dict | |
257 :param headers: A dictionary of header name/value pairs. | |
258 | |
259 :type version_id: string | |
260 :param version_id: Unused in this subclass. | |
261 | |
262 :type mfa_token: tuple or list of strings | |
263 :param mfa_token: Unused in this subclass. | |
264 | |
265 :type generation: int | |
266 :param generation: The generation number of the key to delete. If not | |
267 specified, the latest generation number will be deleted. | |
268 | |
269 :rtype: :class:`boto.gs.key.Key` | |
270 :returns: A key object holding information on what was | |
271 deleted. | |
272 """ | |
273 query_args_l = [] | |
274 if generation: | |
275 query_args_l.append('generation=%s' % generation) | |
276 self._delete_key_internal(key_name, headers=headers, | |
277 version_id=version_id, mfa_token=mfa_token, | |
278 query_args_l=query_args_l) | |
279 | |
280 def set_acl(self, acl_or_str, key_name='', headers=None, version_id=None, | |
281 generation=None, if_generation=None, if_metageneration=None): | |
282 """Sets or changes a bucket's or key's ACL. | |
283 | |
284 :type acl_or_str: string or :class:`boto.gs.acl.ACL` | |
285 :param acl_or_str: A canned ACL string (see | |
286 :data:`~.gs.acl.CannedACLStrings`) or an ACL object. | |
287 | |
288 :type key_name: string | |
289 :param key_name: A key name within the bucket to set the ACL for. If not | |
290 specified, the ACL for the bucket will be set. | |
291 | |
292 :type headers: dict | |
293 :param headers: Additional headers to set during the request. | |
294 | |
295 :type version_id: string | |
296 :param version_id: Unused in this subclass. | |
297 | |
298 :type generation: int | |
299 :param generation: If specified, sets the ACL for a specific generation | |
300 of a versioned object. If not specified, the current version is | |
301 modified. | |
302 | |
303 :type if_generation: int | |
304 :param if_generation: (optional) If set to a generation number, the acl | |
305 will only be updated if its current generation number is this value. | |
306 | |
307 :type if_metageneration: int | |
308 :param if_metageneration: (optional) If set to a metageneration number, | |
309 the acl will only be updated if its current metageneration number is | |
310 this value. | |
311 """ | |
312 if isinstance(acl_or_str, Policy): | |
313 raise InvalidAclError('Attempt to set S3 Policy on GS ACL') | |
314 elif isinstance(acl_or_str, ACL): | |
315 self.set_xml_acl(acl_or_str.to_xml(), key_name, headers=headers, | |
316 generation=generation, | |
317 if_generation=if_generation, | |
318 if_metageneration=if_metageneration) | |
319 else: | |
320 self.set_canned_acl(acl_or_str, key_name, headers=headers, | |
321 generation=generation, | |
322 if_generation=if_generation, | |
323 if_metageneration=if_metageneration) | |
324 | |
325 def set_def_acl(self, acl_or_str, headers=None): | |
326 """Sets or changes a bucket's default ACL. | |
327 | |
328 :type acl_or_str: string or :class:`boto.gs.acl.ACL` | |
329 :param acl_or_str: A canned ACL string (see | |
330 :data:`~.gs.acl.CannedACLStrings`) or an ACL object. | |
331 | |
332 :type headers: dict | |
333 :param headers: Additional headers to set during the request. | |
334 """ | |
335 if isinstance(acl_or_str, Policy): | |
336 raise InvalidAclError('Attempt to set S3 Policy on GS ACL') | |
337 elif isinstance(acl_or_str, ACL): | |
338 self.set_def_xml_acl(acl_or_str.to_xml(), headers=headers) | |
339 else: | |
340 self.set_def_canned_acl(acl_or_str, headers=headers) | |
341 | |
342 def _get_xml_acl_helper(self, key_name, headers, query_args): | |
343 """Provides common functionality for get_xml_acl and _get_acl_helper.""" | |
344 response = self.connection.make_request('GET', self.name, key_name, | |
345 query_args=query_args, | |
346 headers=headers) | |
347 body = response.read() | |
348 if response.status != 200: | |
349 if response.status == 403: | |
350 match = ERROR_DETAILS_REGEX.search(body) | |
351 details = match.group('details') if match else None | |
352 if details: | |
353 details = (('<Details>%s. Note that Full Control access' | |
354 ' is required to access ACLs.</Details>') % | |
355 details) | |
356 body = re.sub(ERROR_DETAILS_REGEX, details, body) | |
357 raise self.connection.provider.storage_response_error( | |
358 response.status, response.reason, body) | |
359 return body | |
360 | |
361 def _get_acl_helper(self, key_name, headers, query_args): | |
362 """Provides common functionality for get_acl and get_def_acl.""" | |
363 body = self._get_xml_acl_helper(key_name, headers, query_args) | |
364 acl = ACL(self) | |
365 h = handler.XmlHandler(acl, self) | |
366 xml.sax.parseString(body, h) | |
367 return acl | |
368 | |
369 def get_acl(self, key_name='', headers=None, version_id=None, | |
370 generation=None): | |
371 """Returns the ACL of the bucket or an object in the bucket. | |
372 | |
373 :param str key_name: The name of the object to get the ACL for. If not | |
374 specified, the ACL for the bucket will be returned. | |
375 | |
376 :param dict headers: Additional headers to set during the request. | |
377 | |
378 :type version_id: string | |
379 :param version_id: Unused in this subclass. | |
380 | |
381 :param int generation: If specified, gets the ACL for a specific | |
382 generation of a versioned object. If not specified, the current | |
383 version is returned. This parameter is only valid when retrieving | |
384 the ACL of an object, not a bucket. | |
385 | |
386 :rtype: :class:`.gs.acl.ACL` | |
387 """ | |
388 query_args = STANDARD_ACL | |
389 if generation: | |
390 query_args += '&generation=%s' % generation | |
391 return self._get_acl_helper(key_name, headers, query_args) | |
392 | |
393 def get_xml_acl(self, key_name='', headers=None, version_id=None, | |
394 generation=None): | |
395 """Returns the ACL string of the bucket or an object in the bucket. | |
396 | |
397 :param str key_name: The name of the object to get the ACL for. If not | |
398 specified, the ACL for the bucket will be returned. | |
399 | |
400 :param dict headers: Additional headers to set during the request. | |
401 | |
402 :type version_id: string | |
403 :param version_id: Unused in this subclass. | |
404 | |
405 :param int generation: If specified, gets the ACL for a specific | |
406 generation of a versioned object. If not specified, the current | |
407 version is returned. This parameter is only valid when retrieving | |
408 the ACL of an object, not a bucket. | |
409 | |
410 :rtype: str | |
411 """ | |
412 query_args = STANDARD_ACL | |
413 if generation: | |
414 query_args += '&generation=%s' % generation | |
415 return self._get_xml_acl_helper(key_name, headers, query_args) | |
416 | |
417 def get_def_acl(self, headers=None): | |
418 """Returns the bucket's default ACL. | |
419 | |
420 :param dict headers: Additional headers to set during the request. | |
421 | |
422 :rtype: :class:`.gs.acl.ACL` | |
423 """ | |
424 return self._get_acl_helper('', headers, DEF_OBJ_ACL) | |
425 | |
426 def _set_acl_helper(self, acl_or_str, key_name, headers, query_args, | |
427 generation, if_generation, if_metageneration, | |
428 canned=False): | |
429 """Provides common functionality for set_acl, set_xml_acl, | |
430 set_canned_acl, set_def_acl, set_def_xml_acl, and | |
431 set_def_canned_acl().""" | |
432 | |
433 headers = headers or {} | |
434 data = '' | |
435 if canned: | |
436 headers[self.connection.provider.acl_header] = acl_or_str | |
437 else: | |
438 data = acl_or_str | |
439 | |
440 if generation: | |
441 query_args += '&generation=%s' % generation | |
442 | |
443 if if_metageneration is not None and if_generation is None: | |
444 raise ValueError("Received if_metageneration argument with no " | |
445 "if_generation argument. A metageneration has no " | |
446 "meaning without a content generation.") | |
447 if not key_name and (if_generation or if_metageneration): | |
448 raise ValueError("Received if_generation or if_metageneration " | |
449 "parameter while setting the ACL of a bucket.") | |
450 if if_generation is not None: | |
451 headers['x-goog-if-generation-match'] = str(if_generation) | |
452 if if_metageneration is not None: | |
453 headers['x-goog-if-metageneration-match'] = str(if_metageneration) | |
454 | |
455 response = self.connection.make_request( | |
456 'PUT', get_utf8_value(self.name), get_utf8_value(key_name), | |
457 data=get_utf8_value(data), headers=headers, query_args=query_args) | |
458 body = response.read() | |
459 if response.status != 200: | |
460 raise self.connection.provider.storage_response_error( | |
461 response.status, response.reason, body) | |
462 | |
463 def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None, | |
464 query_args='acl', generation=None, if_generation=None, | |
465 if_metageneration=None): | |
466 """Sets a bucket's or objects's ACL to an XML string. | |
467 | |
468 :type acl_str: string | |
469 :param acl_str: A string containing the ACL XML. | |
470 | |
471 :type key_name: string | |
472 :param key_name: A key name within the bucket to set the ACL for. If not | |
473 specified, the ACL for the bucket will be set. | |
474 | |
475 :type headers: dict | |
476 :param headers: Additional headers to set during the request. | |
477 | |
478 :type version_id: string | |
479 :param version_id: Unused in this subclass. | |
480 | |
481 :type query_args: str | |
482 :param query_args: The query parameters to pass with the request. | |
483 | |
484 :type generation: int | |
485 :param generation: If specified, sets the ACL for a specific generation | |
486 of a versioned object. If not specified, the current version is | |
487 modified. | |
488 | |
489 :type if_generation: int | |
490 :param if_generation: (optional) If set to a generation number, the acl | |
491 will only be updated if its current generation number is this value. | |
492 | |
493 :type if_metageneration: int | |
494 :param if_metageneration: (optional) If set to a metageneration number, | |
495 the acl will only be updated if its current metageneration number is | |
496 this value. | |
497 """ | |
498 return self._set_acl_helper(acl_str, key_name=key_name, headers=headers, | |
499 query_args=query_args, | |
500 generation=generation, | |
501 if_generation=if_generation, | |
502 if_metageneration=if_metageneration) | |
503 | |
504 def set_canned_acl(self, acl_str, key_name='', headers=None, | |
505 version_id=None, generation=None, if_generation=None, | |
506 if_metageneration=None): | |
507 """Sets a bucket's or objects's ACL using a predefined (canned) value. | |
508 | |
509 :type acl_str: string | |
510 :param acl_str: A canned ACL string. See | |
511 :data:`~.gs.acl.CannedACLStrings`. | |
512 | |
513 :type key_name: string | |
514 :param key_name: A key name within the bucket to set the ACL for. If not | |
515 specified, the ACL for the bucket will be set. | |
516 | |
517 :type headers: dict | |
518 :param headers: Additional headers to set during the request. | |
519 | |
520 :type version_id: string | |
521 :param version_id: Unused in this subclass. | |
522 | |
523 :type generation: int | |
524 :param generation: If specified, sets the ACL for a specific generation | |
525 of a versioned object. If not specified, the current version is | |
526 modified. | |
527 | |
528 :type if_generation: int | |
529 :param if_generation: (optional) If set to a generation number, the acl | |
530 will only be updated if its current generation number is this value. | |
531 | |
532 :type if_metageneration: int | |
533 :param if_metageneration: (optional) If set to a metageneration number, | |
534 the acl will only be updated if its current metageneration number is | |
535 this value. | |
536 """ | |
537 if acl_str not in CannedACLStrings: | |
538 raise ValueError("Provided canned ACL string (%s) is not valid." | |
539 % acl_str) | |
540 query_args = STANDARD_ACL | |
541 return self._set_acl_helper(acl_str, key_name, headers, query_args, | |
542 generation, if_generation, | |
543 if_metageneration, canned=True) | |
544 | |
545 def set_def_canned_acl(self, acl_str, headers=None): | |
546 """Sets a bucket's default ACL using a predefined (canned) value. | |
547 | |
548 :type acl_str: string | |
549 :param acl_str: A canned ACL string. See | |
550 :data:`~.gs.acl.CannedACLStrings`. | |
551 | |
552 :type headers: dict | |
553 :param headers: Additional headers to set during the request. | |
554 """ | |
555 if acl_str not in CannedACLStrings: | |
556 raise ValueError("Provided canned ACL string (%s) is not valid." | |
557 % acl_str) | |
558 query_args = DEF_OBJ_ACL | |
559 return self._set_acl_helper(acl_str, '', headers, query_args, | |
560 generation=None, if_generation=None, | |
561 if_metageneration=None, canned=True) | |
562 | |
563 def set_def_xml_acl(self, acl_str, headers=None): | |
564 """Sets a bucket's default ACL to an XML string. | |
565 | |
566 :type acl_str: string | |
567 :param acl_str: A string containing the ACL XML. | |
568 | |
569 :type headers: dict | |
570 :param headers: Additional headers to set during the request. | |
571 """ | |
572 return self.set_xml_acl(acl_str, '', headers, | |
573 query_args=DEF_OBJ_ACL) | |
574 | |
575 def get_cors(self, headers=None): | |
576 """Returns a bucket's CORS XML document. | |
577 | |
578 :param dict headers: Additional headers to send with the request. | |
579 :rtype: :class:`~.cors.Cors` | |
580 """ | |
581 response = self.connection.make_request('GET', self.name, | |
582 query_args=CORS_ARG, | |
583 headers=headers) | |
584 body = response.read() | |
585 if response.status == 200: | |
586 # Success - parse XML and return Cors object. | |
587 cors = Cors() | |
588 h = handler.XmlHandler(cors, self) | |
589 xml.sax.parseString(body, h) | |
590 return cors | |
591 else: | |
592 raise self.connection.provider.storage_response_error( | |
593 response.status, response.reason, body) | |
594 | |
595 def set_cors(self, cors, headers=None): | |
596 """Sets a bucket's CORS XML document. | |
597 | |
598 :param str cors: A string containing the CORS XML. | |
599 :param dict headers: Additional headers to send with the request. | |
600 """ | |
601 response = self.connection.make_request( | |
602 'PUT', get_utf8_value(self.name), data=get_utf8_value(cors), | |
603 query_args=CORS_ARG, headers=headers) | |
604 body = response.read() | |
605 if response.status != 200: | |
606 raise self.connection.provider.storage_response_error( | |
607 response.status, response.reason, body) | |
608 | |
609 def get_storage_class(self, headers=None): | |
610 """ | |
611 Returns the StorageClass for the bucket. | |
612 | |
613 :rtype: str | |
614 :return: The StorageClass for the bucket. | |
615 """ | |
616 response = self.connection.make_request('GET', self.name, | |
617 query_args=STORAGE_CLASS_ARG, | |
618 headers=headers) | |
619 body = response.read() | |
620 if response.status == 200: | |
621 rs = ResultSet(self) | |
622 h = handler.XmlHandler(rs, self) | |
623 xml.sax.parseString(body, h) | |
624 return rs.StorageClass | |
625 else: | |
626 raise self.connection.provider.storage_response_error( | |
627 response.status, response.reason, body) | |
628 | |
629 def set_storage_class(self, storage_class, headers=None): | |
630 """ | |
631 Sets a bucket's storage class. | |
632 | |
633 :param str storage_class: A string containing the storage class. | |
634 :param dict headers: Additional headers to send with the request. | |
635 """ | |
636 req_body = self.StorageClassBody % (get_utf8_value(storage_class)) | |
637 self.set_subresource(STORAGE_CLASS_ARG, req_body, headers=headers) | |
638 | |
639 # Method with same signature as boto.s3.bucket.Bucket.add_email_grant(), | |
640 # to allow polymorphic treatment at application layer. | |
641 def add_email_grant(self, permission, email_address, | |
642 recursive=False, headers=None): | |
643 """ | |
644 Convenience method that provides a quick way to add an email grant | |
645 to a bucket. This method retrieves the current ACL, creates a new | |
646 grant based on the parameters passed in, adds that grant to the ACL | |
647 and then PUT's the new ACL back to GCS. | |
648 | |
649 :type permission: string | |
650 :param permission: The permission being granted. Should be one of: | |
651 (READ, WRITE, FULL_CONTROL). | |
652 | |
653 :type email_address: string | |
654 :param email_address: The email address associated with the GS | |
655 account your are granting the permission to. | |
656 | |
657 :type recursive: bool | |
658 :param recursive: A boolean value to controls whether the call | |
659 will apply the grant to all keys within the bucket | |
660 or not. The default value is False. By passing a | |
661 True value, the call will iterate through all keys | |
662 in the bucket and apply the same grant to each key. | |
663 CAUTION: If you have a lot of keys, this could take | |
664 a long time! | |
665 """ | |
666 if permission not in GSPermissions: | |
667 raise self.connection.provider.storage_permissions_error( | |
668 'Unknown Permission: %s' % permission) | |
669 acl = self.get_acl(headers=headers) | |
670 acl.add_email_grant(permission, email_address) | |
671 self.set_acl(acl, headers=headers) | |
672 if recursive: | |
673 for key in self: | |
674 key.add_email_grant(permission, email_address, headers=headers) | |
675 | |
676 # Method with same signature as boto.s3.bucket.Bucket.add_user_grant(), | |
677 # to allow polymorphic treatment at application layer. | |
678 def add_user_grant(self, permission, user_id, recursive=False, | |
679 headers=None): | |
680 """ | |
681 Convenience method that provides a quick way to add a canonical user | |
682 grant to a bucket. This method retrieves the current ACL, creates a new | |
683 grant based on the parameters passed in, adds that grant to the ACL and | |
684 then PUTs the new ACL back to GCS. | |
685 | |
686 :type permission: string | |
687 :param permission: The permission being granted. Should be one of: | |
688 (READ|WRITE|FULL_CONTROL) | |
689 | |
690 :type user_id: string | |
691 :param user_id: The canonical user id associated with the GS account | |
692 you are granting the permission to. | |
693 | |
694 :type recursive: bool | |
695 :param recursive: A boolean value to controls whether the call | |
696 will apply the grant to all keys within the bucket | |
697 or not. The default value is False. By passing a | |
698 True value, the call will iterate through all keys | |
699 in the bucket and apply the same grant to each key. | |
700 CAUTION: If you have a lot of keys, this could take | |
701 a long time! | |
702 """ | |
703 if permission not in GSPermissions: | |
704 raise self.connection.provider.storage_permissions_error( | |
705 'Unknown Permission: %s' % permission) | |
706 acl = self.get_acl(headers=headers) | |
707 acl.add_user_grant(permission, user_id) | |
708 self.set_acl(acl, headers=headers) | |
709 if recursive: | |
710 for key in self: | |
711 key.add_user_grant(permission, user_id, headers=headers) | |
712 | |
713 def add_group_email_grant(self, permission, email_address, recursive=False, | |
714 headers=None): | |
715 """ | |
716 Convenience method that provides a quick way to add an email group | |
717 grant to a bucket. This method retrieves the current ACL, creates a new | |
718 grant based on the parameters passed in, adds that grant to the ACL and | |
719 then PUT's the new ACL back to GCS. | |
720 | |
721 :type permission: string | |
722 :param permission: The permission being granted. Should be one of: | |
723 READ|WRITE|FULL_CONTROL | |
724 See http://code.google.com/apis/storage/docs/developer-guide.html#authorization | |
725 for more details on permissions. | |
726 | |
727 :type email_address: string | |
728 :param email_address: The email address associated with the Google | |
729 Group to which you are granting the permission. | |
730 | |
731 :type recursive: bool | |
732 :param recursive: A boolean value to controls whether the call | |
733 will apply the grant to all keys within the bucket | |
734 or not. The default value is False. By passing a | |
735 True value, the call will iterate through all keys | |
736 in the bucket and apply the same grant to each key. | |
737 CAUTION: If you have a lot of keys, this could take | |
738 a long time! | |
739 """ | |
740 if permission not in GSPermissions: | |
741 raise self.connection.provider.storage_permissions_error( | |
742 'Unknown Permission: %s' % permission) | |
743 acl = self.get_acl(headers=headers) | |
744 acl.add_group_email_grant(permission, email_address) | |
745 self.set_acl(acl, headers=headers) | |
746 if recursive: | |
747 for key in self: | |
748 key.add_group_email_grant(permission, email_address, | |
749 headers=headers) | |
750 | |
751 # Method with same input signature as boto.s3.bucket.Bucket.list_grants() | |
752 # (but returning different object type), to allow polymorphic treatment | |
753 # at application layer. | |
754 def list_grants(self, headers=None): | |
755 """Returns the ACL entries applied to this bucket. | |
756 | |
757 :param dict headers: Additional headers to send with the request. | |
758 :rtype: list containing :class:`~.gs.acl.Entry` objects. | |
759 """ | |
760 acl = self.get_acl(headers=headers) | |
761 return acl.entries | |
762 | |
763 def disable_logging(self, headers=None): | |
764 """Disable logging on this bucket. | |
765 | |
766 :param dict headers: Additional headers to send with the request. | |
767 """ | |
768 xml_str = '<?xml version="1.0" encoding="UTF-8"?><Logging/>' | |
769 self.set_subresource('logging', xml_str, headers=headers) | |
770 | |
771 def enable_logging(self, target_bucket, target_prefix=None, headers=None): | |
772 """Enable logging on a bucket. | |
773 | |
774 :type target_bucket: bucket or string | |
775 :param target_bucket: The bucket to log to. | |
776 | |
777 :type target_prefix: string | |
778 :param target_prefix: The prefix which should be prepended to the | |
779 generated log files written to the target_bucket. | |
780 | |
781 :param dict headers: Additional headers to send with the request. | |
782 """ | |
783 if isinstance(target_bucket, Bucket): | |
784 target_bucket = target_bucket.name | |
785 xml_str = '<?xml version="1.0" encoding="UTF-8"?><Logging>' | |
786 xml_str = (xml_str + '<LogBucket>%s</LogBucket>' % target_bucket) | |
787 if target_prefix: | |
788 xml_str = (xml_str + | |
789 '<LogObjectPrefix>%s</LogObjectPrefix>' % target_prefix) | |
790 xml_str = xml_str + '</Logging>' | |
791 | |
792 self.set_subresource('logging', xml_str, headers=headers) | |
793 | |
794 def get_logging_config_with_xml(self, headers=None): | |
795 """Returns the current status of logging configuration on the bucket as | |
796 unparsed XML. | |
797 | |
798 :param dict headers: Additional headers to send with the request. | |
799 | |
800 :rtype: 2-Tuple | |
801 :returns: 2-tuple containing: | |
802 | |
803 1) A dictionary containing the parsed XML response from GCS. The | |
804 overall structure is: | |
805 | |
806 * Logging | |
807 | |
808 * LogObjectPrefix: Prefix that is prepended to log objects. | |
809 * LogBucket: Target bucket for log objects. | |
810 | |
811 2) Unparsed XML describing the bucket's logging configuration. | |
812 """ | |
813 response = self.connection.make_request('GET', self.name, | |
814 query_args='logging', | |
815 headers=headers) | |
816 body = response.read() | |
817 boto.log.debug(body) | |
818 | |
819 if response.status != 200: | |
820 raise self.connection.provider.storage_response_error( | |
821 response.status, response.reason, body) | |
822 | |
823 e = boto.jsonresponse.Element() | |
824 h = boto.jsonresponse.XmlHandler(e, None) | |
825 h.parse(body) | |
826 return e, body | |
827 | |
828 def get_logging_config(self, headers=None): | |
829 """Returns the current status of logging configuration on the bucket. | |
830 | |
831 :param dict headers: Additional headers to send with the request. | |
832 | |
833 :rtype: dict | |
834 :returns: A dictionary containing the parsed XML response from GCS. The | |
835 overall structure is: | |
836 | |
837 * Logging | |
838 | |
839 * LogObjectPrefix: Prefix that is prepended to log objects. | |
840 * LogBucket: Target bucket for log objects. | |
841 """ | |
842 return self.get_logging_config_with_xml(headers)[0] | |
843 | |
844 def configure_website(self, main_page_suffix=None, error_key=None, | |
845 headers=None): | |
846 """Configure this bucket to act as a website | |
847 | |
848 :type main_page_suffix: str | |
849 :param main_page_suffix: Suffix that is appended to a request that is | |
850 for a "directory" on the website endpoint (e.g. if the suffix is | |
851 index.html and you make a request to samplebucket/images/ the data | |
852 that is returned will be for the object with the key name | |
853 images/index.html). The suffix must not be empty and must not | |
854 include a slash character. This parameter is optional and the | |
855 property is disabled if excluded. | |
856 | |
857 :type error_key: str | |
858 :param error_key: The object key name to use when a 400 error occurs. | |
859 This parameter is optional and the property is disabled if excluded. | |
860 | |
861 :param dict headers: Additional headers to send with the request. | |
862 """ | |
863 if main_page_suffix: | |
864 main_page_frag = self.WebsiteMainPageFragment % main_page_suffix | |
865 else: | |
866 main_page_frag = '' | |
867 | |
868 if error_key: | |
869 error_frag = self.WebsiteErrorFragment % error_key | |
870 else: | |
871 error_frag = '' | |
872 | |
873 body = self.WebsiteBody % (main_page_frag, error_frag) | |
874 response = self.connection.make_request( | |
875 'PUT', get_utf8_value(self.name), data=get_utf8_value(body), | |
876 query_args='websiteConfig', headers=headers) | |
877 body = response.read() | |
878 if response.status == 200: | |
879 return True | |
880 else: | |
881 raise self.connection.provider.storage_response_error( | |
882 response.status, response.reason, body) | |
883 | |
884 def get_website_configuration(self, headers=None): | |
885 """Returns the current status of website configuration on the bucket. | |
886 | |
887 :param dict headers: Additional headers to send with the request. | |
888 | |
889 :rtype: dict | |
890 :returns: A dictionary containing the parsed XML response from GCS. The | |
891 overall structure is: | |
892 | |
893 * WebsiteConfiguration | |
894 | |
895 * MainPageSuffix: suffix that is appended to request that | |
896 is for a "directory" on the website endpoint. | |
897 * NotFoundPage: name of an object to serve when site visitors | |
898 encounter a 404. | |
899 """ | |
900 return self.get_website_configuration_with_xml(headers)[0] | |
901 | |
902 def get_website_configuration_with_xml(self, headers=None): | |
903 """Returns the current status of website configuration on the bucket as | |
904 unparsed XML. | |
905 | |
906 :param dict headers: Additional headers to send with the request. | |
907 | |
908 :rtype: 2-Tuple | |
909 :returns: 2-tuple containing: | |
910 | |
911 1) A dictionary containing the parsed XML response from GCS. The | |
912 overall structure is: | |
913 | |
914 * WebsiteConfiguration | |
915 | |
916 * MainPageSuffix: suffix that is appended to request that is for | |
917 a "directory" on the website endpoint. | |
918 * NotFoundPage: name of an object to serve when site visitors | |
919 encounter a 404 | |
920 | |
921 2) Unparsed XML describing the bucket's website configuration. | |
922 """ | |
923 response = self.connection.make_request('GET', self.name, | |
924 query_args='websiteConfig', headers=headers) | |
925 body = response.read() | |
926 boto.log.debug(body) | |
927 | |
928 if response.status != 200: | |
929 raise self.connection.provider.storage_response_error( | |
930 response.status, response.reason, body) | |
931 | |
932 e = boto.jsonresponse.Element() | |
933 h = boto.jsonresponse.XmlHandler(e, None) | |
934 h.parse(body) | |
935 return e, body | |
936 | |
937 def delete_website_configuration(self, headers=None): | |
938 """Remove the website configuration from this bucket. | |
939 | |
940 :param dict headers: Additional headers to send with the request. | |
941 """ | |
942 self.configure_website(headers=headers) | |
943 | |
944 def get_versioning_status(self, headers=None): | |
945 """Returns the current status of versioning configuration on the bucket. | |
946 | |
947 :rtype: bool | |
948 """ | |
949 response = self.connection.make_request('GET', self.name, | |
950 query_args='versioning', | |
951 headers=headers) | |
952 body = response.read() | |
953 boto.log.debug(body) | |
954 if response.status != 200: | |
955 raise self.connection.provider.storage_response_error( | |
956 response.status, response.reason, body) | |
957 resp_json = boto.jsonresponse.Element() | |
958 boto.jsonresponse.XmlHandler(resp_json, None).parse(body) | |
959 resp_json = resp_json['VersioningConfiguration'] | |
960 return ('Status' in resp_json) and (resp_json['Status'] == 'Enabled') | |
961 | |
962 def configure_versioning(self, enabled, headers=None): | |
963 """Configure versioning for this bucket. | |
964 | |
965 :param bool enabled: If set to True, enables versioning on this bucket. | |
966 If set to False, disables versioning. | |
967 | |
968 :param dict headers: Additional headers to send with the request. | |
969 """ | |
970 if enabled == True: | |
971 req_body = self.VersioningBody % ('Enabled') | |
972 else: | |
973 req_body = self.VersioningBody % ('Suspended') | |
974 self.set_subresource('versioning', req_body, headers=headers) | |
975 | |
976 def get_lifecycle_config(self, headers=None): | |
977 """ | |
978 Returns the current lifecycle configuration on the bucket. | |
979 | |
980 :rtype: :class:`boto.gs.lifecycle.LifecycleConfig` | |
981 :returns: A LifecycleConfig object that describes all current | |
982 lifecycle rules in effect for the bucket. | |
983 """ | |
984 response = self.connection.make_request('GET', self.name, | |
985 query_args=LIFECYCLE_ARG, headers=headers) | |
986 body = response.read() | |
987 boto.log.debug(body) | |
988 if response.status == 200: | |
989 lifecycle_config = LifecycleConfig() | |
990 h = handler.XmlHandler(lifecycle_config, self) | |
991 xml.sax.parseString(body, h) | |
992 return lifecycle_config | |
993 else: | |
994 raise self.connection.provider.storage_response_error( | |
995 response.status, response.reason, body) | |
996 | |
997 def configure_lifecycle(self, lifecycle_config, headers=None): | |
998 """ | |
999 Configure lifecycle for this bucket. | |
1000 | |
1001 :type lifecycle_config: :class:`boto.gs.lifecycle.LifecycleConfig` | |
1002 :param lifecycle_config: The lifecycle configuration you want | |
1003 to configure for this bucket. | |
1004 """ | |
1005 xml = lifecycle_config.to_xml() | |
1006 response = self.connection.make_request( | |
1007 'PUT', get_utf8_value(self.name), data=get_utf8_value(xml), | |
1008 query_args=LIFECYCLE_ARG, headers=headers) | |
1009 body = response.read() | |
1010 if response.status == 200: | |
1011 return True | |
1012 else: | |
1013 raise self.connection.provider.storage_response_error( | |
1014 response.status, response.reason, body) | |
1015 | |
1016 def get_billing_config(self, headers=None): | |
1017 """Returns the current status of billing configuration on the bucket. | |
1018 | |
1019 :param dict headers: Additional headers to send with the request. | |
1020 | |
1021 :rtype: dict | |
1022 :returns: A dictionary containing the parsed XML response from GCS. The | |
1023 overall structure is: | |
1024 | |
1025 * BillingConfiguration | |
1026 | |
1027 * RequesterPays: Enabled/Disabled. | |
1028 """ | |
1029 return self.get_billing_configuration_with_xml(headers)[0] | |
1030 | |
1031 def get_billing_configuration_with_xml(self, headers=None): | |
1032 """Returns the current status of billing configuration on the bucket as | |
1033 unparsed XML. | |
1034 | |
1035 :param dict headers: Additional headers to send with the request. | |
1036 | |
1037 :rtype: 2-Tuple | |
1038 :returns: 2-tuple containing: | |
1039 | |
1040 1) A dictionary containing the parsed XML response from GCS. The | |
1041 overall structure is: | |
1042 | |
1043 * BillingConfiguration | |
1044 | |
1045 * RequesterPays: Enabled/Disabled. | |
1046 | |
1047 2) Unparsed XML describing the bucket's website configuration. | |
1048 """ | |
1049 response = self.connection.make_request('GET', self.name, | |
1050 query_args='billing', | |
1051 headers=headers) | |
1052 body = response.read() | |
1053 boto.log.debug(body) | |
1054 | |
1055 if response.status != 200: | |
1056 raise self.connection.provider.storage_response_error( | |
1057 response.status, response.reason, body) | |
1058 | |
1059 e = boto.jsonresponse.Element() | |
1060 h = boto.jsonresponse.XmlHandler(e, None); | |
1061 h.parse(body) | |
1062 return e, body | |
1063 | |
1064 def configure_billing(self, requester_pays=False, headers=None): | |
1065 """Configure billing for this bucket. | |
1066 | |
1067 :param bool requester_pays: If set to True, enables requester pays on | |
1068 this bucket. If set to False, disables requester pays. | |
1069 | |
1070 :param dict headers: Additional headers to send with the request. | |
1071 """ | |
1072 if requester_pays == True: | |
1073 req_body = self.BillingBody % ('Enabled') | |
1074 else: | |
1075 req_body = self.BillingBody % ('Disabled') | |
1076 self.set_subresource('billing', req_body, headers=headers) | |
1077 | |
1078 def get_encryption_config(self, headers=None): | |
1079 """Returns a bucket's EncryptionConfig. | |
1080 | |
1081 :param dict headers: Additional headers to send with the request. | |
1082 :rtype: :class:`~.encryption_config.EncryptionConfig` | |
1083 """ | |
1084 response = self.connection.make_request( | |
1085 'GET', self.name, query_args=ENCRYPTION_CONFIG_ARG, headers=headers) | |
1086 body = response.read() | |
1087 if response.status == 200: | |
1088 # Success - parse XML and return EncryptionConfig object. | |
1089 encryption_config = EncryptionConfig() | |
1090 h = handler.XmlHandler(encryption_config, self) | |
1091 xml.sax.parseString(body, h) | |
1092 return encryption_config | |
1093 else: | |
1094 raise self.connection.provider.storage_response_error( | |
1095 response.status, response.reason, body) | |
1096 | |
1097 def _construct_encryption_config_xml(self, default_kms_key_name=None): | |
1098 """Creates an XML document for setting a bucket's EncryptionConfig. | |
1099 | |
1100 This method is internal as it's only here for testing purposes. As | |
1101 managing Cloud KMS resources for testing is complex, we settle for | |
1102 testing that we're creating correctly-formed XML for setting a bucket's | |
1103 encryption configuration. | |
1104 | |
1105 :param str default_kms_key_name: A string containing a fully-qualified | |
1106 Cloud KMS key name. | |
1107 :rtype: str | |
1108 """ | |
1109 if default_kms_key_name: | |
1110 default_kms_key_name_frag = ( | |
1111 self.EncryptionConfigDefaultKeyNameFragment % | |
1112 default_kms_key_name) | |
1113 else: | |
1114 default_kms_key_name_frag = '' | |
1115 | |
1116 return self.EncryptionConfigBody % default_kms_key_name_frag | |
1117 | |
1118 | |
1119 def set_encryption_config(self, default_kms_key_name=None, headers=None): | |
1120 """Sets a bucket's EncryptionConfig XML document. | |
1121 | |
1122 :param str default_kms_key_name: A string containing a fully-qualified | |
1123 Cloud KMS key name. | |
1124 :param dict headers: Additional headers to send with the request. | |
1125 """ | |
1126 body = self._construct_encryption_config_xml( | |
1127 default_kms_key_name=default_kms_key_name) | |
1128 response = self.connection.make_request( | |
1129 'PUT', get_utf8_value(self.name), data=get_utf8_value(body), | |
1130 query_args=ENCRYPTION_CONFIG_ARG, headers=headers) | |
1131 body = response.read() | |
1132 if response.status != 200: | |
1133 raise self.connection.provider.storage_response_error( | |
1134 response.status, response.reason, body) |