Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(436)

Side by Side Diff: third_party/gsutil/boto/boto/s3/bucket.py

Issue 10199002: Upgrade gsutil to 3.4 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed comments Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ 1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
2 # Copyright (c) 2010, Eucalyptus Systems, Inc. 2 # Copyright (c) 2010, Eucalyptus Systems, Inc.
3 # All rights reserved. 3 # All rights reserved.
4 # 4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a 5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the 6 # copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including 7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish, dis- 8 # without limitation the rights to use, copy, modify, merge, publish, dis-
9 # tribute, sublicense, and/or sell copies of the Software, and to permit 9 # tribute, sublicense, and/or sell copies of the Software, and to permit
10 # persons to whom the Software is furnished to do so, subject to the fol- 10 # persons to whom the Software is furnished to do so, subject to the fol-
11 # lowing conditions: 11 # lowing conditions:
12 # 12 #
13 # The above copyright notice and this permission notice shall be included 13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software. 14 # in all copies or substantial portions of the Software.
15 # 15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 # IN THE SOFTWARE. 22 # IN THE SOFTWARE.
23 23
24 import boto 24 import boto
25 from boto import handler 25 from boto import handler
26 from boto.provider import Provider
27 from boto.resultset import ResultSet 26 from boto.resultset import ResultSet
28 from boto.s3.acl import ACL, Policy, CannedACLStrings, Grant 27 from boto.exception import BotoClientError
28 from boto.s3.acl import Policy, CannedACLStrings, Grant
29 from boto.s3.key import Key 29 from boto.s3.key import Key
30 from boto.s3.prefix import Prefix 30 from boto.s3.prefix import Prefix
31 from boto.s3.deletemarker import DeleteMarker 31 from boto.s3.deletemarker import DeleteMarker
32 from boto.s3.user import User
33 from boto.s3.multipart import MultiPartUpload 32 from boto.s3.multipart import MultiPartUpload
34 from boto.s3.multipart import CompleteMultiPartUpload 33 from boto.s3.multipart import CompleteMultiPartUpload
34 from boto.s3.multidelete import MultiDeleteResult
35 from boto.s3.multidelete import Error
35 from boto.s3.bucketlistresultset import BucketListResultSet 36 from boto.s3.bucketlistresultset import BucketListResultSet
36 from boto.s3.bucketlistresultset import VersionedBucketListResultSet 37 from boto.s3.bucketlistresultset import VersionedBucketListResultSet
37 from boto.s3.bucketlistresultset import MultiPartUploadListResultSet 38 from boto.s3.bucketlistresultset import MultiPartUploadListResultSet
39 from boto.s3.lifecycle import Lifecycle
40 from boto.s3.bucketlogging import BucketLogging
38 import boto.jsonresponse 41 import boto.jsonresponse
39 import boto.utils 42 import boto.utils
40 import xml.sax 43 import xml.sax
44 import xml.sax.saxutils
45 import StringIO
41 import urllib 46 import urllib
42 import re 47 import re
48 import base64
43 from collections import defaultdict 49 from collections import defaultdict
44 50
45 # as per http://goo.gl/BDuud (02/19/2011) 51 # as per http://goo.gl/BDuud (02/19/2011)
46 class S3WebsiteEndpointTranslate: 52 class S3WebsiteEndpointTranslate:
47 trans_region = defaultdict(lambda :'s3-website-us-east-1') 53 trans_region = defaultdict(lambda :'s3-website-us-east-1')
48 54
49 trans_region['EU'] = 's3-website-eu-west-1' 55 trans_region['eu-west-1'] = 's3-website-eu-west-1'
50 trans_region['us-west-1'] = 's3-website-us-west-1' 56 trans_region['us-west-1'] = 's3-website-us-west-1'
57 trans_region['us-west-2'] = 's3-website-us-west-2'
58 trans_region['sa-east-1'] = 's3-website-sa-east-1'
51 trans_region['ap-northeast-1'] = 's3-website-ap-northeast-1' 59 trans_region['ap-northeast-1'] = 's3-website-ap-northeast-1'
52 trans_region['ap-southeast-1'] = 's3-website-ap-southeast-1' 60 trans_region['ap-southeast-1'] = 's3-website-ap-southeast-1'
53 61
54 @classmethod 62 @classmethod
55 def translate_region(self, reg): 63 def translate_region(self, reg):
56 return self.trans_region[reg] 64 return self.trans_region[reg]
57 65
58 S3Permissions = ['READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'] 66 S3Permissions = ['READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL']
59 67
60 class Bucket(object): 68 class Bucket(object):
61 69
62 BucketLoggingBody = """<?xml version="1.0" encoding="UTF-8"?>
63 <BucketLoggingStatus xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
64 <LoggingEnabled>
65 <TargetBucket>%s</TargetBucket>
66 <TargetPrefix>%s</TargetPrefix>
67 </LoggingEnabled>
68 </BucketLoggingStatus>"""
69
70 EmptyBucketLoggingBody = """<?xml version="1.0" encoding="UTF-8"?>
71 <BucketLoggingStatus xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
72 </BucketLoggingStatus>"""
73
74 LoggingGroup = 'http://acs.amazonaws.com/groups/s3/LogDelivery' 70 LoggingGroup = 'http://acs.amazonaws.com/groups/s3/LogDelivery'
75 71
76 BucketPaymentBody = """<?xml version="1.0" encoding="UTF-8"?> 72 BucketPaymentBody = """<?xml version="1.0" encoding="UTF-8"?>
77 <RequestPaymentConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-0 1/"> 73 <RequestPaymentConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-0 1/">
78 <Payer>%s</Payer> 74 <Payer>%s</Payer>
79 </RequestPaymentConfiguration>""" 75 </RequestPaymentConfiguration>"""
80 76
81 VersioningBody = """<?xml version="1.0" encoding="UTF-8"?> 77 VersioningBody = """<?xml version="1.0" encoding="UTF-8"?>
82 <VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> 78 <VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
83 <Status>%s</Status> 79 <Status>%s</Status>
(...skipping 16 matching lines...) Expand all
100 self.connection = connection 96 self.connection = connection
101 self.key_class = key_class 97 self.key_class = key_class
102 98
103 def __repr__(self): 99 def __repr__(self):
104 return '<Bucket: %s>' % self.name 100 return '<Bucket: %s>' % self.name
105 101
106 def __iter__(self): 102 def __iter__(self):
107 return iter(BucketListResultSet(self)) 103 return iter(BucketListResultSet(self))
108 104
109 def __contains__(self, key_name): 105 def __contains__(self, key_name):
110 return not (self.get_key(key_name) is None) 106 return not (self.get_key(key_name) is None)
111 107
112 def startElement(self, name, attrs, connection): 108 def startElement(self, name, attrs, connection):
113 return None 109 return None
114 110
115 def endElement(self, name, value, connection): 111 def endElement(self, name, value, connection):
116 if name == 'Name': 112 if name == 'Name':
117 self.name = value 113 self.name = value
118 elif name == 'CreationDate': 114 elif name == 'CreationDate':
119 self.creation_date = value 115 self.creation_date = value
120 else: 116 else:
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 # requests when the content-length is zero. 177 # requests when the content-length is zero.
182 # See http://goo.gl/0Tdax for more details. 178 # See http://goo.gl/0Tdax for more details.
183 clen = response.getheader('content-length') 179 clen = response.getheader('content-length')
184 if clen: 180 if clen:
185 k.size = int(response.getheader('content-length')) 181 k.size = int(response.getheader('content-length'))
186 else: 182 else:
187 k.size = 0 183 k.size = 0
188 k.cache_control = response.getheader('cache-control') 184 k.cache_control = response.getheader('cache-control')
189 k.name = key_name 185 k.name = key_name
190 k.handle_version_headers(response) 186 k.handle_version_headers(response)
187 k.handle_encryption_headers(response)
191 return k 188 return k
192 else: 189 else:
193 if response.status == 404: 190 if response.status == 404:
194 response.read() 191 response.read()
195 return None 192 return None
196 else: 193 else:
197 raise self.connection.provider.storage_response_error( 194 raise self.connection.provider.storage_response_error(
198 response.status, response.reason, '') 195 response.status, response.reason, '')
199 196
200 def list(self, prefix='', delimiter='', marker='', headers=None): 197 def list(self, prefix='', delimiter='', marker='', headers=None):
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 :rtype: :class:`boto.s3.bucketlistresultset.BucketListResultSet` 280 :rtype: :class:`boto.s3.bucketlistresultset.BucketListResultSet`
284 :return: an instance of a BucketListResultSet that handles paging, etc 281 :return: an instance of a BucketListResultSet that handles paging, etc
285 """ 282 """
286 return MultiPartUploadListResultSet(self, key_marker, 283 return MultiPartUploadListResultSet(self, key_marker,
287 upload_id_marker, 284 upload_id_marker,
288 headers) 285 headers)
289 286
290 def _get_all(self, element_map, initial_query_string='', 287 def _get_all(self, element_map, initial_query_string='',
291 headers=None, **params): 288 headers=None, **params):
292 l = [] 289 l = []
293 for k,v in params.items(): 290 for k, v in params.items():
294 k = k.replace('_', '-') 291 k = k.replace('_', '-')
295 if k == 'maxkeys': 292 if k == 'maxkeys':
296 k = 'max-keys' 293 k = 'max-keys'
297 if isinstance(v, unicode): 294 if isinstance(v, unicode):
298 v = v.encode('utf-8') 295 v = v.encode('utf-8')
299 if v is not None and v != '': 296 if v is not None and v != '':
300 l.append('%s=%s' % (urllib.quote(k), urllib.quote(str(v)))) 297 l.append('%s=%s' % (urllib.quote(k), urllib.quote(str(v))))
301 if len(l): 298 if len(l):
302 s = initial_query_string + '&' + '&'.join(l) 299 s = initial_query_string + '&' + '&'.join(l)
303 else: 300 else:
304 s = initial_query_string 301 s = initial_query_string
305 response = self.connection.make_request('GET', self.name, 302 response = self.connection.make_request('GET', self.name,
306 headers=headers, query_args=s) 303 headers=headers,
304 query_args=s)
307 body = response.read() 305 body = response.read()
308 boto.log.debug(body) 306 boto.log.debug(body)
309 if response.status == 200: 307 if response.status == 200:
310 rs = ResultSet(element_map) 308 rs = ResultSet(element_map)
311 h = handler.XmlHandler(rs, self) 309 h = handler.XmlHandler(rs, self)
312 xml.sax.parseString(body, h) 310 xml.sax.parseString(body, h)
313 return rs 311 return rs
314 else: 312 else:
315 raise self.connection.provider.storage_response_error( 313 raise self.connection.provider.storage_response_error(
316 response.status, response.reason, body) 314 response.status, response.reason, body)
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 equal to the key_marker might be included 419 equal to the key_marker might be included
422 in the list only if they have an upload ID 420 in the list only if they have an upload ID
423 lexicographically greater than the specified 421 lexicographically greater than the specified
424 upload_id_marker. 422 upload_id_marker.
425 423
426 424
427 :rtype: ResultSet 425 :rtype: ResultSet
428 :return: The result from S3 listing the uploads requested 426 :return: The result from S3 listing the uploads requested
429 427
430 """ 428 """
431 return self._get_all([('Upload', MultiPartUpload)], 429 return self._get_all([('Upload', MultiPartUpload),
430 ('CommonPrefixes', Prefix)],
432 'uploads', headers, **params) 431 'uploads', headers, **params)
433 432
434 def new_key(self, key_name=None): 433 def new_key(self, key_name=None):
435 """ 434 """
436 Creates a new key 435 Creates a new key
437 436
438 :type key_name: string 437 :type key_name: string
439 :param key_name: The name of the key to create 438 :param key_name: The name of the key to create
440 439
441 :rtype: :class:`boto.s3.key.Key` or subclass 440 :rtype: :class:`boto.s3.key.Key` or subclass
442 :returns: An instance of the newly created key object 441 :returns: An instance of the newly created key object
443 """ 442 """
444 return self.key_class(self, key_name) 443 return self.key_class(self, key_name)
445 444
446 def generate_url(self, expires_in, method='GET', headers=None, 445 def generate_url(self, expires_in, method='GET', headers=None,
447 force_http=False, response_headers=None): 446 force_http=False, response_headers=None,
447 expires_in_absolute=False):
448 return self.connection.generate_url(expires_in, method, self.name, 448 return self.connection.generate_url(expires_in, method, self.name,
449 headers=headers, 449 headers=headers,
450 force_http=force_http, 450 force_http=force_http,
451 response_headers=response_headers) 451 response_headers=response_headers,
452 expires_in_absolute=expires_in_absol ute)
453
454 def delete_keys(self, keys, quiet=False, mfa_token=None, headers=None):
455 """
456 Deletes a set of keys using S3's Multi-object delete API. If a
457 VersionID is specified for that key then that version is removed.
458 Returns a MultiDeleteResult Object, which contains Deleted
459 and Error elements for each key you ask to delete.
460
461 :type keys: list
462 :param keys: A list of either key_names or (key_name, versionid) pairs
463 or a list of Key instances.
464
465 :type quiet: boolean
466 :param quiet: In quiet mode the response includes only keys where
467 the delete operation encountered an error. For a
468 successful deletion, the operation does not return
469 any information about the delete in the response body.
470
471 :type mfa_token: tuple or list of strings
472 :param mfa_token: A tuple or list consisting of the serial number
473 from the MFA device and the current value of
474 the six-digit token associated with the device.
475 This value is required anytime you are
476 deleting versioned objects from a bucket
477 that has the MFADelete option on the bucket.
478
479 :returns: An instance of MultiDeleteResult
480 """
481 ikeys = iter(keys)
482 result = MultiDeleteResult(self)
483 provider = self.connection.provider
484 query_args = 'delete'
485 def delete_keys2(hdrs):
486 hdrs = hdrs or {}
487 data = u"""<?xml version="1.0" encoding="UTF-8"?>"""
488 data += u"<Delete>"
489 if quiet:
490 data += u"<Quiet>true</Quiet>"
491 count = 0
492 while count < 1000:
493 try:
494 key = ikeys.next()
495 except StopIteration:
496 break
497 if isinstance(key, basestring):
498 key_name = key
499 version_id = None
500 elif isinstance(key, tuple) and len(key) == 2:
501 key_name, version_id = key
502 elif (isinstance(key, Key) or isinstance(key, DeleteMarker)) and key.name:
503 key_name = key.name
504 version_id = key.version_id
505 else:
506 if isinstance(key, Prefix):
507 key_name = key.name
508 code = 'PrefixSkipped' # Don't delete Prefix
509 else:
510 key_name = repr(key) # try get a string
511 code = 'InvalidArgument' # other unknown type
512 message = 'Invalid. No delete action taken for this object.'
513 error = Error(key_name, code=code, message=message)
514 result.errors.append(error)
515 continue
516 count += 1
517 #key_name = key_name.decode('utf-8')
518 data += u"<Object><Key>%s</Key>" % xml.sax.saxutils.escape(key_n ame)
519 if version_id:
520 data += u"<VersionId>%s</VersionId>" % version_id
521 data += u"</Object>"
522 data += u"</Delete>"
523 if count <= 0:
524 return False # no more
525 data = data.encode('utf-8')
526 fp = StringIO.StringIO(data)
527 md5 = boto.utils.compute_md5(fp)
528 hdrs['Content-MD5'] = md5[1]
529 hdrs['Content-Type'] = 'text/xml'
530 if mfa_token:
531 hdrs[provider.mfa_header] = ' '.join(mfa_token)
532 response = self.connection.make_request('POST', self.name,
533 headers=hdrs,
534 query_args=query_args,
535 data=data)
536 body = response.read()
537 if response.status == 200:
538 h = handler.XmlHandler(result, self)
539 xml.sax.parseString(body, h)
540 return count >= 1000 # more?
541 else:
542 raise provider.storage_response_error(response.status,
543 response.reason,
544 body)
545 while delete_keys2(headers):
546 pass
547 return result
452 548
453 def delete_key(self, key_name, headers=None, 549 def delete_key(self, key_name, headers=None,
454 version_id=None, mfa_token=None): 550 version_id=None, mfa_token=None):
455 """ 551 """
456 Deletes a key from the bucket. If a version_id is provided, 552 Deletes a key from the bucket. If a version_id is provided,
457 only that version of the key will be deleted. 553 only that version of the key will be deleted.
458 554
459 :type key_name: string 555 :type key_name: string
460 :param key_name: The key name to delete 556 :param key_name: The key name to delete
461 557
462 :type version_id: string 558 :type version_id: string
463 :param version_id: The version ID (optional) 559 :param version_id: The version ID (optional)
464 560
465 :type mfa_token: tuple or list of strings 561 :type mfa_token: tuple or list of strings
466 :param mfa_token: A tuple or list consisting of the serial number 562 :param mfa_token: A tuple or list consisting of the serial number
467 from the MFA device and the current value of 563 from the MFA device and the current value of
468 the six-digit token associated with the device. 564 the six-digit token associated with the device.
469 This value is required anytime you are 565 This value is required anytime you are
470 deleting versioned objects from a bucket 566 deleting versioned objects from a bucket
471 that has the MFADelete option on the bucket. 567 that has the MFADelete option on the bucket.
568
569 :rtype: :class:`boto.s3.key.Key` or subclass
570 :returns: A key object holding information on what was deleted.
571 The Caller can see if a delete_marker was created or
572 removed and what version_id the delete created or removed.
472 """ 573 """
473 provider = self.connection.provider 574 provider = self.connection.provider
474 if version_id: 575 if version_id:
475 query_args = 'versionId=%s' % version_id 576 query_args = 'versionId=%s' % version_id
476 else: 577 else:
477 query_args = None 578 query_args = None
478 if mfa_token: 579 if mfa_token:
479 if not headers: 580 if not headers:
480 headers = {} 581 headers = {}
481 headers[provider.mfa_header] = ' '.join(mfa_token) 582 headers[provider.mfa_header] = ' '.join(mfa_token)
482 response = self.connection.make_request('DELETE', self.name, key_name, 583 response = self.connection.make_request('DELETE', self.name, key_name,
483 headers=headers, 584 headers=headers,
484 query_args=query_args) 585 query_args=query_args)
485 body = response.read() 586 body = response.read()
486 if response.status != 204: 587 if response.status != 204:
487 raise provider.storage_response_error(response.status, 588 raise provider.storage_response_error(response.status,
488 response.reason, body) 589 response.reason, body)
590 else:
591 # return a key object with information on what was deleted.
592 k = self.key_class(self)
593 k.name = key_name
594 k.handle_version_headers(response)
595 return k
489 596
490 def copy_key(self, new_key_name, src_bucket_name, 597 def copy_key(self, new_key_name, src_bucket_name,
491 src_key_name, metadata=None, src_version_id=None, 598 src_key_name, metadata=None, src_version_id=None,
492 storage_class='STANDARD', preserve_acl=False): 599 storage_class='STANDARD', preserve_acl=False,
600 encrypt_key=False, headers=None, query_args=None):
493 """ 601 """
494 Create a new key in the bucket by copying another existing key. 602 Create a new key in the bucket by copying another existing key.
495 603
496 :type new_key_name: string 604 :type new_key_name: string
497 :param new_key_name: The name of the new key 605 :param new_key_name: The name of the new key
498 606
499 :type src_bucket_name: string 607 :type src_bucket_name: string
500 :param src_bucket_name: The name of the source bucket 608 :param src_bucket_name: The name of the source bucket
501 609
502 :type src_key_name: string 610 :type src_key_name: string
(...skipping 24 matching lines...) Expand all
527 will have the default ACL. 635 will have the default ACL.
528 Note that preserving the ACL in the 636 Note that preserving the ACL in the
529 new key object will require two 637 new key object will require two
530 additional API calls to S3, one to 638 additional API calls to S3, one to
531 retrieve the current ACL and one to 639 retrieve the current ACL and one to
532 set that ACL on the new object. If 640 set that ACL on the new object. If
533 you don't care about the ACL, a value 641 you don't care about the ACL, a value
534 of False will be significantly more 642 of False will be significantly more
535 efficient. 643 efficient.
536 644
645 :type encrypt_key: bool
646 :param encrypt_key: If True, the new copy of the object will
647 be encrypted on the server-side by S3 and
648 will be stored in an encrypted form while
649 at rest in S3.
650
651 :type headers: dict
652 :param headers: A dictionary of header name/value pairs.
653
654 :type query_args: string
655 :param query_args: A string of additional querystring arguments
656 to append to the request
657
537 :rtype: :class:`boto.s3.key.Key` or subclass 658 :rtype: :class:`boto.s3.key.Key` or subclass
538 :returns: An instance of the newly created key object 659 :returns: An instance of the newly created key object
539 """ 660 """
540 661 headers = headers or {}
662 provider = self.connection.provider
541 src_key_name = boto.utils.get_utf8_value(src_key_name) 663 src_key_name = boto.utils.get_utf8_value(src_key_name)
542 if preserve_acl: 664 if preserve_acl:
543 if self.name == src_bucket_name: 665 if self.name == src_bucket_name:
544 src_bucket = self 666 src_bucket = self
545 else: 667 else:
546 src_bucket = self.connection.get_bucket(src_bucket_name) 668 src_bucket = self.connection.get_bucket(src_bucket_name)
547 acl = src_bucket.get_xml_acl(src_key_name) 669 acl = src_bucket.get_xml_acl(src_key_name)
670 if encrypt_key:
671 headers[provider.server_side_encryption_header] = 'AES256'
548 src = '%s/%s' % (src_bucket_name, urllib.quote(src_key_name)) 672 src = '%s/%s' % (src_bucket_name, urllib.quote(src_key_name))
549 if src_version_id: 673 if src_version_id:
550 src += '?version_id=%s' % src_version_id 674 src += '?versionId=%s' % src_version_id
551 provider = self.connection.provider 675 headers[provider.copy_source_header] = str(src)
552 headers = {provider.copy_source_header : str(src)} 676 # make sure storage_class_header key exists before accessing it
553 if storage_class != 'STANDARD': 677 if provider.storage_class_header and storage_class:
554 headers[provider.storage_class_header] = storage_class 678 headers[provider.storage_class_header] = storage_class
555 if metadata: 679 if metadata:
556 headers[provider.metadata_directive_header] = 'REPLACE' 680 headers[provider.metadata_directive_header] = 'REPLACE'
557 headers = boto.utils.merge_meta(headers, metadata, provider) 681 headers = boto.utils.merge_meta(headers, metadata, provider)
558 else: 682 elif not query_args: # Can't use this header with multi-part copy.
559 headers[provider.metadata_directive_header] = 'COPY' 683 headers[provider.metadata_directive_header] = 'COPY'
560 response = self.connection.make_request('PUT', self.name, new_key_name, 684 response = self.connection.make_request('PUT', self.name, new_key_name,
561 headers=headers) 685 headers=headers,
686 query_args=query_args)
562 body = response.read() 687 body = response.read()
563 if response.status == 200: 688 if response.status == 200:
564 key = self.new_key(new_key_name) 689 key = self.new_key(new_key_name)
565 h = handler.XmlHandler(key, self) 690 h = handler.XmlHandler(key, self)
566 xml.sax.parseString(body, h) 691 xml.sax.parseString(body, h)
567 if hasattr(key, 'Error'): 692 if hasattr(key, 'Error'):
568 raise provider.storage_copy_error(key.Code, key.Message, body) 693 raise provider.storage_copy_error(key.Code, key.Message, body)
569 key.handle_version_headers(response) 694 key.handle_version_headers(response)
570 if preserve_acl: 695 if preserve_acl:
571 self.set_xml_acl(acl, new_key_name) 696 self.set_xml_acl(acl, new_key_name)
572 return key 697 return key
573 else: 698 else:
574 raise provider.storage_response_error(response.status, response.reas on, body) 699 raise provider.storage_response_error(response.status,
700 response.reason, body)
575 701
576 def set_canned_acl(self, acl_str, key_name='', headers=None, 702 def set_canned_acl(self, acl_str, key_name='', headers=None,
577 version_id=None): 703 version_id=None):
578 assert acl_str in CannedACLStrings 704 assert acl_str in CannedACLStrings
579 705
580 if headers: 706 if headers:
581 headers[self.connection.provider.acl_header] = acl_str 707 headers[self.connection.provider.acl_header] = acl_str
582 else: 708 else:
583 headers={self.connection.provider.acl_header: acl_str} 709 headers={self.connection.provider.acl_header: acl_str}
584 710
585 query_args='acl' 711 query_args = 'acl'
586 if version_id: 712 if version_id:
587 query_args += '&versionId=%s' % version_id 713 query_args += '&versionId=%s' % version_id
588 response = self.connection.make_request('PUT', self.name, key_name, 714 response = self.connection.make_request('PUT', self.name, key_name,
589 headers=headers, query_args=query_args) 715 headers=headers, query_args=query_args)
590 body = response.read() 716 body = response.read()
591 if response.status != 200: 717 if response.status != 200:
592 raise self.connection.provider.storage_response_error( 718 raise self.connection.provider.storage_response_error(
593 response.status, response.reason, body) 719 response.status, response.reason, body)
594 720
595 def get_xml_acl(self, key_name='', headers=None, version_id=None): 721 def get_xml_acl(self, key_name='', headers=None, version_id=None):
596 query_args = 'acl' 722 query_args = 'acl'
597 if version_id: 723 if version_id:
598 query_args += '&versionId=%s' % version_id 724 query_args += '&versionId=%s' % version_id
599 response = self.connection.make_request('GET', self.name, key_name, 725 response = self.connection.make_request('GET', self.name, key_name,
600 query_args=query_args, 726 query_args=query_args,
601 headers=headers) 727 headers=headers)
602 body = response.read() 728 body = response.read()
603 if response.status != 200: 729 if response.status != 200:
604 raise self.connection.provider.storage_response_error( 730 raise self.connection.provider.storage_response_error(
605 response.status, response.reason, body) 731 response.status, response.reason, body)
606 return body 732 return body
607 733
608 def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None): 734 def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None,
609 query_args = 'acl' 735 query_args='acl'):
610 if version_id: 736 if version_id:
611 query_args += '&versionId=%s' % version_id 737 query_args += '&versionId=%s' % version_id
612 response = self.connection.make_request('PUT', self.name, key_name, 738 response = self.connection.make_request('PUT', self.name, key_name,
613 data=acl_str.encode('ISO-8859-1' ), 739 data=acl_str.encode('ISO-8859-1' ),
614 query_args=query_args, 740 query_args=query_args,
615 headers=headers) 741 headers=headers)
616 body = response.read() 742 body = response.read()
617 if response.status != 200: 743 if response.status != 200:
618 raise self.connection.provider.storage_response_error( 744 raise self.connection.provider.storage_response_error(
619 response.status, response.reason, body) 745 response.status, response.reason, body)
(...skipping 16 matching lines...) Expand all
636 body = response.read() 762 body = response.read()
637 if response.status == 200: 763 if response.status == 200:
638 policy = Policy(self) 764 policy = Policy(self)
639 h = handler.XmlHandler(policy, self) 765 h = handler.XmlHandler(policy, self)
640 xml.sax.parseString(body, h) 766 xml.sax.parseString(body, h)
641 return policy 767 return policy
642 else: 768 else:
643 raise self.connection.provider.storage_response_error( 769 raise self.connection.provider.storage_response_error(
644 response.status, response.reason, body) 770 response.status, response.reason, body)
645 771
772 def set_subresource(self, subresource, value, key_name = '', headers=None,
773 version_id=None):
774 """
775 Set a subresource for a bucket or key.
776
777 :type subresource: string
778 :param subresource: The subresource to set.
779
780 :type value: string
781 :param value: The value of the subresource.
782
783 :type key_name: string
784 :param key_name: The key to operate on, or None to operate on the
785 bucket.
786
787 :type headers: dict
788 :param headers: Additional HTTP headers to include in the request.
789
790 :type src_version_id: string
791 :param src_version_id: Optional. The version id of the key to operate
792 on. If not specified, operate on the newest
793 version.
794 """
795 if not subresource:
796 raise TypeError('set_subresource called with subresource=None')
797 query_args = subresource
798 if version_id:
799 query_args += '&versionId=%s' % version_id
800 response = self.connection.make_request('PUT', self.name, key_name,
801 data=value.encode('UTF-8'),
802 query_args=query_args,
803 headers=headers)
804 body = response.read()
805 if response.status != 200:
806 raise self.connection.provider.storage_response_error(
807 response.status, response.reason, body)
808
809 def get_subresource(self, subresource, key_name='', headers=None,
810 version_id=None):
811 """
812 Get a subresource for a bucket or key.
813
814 :type subresource: string
815 :param subresource: The subresource to get.
816
817 :type key_name: string
818 :param key_name: The key to operate on, or None to operate on the
819 bucket.
820
821 :type headers: dict
822 :param headers: Additional HTTP headers to include in the request.
823
824 :type src_version_id: string
825 :param src_version_id: Optional. The version id of the key to operate
826 on. If not specified, operate on the newest
827 version.
828
829 :rtype: string
830 :returns: The value of the subresource.
831 """
832 if not subresource:
833 raise TypeError('get_subresource called with subresource=None')
834 query_args = subresource
835 if version_id:
836 query_args += '&versionId=%s' % version_id
837 response = self.connection.make_request('GET', self.name, key_name,
838 query_args=query_args,
839 headers=headers)
840 body = response.read()
841 if response.status != 200:
842 raise self.connection.provider.storage_response_error(
843 response.status, response.reason, body)
844 return body
845
646 def make_public(self, recursive=False, headers=None): 846 def make_public(self, recursive=False, headers=None):
647 self.set_canned_acl('public-read', headers=headers) 847 self.set_canned_acl('public-read', headers=headers)
648 if recursive: 848 if recursive:
649 for key in self: 849 for key in self:
650 self.set_canned_acl('public-read', key.name, headers=headers) 850 self.set_canned_acl('public-read', key.name, headers=headers)
651 851
652 def add_email_grant(self, permission, email_address, 852 def add_email_grant(self, permission, email_address,
653 recursive=False, headers=None): 853 recursive=False, headers=None):
654 """ 854 """
655 Convenience method that provides a quick way to add an email grant 855 Convenience method that provides a quick way to add an email grant
(...skipping 21 matching lines...) Expand all
677 if permission not in S3Permissions: 877 if permission not in S3Permissions:
678 raise self.connection.provider.storage_permissions_error( 878 raise self.connection.provider.storage_permissions_error(
679 'Unknown Permission: %s' % permission) 879 'Unknown Permission: %s' % permission)
680 policy = self.get_acl(headers=headers) 880 policy = self.get_acl(headers=headers)
681 policy.acl.add_email_grant(permission, email_address) 881 policy.acl.add_email_grant(permission, email_address)
682 self.set_acl(policy, headers=headers) 882 self.set_acl(policy, headers=headers)
683 if recursive: 883 if recursive:
684 for key in self: 884 for key in self:
685 key.add_email_grant(permission, email_address, headers=headers) 885 key.add_email_grant(permission, email_address, headers=headers)
686 886
687 def add_user_grant(self, permission, user_id, 887 def add_user_grant(self, permission, user_id, recursive=False,
688 recursive=False, headers=None): 888 headers=None, display_name=None):
689 """ 889 """
690 Convenience method that provides a quick way to add a canonical 890 Convenience method that provides a quick way to add a canonical
691 user grant to a bucket. This method retrieves the current ACL, 891 user grant to a bucket. This method retrieves the current ACL,
692 creates a new grant based on the parameters passed in, adds that 892 creates a new grant based on the parameters passed in, adds that
693 grant to the ACL and then PUT's the new ACL back to S3. 893 grant to the ACL and then PUT's the new ACL back to S3.
694 894
695 :type permission: string 895 :type permission: string
696 :param permission: The permission being granted. Should be one of: 896 :param permission: The permission being granted. Should be one of:
697 (READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL). 897 (READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL).
698 898
699 :type user_id: string 899 :type user_id: string
700 :param user_id: The canonical user id associated with the AWS 900 :param user_id: The canonical user id associated with the AWS
701 account your are granting the permission to. 901 account your are granting the permission to.
702 902
703 :type recursive: boolean 903 :type recursive: boolean
704 :param recursive: A boolean value to controls whether the command 904 :param recursive: A boolean value to controls whether the command
705 will apply the grant to all keys within the bucket 905 will apply the grant to all keys within the bucket
706 or not. The default value is False. By passing a 906 or not. The default value is False. By passing a
707 True value, the call will iterate through all keys 907 True value, the call will iterate through all keys
708 in the bucket and apply the same grant to each key. 908 in the bucket and apply the same grant to each key.
709 CAUTION: If you have a lot of keys, this could take 909 CAUTION: If you have a lot of keys, this could take
710 a long time! 910 a long time!
911
912 :type display_name: string
913 :param display_name: An option string containing the user's
914 Display Name. Only required on Walrus.
711 """ 915 """
712 if permission not in S3Permissions: 916 if permission not in S3Permissions:
713 raise self.connection.provider.storage_permissions_error( 917 raise self.connection.provider.storage_permissions_error(
714 'Unknown Permission: %s' % permission) 918 'Unknown Permission: %s' % permission)
715 policy = self.get_acl(headers=headers) 919 policy = self.get_acl(headers=headers)
716 policy.acl.add_user_grant(permission, user_id) 920 policy.acl.add_user_grant(permission, user_id,
921 display_name=display_name)
717 self.set_acl(policy, headers=headers) 922 self.set_acl(policy, headers=headers)
718 if recursive: 923 if recursive:
719 for key in self: 924 for key in self:
720 key.add_user_grant(permission, user_id, headers=headers) 925 key.add_user_grant(permission, user_id, headers=headers,
926 display_name=display_name)
721 927
722 def list_grants(self, headers=None): 928 def list_grants(self, headers=None):
723 policy = self.get_acl(headers=headers) 929 policy = self.get_acl(headers=headers)
724 return policy.acl.grants 930 return policy.acl.grants
725 931
726 def get_location(self): 932 def get_location(self):
727 """ 933 """
728 Returns the LocationConstraint for the bucket. 934 Returns the LocationConstraint for the bucket.
729 935
730 :rtype: str 936 :rtype: str
731 :return: The LocationConstraint for the bucket or the empty 937 :return: The LocationConstraint for the bucket or the empty
732 string if no constraint was specified when bucket 938 string if no constraint was specified when bucket
733 was created. 939 was created.
734 """ 940 """
735 response = self.connection.make_request('GET', self.name, 941 response = self.connection.make_request('GET', self.name,
736 query_args='location') 942 query_args='location')
737 body = response.read() 943 body = response.read()
738 if response.status == 200: 944 if response.status == 200:
739 rs = ResultSet(self) 945 rs = ResultSet(self)
740 h = handler.XmlHandler(rs, self) 946 h = handler.XmlHandler(rs, self)
741 xml.sax.parseString(body, h) 947 xml.sax.parseString(body, h)
742 return rs.LocationConstraint 948 return rs.LocationConstraint
743 else: 949 else:
744 raise self.connection.provider.storage_response_error( 950 raise self.connection.provider.storage_response_error(
745 response.status, response.reason, body) 951 response.status, response.reason, body)
746 952
747 def enable_logging(self, target_bucket, target_prefix='', headers=None): 953 def set_xml_logging(self, logging_str, headers=None):
748 if isinstance(target_bucket, Bucket): 954 """
749 target_bucket = target_bucket.name 955 Set logging on a bucket directly to the given xml string.
750 body = self.BucketLoggingBody % (target_bucket, target_prefix) 956
957 :type logging_str: unicode string
958 :param logging_str: The XML for the bucketloggingstatus which will be se t.
959 The string will be converted to utf-8 before it is s ent.
960 Usually, you will obtain this XML from the BucketLog ging
961 object.
962
963 :rtype: bool
964 :return: True if ok or raises an exception.
965 """
966 body = logging_str.encode('utf-8')
751 response = self.connection.make_request('PUT', self.name, data=body, 967 response = self.connection.make_request('PUT', self.name, data=body,
752 query_args='logging', headers=headers) 968 query_args='logging', headers=headers)
753 body = response.read() 969 body = response.read()
754 if response.status == 200: 970 if response.status == 200:
755 return True 971 return True
756 else: 972 else:
757 raise self.connection.provider.storage_response_error( 973 raise self.connection.provider.storage_response_error(
758 response.status, response.reason, body) 974 response.status, response.reason, body)
759 975
976 def enable_logging(self, target_bucket, target_prefix='', grants=None, heade rs=None):
977 """
978 Enable logging on a bucket.
979
980 :type target_bucket: bucket or string
981 :param target_bucket: The bucket to log to.
982
983 :type target_prefix: string
984 :param target_prefix: The prefix which should be prepended to the
985 generated log files written to the target_bucket.
986
987 :type grants: list of Grant objects
988 :param grants: A list of extra permissions which will be granted on
989 the log files which are created.
990
991 :rtype: bool
992 :return: True if ok or raises an exception.
993 """
994 if isinstance(target_bucket, Bucket):
995 target_bucket = target_bucket.name
996 blogging = BucketLogging(target=target_bucket, prefix=target_prefix, gra nts=grants)
997 return self.set_xml_logging(blogging.to_xml(), headers=headers)
998
760 def disable_logging(self, headers=None): 999 def disable_logging(self, headers=None):
761 body = self.EmptyBucketLoggingBody 1000 """
762 response = self.connection.make_request('PUT', self.name, data=body, 1001 Disable logging on a bucket.
763 query_args='logging', headers=headers) 1002
764 body = response.read() 1003 :rtype: bool
765 if response.status == 200: 1004 :return: True if ok or raises an exception.
766 return True 1005 """
767 else: 1006 blogging = BucketLogging()
768 raise self.connection.provider.storage_response_error( 1007 return self.set_xml_logging(blogging.to_xml(), headers=headers)
769 response.status, response.reason, body)
770 1008
771 def get_logging_status(self, headers=None): 1009 def get_logging_status(self, headers=None):
1010 """
1011 Get the logging status for this bucket.
1012
1013 :rtype: :class:`boto.s3.bucketlogging.BucketLogging`
1014 :return: A BucketLogging object for this bucket.
1015 """
772 response = self.connection.make_request('GET', self.name, 1016 response = self.connection.make_request('GET', self.name,
773 query_args='logging', headers=headers) 1017 query_args='logging', headers=headers)
774 body = response.read() 1018 body = response.read()
775 if response.status == 200: 1019 if response.status == 200:
776 return body 1020 blogging = BucketLogging()
1021 h = handler.XmlHandler(blogging, self)
1022 xml.sax.parseString(body, h)
1023 return blogging
777 else: 1024 else:
778 raise self.connection.provider.storage_response_error( 1025 raise self.connection.provider.storage_response_error(
779 response.status, response.reason, body) 1026 response.status, response.reason, body)
780 1027
781 def set_as_logging_target(self, headers=None): 1028 def set_as_logging_target(self, headers=None):
1029 """
1030 Setup the current bucket as a logging target by granting the necessary
1031 permissions to the LogDelivery group to write log files to this bucket.
1032 """
782 policy = self.get_acl(headers=headers) 1033 policy = self.get_acl(headers=headers)
783 g1 = Grant(permission='WRITE', type='Group', uri=self.LoggingGroup) 1034 g1 = Grant(permission='WRITE', type='Group', uri=self.LoggingGroup)
784 g2 = Grant(permission='READ_ACP', type='Group', uri=self.LoggingGroup) 1035 g2 = Grant(permission='READ_ACP', type='Group', uri=self.LoggingGroup)
785 policy.acl.add_grant(g1) 1036 policy.acl.add_grant(g1)
786 policy.acl.add_grant(g2) 1037 policy.acl.add_grant(g2)
787 self.set_acl(policy, headers=headers) 1038 self.set_acl(policy, headers=headers)
788 1039
789 def get_request_payment(self, headers=None): 1040 def get_request_payment(self, headers=None):
790 response = self.connection.make_request('GET', self.name, 1041 response = self.connection.make_request('GET', self.name,
791 query_args='requestPayment', headers=headers) 1042 query_args='requestPayment', headers=headers)
(...skipping 13 matching lines...) Expand all
805 return True 1056 return True
806 else: 1057 else:
807 raise self.connection.provider.storage_response_error( 1058 raise self.connection.provider.storage_response_error(
808 response.status, response.reason, body) 1059 response.status, response.reason, body)
809 1060
810 def configure_versioning(self, versioning, mfa_delete=False, 1061 def configure_versioning(self, versioning, mfa_delete=False,
811 mfa_token=None, headers=None): 1062 mfa_token=None, headers=None):
812 """ 1063 """
813 Configure versioning for this bucket. 1064 Configure versioning for this bucket.
814 1065
815 ..note:: This feature is currently in beta release and is available 1066 ..note:: This feature is currently in beta.
816 only in the Northern California region.
817 1067
818 :type versioning: bool 1068 :type versioning: bool
819 :param versioning: A boolean indicating whether version is 1069 :param versioning: A boolean indicating whether version is
820 enabled (True) or disabled (False). 1070 enabled (True) or disabled (False).
821 1071
822 :type mfa_delete: bool 1072 :type mfa_delete: bool
823 :param mfa_delete: A boolean indicating whether the Multi-Factor 1073 :param mfa_delete: A boolean indicating whether the Multi-Factor
824 Authentication Delete feature is enabled (True) 1074 Authentication Delete feature is enabled (True)
825 or disabled (False). If mfa_delete is enabled 1075 or disabled (False). If mfa_delete is enabled
826 then all Delete operations will require the 1076 then all Delete operations will require the
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
880 if ver: 1130 if ver:
881 d['Versioning'] = ver.group(1) 1131 d['Versioning'] = ver.group(1)
882 mfa = re.search(self.MFADeleteRE, body) 1132 mfa = re.search(self.MFADeleteRE, body)
883 if mfa: 1133 if mfa:
884 d['MfaDelete'] = mfa.group(1) 1134 d['MfaDelete'] = mfa.group(1)
885 return d 1135 return d
886 else: 1136 else:
887 raise self.connection.provider.storage_response_error( 1137 raise self.connection.provider.storage_response_error(
888 response.status, response.reason, body) 1138 response.status, response.reason, body)
889 1139
1140 def configure_lifecycle(self, lifecycle_config, headers=None):
1141 """
1142 Configure lifecycle for this bucket.
1143
1144 :type lifecycle_config: :class:`boto.s3.lifecycle.Lifecycle`
1145 :param lifecycle_config: The lifecycle configuration you want
1146 to configure for this bucket.
1147 """
1148 fp = StringIO.StringIO(lifecycle_config.to_xml())
1149 md5 = boto.utils.compute_md5(fp)
1150 if headers is None:
1151 headers = {}
1152 headers['Content-MD5'] = md5[1]
1153 headers['Content-Type'] = 'text/xml'
1154 response = self.connection.make_request('PUT', self.name,
1155 data=fp.getvalue(),
1156 query_args='lifecycle',
1157 headers=headers)
1158 body = response.read()
1159 if response.status == 200:
1160 return True
1161 else:
1162 raise self.connection.provider.storage_response_error(
1163 response.status, response.reason, body)
1164
1165 def get_lifecycle_config(self, headers=None):
1166 """
1167 Returns the current lifecycle configuration on the bucket.
1168
1169 :rtype: :class:`boto.s3.lifecycle.Lifecycle`
1170 :returns: A LifecycleConfig object that describes all current
1171 lifecycle rules in effect for the bucket.
1172 """
1173 response = self.connection.make_request('GET', self.name,
1174 query_args='lifecycle', headers=headers)
1175 body = response.read()
1176 boto.log.debug(body)
1177 if response.status == 200:
1178 lifecycle = Lifecycle()
1179 h = handler.XmlHandler(lifecycle, self)
1180 xml.sax.parseString(body, h)
1181 return lifecycle
1182 else:
1183 raise self.connection.provider.storage_response_error(
1184 response.status, response.reason, body)
1185
1186 def delete_lifecycle_configuration(self, headers=None):
1187 """
1188 Removes all lifecycle configuration from the bucket.
1189 """
1190 response = self.connection.make_request('DELETE', self.name,
1191 query_args='lifecycle',
1192 headers=headers)
1193 body = response.read()
1194 boto.log.debug(body)
1195 if response.status == 204:
1196 return True
1197 else:
1198 raise self.connection.provider.storage_response_error(
1199 response.status, response.reason, body)
1200
890 def configure_website(self, suffix, error_key='', headers=None): 1201 def configure_website(self, suffix, error_key='', headers=None):
891 """ 1202 """
892 Configure this bucket to act as a website 1203 Configure this bucket to act as a website
893 1204
894 :type suffix: str 1205 :type suffix: str
895 :param suffix: Suffix that is appended to a request that is for a 1206 :param suffix: Suffix that is appended to a request that is for a
896 "directory" on the website endpoint (e.g. if the suffix 1207 "directory" on the website endpoint (e.g. if the suffix
897 is index.html and you make a request to 1208 is index.html and you make a request to
898 samplebucket/images/ the data that is returned will 1209 samplebucket/images/ the data that is returned will
899 be for the object with the key name images/index.html). 1210 be for the object with the key name images/index.html).
(...skipping 21 matching lines...) Expand all
921 response.status, response.reason, body) 1232 response.status, response.reason, body)
922 1233
923 def get_website_configuration(self, headers=None): 1234 def get_website_configuration(self, headers=None):
924 """ 1235 """
925 Returns the current status of website configuration on the bucket. 1236 Returns the current status of website configuration on the bucket.
926 1237
927 :rtype: dict 1238 :rtype: dict
928 :returns: A dictionary containing a Python representation 1239 :returns: A dictionary containing a Python representation
929 of the XML response from S3. The overall structure is: 1240 of the XML response from S3. The overall structure is:
930 1241
931 * WebsiteConfiguration 1242 * WebsiteConfiguration
932 1243
933 * IndexDocument 1244 * IndexDocument
934 1245
935 * Suffix : suffix that is appended to request that 1246 * Suffix : suffix that is appended to request that
936 is for a "directory" on the website endpoint 1247 is for a "directory" on the website endpoint
937 * ErrorDocument 1248 * ErrorDocument
938 1249
939 * Key : name of object to serve when an error occurs 1250 * Key : name of object to serve when an error occurs
940 """ 1251 """
941 response = self.connection.make_request('GET', self.name, 1252 response = self.connection.make_request('GET', self.name,
942 query_args='website', headers=headers) 1253 query_args='website', headers=headers)
943 body = response.read() 1254 body = response.read()
944 boto.log.debug(body) 1255 boto.log.debug(body)
945 if response.status == 200: 1256 if response.status == 200:
946 e = boto.jsonresponse.Element() 1257 e = boto.jsonresponse.Element()
947 h = boto.jsonresponse.XmlHandler(e, None) 1258 h = boto.jsonresponse.XmlHandler(e, None)
948 h.parse(body) 1259 h.parse(body)
949 return e 1260 return e
(...skipping 20 matching lines...) Expand all
970 Returns the fully qualified hostname to use is you want to access this 1281 Returns the fully qualified hostname to use is you want to access this
971 bucket as a website. This doesn't validate whether the bucket has 1282 bucket as a website. This doesn't validate whether the bucket has
972 been correctly configured as a website or not. 1283 been correctly configured as a website or not.
973 """ 1284 """
974 l = [self.name] 1285 l = [self.name]
975 l.append(S3WebsiteEndpointTranslate.translate_region(self.get_location() )) 1286 l.append(S3WebsiteEndpointTranslate.translate_region(self.get_location() ))
976 l.append('.'.join(self.connection.host.split('.')[-2:])) 1287 l.append('.'.join(self.connection.host.split('.')[-2:]))
977 return '.'.join(l) 1288 return '.'.join(l)
978 1289
979 def get_policy(self, headers=None): 1290 def get_policy(self, headers=None):
1291 """
1292 Returns the JSON policy associated with the bucket. The policy
1293 is returned as an uninterpreted JSON string.
1294 """
980 response = self.connection.make_request('GET', self.name, 1295 response = self.connection.make_request('GET', self.name,
981 query_args='policy', headers=headers) 1296 query_args='policy', headers=headers)
982 body = response.read() 1297 body = response.read()
983 if response.status == 200: 1298 if response.status == 200:
984 return body 1299 return body
985 else: 1300 else:
986 raise self.connection.provider.storage_response_error( 1301 raise self.connection.provider.storage_response_error(
987 response.status, response.reason, body) 1302 response.status, response.reason, body)
988 1303
989 def set_policy(self, policy, headers=None): 1304 def set_policy(self, policy, headers=None):
1305 """
1306 Add or replace the JSON policy associated with the bucket.
1307
1308 :type policy: str
1309 :param policy: The JSON policy as a string.
1310 """
990 response = self.connection.make_request('PUT', self.name, 1311 response = self.connection.make_request('PUT', self.name,
991 data=policy, 1312 data=policy,
992 query_args='policy', 1313 query_args='policy',
993 headers=headers) 1314 headers=headers)
994 body = response.read() 1315 body = response.read()
995 if response.status >= 200 and response.status <= 204: 1316 if response.status >= 200 and response.status <= 204:
996 return True 1317 return True
997 else: 1318 else:
998 raise self.connection.provider.storage_response_error( 1319 raise self.connection.provider.storage_response_error(
999 response.status, response.reason, body) 1320 response.status, response.reason, body)
1000 1321
1322 def delete_policy(self, headers=None):
1323 response = self.connection.make_request('DELETE', self.name,
1324 data='/?policy',
1325 query_args='policy',
1326 headers=headers)
1327 body = response.read()
1328 if response.status >= 200 and response.status <= 204:
1329 return True
1330 else:
1331 raise self.connection.provider.storage_response_error(
1332 response.status, response.reason, body)
1333
1334
1001 def initiate_multipart_upload(self, key_name, headers=None, 1335 def initiate_multipart_upload(self, key_name, headers=None,
1002 reduced_redundancy=False, metadata=None): 1336 reduced_redundancy=False,
1337 metadata=None, encrypt_key=False):
1003 """ 1338 """
1004 Start a multipart upload operation. 1339 Start a multipart upload operation.
1005 1340
1006 :type key_name: string 1341 :type key_name: string
1007 :param key_name: The name of the key that will ultimately result from 1342 :param key_name: The name of the key that will ultimately result from
1008 this multipart upload operation. This will be exactly 1343 this multipart upload operation. This will be exactly
1009 as the key appears in the bucket after the upload 1344 as the key appears in the bucket after the upload
1010 process has been completed. 1345 process has been completed.
1011 1346
1012 :type headers: dict 1347 :type headers: dict
1013 :param headers: Additional HTTP headers to send and store with the 1348 :param headers: Additional HTTP headers to send and store with the
1014 resulting key in S3. 1349 resulting key in S3.
1015 1350
1016 :type reduced_redundancy: boolean 1351 :type reduced_redundancy: boolean
1017 :param reduced_redundancy: In multipart uploads, the storage class is 1352 :param reduced_redundancy: In multipart uploads, the storage class is
1018 specified when initiating the upload, 1353 specified when initiating the upload,
1019 not when uploading individual parts. So 1354 not when uploading individual parts. So
1020 if you want the resulting key to use the 1355 if you want the resulting key to use the
1021 reduced redundancy storage class set this 1356 reduced redundancy storage class set this
1022 flag when you initiate the upload. 1357 flag when you initiate the upload.
1023 1358
1024 :type metadata: dict 1359 :type metadata: dict
1025 :param metadata: Any metadata that you would like to set on the key 1360 :param metadata: Any metadata that you would like to set on the key
1026 that results from the multipart upload. 1361 that results from the multipart upload.
1362
1363 :type encrypt_key: bool
1364 :param encrypt_key: If True, the new copy of the object will
1365 be encrypted on the server-side by S3 and
1366 will be stored in an encrypted form while
1367 at rest in S3.
1027 """ 1368 """
1028 query_args = 'uploads' 1369 query_args = 'uploads'
1370 provider = self.connection.provider
1029 if headers is None: 1371 if headers is None:
1030 headers = {} 1372 headers = {}
1031 if reduced_redundancy: 1373 if reduced_redundancy:
1032 storage_class_header = self.connection.provider.storage_class_header 1374 storage_class_header = provider.storage_class_header
1033 if storage_class_header: 1375 if storage_class_header:
1034 headers[storage_class_header] = 'REDUCED_REDUNDANCY' 1376 headers[storage_class_header] = 'REDUCED_REDUNDANCY'
1035 # TODO: what if the provider doesn't support reduced redundancy? 1377 # TODO: what if the provider doesn't support reduced redundancy?
1036 # (see boto.s3.key.Key.set_contents_from_file) 1378 # (see boto.s3.key.Key.set_contents_from_file)
1379 if encrypt_key:
1380 headers[provider.server_side_encryption_header] = 'AES256'
1037 if metadata is None: 1381 if metadata is None:
1038 metadata = {} 1382 metadata = {}
1039 1383
1040 headers = boto.utils.merge_meta(headers, metadata, 1384 headers = boto.utils.merge_meta(headers, metadata,
1041 self.connection.provider) 1385 self.connection.provider)
1042 response = self.connection.make_request('POST', self.name, key_name, 1386 response = self.connection.make_request('POST', self.name, key_name,
1043 query_args=query_args, 1387 query_args=query_args,
1044 headers=headers) 1388 headers=headers)
1045 body = response.read() 1389 body = response.read()
1046 boto.log.debug(body) 1390 boto.log.debug(body)
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1088 query_args=query_args, 1432 query_args=query_args,
1089 headers=headers) 1433 headers=headers)
1090 body = response.read() 1434 body = response.read()
1091 boto.log.debug(body) 1435 boto.log.debug(body)
1092 if response.status != 204: 1436 if response.status != 204:
1093 raise self.connection.provider.storage_response_error( 1437 raise self.connection.provider.storage_response_error(
1094 response.status, response.reason, body) 1438 response.status, response.reason, body)
1095 1439
1096 def delete(self, headers=None): 1440 def delete(self, headers=None):
1097 return self.connection.delete_bucket(self.name, headers=headers) 1441 return self.connection.delete_bucket(self.name, headers=headers)
1098
OLDNEW
« no previous file with comments | « third_party/gsutil/boto/boto/s3/acl.py ('k') | third_party/gsutil/boto/boto/s3/bucketlistresultset.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698