[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#769334: unblock: python-boto/2.34.0-2



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package python-boto to address bts#769310.

Much of the full diff consists of whitespace changes, so I've attached
output that separates them (filterdiff commands at the bottom of this
email).  

I realize that new upstreams are frowned upon at this stage, but aside
from the sigv4 fixes (important in their own right), and the addition of
support for eu-central-1 (a big deal, particularly for German users of
AWS), there is only a handful of minor changes (typos and the like) that
remain.  Backporting the required changes would come very close to
yielding the same results.

I also realize that I pulled the trigger on an unstable upload too quickly,
and that I should have sought approval first.  I also failed to reference
#769310 in the changelog.  Sorry about that, I have no excuse.

Thanks,


unblock python-boto/2.34.0-2

-- System Information:
Debian Release: jessie/sid
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing'), (500, 'stable'), (1, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.16.0-4-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_US.utf8, LC_CTYPE=en_US.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash

[1]: whitespace
  debdiff python-boto_2.33.0-1.dsc python-boto_2.34.0-2.dsc \
      | filterdiff \
            -ipython-boto-2.33.0/boto/cloudtrail/exceptions.py \
            -ipython-boto-2.33.0/boto/cloudtrail/layer1.py \
            -ipython-boto-2.33.0/boto/datapipeline/layer1.py \
            -ipython-boto-2.33.0/boto/directconnect/exceptions.py \
            -ipython-boto-2.33.0/boto/directconnect/layer1.py \
            -ipython-boto-2.33.0/boto/logs/layer1.py

[2]: without whitespace
  debdiff python-boto_2.33.0-1.dsc python-boto_2.34.0-2.dsc \
      | filterdiff \
            -xpython-boto-2.33.0/boto/cloudtrail/exceptions.py \
            -xpython-boto-2.33.0/boto/cloudtrail/layer1.py \
            -xpython-boto-2.33.0/boto/datapipeline/layer1.py \
            -xpython-boto-2.33.0/boto/directconnect/exceptions.py \
            -xpython-boto-2.33.0/boto/directconnect/layer1.py \
            -xpython-boto-2.33.0/boto/logs/layer1.py
 

-- 
Eric Evans
eevans@debian.org
--- python-boto-2.33.0/boto/cloudtrail/exceptions.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/cloudtrail/exceptions.py	2014-10-23 11:19:50.000000000 -0500
@@ -24,6 +24,7 @@
     """
     pass
 
+
 class InsufficientSnsTopicPolicyException(BotoServerError):
     """
     Raised when the SNS topic does not allow Cloudtrail to post
@@ -31,18 +32,21 @@
     """
     pass
 
+
 class InvalidTrailNameException(BotoServerError):
     """
     Raised when the trail name is invalid.
     """
     pass
 
+
 class InternalErrorException(BotoServerError):
     """
     Raised when there was an internal Cloudtrail error.
     """
     pass
 
+
 class TrailNotFoundException(BotoServerError):
     """
     Raised when the given trail name is not found.
--- python-boto-2.33.0/boto/cloudtrail/layer1.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/cloudtrail/layer1.py	2014-10-23 11:19:50.000000000 -0500
@@ -75,7 +75,6 @@
         "InsufficientS3BucketPolicyException": exceptions.InsufficientS3BucketPolicyException,
     }
 
-
     def __init__(self, **kwargs):
         region = kwargs.pop('region', None)
         if not region:
@@ -351,4 +350,3 @@
             exception_class = self._faults.get(fault_name, self.ResponseError)
             raise exception_class(response.status, response.reason,
                                   body=json_body)
-
--- python-boto-2.33.0/boto/datapipeline/layer1.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/datapipeline/layer1.py	2014-10-23 11:19:50.000000000 -0500
@@ -83,7 +83,6 @@
         "InternalServiceError": exceptions.InternalServiceError,
     }
 
-
     def __init__(self, **kwargs):
         region = kwargs.pop('region', None)
         if not region:
@@ -638,4 +637,3 @@
             exception_class = self._faults.get(fault_name, self.ResponseError)
             raise exception_class(response.status, response.reason,
                                   body=json_body)
-
--- python-boto-2.33.0/boto/directconnect/exceptions.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/directconnect/exceptions.py	2014-10-23 11:19:50.000000000 -0500
@@ -20,6 +20,7 @@
 # IN THE SOFTWARE.
 #
 
+
 class DirectConnectClientException(Exception):
     pass
 
--- python-boto-2.33.0/boto/directconnect/layer1.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/directconnect/layer1.py	2014-10-23 11:19:50.000000000 -0500
@@ -65,7 +65,6 @@
         "DirectConnectServerException": exceptions.DirectConnectServerException,
     }
 
-
     def __init__(self, **kwargs):
         region = kwargs.pop('region', None)
         if not region:
@@ -626,4 +625,3 @@
             exception_class = self._faults.get(fault_name, self.ResponseError)
             raise exception_class(response.status, response.reason,
                                   body=json_body)
-
--- python-boto-2.33.0/boto/logs/layer1.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/logs/layer1.py	2014-10-23 11:19:50.000000000 -0500
@@ -95,7 +95,6 @@
         "InvalidSequenceTokenException": exceptions.InvalidSequenceTokenException,
     }
 
-
     def __init__(self, **kwargs):
         region = kwargs.pop('region', None)
         if not region:
