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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/gsutil/boto/boto/s3/bucket.py
diff --git a/third_party/gsutil/20110627/boto/boto/s3/bucket.py b/third_party/gsutil/boto/boto/s3/bucket.py
similarity index 73%
rename from third_party/gsutil/20110627/boto/boto/s3/bucket.py
rename to third_party/gsutil/boto/boto/s3/bucket.py
index 0e60be3af163d59e82e9761b5d650b47bba0cde0..6cb37b91d198e19171ee122cdd1d330a3565c9e2 100644
--- a/third_party/gsutil/20110627/boto/boto/s3/bucket.py
+++ b/third_party/gsutil/boto/boto/s3/bucket.py
@@ -23,31 +23,39 @@
import boto
from boto import handler
-from boto.provider import Provider
from boto.resultset import ResultSet
-from boto.s3.acl import ACL, Policy, CannedACLStrings, Grant
+from boto.exception import BotoClientError
+from boto.s3.acl import Policy, CannedACLStrings, Grant
from boto.s3.key import Key
from boto.s3.prefix import Prefix
from boto.s3.deletemarker import DeleteMarker
-from boto.s3.user import User
from boto.s3.multipart import MultiPartUpload
from boto.s3.multipart import CompleteMultiPartUpload
+from boto.s3.multidelete import MultiDeleteResult
+from boto.s3.multidelete import Error
from boto.s3.bucketlistresultset import BucketListResultSet
from boto.s3.bucketlistresultset import VersionedBucketListResultSet
from boto.s3.bucketlistresultset import MultiPartUploadListResultSet
+from boto.s3.lifecycle import Lifecycle
+from boto.s3.bucketlogging import BucketLogging
import boto.jsonresponse
import boto.utils
import xml.sax
+import xml.sax.saxutils
+import StringIO
import urllib
import re
+import base64
from collections import defaultdict
# as per http://goo.gl/BDuud (02/19/2011)
class S3WebsiteEndpointTranslate:
trans_region = defaultdict(lambda :'s3-website-us-east-1')
- trans_region['EU'] = 's3-website-eu-west-1'
+ trans_region['eu-west-1'] = 's3-website-eu-west-1'
trans_region['us-west-1'] = 's3-website-us-west-1'
+ trans_region['us-west-2'] = 's3-website-us-west-2'
+ trans_region['sa-east-1'] = 's3-website-sa-east-1'
trans_region['ap-northeast-1'] = 's3-website-ap-northeast-1'
trans_region['ap-southeast-1'] = 's3-website-ap-southeast-1'
@@ -59,18 +67,6 @@ S3Permissions = ['READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL']
class Bucket(object):
- BucketLoggingBody = """<?xml version="1.0" encoding="UTF-8"?>
- <BucketLoggingStatus xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- <LoggingEnabled>
- <TargetBucket>%s</TargetBucket>
- <TargetPrefix>%s</TargetPrefix>
- </LoggingEnabled>
- </BucketLoggingStatus>"""
-
- EmptyBucketLoggingBody = """<?xml version="1.0" encoding="UTF-8"?>
- <BucketLoggingStatus xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
- </BucketLoggingStatus>"""
-
LoggingGroup = 'http://acs.amazonaws.com/groups/s3/LogDelivery'
BucketPaymentBody = """<?xml version="1.0" encoding="UTF-8"?>
@@ -107,7 +103,7 @@ class Bucket(object):
return iter(BucketListResultSet(self))
def __contains__(self, key_name):
- return not (self.get_key(key_name) is None)
+ return not (self.get_key(key_name) is None)
def startElement(self, name, attrs, connection):
return None
@@ -188,6 +184,7 @@ class Bucket(object):
k.cache_control = response.getheader('cache-control')
k.name = key_name
k.handle_version_headers(response)
+ k.handle_encryption_headers(response)
return k
else:
if response.status == 404:
@@ -290,7 +287,7 @@ class Bucket(object):
def _get_all(self, element_map, initial_query_string='',
headers=None, **params):
l = []
- for k,v in params.items():
+ for k, v in params.items():
k = k.replace('_', '-')
if k == 'maxkeys':
k = 'max-keys'
@@ -303,7 +300,8 @@ class Bucket(object):
else:
s = initial_query_string
response = self.connection.make_request('GET', self.name,
- headers=headers, query_args=s)
+ headers=headers,
+ query_args=s)
body = response.read()
boto.log.debug(body)
if response.status == 200:
@@ -428,7 +426,8 @@ class Bucket(object):
:return: The result from S3 listing the uploads requested
"""
- return self._get_all([('Upload', MultiPartUpload)],
+ return self._get_all([('Upload', MultiPartUpload),
+ ('CommonPrefixes', Prefix)],
'uploads', headers, **params)
def new_key(self, key_name=None):
@@ -444,11 +443,108 @@ class Bucket(object):
return self.key_class(self, key_name)
def generate_url(self, expires_in, method='GET', headers=None,
- force_http=False, response_headers=None):
+ force_http=False, response_headers=None,
+ expires_in_absolute=False):
return self.connection.generate_url(expires_in, method, self.name,
headers=headers,
force_http=force_http,
- response_headers=response_headers)
+ response_headers=response_headers,
+ expires_in_absolute=expires_in_absolute)
+
+ def delete_keys(self, keys, quiet=False, mfa_token=None, headers=None):
+ """
+ Deletes a set of keys using S3's Multi-object delete API. If a
+ VersionID is specified for that key then that version is removed.
+ Returns a MultiDeleteResult Object, which contains Deleted
+ and Error elements for each key you ask to delete.
+
+ :type keys: list
+ :param keys: A list of either key_names or (key_name, versionid) pairs
+ or a list of Key instances.
+
+ :type quiet: boolean
+ :param quiet: In quiet mode the response includes only keys where
+ the delete operation encountered an error. For a
+ successful deletion, the operation does not return
+ any information about the delete in the response body.
+
+ :type mfa_token: tuple or list of strings
+ :param mfa_token: A tuple or list consisting of the serial number
+ from the MFA device and the current value of
+ the six-digit token associated with the device.
+ This value is required anytime you are
+ deleting versioned objects from a bucket
+ that has the MFADelete option on the bucket.
+
+ :returns: An instance of MultiDeleteResult
+ """
+ ikeys = iter(keys)
+ result = MultiDeleteResult(self)
+ provider = self.connection.provider
+ query_args = 'delete'
+ def delete_keys2(hdrs):
+ hdrs = hdrs or {}
+ data = u"""<?xml version="1.0" encoding="UTF-8"?>"""
+ data += u"<Delete>"
+ if quiet:
+ data += u"<Quiet>true</Quiet>"
+ count = 0
+ while count < 1000:
+ try:
+ key = ikeys.next()
+ except StopIteration:
+ break
+ if isinstance(key, basestring):
+ key_name = key
+ version_id = None
+ elif isinstance(key, tuple) and len(key) == 2:
+ key_name, version_id = key
+ elif (isinstance(key, Key) or isinstance(key, DeleteMarker)) and key.name:
+ key_name = key.name
+ version_id = key.version_id
+ else:
+ if isinstance(key, Prefix):
+ key_name = key.name
+ code = 'PrefixSkipped' # Don't delete Prefix
+ else:
+ key_name = repr(key) # try get a string
+ code = 'InvalidArgument' # other unknown type
+ message = 'Invalid. No delete action taken for this object.'
+ error = Error(key_name, code=code, message=message)
+ result.errors.append(error)
+ continue
+ count += 1
+ #key_name = key_name.decode('utf-8')
+ data += u"<Object><Key>%s</Key>" % xml.sax.saxutils.escape(key_name)
+ if version_id:
+ data += u"<VersionId>%s</VersionId>" % version_id
+ data += u"</Object>"
+ data += u"</Delete>"
+ if count <= 0:
+ return False # no more
+ data = data.encode('utf-8')
+ fp = StringIO.StringIO(data)
+ md5 = boto.utils.compute_md5(fp)
+ hdrs['Content-MD5'] = md5[1]
+ hdrs['Content-Type'] = 'text/xml'
+ if mfa_token:
+ hdrs[provider.mfa_header] = ' '.join(mfa_token)
+ response = self.connection.make_request('POST', self.name,
+ headers=hdrs,
+ query_args=query_args,
+ data=data)
+ body = response.read()
+ if response.status == 200:
+ h = handler.XmlHandler(result, self)
+ xml.sax.parseString(body, h)
+ return count >= 1000 # more?
+ else:
+ raise provider.storage_response_error(response.status,
+ response.reason,
+ body)
+ while delete_keys2(headers):
+ pass
+ return result
def delete_key(self, key_name, headers=None,
version_id=None, mfa_token=None):
@@ -469,6 +565,11 @@ class Bucket(object):
This value is required anytime you are
deleting versioned objects from a bucket
that has the MFADelete option on the bucket.
+
+ :rtype: :class:`boto.s3.key.Key` or subclass
+ :returns: A key object holding information on what was deleted.
+ The Caller can see if a delete_marker was created or
+ removed and what version_id the delete created or removed.
"""
provider = self.connection.provider
if version_id:
@@ -486,10 +587,17 @@ class Bucket(object):
if response.status != 204:
raise provider.storage_response_error(response.status,
response.reason, body)
+ else:
+ # return a key object with information on what was deleted.
+ k = self.key_class(self)
+ k.name = key_name
+ k.handle_version_headers(response)
+ return k
def copy_key(self, new_key_name, src_bucket_name,
src_key_name, metadata=None, src_version_id=None,
- storage_class='STANDARD', preserve_acl=False):
+ storage_class='STANDARD', preserve_acl=False,
+ encrypt_key=False, headers=None, query_args=None):
"""
Create a new key in the bucket by copying another existing key.
@@ -534,10 +642,24 @@ class Bucket(object):
of False will be significantly more
efficient.
+ :type encrypt_key: bool
+ :param encrypt_key: If True, the new copy of the object will
+ be encrypted on the server-side by S3 and
+ will be stored in an encrypted form while
+ at rest in S3.
+
+ :type headers: dict
+ :param headers: A dictionary of header name/value pairs.
+
+ :type query_args: string
+ :param query_args: A string of additional querystring arguments
+ to append to the request
+
:rtype: :class:`boto.s3.key.Key` or subclass
:returns: An instance of the newly created key object
"""
-
+ headers = headers or {}
+ provider = self.connection.provider
src_key_name = boto.utils.get_utf8_value(src_key_name)
if preserve_acl:
if self.name == src_bucket_name:
@@ -545,20 +667,23 @@ class Bucket(object):
else:
src_bucket = self.connection.get_bucket(src_bucket_name)
acl = src_bucket.get_xml_acl(src_key_name)
+ if encrypt_key:
+ headers[provider.server_side_encryption_header] = 'AES256'
src = '%s/%s' % (src_bucket_name, urllib.quote(src_key_name))
if src_version_id:
- src += '?version_id=%s' % src_version_id
- provider = self.connection.provider
- headers = {provider.copy_source_header : str(src)}
- if storage_class != 'STANDARD':
+ src += '?versionId=%s' % src_version_id
+ headers[provider.copy_source_header] = str(src)
+ # make sure storage_class_header key exists before accessing it
+ if provider.storage_class_header and storage_class:
headers[provider.storage_class_header] = storage_class
if metadata:
headers[provider.metadata_directive_header] = 'REPLACE'
headers = boto.utils.merge_meta(headers, metadata, provider)
- else:
+ elif not query_args: # Can't use this header with multi-part copy.
headers[provider.metadata_directive_header] = 'COPY'
response = self.connection.make_request('PUT', self.name, new_key_name,
- headers=headers)
+ headers=headers,
+ query_args=query_args)
body = response.read()
if response.status == 200:
key = self.new_key(new_key_name)
@@ -571,7 +696,8 @@ class Bucket(object):
self.set_xml_acl(acl, new_key_name)
return key
else:
- raise provider.storage_response_error(response.status, response.reason, body)
+ raise provider.storage_response_error(response.status,
+ response.reason, body)
def set_canned_acl(self, acl_str, key_name='', headers=None,
version_id=None):
@@ -582,7 +708,7 @@ class Bucket(object):
else:
headers={self.connection.provider.acl_header: acl_str}
- query_args='acl'
+ query_args = 'acl'
if version_id:
query_args += '&versionId=%s' % version_id
response = self.connection.make_request('PUT', self.name, key_name,
@@ -605,8 +731,8 @@ class Bucket(object):
response.status, response.reason, body)
return body
- def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None):
- query_args = 'acl'
+ def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None,
+ query_args='acl'):
if version_id:
query_args += '&versionId=%s' % version_id
response = self.connection.make_request('PUT', self.name, key_name,
@@ -643,6 +769,80 @@ class Bucket(object):
raise self.connection.provider.storage_response_error(
response.status, response.reason, body)
+ def set_subresource(self, subresource, value, key_name = '', headers=None,
+ version_id=None):
+ """
+ Set a subresource for a bucket or key.
+
+ :type subresource: string
+ :param subresource: The subresource to set.
+
+ :type value: string
+ :param value: The value of the subresource.
+
+ :type key_name: string
+ :param key_name: The key to operate on, or None to operate on the
+ bucket.
+
+ :type headers: dict
+ :param headers: Additional HTTP headers to include in the request.
+
+ :type src_version_id: string
+ :param src_version_id: Optional. The version id of the key to operate
+ on. If not specified, operate on the newest
+ version.
+ """
+ if not subresource:
+ raise TypeError('set_subresource called with subresource=None')
+ query_args = subresource
+ if version_id:
+ query_args += '&versionId=%s' % version_id
+ response = self.connection.make_request('PUT', self.name, key_name,
+ data=value.encode('UTF-8'),
+ query_args=query_args,
+ headers=headers)
+ body = response.read()
+ if response.status != 200:
+ raise self.connection.provider.storage_response_error(
+ response.status, response.reason, body)
+
+ def get_subresource(self, subresource, key_name='', headers=None,
+ version_id=None):
+ """
+ Get a subresource for a bucket or key.
+
+ :type subresource: string
+ :param subresource: The subresource to get.
+
+ :type key_name: string
+ :param key_name: The key to operate on, or None to operate on the
+ bucket.
+
+ :type headers: dict
+ :param headers: Additional HTTP headers to include in the request.
+
+ :type src_version_id: string
+ :param src_version_id: Optional. The version id of the key to operate
+ on. If not specified, operate on the newest
+ version.
+
+ :rtype: string
+ :returns: The value of the subresource.
+ """
+ if not subresource:
+ raise TypeError('get_subresource called with subresource=None')
+ query_args = subresource
+ if version_id:
+ query_args += '&versionId=%s' % version_id
+ response = self.connection.make_request('GET', self.name, key_name,
+ query_args=query_args,
+ headers=headers)
+ body = response.read()
+ if response.status != 200:
+ raise self.connection.provider.storage_response_error(
+ response.status, response.reason, body)
+ return body
+
def make_public(self, recursive=False, headers=None):
self.set_canned_acl('public-read', headers=headers)
if recursive:
@@ -684,8 +884,8 @@ class Bucket(object):
for key in self:
key.add_email_grant(permission, email_address, headers=headers)
- def add_user_grant(self, permission, user_id,
- recursive=False, headers=None):
+ def add_user_grant(self, permission, user_id, recursive=False,
+ headers=None, display_name=None):
"""
Convenience method that provides a quick way to add a canonical
user grant to a bucket. This method retrieves the current ACL,
@@ -708,16 +908,22 @@ class Bucket(object):
in the bucket and apply the same grant to each key.
CAUTION: If you have a lot of keys, this could take
a long time!
+
+ :type display_name: string
+ :param display_name: An option string containing the user's
+ Display Name. Only required on Walrus.
"""
if permission not in S3Permissions:
raise self.connection.provider.storage_permissions_error(
'Unknown Permission: %s' % permission)
policy = self.get_acl(headers=headers)
- policy.acl.add_user_grant(permission, user_id)
+ policy.acl.add_user_grant(permission, user_id,
+ display_name=display_name)
self.set_acl(policy, headers=headers)
if recursive:
for key in self:
- key.add_user_grant(permission, user_id, headers=headers)
+ key.add_user_grant(permission, user_id, headers=headers,
+ display_name=display_name)
def list_grants(self, headers=None):
policy = self.get_acl(headers=headers)
@@ -744,10 +950,20 @@ class Bucket(object):
raise self.connection.provider.storage_response_error(
response.status, response.reason, body)
- def enable_logging(self, target_bucket, target_prefix='', headers=None):
- if isinstance(target_bucket, Bucket):
- target_bucket = target_bucket.name
- body = self.BucketLoggingBody % (target_bucket, target_prefix)
+ def set_xml_logging(self, logging_str, headers=None):
+ """
+ Set logging on a bucket directly to the given xml string.
+
+ :type logging_str: unicode string
+ :param logging_str: The XML for the bucketloggingstatus which will be set.
+ The string will be converted to utf-8 before it is sent.
+ Usually, you will obtain this XML from the BucketLogging
+ object.
+
+ :rtype: bool
+ :return: True if ok or raises an exception.
+ """
+ body = logging_str.encode('utf-8')
response = self.connection.make_request('PUT', self.name, data=body,
query_args='logging', headers=headers)
body = response.read()
@@ -757,28 +973,63 @@ class Bucket(object):
raise self.connection.provider.storage_response_error(
response.status, response.reason, body)
+ def enable_logging(self, target_bucket, target_prefix='', grants=None, headers=None):
+ """
+ Enable logging on a bucket.
+
+ :type target_bucket: bucket or string
+ :param target_bucket: The bucket to log to.
+
+ :type target_prefix: string
+ :param target_prefix: The prefix which should be prepended to the
+ generated log files written to the target_bucket.
+
+ :type grants: list of Grant objects
+ :param grants: A list of extra permissions which will be granted on
+ the log files which are created.
+
+ :rtype: bool
+ :return: True if ok or raises an exception.
+ """
+ if isinstance(target_bucket, Bucket):
+ target_bucket = target_bucket.name
+ blogging = BucketLogging(target=target_bucket, prefix=target_prefix, grants=grants)
+ return self.set_xml_logging(blogging.to_xml(), headers=headers)
+
def disable_logging(self, headers=None):
- body = self.EmptyBucketLoggingBody
- response = self.connection.make_request('PUT', self.name, data=body,
- query_args='logging', headers=headers)
- body = response.read()
- if response.status == 200:
- return True
- else:
- raise self.connection.provider.storage_response_error(
- response.status, response.reason, body)
+ """
+ Disable logging on a bucket.
+
+ :rtype: bool
+ :return: True if ok or raises an exception.
+ """
+ blogging = BucketLogging()
+ return self.set_xml_logging(blogging.to_xml(), headers=headers)
def get_logging_status(self, headers=None):
+ """
+ Get the logging status for this bucket.
+
+ :rtype: :class:`boto.s3.bucketlogging.BucketLogging`
+ :return: A BucketLogging object for this bucket.
+ """
response = self.connection.make_request('GET', self.name,
query_args='logging', headers=headers)
body = response.read()
if response.status == 200:
- return body
+ blogging = BucketLogging()
+ h = handler.XmlHandler(blogging, self)
+ xml.sax.parseString(body, h)
+ return blogging
else:
raise self.connection.provider.storage_response_error(
response.status, response.reason, body)
def set_as_logging_target(self, headers=None):
+ """
+ Setup the current bucket as a logging target by granting the necessary
+ permissions to the LogDelivery group to write log files to this bucket.
+ """
policy = self.get_acl(headers=headers)
g1 = Grant(permission='WRITE', type='Group', uri=self.LoggingGroup)
g2 = Grant(permission='READ_ACP', type='Group', uri=self.LoggingGroup)
@@ -812,8 +1063,7 @@ class Bucket(object):
"""
Configure versioning for this bucket.
- ..note:: This feature is currently in beta release and is available
- only in the Northern California region.
+ ..note:: This feature is currently in beta.
:type versioning: bool
:param versioning: A boolean indicating whether version is
@@ -887,6 +1137,67 @@ class Bucket(object):
raise self.connection.provider.storage_response_error(
response.status, response.reason, body)
+ def configure_lifecycle(self, lifecycle_config, headers=None):
+ """
+ Configure lifecycle for this bucket.
+
+ :type lifecycle_config: :class:`boto.s3.lifecycle.Lifecycle`
+ :param lifecycle_config: The lifecycle configuration you want
+ to configure for this bucket.
+ """
+ fp = StringIO.StringIO(lifecycle_config.to_xml())
+ md5 = boto.utils.compute_md5(fp)
+ if headers is None:
+ headers = {}
+ headers['Content-MD5'] = md5[1]
+ headers['Content-Type'] = 'text/xml'
+ response = self.connection.make_request('PUT', self.name,
+ data=fp.getvalue(),
+ query_args='lifecycle',
+ headers=headers)
+ body = response.read()
+ if response.status == 200:
+ return True
+ else:
+ raise self.connection.provider.storage_response_error(
+ response.status, response.reason, body)
+
+ def get_lifecycle_config(self, headers=None):
+ """
+ Returns the current lifecycle configuration on the bucket.
+
+ :rtype: :class:`boto.s3.lifecycle.Lifecycle`
+ :returns: A LifecycleConfig object that describes all current
+ lifecycle rules in effect for the bucket.
+ """
+ response = self.connection.make_request('GET', self.name,
+ query_args='lifecycle', headers=headers)
+ body = response.read()
+ boto.log.debug(body)
+ if response.status == 200:
+ lifecycle = Lifecycle()
+ h = handler.XmlHandler(lifecycle, self)
+ xml.sax.parseString(body, h)
+ return lifecycle
+ else:
+ raise self.connection.provider.storage_response_error(
+ response.status, response.reason, body)
+
+ def delete_lifecycle_configuration(self, headers=None):
+ """
+ Removes all lifecycle configuration from the bucket.
+ """
+ response = self.connection.make_request('DELETE', self.name,
+ query_args='lifecycle',
+ headers=headers)
+ body = response.read()
+ boto.log.debug(body)
+ if response.status == 204:
+ return True
+ else:
+ raise self.connection.provider.storage_response_error(
+ response.status, response.reason, body)
+
def configure_website(self, suffix, error_key='', headers=None):
"""
Configure this bucket to act as a website
@@ -928,15 +1239,15 @@ class Bucket(object):
:returns: A dictionary containing a Python representation
of the XML response from S3. The overall structure is:
- * WebsiteConfiguration
-
- * IndexDocument
-
- * Suffix : suffix that is appended to request that
- is for a "directory" on the website endpoint
- * ErrorDocument
-
- * Key : name of object to serve when an error occurs
+ * WebsiteConfiguration
+
+ * IndexDocument
+
+ * Suffix : suffix that is appended to request that
+ is for a "directory" on the website endpoint
+ * ErrorDocument
+
+ * Key : name of object to serve when an error occurs
"""
response = self.connection.make_request('GET', self.name,
query_args='website', headers=headers)
@@ -977,6 +1288,10 @@ class Bucket(object):
return '.'.join(l)
def get_policy(self, headers=None):
+ """
+ Returns the JSON policy associated with the bucket. The policy
+ is returned as an uninterpreted JSON string.
+ """
response = self.connection.make_request('GET', self.name,
query_args='policy', headers=headers)
body = response.read()
@@ -987,6 +1302,12 @@ class Bucket(object):
response.status, response.reason, body)
def set_policy(self, policy, headers=None):
+ """
+ Add or replace the JSON policy associated with the bucket.
+
+ :type policy: str
+ :param policy: The JSON policy as a string.
+ """
response = self.connection.make_request('PUT', self.name,
data=policy,
query_args='policy',
@@ -998,8 +1319,22 @@ class Bucket(object):
raise self.connection.provider.storage_response_error(
response.status, response.reason, body)
+ def delete_policy(self, headers=None):
+ response = self.connection.make_request('DELETE', self.name,
+ data='/?policy',
+ query_args='policy',
+ headers=headers)
+ body = response.read()
+ if response.status >= 200 and response.status <= 204:
+ return True
+ else:
+ raise self.connection.provider.storage_response_error(
+ response.status, response.reason, body)
+
+
def initiate_multipart_upload(self, key_name, headers=None,
- reduced_redundancy=False, metadata=None):
+ reduced_redundancy=False,
+ metadata=None, encrypt_key=False):
"""
Start a multipart upload operation.
@@ -1024,16 +1359,25 @@ class Bucket(object):
:type metadata: dict
:param metadata: Any metadata that you would like to set on the key
that results from the multipart upload.
+
+ :type encrypt_key: bool
+ :param encrypt_key: If True, the new copy of the object will
+ be encrypted on the server-side by S3 and
+ will be stored in an encrypted form while
+ at rest in S3.
"""
query_args = 'uploads'
+ provider = self.connection.provider
if headers is None:
headers = {}
if reduced_redundancy:
- storage_class_header = self.connection.provider.storage_class_header
+ storage_class_header = provider.storage_class_header
if storage_class_header:
headers[storage_class_header] = 'REDUCED_REDUNDANCY'
# TODO: what if the provider doesn't support reduced redundancy?
# (see boto.s3.key.Key.set_contents_from_file)
+ if encrypt_key:
+ headers[provider.server_side_encryption_header] = 'AES256'
if metadata is None:
metadata = {}
@@ -1095,4 +1439,3 @@ class Bucket(object):
def delete(self, headers=None):
return self.connection.delete_bucket(self.name, headers=headers)
-
« 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