Index: tools/telemetry/third_party/gsutil/gslib/translation_helper.py |
diff --git a/tools/telemetry/third_party/gsutil/gslib/translation_helper.py b/tools/telemetry/third_party/gsutil/gslib/translation_helper.py |
deleted file mode 100644 |
index 91adc83a3720d82b94a2ffc723b35aed49587708..0000000000000000000000000000000000000000 |
--- a/tools/telemetry/third_party/gsutil/gslib/translation_helper.py |
+++ /dev/null |
@@ -1,791 +0,0 @@ |
-# -*- coding: utf-8 -*- |
-# Copyright 2014 Google Inc. All Rights Reserved. |
-# |
-# Licensed under the Apache License, Version 2.0 (the "License"); |
-# you may not use this file except in compliance with the License. |
-# You may obtain a copy of the License at |
-# |
-# http://www.apache.org/licenses/LICENSE-2.0 |
-# |
-# Unless required by applicable law or agreed to in writing, software |
-# distributed under the License is distributed on an "AS IS" BASIS, |
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
-# See the License for the specific language governing permissions and |
-# limitations under the License. |
-"""Utility module for translating XML API objects to/from JSON objects.""" |
- |
-from __future__ import absolute_import |
- |
-import datetime |
-import json |
-import re |
-import textwrap |
-import xml.etree.ElementTree |
- |
-from apitools.base.py import encoding |
-import boto |
-from boto.gs.acl import ACL |
-from boto.gs.acl import ALL_AUTHENTICATED_USERS |
-from boto.gs.acl import ALL_USERS |
-from boto.gs.acl import Entries |
-from boto.gs.acl import Entry |
-from boto.gs.acl import GROUP_BY_DOMAIN |
-from boto.gs.acl import GROUP_BY_EMAIL |
-from boto.gs.acl import GROUP_BY_ID |
-from boto.gs.acl import USER_BY_EMAIL |
-from boto.gs.acl import USER_BY_ID |
- |
-from gslib.cloud_api import ArgumentException |
-from gslib.cloud_api import NotFoundException |
-from gslib.cloud_api import Preconditions |
-from gslib.exception import CommandException |
-from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages |
- |
-# In Python 2.6, ElementTree raises ExpatError instead of ParseError. |
-# pylint: disable=g-import-not-at-top |
-try: |
- from xml.etree.ElementTree import ParseError as XmlParseError |
-except ImportError: |
- from xml.parsers.expat import ExpatError as XmlParseError |
- |
-CACHE_CONTROL_REGEX = re.compile(r'^cache-control', re.I) |
-CONTENT_DISPOSITION_REGEX = re.compile(r'^content-disposition', re.I) |
-CONTENT_ENCODING_REGEX = re.compile(r'^content-encoding', re.I) |
-CONTENT_LANGUAGE_REGEX = re.compile(r'^content-language', re.I) |
-CONTENT_MD5_REGEX = re.compile(r'^content-md5', re.I) |
-CONTENT_TYPE_REGEX = re.compile(r'^content-type', re.I) |
-GOOG_API_VERSION_REGEX = re.compile(r'^x-goog-api-version', re.I) |
-GOOG_GENERATION_MATCH_REGEX = re.compile(r'^x-goog-if-generation-match', re.I) |
-GOOG_METAGENERATION_MATCH_REGEX = re.compile( |
- r'^x-goog-if-metageneration-match', re.I) |
-CUSTOM_GOOG_METADATA_REGEX = re.compile(r'^x-goog-meta-(?P<header_key>.*)', |
- re.I) |
-CUSTOM_AMZ_METADATA_REGEX = re.compile(r'^x-amz-meta-(?P<header_key>.*)', re.I) |
-CUSTOM_AMZ_HEADER_REGEX = re.compile(r'^x-amz-(?P<header_key>.*)', re.I) |
- |
-# gsutil-specific GUIDs for marking special metadata for S3 compatibility. |
-S3_ACL_MARKER_GUID = '3b89a6b5-b55a-4900-8c44-0b0a2f5eab43-s3-AclMarker' |
-S3_DELETE_MARKER_GUID = 'eadeeee8-fa8c-49bb-8a7d-0362215932d8-s3-DeleteMarker' |
-S3_MARKER_GUIDS = [S3_ACL_MARKER_GUID, S3_DELETE_MARKER_GUID] |
-# This distinguishes S3 custom headers from S3 metadata on objects. |
-S3_HEADER_PREFIX = 'custom-amz-header' |
- |
-DEFAULT_CONTENT_TYPE = 'application/octet-stream' |
- |
-# Because CORS is just a list in apitools, we need special handling or blank |
-# CORS lists will get sent with other configuration commands such as lifecycle, |
-# commands, which would cause CORS configuration to be unintentionally removed. |
-# Protorpc defaults list values to an empty list, and won't allow us to set the |
-# value to None like other configuration fields, so there is no way to |
-# distinguish the default value from when we actually want to remove the CORS |
-# configuration. To work around this, we create a dummy CORS entry that |
-# signifies that we should nullify the CORS configuration. |
-# A value of [] means don't modify the CORS configuration. |
-# A value of REMOVE_CORS_CONFIG means remove the CORS configuration. |
-REMOVE_CORS_CONFIG = [apitools_messages.Bucket.CorsValueListEntry( |
- maxAgeSeconds=-1, method=['REMOVE_CORS_CONFIG'])] |
- |
- |
-def ObjectMetadataFromHeaders(headers): |
- """Creates object metadata according to the provided headers. |
- |
- gsutil -h allows specifiying various headers (originally intended |
- to be passed to boto in gsutil v3). For the JSON API to be compatible with |
- this option, we need to parse these headers into gsutil_api Object fields. |
- |
- Args: |
- headers: Dict of headers passed via gsutil -h |
- |
- Raises: |
- ArgumentException if an invalid header is encountered. |
- |
- Returns: |
- apitools Object with relevant fields populated from headers. |
- """ |
- obj_metadata = apitools_messages.Object() |
- for header, value in headers.items(): |
- if CACHE_CONTROL_REGEX.match(header): |
- obj_metadata.cacheControl = value.strip() |
- elif CONTENT_DISPOSITION_REGEX.match(header): |
- obj_metadata.contentDisposition = value.strip() |
- elif CONTENT_ENCODING_REGEX.match(header): |
- obj_metadata.contentEncoding = value.strip() |
- elif CONTENT_MD5_REGEX.match(header): |
- obj_metadata.md5Hash = value.strip() |
- elif CONTENT_LANGUAGE_REGEX.match(header): |
- obj_metadata.contentLanguage = value.strip() |
- elif CONTENT_TYPE_REGEX.match(header): |
- if not value: |
- obj_metadata.contentType = DEFAULT_CONTENT_TYPE |
- else: |
- obj_metadata.contentType = value.strip() |
- elif GOOG_API_VERSION_REGEX.match(header): |
- # API version is only relevant for XML, ignore and rely on the XML API |
- # to add the appropriate version. |
- continue |
- elif GOOG_GENERATION_MATCH_REGEX.match(header): |
- # Preconditions are handled elsewhere, but allow these headers through. |
- continue |
- elif GOOG_METAGENERATION_MATCH_REGEX.match(header): |
- # Preconditions are handled elsewhere, but allow these headers through. |
- continue |
- else: |
- custom_goog_metadata_match = CUSTOM_GOOG_METADATA_REGEX.match(header) |
- custom_amz_metadata_match = CUSTOM_AMZ_METADATA_REGEX.match(header) |
- custom_amz_header_match = CUSTOM_AMZ_HEADER_REGEX.match(header) |
- header_key = None |
- if custom_goog_metadata_match: |
- header_key = custom_goog_metadata_match.group('header_key') |
- elif custom_amz_metadata_match: |
- header_key = custom_amz_metadata_match.group('header_key') |
- elif custom_amz_header_match: |
- # If we got here we are guaranteed by the prior statement that this is |
- # not an x-amz-meta- header. |
- header_key = (S3_HEADER_PREFIX + |
- custom_amz_header_match.group('header_key')) |
- if header_key: |
- if header_key.lower() == 'x-goog-content-language': |
- # Work around content-language being inserted into custom metadata. |
- continue |
- if not obj_metadata.metadata: |
- obj_metadata.metadata = apitools_messages.Object.MetadataValue() |
- if not obj_metadata.metadata.additionalProperties: |
- obj_metadata.metadata.additionalProperties = [] |
- obj_metadata.metadata.additionalProperties.append( |
- apitools_messages.Object.MetadataValue.AdditionalProperty( |
- key=header_key, value=value)) |
- else: |
- raise ArgumentException( |
- 'Invalid header specifed: %s:%s' % (header, value)) |
- return obj_metadata |
- |
- |
-def HeadersFromObjectMetadata(dst_obj_metadata, provider): |
- """Creates a header dictionary based on existing object metadata. |
- |
- Args: |
- dst_obj_metadata: Object metadata to create the headers from. |
- provider: Provider string ('gs' or 's3') |
- |
- Returns: |
- Headers dictionary. |
- """ |
- headers = {} |
- if not dst_obj_metadata: |
- return |
- # Metadata values of '' mean suppress/remove this header. |
- if dst_obj_metadata.cacheControl is not None: |
- if not dst_obj_metadata.cacheControl: |
- headers['cache-control'] = None |
- else: |
- headers['cache-control'] = dst_obj_metadata.cacheControl.strip() |
- if dst_obj_metadata.contentDisposition: |
- if not dst_obj_metadata.contentDisposition: |
- headers['content-disposition'] = None |
- else: |
- headers['content-disposition'] = ( |
- dst_obj_metadata.contentDisposition.strip()) |
- if dst_obj_metadata.contentEncoding: |
- if not dst_obj_metadata.contentEncoding: |
- headers['content-encoding'] = None |
- else: |
- headers['content-encoding'] = dst_obj_metadata.contentEncoding.strip() |
- if dst_obj_metadata.contentLanguage: |
- if not dst_obj_metadata.contentLanguage: |
- headers['content-language'] = None |
- else: |
- headers['content-language'] = dst_obj_metadata.contentLanguage.strip() |
- if dst_obj_metadata.md5Hash: |
- if not dst_obj_metadata.md5Hash: |
- headers['Content-MD5'] = None |
- else: |
- headers['Content-MD5'] = dst_obj_metadata.md5Hash.strip() |
- if dst_obj_metadata.contentType is not None: |
- if not dst_obj_metadata.contentType: |
- headers['content-type'] = None |
- else: |
- headers['content-type'] = dst_obj_metadata.contentType.strip() |
- if (dst_obj_metadata.metadata and |
- dst_obj_metadata.metadata.additionalProperties): |
- for additional_property in dst_obj_metadata.metadata.additionalProperties: |
- # Work around content-language being inserted into custom metadata by |
- # the XML API. |
- if additional_property.key == 'content-language': |
- continue |
- # Don't translate special metadata markers. |
- if additional_property.key in S3_MARKER_GUIDS: |
- continue |
- if provider == 'gs': |
- header_name = 'x-goog-meta-' + additional_property.key |
- elif provider == 's3': |
- if additional_property.key.startswith(S3_HEADER_PREFIX): |
- header_name = ('x-amz-' + |
- additional_property.key[len(S3_HEADER_PREFIX):]) |
- else: |
- header_name = 'x-amz-meta-' + additional_property.key |
- else: |
- raise ArgumentException('Invalid provider specified: %s' % provider) |
- if (additional_property.value is not None and |
- not additional_property.value): |
- headers[header_name] = None |
- else: |
- headers[header_name] = additional_property.value |
- return headers |
- |
- |
-def CopyObjectMetadata(src_obj_metadata, dst_obj_metadata, override=False): |
- """Copies metadata from src_obj_metadata to dst_obj_metadata. |
- |
- Args: |
- src_obj_metadata: Metadata from source object |
- dst_obj_metadata: Initialized metadata for destination object |
- override: If true, will overwrite metadata in destination object. |
- If false, only writes metadata for values that don't already |
- exist. |
- """ |
- if override or not dst_obj_metadata.cacheControl: |
- dst_obj_metadata.cacheControl = src_obj_metadata.cacheControl |
- if override or not dst_obj_metadata.contentDisposition: |
- dst_obj_metadata.contentDisposition = src_obj_metadata.contentDisposition |
- if override or not dst_obj_metadata.contentEncoding: |
- dst_obj_metadata.contentEncoding = src_obj_metadata.contentEncoding |
- if override or not dst_obj_metadata.contentLanguage: |
- dst_obj_metadata.contentLanguage = src_obj_metadata.contentLanguage |
- if override or not dst_obj_metadata.contentType: |
- dst_obj_metadata.contentType = src_obj_metadata.contentType |
- if override or not dst_obj_metadata.md5Hash: |
- dst_obj_metadata.md5Hash = src_obj_metadata.md5Hash |
- |
- # TODO: Apitools should ideally treat metadata like a real dictionary instead |
- # of a list of key/value pairs (with an O(N^2) lookup). In practice the |
- # number of values is typically small enough not to matter. |
- # Work around this by creating our own dictionary. |
- if (src_obj_metadata.metadata and |
- src_obj_metadata.metadata.additionalProperties): |
- if not dst_obj_metadata.metadata: |
- dst_obj_metadata.metadata = apitools_messages.Object.MetadataValue() |
- if not dst_obj_metadata.metadata.additionalProperties: |
- dst_obj_metadata.metadata.additionalProperties = [] |
- dst_metadata_dict = {} |
- for dst_prop in dst_obj_metadata.metadata.additionalProperties: |
- dst_metadata_dict[dst_prop.key] = dst_prop.value |
- for src_prop in src_obj_metadata.metadata.additionalProperties: |
- if src_prop.key in dst_metadata_dict: |
- if override: |
- # Metadata values of '' mean suppress/remove this header. |
- if src_prop.value is not None and not src_prop.value: |
- dst_metadata_dict[src_prop.key] = None |
- else: |
- dst_metadata_dict[src_prop.key] = src_prop.value |
- else: |
- dst_metadata_dict[src_prop.key] = src_prop.value |
- # Rewrite the list with our updated dict. |
- dst_obj_metadata.metadata.additionalProperties = [] |
- for k, v in dst_metadata_dict.iteritems(): |
- dst_obj_metadata.metadata.additionalProperties.append( |
- apitools_messages.Object.MetadataValue.AdditionalProperty(key=k, |
- value=v)) |
- |
- |
-def PreconditionsFromHeaders(headers): |
- """Creates bucket or object preconditions acccording to the provided headers. |
- |
- Args: |
- headers: Dict of headers passed via gsutil -h |
- |
- Returns: |
- gsutil Cloud API Preconditions object fields populated from headers, or None |
- if no precondition headers are present. |
- """ |
- return_preconditions = Preconditions() |
- try: |
- for header, value in headers.items(): |
- if GOOG_GENERATION_MATCH_REGEX.match(header): |
- return_preconditions.gen_match = long(value) |
- if GOOG_METAGENERATION_MATCH_REGEX.match(header): |
- return_preconditions.meta_gen_match = long(value) |
- except ValueError, _: |
- raise ArgumentException('Invalid precondition header specified. ' |
- 'x-goog-if-generation-match and ' |
- 'x-goog-if-metageneration match must be specified ' |
- 'with a positive integer value.') |
- return return_preconditions |
- |
- |
-def CreateBucketNotFoundException(code, provider, bucket_name): |
- return NotFoundException('%s://%s bucket does not exist.' % |
- (provider, bucket_name), status=code) |
- |
- |
-def CreateObjectNotFoundException(code, provider, bucket_name, object_name, |
- generation=None): |
- uri_string = '%s://%s/%s' % (provider, bucket_name, object_name) |
- if generation: |
- uri_string += '#%s' % str(generation) |
- return NotFoundException('%s does not exist.' % uri_string, status=code) |
- |
- |
-def EncodeStringAsLong(string_to_convert): |
- """Encodes an ASCII string as a python long. |
- |
- This is used for modeling S3 version_id's as apitools generation. Because |
- python longs can be arbitrarily large, this works. |
- |
- Args: |
- string_to_convert: ASCII string to convert to a long. |
- |
- Returns: |
- Long that represents the input string. |
- """ |
- return long(string_to_convert.encode('hex'), 16) |
- |
- |
-def _DecodeLongAsString(long_to_convert): |
- """Decodes an encoded python long into an ASCII string. |
- |
- This is used for modeling S3 version_id's as apitools generation. |
- |
- Args: |
- long_to_convert: long to convert to ASCII string. If this is already a |
- string, it is simply returned. |
- |
- Returns: |
- String decoded from the input long. |
- """ |
- if isinstance(long_to_convert, basestring): |
- # Already converted. |
- return long_to_convert |
- return hex(long_to_convert)[2:-1].decode('hex') |
- |
- |
-def GenerationFromUrlAndString(url, generation): |
- """Decodes a generation from a StorageURL and a generation string. |
- |
- This is used to represent gs and s3 versioning. |
- |
- Args: |
- url: StorageUrl representing the object. |
- generation: Long or string representing the object's generation or |
- version. |
- |
- Returns: |
- Valid generation string for use in URLs. |
- """ |
- if url.scheme == 's3' and generation: |
- return _DecodeLongAsString(generation) |
- return generation |
- |
- |
-def CheckForXmlConfigurationAndRaise(config_type_string, json_txt): |
- """Checks a JSON parse exception for provided XML configuration.""" |
- try: |
- xml.etree.ElementTree.fromstring(str(json_txt)) |
- raise ArgumentException('\n'.join(textwrap.wrap( |
- 'XML {0} data provided; Google Cloud Storage {0} configuration ' |
- 'now uses JSON format. To convert your {0}, set the desired XML ' |
- 'ACL using \'gsutil {1} set ...\' with gsutil version 3.x. Then ' |
- 'use \'gsutil {1} get ...\' with gsutil version 4 or greater to ' |
- 'get the corresponding JSON {0}.'.format(config_type_string, |
- config_type_string.lower())))) |
- except XmlParseError: |
- pass |
- raise ArgumentException('JSON %s data could not be loaded ' |
- 'from: %s' % (config_type_string, json_txt)) |
- |
- |
-class LifecycleTranslation(object): |
- """Functions for converting between various lifecycle formats. |
- |
- This class handles conversation to and from Boto Cors objects, JSON text, |
- and apitools Message objects. |
- """ |
- |
- @classmethod |
- def BotoLifecycleFromMessage(cls, lifecycle_message): |
- """Translates an apitools message to a boto lifecycle object.""" |
- boto_lifecycle = boto.gs.lifecycle.LifecycleConfig() |
- if lifecycle_message: |
- for rule_message in lifecycle_message.rule: |
- boto_rule = boto.gs.lifecycle.Rule() |
- if (rule_message.action and rule_message.action.type and |
- rule_message.action.type.lower() == 'delete'): |
- boto_rule.action = boto.gs.lifecycle.DELETE |
- if rule_message.condition: |
- if rule_message.condition.age: |
- boto_rule.conditions[boto.gs.lifecycle.AGE] = ( |
- str(rule_message.condition.age)) |
- if rule_message.condition.createdBefore: |
- boto_rule.conditions[boto.gs.lifecycle.CREATED_BEFORE] = ( |
- str(rule_message.condition.createdBefore)) |
- if rule_message.condition.isLive: |
- boto_rule.conditions[boto.gs.lifecycle.IS_LIVE] = ( |
- str(rule_message.condition.isLive)) |
- if rule_message.condition.numNewerVersions: |
- boto_rule.conditions[boto.gs.lifecycle.NUM_NEWER_VERSIONS] = ( |
- str(rule_message.condition.numNewerVersions)) |
- boto_lifecycle.append(boto_rule) |
- return boto_lifecycle |
- |
- @classmethod |
- def BotoLifecycleToMessage(cls, boto_lifecycle): |
- """Translates a boto lifecycle object to an apitools message.""" |
- lifecycle_message = None |
- if boto_lifecycle: |
- lifecycle_message = apitools_messages.Bucket.LifecycleValue() |
- for boto_rule in boto_lifecycle: |
- lifecycle_rule = ( |
- apitools_messages.Bucket.LifecycleValue.RuleValueListEntry()) |
- lifecycle_rule.condition = (apitools_messages.Bucket.LifecycleValue. |
- RuleValueListEntry.ConditionValue()) |
- if boto_rule.action and boto_rule.action == boto.gs.lifecycle.DELETE: |
- lifecycle_rule.action = (apitools_messages.Bucket.LifecycleValue. |
- RuleValueListEntry.ActionValue( |
- type='Delete')) |
- if boto.gs.lifecycle.AGE in boto_rule.conditions: |
- lifecycle_rule.condition.age = int( |
- boto_rule.conditions[boto.gs.lifecycle.AGE]) |
- if boto.gs.lifecycle.CREATED_BEFORE in boto_rule.conditions: |
- lifecycle_rule.condition.createdBefore = ( |
- LifecycleTranslation.TranslateBotoLifecycleTimestamp( |
- boto_rule.conditions[boto.gs.lifecycle.CREATED_BEFORE])) |
- if boto.gs.lifecycle.IS_LIVE in boto_rule.conditions: |
- lifecycle_rule.condition.isLive = bool( |
- boto_rule.conditions[boto.gs.lifecycle.IS_LIVE]) |
- if boto.gs.lifecycle.NUM_NEWER_VERSIONS in boto_rule.conditions: |
- lifecycle_rule.condition.numNewerVersions = int( |
- boto_rule.conditions[boto.gs.lifecycle.NUM_NEWER_VERSIONS]) |
- lifecycle_message.rule.append(lifecycle_rule) |
- return lifecycle_message |
- |
- @classmethod |
- def JsonLifecycleFromMessage(cls, lifecycle_message): |
- """Translates an apitools message to lifecycle JSON.""" |
- return str(encoding.MessageToJson(lifecycle_message)) + '\n' |
- |
- @classmethod |
- def JsonLifecycleToMessage(cls, json_txt): |
- """Translates lifecycle JSON to an apitools message.""" |
- try: |
- deserialized_lifecycle = json.loads(json_txt) |
- # If lifecycle JSON is the in the following format |
- # {'lifecycle': {'rule': ... then strip out the 'lifecycle' key |
- # and reduce it to the following format |
- # {'rule': ... |
- if 'lifecycle' in deserialized_lifecycle: |
- deserialized_lifecycle = deserialized_lifecycle['lifecycle'] |
- lifecycle = encoding.DictToMessage( |
- deserialized_lifecycle, apitools_messages.Bucket.LifecycleValue) |
- return lifecycle |
- except ValueError: |
- CheckForXmlConfigurationAndRaise('lifecycle', json_txt) |
- |
- @classmethod |
- def TranslateBotoLifecycleTimestamp(cls, lifecycle_datetime): |
- """Parses the timestamp from the boto lifecycle into a datetime object.""" |
- return datetime.datetime.strptime(lifecycle_datetime, '%Y-%m-%d').date() |
- |
- |
-class CorsTranslation(object): |
- """Functions for converting between various CORS formats. |
- |
- This class handles conversation to and from Boto Cors objects, JSON text, |
- and apitools Message objects. |
- """ |
- |
- @classmethod |
- def BotoCorsFromMessage(cls, cors_message): |
- """Translates an apitools message to a boto Cors object.""" |
- cors = boto.gs.cors.Cors() |
- cors.cors = [] |
- for collection_message in cors_message: |
- collection_elements = [] |
- if collection_message.maxAgeSeconds: |
- collection_elements.append((boto.gs.cors.MAXAGESEC, |
- str(collection_message.maxAgeSeconds))) |
- if collection_message.method: |
- method_elements = [] |
- for method in collection_message.method: |
- method_elements.append((boto.gs.cors.METHOD, method)) |
- collection_elements.append((boto.gs.cors.METHODS, method_elements)) |
- if collection_message.origin: |
- origin_elements = [] |
- for origin in collection_message.origin: |
- origin_elements.append((boto.gs.cors.ORIGIN, origin)) |
- collection_elements.append((boto.gs.cors.ORIGINS, origin_elements)) |
- if collection_message.responseHeader: |
- header_elements = [] |
- for header in collection_message.responseHeader: |
- header_elements.append((boto.gs.cors.HEADER, header)) |
- collection_elements.append((boto.gs.cors.HEADERS, header_elements)) |
- cors.cors.append(collection_elements) |
- return cors |
- |
- @classmethod |
- def BotoCorsToMessage(cls, boto_cors): |
- """Translates a boto Cors object to an apitools message.""" |
- message_cors = [] |
- if boto_cors.cors: |
- for cors_collection in boto_cors.cors: |
- if cors_collection: |
- collection_message = apitools_messages.Bucket.CorsValueListEntry() |
- for element_tuple in cors_collection: |
- if element_tuple[0] == boto.gs.cors.MAXAGESEC: |
- collection_message.maxAgeSeconds = int(element_tuple[1]) |
- if element_tuple[0] == boto.gs.cors.METHODS: |
- for method_tuple in element_tuple[1]: |
- collection_message.method.append(method_tuple[1]) |
- if element_tuple[0] == boto.gs.cors.ORIGINS: |
- for origin_tuple in element_tuple[1]: |
- collection_message.origin.append(origin_tuple[1]) |
- if element_tuple[0] == boto.gs.cors.HEADERS: |
- for header_tuple in element_tuple[1]: |
- collection_message.responseHeader.append(header_tuple[1]) |
- message_cors.append(collection_message) |
- return message_cors |
- |
- @classmethod |
- def JsonCorsToMessageEntries(cls, json_cors): |
- """Translates CORS JSON to an apitools message. |
- |
- Args: |
- json_cors: JSON string representing CORS configuration. |
- |
- Returns: |
- List of apitools Bucket.CorsValueListEntry. An empty list represents |
- no CORS configuration. |
- """ |
- try: |
- deserialized_cors = json.loads(json_cors) |
- cors = [] |
- for cors_entry in deserialized_cors: |
- cors.append(encoding.DictToMessage( |
- cors_entry, apitools_messages.Bucket.CorsValueListEntry)) |
- return cors |
- except ValueError: |
- CheckForXmlConfigurationAndRaise('CORS', json_cors) |
- |
- @classmethod |
- def MessageEntriesToJson(cls, cors_message): |
- """Translates an apitools message to CORS JSON.""" |
- json_text = '' |
- # Because CORS is a MessageField, serialize/deserialize as JSON list. |
- json_text += '[' |
- printed_one = False |
- for cors_entry in cors_message: |
- if printed_one: |
- json_text += ',' |
- else: |
- printed_one = True |
- json_text += encoding.MessageToJson(cors_entry) |
- json_text += ']\n' |
- return json_text |
- |
- |
-def S3MarkerAclFromObjectMetadata(object_metadata): |
- """Retrieves GUID-marked S3 ACL from object metadata, if present. |
- |
- Args: |
- object_metadata: Object metadata to check. |
- |
- Returns: |
- S3 ACL text, if present, None otherwise. |
- """ |
- if (object_metadata and object_metadata.metadata and |
- object_metadata.metadata.additionalProperties): |
- for prop in object_metadata.metadata.additionalProperties: |
- if prop.key == S3_ACL_MARKER_GUID: |
- return prop.value |
- |
- |
-def AddS3MarkerAclToObjectMetadata(object_metadata, acl_text): |
- """Adds a GUID-marked S3 ACL to the object metadata. |
- |
- Args: |
- object_metadata: Object metadata to add the acl to. |
- acl_text: S3 ACL text to add. |
- """ |
- if not object_metadata.metadata: |
- object_metadata.metadata = apitools_messages.Object.MetadataValue() |
- if not object_metadata.metadata.additionalProperties: |
- object_metadata.metadata.additionalProperties = [] |
- |
- object_metadata.metadata.additionalProperties.append( |
- apitools_messages.Object.MetadataValue.AdditionalProperty( |
- key=S3_ACL_MARKER_GUID, value=acl_text)) |
- |
- |
-class AclTranslation(object): |
- """Functions for converting between various ACL formats. |
- |
- This class handles conversion to and from Boto ACL objects, JSON text, |
- and apitools Message objects. |
- """ |
- |
- JSON_TO_XML_ROLES = {'READER': 'READ', 'WRITER': 'WRITE', |
- 'OWNER': 'FULL_CONTROL'} |
- XML_TO_JSON_ROLES = {'READ': 'READER', 'WRITE': 'WRITER', |
- 'FULL_CONTROL': 'OWNER'} |
- |
- @classmethod |
- def BotoAclFromJson(cls, acl_json): |
- acl = ACL() |
- acl.parent = None |
- acl.entries = cls.BotoEntriesFromJson(acl_json, acl) |
- return acl |
- |
- @classmethod |
- # acl_message is a list of messages, either object or bucketaccesscontrol |
- def BotoAclFromMessage(cls, acl_message): |
- acl_dicts = [] |
- for message in acl_message: |
- acl_dicts.append(encoding.MessageToDict(message)) |
- return cls.BotoAclFromJson(acl_dicts) |
- |
- @classmethod |
- def BotoAclToJson(cls, acl): |
- if hasattr(acl, 'entries'): |
- return cls.BotoEntriesToJson(acl.entries) |
- return [] |
- |
- @classmethod |
- def BotoObjectAclToMessage(cls, acl): |
- for entry in cls.BotoAclToJson(acl): |
- message = encoding.DictToMessage(entry, |
- apitools_messages.ObjectAccessControl) |
- message.kind = u'storage#objectAccessControl' |
- yield message |
- |
- @classmethod |
- def BotoBucketAclToMessage(cls, acl): |
- for entry in cls.BotoAclToJson(acl): |
- message = encoding.DictToMessage(entry, |
- apitools_messages.BucketAccessControl) |
- message.kind = u'storage#bucketAccessControl' |
- yield message |
- |
- @classmethod |
- def BotoEntriesFromJson(cls, acl_json, parent): |
- entries = Entries(parent) |
- entries.parent = parent |
- entries.entry_list = [cls.BotoEntryFromJson(entry_json) |
- for entry_json in acl_json] |
- return entries |
- |
- @classmethod |
- def BotoEntriesToJson(cls, entries): |
- return [cls.BotoEntryToJson(entry) for entry in entries.entry_list] |
- |
- @classmethod |
- def BotoEntryFromJson(cls, entry_json): |
- """Converts a JSON entry into a Boto ACL entry.""" |
- entity = entry_json['entity'] |
- permission = cls.JSON_TO_XML_ROLES[entry_json['role']] |
- if entity.lower() == ALL_USERS.lower(): |
- return Entry(type=ALL_USERS, permission=permission) |
- elif entity.lower() == ALL_AUTHENTICATED_USERS.lower(): |
- return Entry(type=ALL_AUTHENTICATED_USERS, permission=permission) |
- elif entity.startswith('project'): |
- raise CommandException('XML API does not support project scopes, ' |
- 'cannot translate ACL.') |
- elif 'email' in entry_json: |
- if entity.startswith('user'): |
- scope_type = USER_BY_EMAIL |
- elif entity.startswith('group'): |
- scope_type = GROUP_BY_EMAIL |
- return Entry(type=scope_type, email_address=entry_json['email'], |
- permission=permission) |
- elif 'entityId' in entry_json: |
- if entity.startswith('user'): |
- scope_type = USER_BY_ID |
- elif entity.startswith('group'): |
- scope_type = GROUP_BY_ID |
- return Entry(type=scope_type, id=entry_json['entityId'], |
- permission=permission) |
- elif 'domain' in entry_json: |
- if entity.startswith('domain'): |
- scope_type = GROUP_BY_DOMAIN |
- return Entry(type=scope_type, domain=entry_json['domain'], |
- permission=permission) |
- raise CommandException('Failed to translate JSON ACL to XML.') |
- |
- @classmethod |
- def BotoEntryToJson(cls, entry): |
- """Converts a Boto ACL entry to a valid JSON dictionary.""" |
- acl_entry_json = {} |
- # JSON API documentation uses camel case. |
- scope_type_lower = entry.scope.type.lower() |
- if scope_type_lower == ALL_USERS.lower(): |
- acl_entry_json['entity'] = 'allUsers' |
- elif scope_type_lower == ALL_AUTHENTICATED_USERS.lower(): |
- acl_entry_json['entity'] = 'allAuthenticatedUsers' |
- elif scope_type_lower == USER_BY_EMAIL.lower(): |
- acl_entry_json['entity'] = 'user-%s' % entry.scope.email_address |
- acl_entry_json['email'] = entry.scope.email_address |
- elif scope_type_lower == USER_BY_ID.lower(): |
- acl_entry_json['entity'] = 'user-%s' % entry.scope.id |
- acl_entry_json['entityId'] = entry.scope.id |
- elif scope_type_lower == GROUP_BY_EMAIL.lower(): |
- acl_entry_json['entity'] = 'group-%s' % entry.scope.email_address |
- acl_entry_json['email'] = entry.scope.email_address |
- elif scope_type_lower == GROUP_BY_ID.lower(): |
- acl_entry_json['entity'] = 'group-%s' % entry.scope.id |
- acl_entry_json['entityId'] = entry.scope.id |
- elif scope_type_lower == GROUP_BY_DOMAIN.lower(): |
- acl_entry_json['entity'] = 'domain-%s' % entry.scope.domain |
- acl_entry_json['domain'] = entry.scope.domain |
- else: |
- raise ArgumentException('ACL contains invalid scope type: %s' % |
- scope_type_lower) |
- |
- acl_entry_json['role'] = cls.XML_TO_JSON_ROLES[entry.permission] |
- return acl_entry_json |
- |
- @classmethod |
- def JsonToMessage(cls, json_data, message_type): |
- """Converts the input JSON data into list of Object/BucketAccessControls. |
- |
- Args: |
- json_data: String of JSON to convert. |
- message_type: Which type of access control entries to return, |
- either ObjectAccessControl or BucketAccessControl. |
- |
- Raises: |
- ArgumentException on invalid JSON data. |
- |
- Returns: |
- List of ObjectAccessControl or BucketAccessControl elements. |
- """ |
- try: |
- deserialized_acl = json.loads(json_data) |
- |
- acl = [] |
- for acl_entry in deserialized_acl: |
- acl.append(encoding.DictToMessage(acl_entry, message_type)) |
- return acl |
- except ValueError: |
- CheckForXmlConfigurationAndRaise('ACL', json_data) |
- |
- @classmethod |
- def JsonFromMessage(cls, acl): |
- """Strips unnecessary fields from an ACL message and returns valid JSON. |
- |
- Args: |
- acl: iterable ObjectAccessControl or BucketAccessControl |
- |
- Returns: |
- ACL JSON string. |
- """ |
- serializable_acl = [] |
- if acl is not None: |
- for acl_entry in acl: |
- if acl_entry.kind == u'storage#objectAccessControl': |
- acl_entry.object = None |
- acl_entry.generation = None |
- acl_entry.kind = None |
- acl_entry.bucket = None |
- acl_entry.id = None |
- acl_entry.selfLink = None |
- acl_entry.etag = None |
- serializable_acl.append(encoding.MessageToDict(acl_entry)) |
- return json.dumps(serializable_acl, sort_keys=True, |
- indent=2, separators=(',', ': ')) |