Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/boto/gs/bucket.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:18:57 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d30785e31577 |
|---|---|
| 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) |