diff -Nru python-boto-2.33.0/boto/auth.py python-boto-2.34.0/boto/auth.py
--- python-boto-2.33.0/boto/auth.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/auth.py	2014-10-23 11:19:50.000000000 -0500
@@ -345,7 +345,7 @@
         for param in sorted(http_request.params):
             value = boto.utils.get_utf8_value(http_request.params[param])
             l.append('%s=%s' % (urllib.parse.quote(param, safe='-_.~'),
-                                urllib.parse.quote(value.decode('utf-8'), safe='-_.~')))
+                                urllib.parse.quote(value, safe='-_.~')))
         return '&'.join(l)
 
     def canonical_headers(self, headers_to_sign):
@@ -494,10 +494,25 @@
         if self._provider.security_token:
             req.headers['X-Amz-Security-Token'] = self._provider.security_token
         qs = self.query_string(req)
-        if qs and req.method == 'POST':
+
+        qs_to_post = qs
+
+        # We do not want to include any params that were mangled into
+        # the params if performing s3-sigv4 since it does not
+        # belong in the body of a post for some requests.  Mangled
+        # refers to items in the query string URL being added to the
+        # http response params. However, these params get added to
+        # the body of the request, but the query string URL does not
+        # belong in the body of the request. ``unmangled_resp`` is the
+        # response that happened prior to the mangling.  This ``unmangled_req``
+        # kwarg will only appear for s3-sigv4.
+        if 'unmangled_req' in kwargs:
+            qs_to_post = self.query_string(kwargs['unmangled_req'])
+
+        if qs_to_post and req.method == 'POST':
             # Stash request parameters into post body
             # before we generate the signature.
-            req.body = qs
+            req.body = qs_to_post
             req.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
             req.headers['Content-Length'] = str(len(req.body))
         else:
@@ -549,6 +564,17 @@
         encoded = urllib.parse.quote(unquoted)
         return encoded
 
+    def canonical_query_string(self, http_request):
+        # Note that we just do not return an empty string for
+        # POST request. Query strings in url are included in canonical
+        # query string.
+        l = []
+        for param in sorted(http_request.params):
+            value = boto.utils.get_utf8_value(http_request.params[param])
+            l.append('%s=%s' % (urllib.parse.quote(param, safe='-_.~'),
+                                urllib.parse.quote(value, safe='-_.~')))
+        return '&'.join(l)
+
     def host_header(self, host, http_request):
         port = http_request.port
         secure = http_request.protocol == 'https'
@@ -641,6 +667,13 @@
 
         if modified_req.params is None:
             modified_req.params = {}
