Index: tools/telemetry/third_party/gsutil/third_party/boto/boto/s3/connection.py |
diff --git a/tools/telemetry/third_party/gsutil/third_party/boto/boto/s3/connection.py b/tools/telemetry/third_party/gsutil/third_party/boto/boto/s3/connection.py |
deleted file mode 100644 |
index 0fcc1f5957fc9fbd7981f35baefb2eca006ff9bb..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/third_party/gsutil/third_party/boto/boto/s3/connection.py |
+++ /dev/null |
@@ -1,665 +0,0 @@ |
-# Copyright (c) 2006-2012 Mitch Garnaat http://garnaat.org/ |
-# Copyright (c) 2012 Amazon.com, Inc. or its affiliates. |
-# Copyright (c) 2010, Eucalyptus Systems, Inc. |
-# All rights reserved. |
-# |
-# Permission is hereby granted, free of charge, to any person obtaining a |
-# copy of this software and associated documentation files (the |
-# "Software"), to deal in the Software without restriction, including |
-# without limitation the rights to use, copy, modify, merge, publish, dis- |
-# tribute, sublicense, and/or sell copies of the Software, and to permit |
-# persons to whom the Software is furnished to do so, subject to the fol- |
-# lowing conditions: |
-# |
-# The above copyright notice and this permission notice shall be included |
-# in all copies or substantial portions of the Software. |
-# |
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
-# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
-# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
-# IN THE SOFTWARE. |
- |
-import xml.sax |
-import base64 |
-from boto.compat import six, urllib |
-import time |
- |
-from boto.auth import detect_potential_s3sigv4 |
-import boto.utils |
-from boto.connection import AWSAuthConnection |
-from boto import handler |
-from boto.s3.bucket import Bucket |
-from boto.s3.key import Key |
-from boto.resultset import ResultSet |
-from boto.exception import BotoClientError, S3ResponseError |
- |
- |
-def check_lowercase_bucketname(n): |
- """ |
- Bucket names must not contain uppercase characters. We check for |
- this by appending a lowercase character and testing with islower(). |
- Note this also covers cases like numeric bucket names with dashes. |
- |
- >>> check_lowercase_bucketname("Aaaa") |
- Traceback (most recent call last): |
- ... |
- BotoClientError: S3Error: Bucket names cannot contain upper-case |
- characters when using either the sub-domain or virtual hosting calling |
- format. |
- |
- >>> check_lowercase_bucketname("1234-5678-9123") |
- True |
- >>> check_lowercase_bucketname("abcdefg1234") |
- True |
- """ |
- if not (n + 'a').islower(): |
- raise BotoClientError("Bucket names cannot contain upper-case " \ |
- "characters when using either the sub-domain or virtual " \ |
- "hosting calling format.") |
- return True |
- |
- |
-def assert_case_insensitive(f): |
- def wrapper(*args, **kwargs): |
- if len(args) == 3 and check_lowercase_bucketname(args[2]): |
- pass |
- return f(*args, **kwargs) |
- return wrapper |
- |
- |
-class _CallingFormat(object): |
- |
- def get_bucket_server(self, server, bucket): |
- return '' |
- |
- def build_url_base(self, connection, protocol, server, bucket, key=''): |
- url_base = '%s://' % protocol |
- url_base += self.build_host(server, bucket) |
- url_base += connection.get_path(self.build_path_base(bucket, key)) |
- return url_base |
- |
- def build_host(self, server, bucket): |
- if bucket == '': |
- return server |
- else: |
- return self.get_bucket_server(server, bucket) |
- |
- def build_auth_path(self, bucket, key=''): |
- key = boto.utils.get_utf8_value(key) |
- path = '' |
- if bucket != '': |
- path = '/' + bucket |
- return path + '/%s' % urllib.parse.quote(key) |
- |
- def build_path_base(self, bucket, key=''): |
- key = boto.utils.get_utf8_value(key) |
- return '/%s' % urllib.parse.quote(key) |
- |
- |
-class SubdomainCallingFormat(_CallingFormat): |
- |
- @assert_case_insensitive |
- def get_bucket_server(self, server, bucket): |
- return '%s.%s' % (bucket, server) |
- |
- |
-class VHostCallingFormat(_CallingFormat): |
- |
- @assert_case_insensitive |
- def get_bucket_server(self, server, bucket): |
- return bucket |
- |
- |
-class OrdinaryCallingFormat(_CallingFormat): |
- |
- def get_bucket_server(self, server, bucket): |
- return server |
- |
- def build_path_base(self, bucket, key=''): |
- key = boto.utils.get_utf8_value(key) |
- path_base = '/' |
- if bucket: |
- path_base += "%s/" % bucket |
- return path_base + urllib.parse.quote(key) |
- |
- |
-class ProtocolIndependentOrdinaryCallingFormat(OrdinaryCallingFormat): |
- |
- def build_url_base(self, connection, protocol, server, bucket, key=''): |
- url_base = '//' |
- url_base += self.build_host(server, bucket) |
- url_base += connection.get_path(self.build_path_base(bucket, key)) |
- return url_base |
- |
- |
-class Location(object): |
- |
- DEFAULT = '' # US Classic Region |
- EU = 'EU' |
- USWest = 'us-west-1' |
- USWest2 = 'us-west-2' |
- SAEast = 'sa-east-1' |
- APNortheast = 'ap-northeast-1' |
- APSoutheast = 'ap-southeast-1' |
- APSoutheast2 = 'ap-southeast-2' |
- CNNorth1 = 'cn-north-1' |
- |
- |
-class NoHostProvided(object): |
- # An identifying object to help determine whether the user provided a |
- # ``host`` or not. Never instantiated. |
- pass |
- |
- |
-class HostRequiredError(BotoClientError): |
- pass |
- |
- |
-class S3Connection(AWSAuthConnection): |
- |
- DefaultHost = boto.config.get('s3', 'host', 's3.amazonaws.com') |
- DefaultCallingFormat = boto.config.get('s3', 'calling_format', 'boto.s3.connection.SubdomainCallingFormat') |
- QueryString = 'Signature=%s&Expires=%d&AWSAccessKeyId=%s' |
- |
- def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, |
- is_secure=True, port=None, proxy=None, proxy_port=None, |
- proxy_user=None, proxy_pass=None, |
- host=NoHostProvided, debug=0, https_connection_factory=None, |
- calling_format=DefaultCallingFormat, path='/', |
- provider='aws', bucket_class=Bucket, security_token=None, |
- suppress_consec_slashes=True, anon=False, |
- validate_certs=None, profile_name=None): |
- no_host_provided = False |
- if host is NoHostProvided: |
- no_host_provided = True |
- host = self.DefaultHost |
- if isinstance(calling_format, six.string_types): |
- calling_format=boto.utils.find_class(calling_format)() |
- self.calling_format = calling_format |
- self.bucket_class = bucket_class |
- self.anon = anon |
- super(S3Connection, self).__init__(host, |
- aws_access_key_id, aws_secret_access_key, |
- is_secure, port, proxy, proxy_port, proxy_user, proxy_pass, |
- debug=debug, https_connection_factory=https_connection_factory, |
- path=path, provider=provider, security_token=security_token, |
- suppress_consec_slashes=suppress_consec_slashes, |
- validate_certs=validate_certs, profile_name=profile_name) |
- # We need to delay until after the call to ``super`` before checking |
- # to see if SigV4 is in use. |
- if no_host_provided: |
- if 'hmac-v4-s3' in self._required_auth_capability(): |
- raise HostRequiredError( |
- "When using SigV4, you must specify a 'host' parameter." |
- ) |
- |
- @detect_potential_s3sigv4 |
- def _required_auth_capability(self): |
- if self.anon: |
- return ['anon'] |
- else: |
- return ['s3'] |
- |
- def __iter__(self): |
- for bucket in self.get_all_buckets(): |
- yield bucket |
- |
- def __contains__(self, bucket_name): |
- return not (self.lookup(bucket_name) is None) |
- |
- def set_bucket_class(self, bucket_class): |
- """ |
- Set the Bucket class associated with this bucket. By default, this |
- would be the boto.s3.key.Bucket class but if you want to subclass that |
- for some reason this allows you to associate your new class. |
- |
- :type bucket_class: class |
- :param bucket_class: A subclass of Bucket that can be more specific |
- """ |
- self.bucket_class = bucket_class |
- |
- def build_post_policy(self, expiration_time, conditions): |
- """ |
- Taken from the AWS book Python examples and modified for use with boto |
- """ |
- assert isinstance(expiration_time, time.struct_time), \ |
- 'Policy document must include a valid expiration Time object' |
- |
- # Convert conditions object mappings to condition statements |
- |
- return '{"expiration": "%s",\n"conditions": [%s]}' % \ |
- (time.strftime(boto.utils.ISO8601, expiration_time), ",".join(conditions)) |
- |
- def build_post_form_args(self, bucket_name, key, expires_in=6000, |
- acl=None, success_action_redirect=None, |
- max_content_length=None, |
- http_method='http', fields=None, |
- conditions=None, storage_class='STANDARD', |
- server_side_encryption=None): |
- """ |
- Taken from the AWS book Python examples and modified for use with boto |
- This only returns the arguments required for the post form, not the |
- actual form. This does not return the file input field which also |
- needs to be added |
- |
- :type bucket_name: string |
- :param bucket_name: Bucket to submit to |
- |
- :type key: string |
- :param key: Key name, optionally add ${filename} to the end to |
- attach the submitted filename |
- |
- :type expires_in: integer |
- :param expires_in: Time (in seconds) before this expires, defaults |
- to 6000 |
- |
- :type acl: string |
- :param acl: A canned ACL. One of: |
- * private |
- * public-read |
- * public-read-write |
- * authenticated-read |
- * bucket-owner-read |
- * bucket-owner-full-control |
- |
- :type success_action_redirect: string |
- :param success_action_redirect: URL to redirect to on success |
- |
- :type max_content_length: integer |
- :param max_content_length: Maximum size for this file |
- |
- :type http_method: string |
- :param http_method: HTTP Method to use, "http" or "https" |
- |
- :type storage_class: string |
- :param storage_class: Storage class to use for storing the object. |
- Valid values: STANDARD | REDUCED_REDUNDANCY |
- |
- :type server_side_encryption: string |
- :param server_side_encryption: Specifies server-side encryption |
- algorithm to use when Amazon S3 creates an object. |
- Valid values: None | AES256 |
- |
- :rtype: dict |
- :return: A dictionary containing field names/values as well as |
- a url to POST to |
- |
- .. code-block:: python |
- |
- |
- """ |
- if fields is None: |
- fields = [] |
- if conditions is None: |
- conditions = [] |
- expiration = time.gmtime(int(time.time() + expires_in)) |
- |
- # Generate policy document |
- conditions.append('{"bucket": "%s"}' % bucket_name) |
- if key.endswith("${filename}"): |
- conditions.append('["starts-with", "$key", "%s"]' % key[:-len("${filename}")]) |
- else: |
- conditions.append('{"key": "%s"}' % key) |
- if acl: |
- conditions.append('{"acl": "%s"}' % acl) |
- fields.append({"name": "acl", "value": acl}) |
- if success_action_redirect: |
- conditions.append('{"success_action_redirect": "%s"}' % success_action_redirect) |
- fields.append({"name": "success_action_redirect", "value": success_action_redirect}) |
- if max_content_length: |
- conditions.append('["content-length-range", 0, %i]' % max_content_length) |
- |
- if self.provider.security_token: |
- fields.append({'name': 'x-amz-security-token', |
- 'value': self.provider.security_token}) |
- conditions.append('{"x-amz-security-token": "%s"}' % self.provider.security_token) |
- |
- if storage_class: |
- fields.append({'name': 'x-amz-storage-class', |
- 'value': storage_class}) |
- conditions.append('{"x-amz-storage-class": "%s"}' % storage_class) |
- |
- if server_side_encryption: |
- fields.append({'name': 'x-amz-server-side-encryption', |
- 'value': server_side_encryption}) |
- conditions.append('{"x-amz-server-side-encryption": "%s"}' % server_side_encryption) |
- |
- policy = self.build_post_policy(expiration, conditions) |
- |
- # Add the base64-encoded policy document as the 'policy' field |
- policy_b64 = base64.b64encode(policy) |
- fields.append({"name": "policy", "value": policy_b64}) |
- |
- # Add the AWS access key as the 'AWSAccessKeyId' field |
- fields.append({"name": "AWSAccessKeyId", |
- "value": self.aws_access_key_id}) |
- |
- # Add signature for encoded policy document as the |
- # 'signature' field |
- signature = self._auth_handler.sign_string(policy_b64) |
- fields.append({"name": "signature", "value": signature}) |
- fields.append({"name": "key", "value": key}) |
- |
- # HTTPS protocol will be used if the secure HTTP option is enabled. |
- url = '%s://%s/' % (http_method, |
- self.calling_format.build_host(self.server_name(), |
- bucket_name)) |
- |
- return {"action": url, "fields": fields} |
- |
- def generate_url_sigv4(self, expires_in, method, bucket='', key='', |
- headers=None, force_http=False, |
- response_headers=None, version_id=None, |
- iso_date=None): |
- path = self.calling_format.build_path_base(bucket, key) |
- auth_path = self.calling_format.build_auth_path(bucket, key) |
- host = self.calling_format.build_host(self.server_name(), bucket) |
- |
- # For presigned URLs we should ignore the port if it's HTTPS |
- if host.endswith(':443'): |
- host = host[:-4] |
- |
- params = {} |
- if version_id is not None: |
- params['VersionId'] = version_id |
- |
- http_request = self.build_base_http_request(method, path, auth_path, |
- headers=headers, host=host, |
- params=params) |
- |
- return self._auth_handler.presign(http_request, expires_in, |
- iso_date=iso_date) |
- |
- def generate_url(self, expires_in, method, bucket='', key='', headers=None, |
- query_auth=True, force_http=False, response_headers=None, |
- expires_in_absolute=False, version_id=None): |
- if self._auth_handler.capability[0] == 'hmac-v4-s3': |
- # Handle the special sigv4 case |
- return self.generate_url_sigv4(expires_in, method, bucket=bucket, |
- key=key, headers=headers, force_http=force_http, |
- response_headers=response_headers, version_id=version_id) |
- |
- headers = headers or {} |
- if expires_in_absolute: |
- expires = int(expires_in) |
- else: |
- expires = int(time.time() + expires_in) |
- auth_path = self.calling_format.build_auth_path(bucket, key) |
- auth_path = self.get_path(auth_path) |
- # optional version_id and response_headers need to be added to |
- # the query param list. |
- extra_qp = [] |
- if version_id is not None: |
- extra_qp.append("versionId=%s" % version_id) |
- if response_headers: |
- for k, v in response_headers.items(): |
- extra_qp.append("%s=%s" % (k, urllib.parse.quote(v))) |
- if self.provider.security_token: |
- headers['x-amz-security-token'] = self.provider.security_token |
- if extra_qp: |
- delimiter = '?' if '?' not in auth_path else '&' |
- auth_path += delimiter + '&'.join(extra_qp) |
- c_string = boto.utils.canonical_string(method, auth_path, headers, |
- expires, self.provider) |
- b64_hmac = self._auth_handler.sign_string(c_string) |
- encoded_canonical = urllib.parse.quote(b64_hmac, safe='') |
- self.calling_format.build_path_base(bucket, key) |
- if query_auth: |
- query_part = '?' + self.QueryString % (encoded_canonical, expires, |
- self.aws_access_key_id) |
- else: |
- query_part = '' |
- if headers: |
- hdr_prefix = self.provider.header_prefix |
- for k, v in headers.items(): |
- if k.startswith(hdr_prefix): |
- # headers used for sig generation must be |
- # included in the url also. |
- extra_qp.append("%s=%s" % (k, urllib.parse.quote(v))) |
- if extra_qp: |
- delimiter = '?' if not query_part else '&' |
- query_part += delimiter + '&'.join(extra_qp) |
- if force_http: |
- protocol = 'http' |
- port = 80 |
- else: |
- protocol = self.protocol |
- port = self.port |
- return self.calling_format.build_url_base(self, protocol, |
- self.server_name(port), |
- bucket, key) + query_part |
- |
- def get_all_buckets(self, headers=None): |
- response = self.make_request('GET', headers=headers) |
- body = response.read() |
- if response.status > 300: |
- raise self.provider.storage_response_error( |
- response.status, response.reason, body) |
- rs = ResultSet([('Bucket', self.bucket_class)]) |
- h = handler.XmlHandler(rs, self) |
- if not isinstance(body, bytes): |
- body = body.encode('utf-8') |
- xml.sax.parseString(body, h) |
- return rs |
- |
- def get_canonical_user_id(self, headers=None): |
- """ |
- Convenience method that returns the "CanonicalUserID" of the |
- user who's credentials are associated with the connection. |
- The only way to get this value is to do a GET request on the |
- service which returns all buckets associated with the account. |
- As part of that response, the canonical userid is returned. |
- This method simply does all of that and then returns just the |
- user id. |
- |
- :rtype: string |
- :return: A string containing the canonical user id. |
- """ |
- rs = self.get_all_buckets(headers=headers) |
- return rs.owner.id |
- |
- def get_bucket(self, bucket_name, validate=True, headers=None): |
- """ |
- Retrieves a bucket by name. |
- |
- If the bucket does not exist, an ``S3ResponseError`` will be raised. If |
- you are unsure if the bucket exists or not, you can use the |
- ``S3Connection.lookup`` method, which will either return a valid bucket |
- or ``None``. |
- |
- If ``validate=False`` is passed, no request is made to the service (no |
- charge/communication delay). This is only safe to do if you are **sure** |
- the bucket exists. |
- |
- If the default ``validate=True`` is passed, a request is made to the |
- service to ensure the bucket exists. Prior to Boto v2.25.0, this fetched |
- a list of keys (but with a max limit set to ``0``, always returning an empty |
- list) in the bucket (& included better error messages), at an |
- increased expense. As of Boto v2.25.0, this now performs a HEAD request |
- (less expensive but worse error messages). |
- |
- If you were relying on parsing the error message before, you should call |
- something like:: |
- |
- bucket = conn.get_bucket('<bucket_name>', validate=False) |
- bucket.get_all_keys(maxkeys=0) |
- |
- :type bucket_name: string |
- :param bucket_name: The name of the bucket |
- |
- :type headers: dict |
- :param headers: Additional headers to pass along with the request to |
- AWS. |
- |
- :type validate: boolean |
- :param validate: If ``True``, it will try to verify the bucket exists |
- on the service-side. (Default: ``True``) |
- """ |
- if validate: |
- return self.head_bucket(bucket_name, headers=headers) |
- else: |
- return self.bucket_class(self, bucket_name) |
- |
- def head_bucket(self, bucket_name, headers=None): |
- """ |
- Determines if a bucket exists by name. |
- |
- If the bucket does not exist, an ``S3ResponseError`` will be raised. |
- |
- :type bucket_name: string |
- :param bucket_name: The name of the bucket |
- |
- :type headers: dict |
- :param headers: Additional headers to pass along with the request to |
- AWS. |
- |
- :returns: A <Bucket> object |
- """ |
- response = self.make_request('HEAD', bucket_name, headers=headers) |
- body = response.read() |
- if response.status == 200: |
- return self.bucket_class(self, bucket_name) |
- elif response.status == 403: |
- # For backward-compatibility, we'll populate part of the exception |
- # with the most-common default. |
- err = self.provider.storage_response_error( |
- response.status, |
- response.reason, |
- body |
- ) |
- err.error_code = 'AccessDenied' |
- err.error_message = 'Access Denied' |
- raise err |
- elif response.status == 404: |
- # For backward-compatibility, we'll populate part of the exception |
- # with the most-common default. |
- err = self.provider.storage_response_error( |
- response.status, |
- response.reason, |
- body |
- ) |
- err.error_code = 'NoSuchBucket' |
- err.error_message = 'The specified bucket does not exist' |
- raise err |
- else: |
- raise self.provider.storage_response_error( |
- response.status, response.reason, body) |
- |
- def lookup(self, bucket_name, validate=True, headers=None): |
- """ |
- Attempts to get a bucket from S3. |
- |
- Works identically to ``S3Connection.get_bucket``, save for that it |
- will return ``None`` if the bucket does not exist instead of throwing |
- an exception. |
- |
- :type bucket_name: string |
- :param bucket_name: The name of the bucket |
- |
- :type headers: dict |
- :param headers: Additional headers to pass along with the request to |
- AWS. |
- |
- :type validate: boolean |
- :param validate: If ``True``, it will try to fetch all keys within the |
- given bucket. (Default: ``True``) |
- """ |
- try: |
- bucket = self.get_bucket(bucket_name, validate, headers=headers) |
- except: |
- bucket = None |
- return bucket |
- |
- def create_bucket(self, bucket_name, headers=None, |
- location=Location.DEFAULT, policy=None): |
- """ |
- Creates a new located bucket. By default it's in the USA. You can pass |
- Location.EU to create a European bucket (S3) or European Union bucket |
- (GCS). |
- |
- :type bucket_name: string |
- :param bucket_name: The name of the new bucket |
- |
- :type headers: dict |
- :param headers: Additional headers to pass along with the request to AWS. |
- |
- :type location: str |
- :param location: The location of the new bucket. You can use one of the |
- constants in :class:`boto.s3.connection.Location` (e.g. Location.EU, |
- Location.USWest, etc.). |
- |
- :type policy: :class:`boto.s3.acl.CannedACLStrings` |
- :param policy: A canned ACL policy that will be applied to the |
- new key in S3. |
- |
- """ |
- check_lowercase_bucketname(bucket_name) |
- |
- if policy: |
- if headers: |
- headers[self.provider.acl_header] = policy |
- else: |
- headers = {self.provider.acl_header: policy} |
- if location == Location.DEFAULT: |
- data = '' |
- else: |
- data = '<CreateBucketConfiguration><LocationConstraint>' + \ |
- location + '</LocationConstraint></CreateBucketConfiguration>' |
- response = self.make_request('PUT', bucket_name, headers=headers, |
- data=data) |
- body = response.read() |
- if response.status == 409: |
- raise self.provider.storage_create_error( |
- response.status, response.reason, body) |
- if response.status == 200: |
- return self.bucket_class(self, bucket_name) |
- else: |
- raise self.provider.storage_response_error( |
- response.status, response.reason, body) |
- |
- def delete_bucket(self, bucket, headers=None): |
- """ |
- Removes an S3 bucket. |
- |
- In order to remove the bucket, it must first be empty. If the bucket is |
- not empty, an ``S3ResponseError`` will be raised. |
- |
- :type bucket_name: string |
- :param bucket_name: The name of the bucket |
- |
- :type headers: dict |
- :param headers: Additional headers to pass along with the request to |
- AWS. |
- """ |
- response = self.make_request('DELETE', bucket, headers=headers) |
- body = response.read() |
- if response.status != 204: |
- raise self.provider.storage_response_error( |
- response.status, response.reason, body) |
- |
- def make_request(self, method, bucket='', key='', headers=None, data='', |
- query_args=None, sender=None, override_num_retries=None, |
- retry_handler=None): |
- if isinstance(bucket, self.bucket_class): |
- bucket = bucket.name |
- if isinstance(key, Key): |
- key = key.name |
- path = self.calling_format.build_path_base(bucket, key) |
- boto.log.debug('path=%s' % path) |
- auth_path = self.calling_format.build_auth_path(bucket, key) |
- boto.log.debug('auth_path=%s' % auth_path) |
- host = self.calling_format.build_host(self.server_name(), bucket) |
- if query_args: |
- path += '?' + query_args |
- boto.log.debug('path=%s' % path) |
- auth_path += '?' + query_args |
- boto.log.debug('auth_path=%s' % auth_path) |
- return super(S3Connection, self).make_request( |
- method, path, headers, |
- data, host, auth_path, sender, |
- override_num_retries=override_num_retries, |
- retry_handler=retry_handler |
- ) |