+        else:
+            # To keep the original request object untouched. We must make
+            # a copy of the params dictionary. Because the copy of the
+            # original request directly refers to the params dictionary
+            # of the original request.
+            copy_params = req.params.copy()
+            modified_req.params = copy_params
 
         raw_qs = parsed_path.query
         existing_qs = urllib.parse.parse_qs(
@@ -670,9 +703,10 @@
                 req.headers['x-amz-content-sha256'] = req.headers.pop('_sha256')
             else:
                 req.headers['x-amz-content-sha256'] = self.payload(req)
-
-        req = self.mangle_path_and_params(req)
-        return super(S3HmacAuthV4Handler, self).add_auth(req, **kwargs)
+        updated_req = self.mangle_path_and_params(req)
+        return super(S3HmacAuthV4Handler, self).add_auth(updated_req,
+                                                         unmangled_req=req,
+                                                         **kwargs)
 
     def presign(self, req, expires, iso_date=None):
         """
@@ -966,7 +1000,8 @@
             # ``boto/iam/connection.py``, as several things there are also
             # endpoint-related.
             if getattr(self.region, 'endpoint', ''):
-                if '.cn-' in self.region.endpoint:
+                if '.cn-' in self.region.endpoint or \
+                        '.eu-central' in self.region.endpoint:
                     return ['hmac-v4']
 
         return func(self)
@@ -985,7 +1020,7 @@
             # If you're making changes here, you should also check
             # ``boto/iam/connection.py``, as several things there are also
             # endpoint-related.
-            if '.cn-' in self.host:
+            if '.cn-' in self.host or '.eu-central' in self.host:
                 return ['hmac-v4-s3']
 
         return func(self)
diff -Nru python-boto-2.33.0/boto/cloudtrail/exceptions.py python-boto-2.34.0/boto/cloudtrail/exceptions.py
diff -Nru python-boto-2.33.0/boto/cloudtrail/layer1.py python-boto-2.34.0/boto/cloudtrail/layer1.py
diff -Nru python-boto-2.33.0/boto/datapipeline/layer1.py python-boto-2.34.0/boto/datapipeline/layer1.py
diff -Nru python-boto-2.33.0/boto/directconnect/exceptions.py python-boto-2.34.0/boto/directconnect/exceptions.py
diff -Nru python-boto-2.33.0/boto/directconnect/layer1.py python-boto-2.34.0/boto/directconnect/layer1.py
diff -Nru python-boto-2.33.0/boto/endpoints.json python-boto-2.34.0/boto/endpoints.json
--- python-boto-2.33.0/boto/endpoints.json	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/endpoints.json	2014-10-23 11:19:50.000000000 -0500
@@ -9,7 +9,8 @@
         "us-east-1": "autoscaling.us-east-1.amazonaws.com",
         "us-gov-west-1": "autoscaling.us-gov-west-1.amazonaws.com",
         "us-west-1": "autoscaling.us-west-1.amazonaws.com",
-        "us-west-2": "autoscaling.us-west-2.amazonaws.com"
+        "us-west-2": "autoscaling.us-west-2.amazonaws.com",
+        "eu-central-1": "autoscaling.eu-central-1.amazonaws.com"
     },
     "cloudformation": {
         "ap-northeast-1": "cloudformation.ap-northeast-1.amazonaws.com",
@@ -21,7 +22,8 @@
         "us-east-1": "cloudformation.us-east-1.amazonaws.com",
         "us-gov-west-1": "cloudformation.us-gov-west-1.amazonaws.com",
         "us-west-1": "cloudformation.us-west-1.amazonaws.com",
-        "us-west-2": "cloudformation.us-west-2.amazonaws.com"
+        "us-west-2": "cloudformation.us-west-2.amazonaws.com",
+        "eu-central-1": "cloudformation.eu-central-1.amazonaws.com"
     },
     "cloudfront": {
         "ap-northeast-1": "cloudfront.amazonaws.com",
@@ -31,7 +33,8 @@
         "sa-east-1": "cloudfront.amazonaws.com",
         "us-east-1": "cloudfront.amazonaws.com",
         "us-west-1": "cloudfront.amazonaws.com",
-        "us-west-2": "cloudfront.amazonaws.com"
+        "us-west-2": "cloudfront.amazonaws.com",
+        "eu-central-1": "cloudfront.amazonaws.com"
     },
     "cloudsearch": {
         "ap-southeast-1": "cloudsearch.ap-southeast-1.amazonaws.com",
@@ -41,7 +44,8 @@
         "eu-west-1": "cloudsearch.eu-west-1.amazonaws.com",
         "us-east-1": "cloudsearch.us-east-1.amazonaws.com",
         "us-west-1": "cloudsearch.us-west-1.amazonaws.com",
-        "us-west-2": "cloudsearch.us-west-2.amazonaws.com"
+        "us-west-2": "cloudsearch.us-west-2.amazonaws.com",
+        "eu-central-1": "cloudsearch.eu-central-1.amazonaws.com"
     },
     "cloudtrail": {
         "ap-northeast-1": "cloudtrail.ap-northeast-1.amazonaws.com",
@@ -51,7 +55,8 @@
         "sa-east-1": "cloudtrail.sa-east-1.amazonaws.com",
         "us-east-1": "cloudtrail.us-east-1.amazonaws.com",
         "us-west-1": "cloudtrail.us-west-1.amazonaws.com",
-        "us-west-2": "cloudtrail.us-west-2.amazonaws.com"
+        "us-west-2": "cloudtrail.us-west-2.amazonaws.com",
+        "eu-central-1": "cloudtrail.eu-central-1.amazonaws.com"
     },
     "cloudwatch": {
         "ap-northeast-1": "monitoring.ap-northeast-1.amazonaws.com",
@@ -63,7 +68,8 @@
         "us-east-1": "monitoring.us-east-1.amazonaws.com",
         "us-gov-west-1": "monitoring.us-gov-west-1.amazonaws.com",
         "us-west-1": "monitoring.us-west-1.amazonaws.com",
-        "us-west-2": "monitoring.us-west-2.amazonaws.com"
+        "us-west-2": "monitoring.us-west-2.amazonaws.com",
+        "eu-central-1": "monitoring.eu-central-1.amazonaws.com"
     },
     "cognito-identity": {
         "us-east-1": "cognito-identity.us-east-1.amazonaws.com"
@@ -76,7 +82,8 @@
         "us-west-2": "datapipeline.us-west-2.amazonaws.com",
         "eu-west-1": "datapipeline.eu-west-1.amazonaws.com",
         "ap-southeast-2": "datapipeline.ap-southeast-2.amazonaws.com",
-        "ap-northeast-1": "datapipeline.ap-northeast-1.amazonaws.com"
+        "ap-northeast-1": "datapipeline.ap-northeast-1.amazonaws.com",
+        "eu-central-1": "datapipeline.eu-central-1.amazonaws.com"
     },
     "directconnect": {
         "ap-northeast-1": "directconnect.ap-northeast-1.amazonaws.com",
@@ -86,7 +93,8 @@
         "sa-east-1": "directconnect.sa-east-1.amazonaws.com",
         "us-east-1": "directconnect.us-east-1.amazonaws.com",
         "us-west-1": "directconnect.us-west-1.amazonaws.com",
-        "us-west-2": "directconnect.us-west-2.amazonaws.com"
+        "us-west-2": "directconnect.us-west-2.amazonaws.com",
+        "eu-central-1": "directconnect.eu-central-1.amazonaws.com"
     },
     "dynamodb": {
         "ap-northeast-1": "dynamodb.ap-northeast-1.amazonaws.com",
@@ -98,7 +106,8 @@
         "us-east-1": "dynamodb.us-east-1.amazonaws.com",
         "us-gov-west-1": "dynamodb.us-gov-west-1.amazonaws.com",
         "us-west-1": "dynamodb.us-west-1.amazonaws.com",
-        "us-west-2": "dynamodb.us-west-2.amazonaws.com"
+        "us-west-2": "dynamodb.us-west-2.amazonaws.com",
+        "eu-central-1": "dynamodb.eu-central-1.amazonaws.com"
     },
     "ec2": {
         "ap-northeast-1": "ec2.ap-northeast-1.amazonaws.com",
@@ -110,7 +119,8 @@
         "us-east-1": "ec2.us-east-1.amazonaws.com",
         "us-gov-west-1": "ec2.us-gov-west-1.amazonaws.com",
         "us-west-1": "ec2.us-west-1.amazonaws.com",
-        "us-west-2": "ec2.us-west-2.amazonaws.com"
+        "us-west-2": "ec2.us-west-2.amazonaws.com",
+        "eu-central-1": "ec2.eu-central-1.amazonaws.com"
     },
     "elasticache": {
         "ap-northeast-1": "elasticache.ap-northeast-1.amazonaws.com",
@@ -121,7 +131,8 @@
         "sa-east-1": "elasticache.sa-east-1.amazonaws.com",
         "us-east-1": "elasticache.us-east-1.amazonaws.com",
         "us-west-1": "elasticache.us-west-1.amazonaws.com",
-        "us-west-2": "elasticache.us-west-2.amazonaws.com"
+        "us-west-2": "elasticache.us-west-2.amazonaws.com",
+        "eu-central-1": "elasticache.eu-central-1.amazonaws.com"
     },
     "elasticbeanstalk": {
         "ap-northeast-1": "elasticbeanstalk.ap-northeast-1.amazonaws.com",
@@ -131,7 +142,8 @@
         "sa-east-1": "elasticbeanstalk.sa-east-1.amazonaws.com",
         "us-east-1": "elasticbeanstalk.us-east-1.amazonaws.com",
         "us-west-1": "elasticbeanstalk.us-west-1.amazonaws.com",
-        "us-west-2": "elasticbeanstalk.us-west-2.amazonaws.com"
+        "us-west-2": "elasticbeanstalk.us-west-2.amazonaws.com",
+        "eu-central-1": "elasticbeanstalk.eu-central-1.amazonaws.com"
     },
     "elasticloadbalancing": {
         "ap-northeast-1": "elasticloadbalancing.ap-northeast-1.amazonaws.com",
@@ -143,7 +155,8 @@
         "us-east-1": "elasticloadbalancing.us-east-1.amazonaws.com",
         "us-gov-west-1": "elasticloadbalancing.us-gov-west-1.amazonaws.com",
         "us-west-1": "elasticloadbalancing.us-west-1.amazonaws.com",
-        "us-west-2": "elasticloadbalancing.us-west-2.amazonaws.com"
+        "us-west-2": "elasticloadbalancing.us-west-2.amazonaws.com",
+        "eu-central-1": "elasticloadbalancing.eu-central-1.amazonaws.com"
     },
     "elasticmapreduce": {
         "ap-northeast-1": "ap-northeast-1.elasticmapreduce.amazonaws.com",
@@ -155,7 +168,8 @@
         "us-east-1": "elasticmapreduce.us-east-1.amazonaws.com",
         "us-gov-west-1": "us-gov-west-1.elasticmapreduce.amazonaws.com",
         "us-west-1": "us-west-1.elasticmapreduce.amazonaws.com",
-        "us-west-2": "us-west-2.elasticmapreduce.amazonaws.com"
+        "us-west-2": "us-west-2.elasticmapreduce.amazonaws.com",
+        "eu-central-1": "eu-central-1.elasticmapreduce.amazonaws.com"
     },
     "elastictranscoder": {
         "ap-northeast-1": "elastictranscoder.ap-northeast-1.amazonaws.com",
@@ -163,7 +177,8 @@
         "eu-west-1": "elastictranscoder.eu-west-1.amazonaws.com",
         "us-east-1": "elastictranscoder.us-east-1.amazonaws.com",
         "us-west-1": "elastictranscoder.us-west-1.amazonaws.com",
-        "us-west-2": "elastictranscoder.us-west-2.amazonaws.com"
+        "us-west-2": "elastictranscoder.us-west-2.amazonaws.com",
+        "eu-central-1": "elastictranscoder.eu-central-1.amazonaws.com"
     },
     "glacier": {
         "ap-northeast-1": "glacier.ap-northeast-1.amazonaws.com",
@@ -172,7 +187,8 @@
         "eu-west-1": "glacier.eu-west-1.amazonaws.com",
         "us-east-1": "glacier.us-east-1.amazonaws.com",
         "us-west-1": "glacier.us-west-1.amazonaws.com",
-        "us-west-2": "glacier.us-west-2.amazonaws.com"
+        "us-west-2": "glacier.us-west-2.amazonaws.com",
+        "eu-central-1": "glacier.eu-central-1.amazonaws.com"
     },
     "iam": {
         "ap-northeast-1": "iam.amazonaws.com",
@@ -202,13 +218,18 @@
         "eu-west-1": "kinesis.eu-west-1.amazonaws.com",
         "ap-southeast-1": "kinesis.ap-southeast-1.amazonaws.com",
         "ap-southeast-2": "kinesis.ap-southeast-2.amazonaws.com",
-        "ap-northeast-1": "kinesis.ap-northeast-1.amazonaws.com"
+        "ap-northeast-1": "kinesis.ap-northeast-1.amazonaws.com",
+        "eu-central-1": "kinesis.eu-central-1.amazonaws.com"
     },
     "logs": {
-        "us-east-1": "logs.us-east-1.amazonaws.com"
+        "us-east-1": "logs.us-east-1.amazonaws.com",
+        "us-west-2": "logs.us-west-2.amazonaws.com",
+        "eu-west-1": "logs.eu-west-1.amazonaws.com",
+        "eu-central-1": "logs.eu-central-1.amazonaws.com"
     },
     "opsworks": {
-        "us-east-1": "opsworks.us-east-1.amazonaws.com"
+        "us-east-1": "opsworks.us-east-1.amazonaws.com",
+        "eu-central-1": "opsworks.eu-central-1.amazonaws.com"
     },
     "rds": {
         "ap-northeast-1": "rds.ap-northeast-1.amazonaws.com",
@@ -220,7 +241,8 @@
         "us-east-1": "rds.amazonaws.com",
         "us-gov-west-1": "rds.us-gov-west-1.amazonaws.com",
         "us-west-1": "rds.us-west-1.amazonaws.com",
-        "us-west-2": "rds.us-west-2.amazonaws.com"
+        "us-west-2": "rds.us-west-2.amazonaws.com",
+        "eu-central-1": "rds.eu-central-1.amazonaws.com"
     },
     "redshift": {
         "ap-northeast-1": "redshift.ap-northeast-1.amazonaws.com",
@@ -228,7 +250,8 @@
         "ap-southeast-2": "redshift.ap-southeast-2.amazonaws.com",
         "eu-west-1": "redshift.eu-west-1.amazonaws.com",
         "us-east-1": "redshift.us-east-1.amazonaws.com",
-        "us-west-2": "redshift.us-west-2.amazonaws.com"
+        "us-west-2": "redshift.us-west-2.amazonaws.com",
+        "eu-central-1": "redshift.eu-central-1.amazonaws.com"
     },
     "route53": {
         "ap-northeast-1": "route53.amazonaws.com",
@@ -253,7 +276,8 @@
         "us-east-1": "s3.amazonaws.com",
         "us-gov-west-1": "s3-us-gov-west-1.amazonaws.com",
         "us-west-1": "s3-us-west-1.amazonaws.com",
-        "us-west-2": "s3-us-west-2.amazonaws.com"
+        "us-west-2": "s3-us-west-2.amazonaws.com",
+        "eu-central-1": "s3.eu-central-1.amazonaws.com"
     },
     "sdb": {
         "ap-northeast-1": "sdb.ap-northeast-1.amazonaws.com",
@@ -263,12 +287,14 @@
         "sa-east-1": "sdb.sa-east-1.amazonaws.com",
         "us-east-1": "sdb.amazonaws.com",
         "us-west-1": "sdb.us-west-1.amazonaws.com",
-        "us-west-2": "sdb.us-west-2.amazonaws.com"
+        "us-west-2": "sdb.us-west-2.amazonaws.com",
+        "eu-central-1": "sdb.eu-central-1.amazonaws.com"
     },
     "ses": {
         "eu-west-1": "email.eu-west-1.amazonaws.com",
         "us-east-1": "email.us-east-1.amazonaws.com",
-        "us-west-2": "email.us-west-2.amazonaws.com"
+        "us-west-2": "email.us-west-2.amazonaws.com",
+        "eu-central-1": "email.eu-central-1.amazonaws.com"
     },
     "sns": {
         "ap-northeast-1": "sns.ap-northeast-1.amazonaws.com",
@@ -280,7 +306,8 @@
         "us-east-1": "sns.us-east-1.amazonaws.com",
         "us-gov-west-1": "sns.us-gov-west-1.amazonaws.com",
         "us-west-1": "sns.us-west-1.amazonaws.com",
-        "us-west-2": "sns.us-west-2.amazonaws.com"
+        "us-west-2": "sns.us-west-2.amazonaws.com",
+        "eu-central-1": "sns.eu-central-1.amazonaws.com"
     },
     "sqs": {
         "ap-northeast-1": "ap-northeast-1.queue.amazonaws.com",
@@ -292,7 +319,8 @@
         "us-east-1": "queue.amazonaws.com",
         "us-gov-west-1": "us-gov-west-1.queue.amazonaws.com",
         "us-west-1": "us-west-1.queue.amazonaws.com",
-        "us-west-2": "us-west-2.queue.amazonaws.com"
+        "us-west-2": "us-west-2.queue.amazonaws.com",
+        "eu-central-1": "eu-central-1.queue.amazonaws.com"
     },
     "storagegateway": {
         "ap-northeast-1": "storagegateway.ap-northeast-1.amazonaws.com",
@@ -302,7 +330,8 @@
         "sa-east-1": "storagegateway.sa-east-1.amazonaws.com",
         "us-east-1": "storagegateway.us-east-1.amazonaws.com",
         "us-west-1": "storagegateway.us-west-1.amazonaws.com",
-        "us-west-2": "storagegateway.us-west-2.amazonaws.com"
+        "us-west-2": "storagegateway.us-west-2.amazonaws.com",
+        "eu-central-1": "storagegateway.eu-central-1.amazonaws.com"
     },
     "sts": {
         "ap-northeast-1": "sts.amazonaws.com",
@@ -317,7 +346,8 @@
         "us-west-2": "sts.amazonaws.com"
     },
     "support": {
-        "us-east-1": "support.us-east-1.amazonaws.com"
+        "us-east-1": "support.us-east-1.amazonaws.com",
+        "eu-central-1": "support.eu-central-1.amazonaws.com"
     },
     "swf": {
         "ap-northeast-1": "swf.ap-northeast-1.amazonaws.com",
@@ -329,6 +359,7 @@
         "us-east-1": "swf.us-east-1.amazonaws.com",
         "us-gov-west-1": "swf.us-gov-west-1.amazonaws.com",
         "us-west-1": "swf.us-west-1.amazonaws.com",
-        "us-west-2": "swf.us-west-2.amazonaws.com"
+        "us-west-2": "swf.us-west-2.amazonaws.com",
+        "eu-central-1": "swf.eu-central-1.amazonaws.com"
     }
 }
diff -Nru python-boto-2.33.0/boto/iam/connection.py python-boto-2.34.0/boto/iam/connection.py
--- python-boto-2.33.0/boto/iam/connection.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/iam/connection.py	2014-10-23 11:19:50.000000000 -0500
@@ -1523,3 +1523,24 @@
         """
         params = {}
         return self.get_response('GetCredentialReport', params)
+
+    def create_virtual_mfa_device(self, path, device_name):
+        """
+        Creates a new virtual MFA device for the AWS account.
+
+        After creating the virtual MFA, use enable-mfa-device to
+        attach the MFA device to an IAM user.
+
+        :type path: string
+        :param path: The path for the virtual MFA device.
+
+        :type device_name: string
+        :param device_name: The name of the virtual MFA device.
+            Used with path to uniquely identify a virtual MFA device.
+
+        """
+        params = {
+            'Path': path,
+            'VirtualMFADeviceName': device_name
+        }
+        return self.get_response('CreateVirtualMFADevice', params)
diff -Nru python-boto-2.33.0/boto/__init__.py python-boto-2.34.0/boto/__init__.py
--- python-boto-2.33.0/boto/__init__.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/__init__.py	2014-10-23 11:19:50.000000000 -0500
@@ -38,7 +38,7 @@
 from boto.compat import urlparse
 from boto.exception import InvalidUriError
 
-__version__ = '2.33.0'
+__version__ = '2.34.0'
 Version = __version__  # for backware compatibility
 
 # http://bugs.python.org/issue7980
diff -Nru python-boto-2.33.0/boto/logs/layer1.py python-boto-2.34.0/boto/logs/layer1.py
diff -Nru python-boto-2.33.0/boto/mws/connection.py python-boto-2.34.0/boto/mws/connection.py
--- python-boto-2.33.0/boto/mws/connection.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/mws/connection.py	2014-10-23 11:19:50.000000000 -0500
@@ -309,7 +309,7 @@
         try:
             response = self._mexe(request, override_num_retries=None)
         except BotoServerError as bs:
-            raise self._response_error_factor(bs.status, bs.reason, bs.body)
+            raise self._response_error_factory(bs.status, bs.reason, bs.body)
         body = response.read()
         boto.log.debug(body)
         if not body:
diff -Nru python-boto-2.33.0/boto/s3/key.py python-boto-2.34.0/boto/s3/key.py
--- python-boto-2.33.0/boto/s3/key.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/boto/s3/key.py	2014-10-23 11:19:50.000000000 -0500
@@ -934,7 +934,10 @@
         # the auth mechanism (because closures). Detect if it's SigV4 & embelish
         # while we can before the auth calculations occur.
         if 'hmac-v4-s3' in self.bucket.connection._required_auth_capability():
-            headers['_sha256'] = compute_hash(fp, hash_algorithm=hashlib.sha256)[0]
+            kwargs = {'fp': fp, 'hash_algorithm': hashlib.sha256}
+            if size is not None:
+                kwargs['size'] = size
+            headers['_sha256'] = compute_hash(**kwargs)[0]
         headers['Expect'] = '100-Continue'
         headers = boto.utils.merge_meta(headers, self.metadata, provider)
         resp = self.bucket.connection.make_request(
diff -Nru python-boto-2.33.0/debian/changelog python-boto-2.34.0/debian/changelog
--- python-boto-2.33.0/debian/changelog	2014-11-12 14:56:27.000000000 -0600
+++ python-boto-2.34.0/debian/changelog	2014-11-12 14:56:27.000000000 -0600
@@ -1,3 +1,15 @@
+python-boto (2.34.0-2) unstable; urgency=medium
+
+  * Upload to unstable.
+
+ -- Eric Evans <eevans@debian.org>  Wed, 12 Nov 2014 11:39:34 -0600
+
+python-boto (2.34.0-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Eric Evans <eevans@debian.org>  Mon, 10 Nov 2014 20:39:11 -0600
+
 python-boto (2.33.0-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru python-boto-2.33.0/docs/source/index.rst python-boto-2.34.0/docs/source/index.rst
--- python-boto-2.33.0/docs/source/index.rst	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/docs/source/index.rst	2014-10-23 11:19:50.000000000 -0500
@@ -73,8 +73,8 @@
   * :doc:`Simple Queue Service (SQS) <sqs_tut>` -- (:doc:`API Reference <ref/sqs>`) (Python 3)
   * Simple Notification Service (SNS) -- (:doc:`API Reference <ref/sns>`) (Python 3)
   * :doc:`Simple Email Service (SES) <ses_tut>` -- (:doc:`API Reference <ref/ses>`) (Python 3)
-  * Amazon Cognito Identity -- (:doc:`API Reference <ref/cognito-identity`) (Python 3)
-  * Amazon Cognito Sync -- (:doc:`API Reference <ref/cognito-sync`) (Python 3)
+  * Amazon Cognito Identity -- (:doc:`API Reference <ref/cognito-identity>`) (Python 3)
+  * Amazon Cognito Sync -- (:doc:`API Reference <ref/cognito-sync>`) (Python 3)
 
 * **Monitoring**
 
@@ -135,6 +135,7 @@
 .. toctree::
    :titlesonly:
 
+   releasenotes/v2.34.0
    releasenotes/v2.33.0
    releasenotes/v2.32.1
    releasenotes/v2.32.0
diff -Nru python-boto-2.33.0/docs/source/releasenotes/v2.34.0.rst python-boto-2.34.0/docs/source/releasenotes/v2.34.0.rst
--- python-boto-2.33.0/docs/source/releasenotes/v2.34.0.rst	1969-12-31 18:00:00.000000000 -0600
+++ python-boto-2.34.0/docs/source/releasenotes/v2.34.0.rst	2014-10-23 11:19:50.000000000 -0500
@@ -0,0 +1,21 @@
+boto v2.34.0
+============
+
+:date: 2014/10/23
+
+This release adds region support for ``eu-central-1`` , support to create
+virtual mfa devices for Identity and Access Management, and fixes several
+sigv4 issues.
+
+
+Changes
+-------
+* Calculate sha_256 correctly for s3 (:issue:`2691`, :sha:`c0a001f`)
+* Fix MTurk typo. (:issue:`2429`, :issue:`2428`, :sha:`9bfff19`)
+* Fix Amazon Cognito links in docs (:issue:`2674`, :sha:`7c28577`)
+* Add the ability to IAM to create a virtual mfa device. (:issue:`2675`, :sha:`075d402`)
+* PEP8 tidy up for several modules. (:issue:`2673`, :sha:`38abbd9`)
+* Fix s3 create multipart upload for sigv4 (:issue:`2684`, :sha:`fc73641`)
+* Updated endpoints.json for cloudwatch logs to support more regions. (:issue:`2685`, :sha:`5db2ea8`)
+
+
diff -Nru python-boto-2.33.0/docs/source/s3_tut.rst python-boto-2.34.0/docs/source/s3_tut.rst
--- python-boto-2.33.0/docs/source/s3_tut.rst	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/docs/source/s3_tut.rst	2014-10-23 11:19:50.000000000 -0500
@@ -143,7 +143,7 @@
 to and from S3 so you should be able to send and receive large files without
 any problem.
 
-When fetching a key that has already exists, you have two options. If you're
+When fetching a key that already exists, you have two options. If you're
 uncertain whether a key exists (or if you need the metadata set on it, you can
 call ``Bucket.get_key(key_name_here)``. However, if you're sure a key already
 exists within a bucket, you can skip the check for a key on the server.
diff -Nru python-boto-2.33.0/README.rst python-boto-2.34.0/README.rst
--- python-boto-2.33.0/README.rst	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/README.rst	2014-10-23 11:19:50.000000000 -0500
@@ -1,9 +1,9 @@
 ####
 boto
 ####
-boto 2.33.0
+boto 2.34.0
 
-Released: 08-Oct-2014
+Released: 23-Oct-2014
 
 .. image:: https://travis-ci.org/boto/boto.svg?branch=develop
         :target: https://travis-ci.org/boto/boto
diff -Nru python-boto-2.33.0/requirements-docs.txt python-boto-2.34.0/requirements-docs.txt
--- python-boto-2.33.0/requirements-docs.txt	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/requirements-docs.txt	2014-10-23 11:19:50.000000000 -0500
@@ -1,6 +1,6 @@
 requests>=1.2.3,<=2.0.1
 rsa==3.1.4
-Sphinx==1.1.3
+Sphinx>=1.1.3,<1.3
 simplejson==3.5.2
 argparse==1.2.1
 paramiko>=1.10.0
diff -Nru python-boto-2.33.0/tests/integration/s3/test_multipart.py python-boto-2.34.0/tests/integration/s3/test_multipart.py
--- python-boto-2.33.0/tests/integration/s3/test_multipart.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/tests/integration/s3/test_multipart.py	2014-10-23 11:19:50.000000000 -0500
@@ -32,10 +32,14 @@
 # bigger than 5M. Hence we just use 1 part so we can keep
 # things small and still test logic.
 
+import os
 import unittest
 import time
 from boto.compat import StringIO
 
+import mock
+
+import boto
 from boto.s3.connection import S3Connection
 
 
@@ -163,3 +167,63 @@
             pn += 1
         # Can't complete 2 small parts so just clean up.
         mpu.cancel_upload()
+
+
+class S3MultiPartUploadSigV4Test(unittest.TestCase):
+    s3 = True
+
+    def setUp(self):
+        self.env_patch = mock.patch('os.environ', {'S3_USE_SIGV4': True})
+        self.env_patch.start()
+        self.conn = boto.s3.connect_to_region('us-west-2')
+        self.bucket_name = 'multipart-%d' % int(time.time())
+        self.bucket = self.conn.create_bucket(self.bucket_name,
+                                              location='us-west-2')
+
+    def tearDown(self):
+        for key in self.bucket:
+            key.delete()
+        self.bucket.delete()
+        self.env_patch.stop()
+
+    def test_initiate_multipart(self):
+        key_name = "multipart"
+        multipart_upload = self.bucket.initiate_multipart_upload(key_name)
+        multipart_uploads = self.bucket.get_all_multipart_uploads()
+        for upload in multipart_uploads:
+            # Check that the multipart upload was created.
+            self.assertEqual(upload.key_name, multipart_upload.key_name)
+            self.assertEqual(upload.id, multipart_upload.id)
+        multipart_upload.cancel_upload()
+
+    def test_upload_part_by_size(self):
+        key_name = "k"
+        contents = "01234567890123456789"
+        sfp = StringIO(contents)
+
+        # upload 20 bytes in 4 parts of 5 bytes each
+        mpu = self.bucket.initiate_multipart_upload(key_name)
+        mpu.upload_part_from_file(sfp, part_num=1, size=5)
+        mpu.upload_part_from_file(sfp, part_num=2, size=5)
+        mpu.upload_part_from_file(sfp, part_num=3, size=5)
+        mpu.upload_part_from_file(sfp, part_num=4, size=5)
+        sfp.close()
+
+        etags = {}
+        pn = 0
+        for part in mpu:
+            pn += 1
+            self.assertEqual(5, part.size)
+            etags[pn] = part.etag
+        self.assertEqual(pn, 4)
+        # etags for 01234
+        self.assertEqual(etags[1], etags[3])
+        # etags for 56789
+        self.assertEqual(etags[2], etags[4])
+        # etag 01234 != etag 56789
+        self.assertNotEqual(etags[1], etags[2])
+
+        # parts are too small to complete as each part must
+        # be a min of 5MB so so we'll assume that is enough
+        # testing and abort the upload.
+        mpu.cancel_upload()
diff -Nru python-boto-2.33.0/tests/unit/iam/test_connection.py python-boto-2.34.0/tests/unit/iam/test_connection.py
--- python-boto-2.33.0/tests/unit/iam/test_connection.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/tests/unit/iam/test_connection.py	2014-10-23 11:19:50.000000000 -0500
@@ -360,3 +360,36 @@
         b64decode(response['get_credential_report_response']
                           ['get_credential_report_result']
                           ['content'])
+
+class TestCreateVirtualMFADevice(AWSMockServiceTestCase):
+    connection_class = IAMConnection
+
+    def default_body(self):
+        return b"""
+            <CreateVirtualMFADeviceResponse>
+              <CreateVirtualMFADeviceResult>
+                <VirtualMFADevice>
+                  <SerialNumber>arn:aws:iam::123456789012:mfa/ExampleName</SerialNumber>
+                  <Base32StringSeed>2K5K5XTLA7GGE75TQLYEXAMPLEEXAMPLEEXAMPLECHDFW4KJYZ6
+                  UFQ75LL7COCYKM</Base32StringSeed>
+                  <QRCodePNG>89504E470D0A1A0AASDFAHSDFKJKLJFKALSDFJASDF</QRCodePNG>
+                </VirtualMFADevice>
+              </CreateVirtualMFADeviceResult>
+              <ResponseMetadata>
+                <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
+              </ResponseMetadata>
+            </CreateVirtualMFADeviceResponse>
+        """
+
+    def test_create_virtual_mfa_device(self):
+        self.set_http_response(status_code=200)
+        response = self.service_connection.create_virtual_mfa_device('/', 'ExampleName')
+        self.assert_request_parameters(
+            {'Path': '/',
+             'VirtualMFADeviceName': 'ExampleName',
+             'Action': 'CreateVirtualMFADevice'},
+            ignore_params_values=['Version'])
+        self.assertEquals(response['create_virtual_mfa_device_response']
+                                  ['create_virtual_mfa_device_result']
+                                  ['virtual_mfa_device']
+                                  ['serial_number'], 'arn:aws:iam::123456789012:mfa/ExampleName')
diff -Nru python-boto-2.33.0/tests/unit/mws/test_connection.py python-boto-2.34.0/tests/unit/mws/test_connection.py
--- python-boto-2.33.0/tests/unit/mws/test_connection.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/tests/unit/mws/test_connection.py	2014-10-23 11:19:50.000000000 -0500
@@ -23,10 +23,14 @@
 from boto.mws.connection import MWSConnection, api_call_map, destructure_object
 from boto.mws.response import (ResponseElement, GetFeedSubmissionListResult,
                                ResponseFactory)
+from boto.exception import BotoServerError
+
 from tests.compat import unittest
 
 from tests.unit import AWSMockServiceTestCase
 
+from mock import MagicMock
+
 
 class TestMWSConnection(AWSMockServiceTestCase):
     connection_class = MWSConnection
@@ -51,6 +55,22 @@
   </ResponseMetadata>
 </GetFeedSubmissionListResponse>"""
 
+    def default_body_error(self):
+        return b"""<?xml version="1.0" encoding="UTF-8"?>
+<ErrorResponse xmlns="http://mws.amazonaws.com/doc/2009-01-01/";>
+  <!--1 or more repetitions:-->
+  <Error>
+    <Type>Sender</Type>
+    <Code>string</Code>
+    <Message>string</Message>
+    <Detail>
+      <!--You may enter ANY elements at this point-->
+      <AnyElement xmlns=""/>
+    </Detail>
+  </Error>
+  <RequestId>string</RequestId>
+</ErrorResponse>"""
+
     def test_destructure_object(self):
         # Test that parsing of user input to Amazon input works.
         response = ResponseElement()
@@ -156,6 +176,22 @@
         self.assertTrue('inventory' in str(err.exception))
         self.assertTrue('feeds' in str(err.exception))
 
+    def test_post_request(self):
+
+        self.service_connection._mexe = MagicMock(
+            side_effect=
+                BotoServerError(500, 'You request has bee throttled', body=self.default_body_error()))
+
+        with self.assertRaises(BotoServerError) as err:
+            self.service_connection.get_lowest_offer_listings_for_asin(
+                MarketplaceId='12345',
+                ASINList='ASIN12345',
+                condition='Any',
+                SellerId='1234',
+                excludeme='True')
+
+            self.assertTrue('throttled' in str(err.reason))
+            self.assertEqual(int(err.status), 200)
 
 if __name__ == '__main__':
     unittest.main()
diff -Nru python-boto-2.33.0/tests/unit/test_regioninfo.py python-boto-2.34.0/tests/unit/test_regioninfo.py
--- python-boto-2.33.0/tests/unit/test_regioninfo.py	2014-10-08 14:41:04.000000000 -0500
+++ python-boto-2.34.0/tests/unit/test_regioninfo.py	2014-10-23 11:19:50.000000000 -0500
@@ -104,7 +104,7 @@
     def test_get_regions(self):
         # With defaults.
         ec2_regions = get_regions('ec2')
-        self.assertEqual(len(ec2_regions), 10)
+        self.assertTrue(len(ec2_regions) >= 10)
         west_2 = None
 
         for region_info in ec2_regions:
@@ -124,7 +124,7 @@
             region_cls=TestRegionInfo,
             connection_cls=FakeConn
         )
-        self.assertEqual(len(ec2_regions), 10)
+        self.assertTrue(len(ec2_regions) >= 10)
         west_2 = None
 
         for region_info in ec2_regions:
python-boto (2.34.0-2) unstable; urgency=medium

  * Upload to unstable.

 -- Eric Evans <eevans@debian.org>  Wed, 12 Nov 2014 11:39:34 -0600

python-boto (2.34.0-1) experimental; urgency=medium

  * New upstream release.

 -- Eric Evans <eevans@debian.org>  Mon, 10 Nov 2014 20:39:11 -0600

Attachment: signature.asc
Description: Digital signature


Reply to: