| Index: tools/telemetry/third_party/gsutilz/gslib/gcs_json_api.py
|
| diff --git a/tools/telemetry/third_party/gsutilz/gslib/gcs_json_api.py b/tools/telemetry/third_party/gsutilz/gslib/gcs_json_api.py
|
| deleted file mode 100644
|
| index e57671d35da9d677e9aeb92f30c676193a509ca5..0000000000000000000000000000000000000000
|
| --- a/tools/telemetry/third_party/gsutilz/gslib/gcs_json_api.py
|
| +++ /dev/null
|
| @@ -1,1359 +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.
|
| -"""JSON gsutil Cloud API implementation for Google Cloud Storage."""
|
| -
|
| -from __future__ import absolute_import
|
| -
|
| -import httplib
|
| -import json
|
| -import logging
|
| -import os
|
| -import socket
|
| -import ssl
|
| -import time
|
| -import traceback
|
| -
|
| -from apitools.base.py import credentials_lib
|
| -from apitools.base.py import encoding
|
| -from apitools.base.py import exceptions as apitools_exceptions
|
| -from apitools.base.py import http_wrapper as apitools_http_wrapper
|
| -from apitools.base.py import transfer as apitools_transfer
|
| -from apitools.base.py.util import CalculateWaitForRetry
|
| -
|
| -import boto
|
| -from boto import config
|
| -from gcs_oauth2_boto_plugin import oauth2_helper
|
| -import httplib2
|
| -from oauth2client import devshell
|
| -from oauth2client import multistore_file
|
| -
|
| -from gslib.cloud_api import AccessDeniedException
|
| -from gslib.cloud_api import ArgumentException
|
| -from gslib.cloud_api import BadRequestException
|
| -from gslib.cloud_api import CloudApi
|
| -from gslib.cloud_api import NotEmptyException
|
| -from gslib.cloud_api import NotFoundException
|
| -from gslib.cloud_api import PreconditionException
|
| -from gslib.cloud_api import Preconditions
|
| -from gslib.cloud_api import ResumableDownloadException
|
| -from gslib.cloud_api import ResumableUploadAbortException
|
| -from gslib.cloud_api import ResumableUploadException
|
| -from gslib.cloud_api import ResumableUploadStartOverException
|
| -from gslib.cloud_api import ServiceException
|
| -from gslib.cloud_api_helper import ValidateDstObjectMetadata
|
| -from gslib.cred_types import CredTypes
|
| -from gslib.exception import CommandException
|
| -from gslib.gcs_json_media import BytesTransferredContainer
|
| -from gslib.gcs_json_media import DownloadCallbackConnectionClassFactory
|
| -from gslib.gcs_json_media import HttpWithDownloadStream
|
| -from gslib.gcs_json_media import HttpWithNoRetries
|
| -from gslib.gcs_json_media import UploadCallbackConnectionClassFactory
|
| -from gslib.gcs_json_media import WrapDownloadHttpRequest
|
| -from gslib.gcs_json_media import WrapUploadHttpRequest
|
| -from gslib.no_op_credentials import NoOpCredentials
|
| -from gslib.progress_callback import ProgressCallbackWithBackoff
|
| -from gslib.project_id import PopulateProjectId
|
| -from gslib.third_party.storage_apitools import storage_v1_client as apitools_client
|
| -from gslib.third_party.storage_apitools import storage_v1_messages as apitools_messages
|
| -from gslib.tracker_file import DeleteTrackerFile
|
| -from gslib.tracker_file import GetRewriteTrackerFilePath
|
| -from gslib.tracker_file import HashRewriteParameters
|
| -from gslib.tracker_file import ReadRewriteTrackerFile
|
| -from gslib.tracker_file import WriteRewriteTrackerFile
|
| -from gslib.translation_helper import CreateBucketNotFoundException
|
| -from gslib.translation_helper import CreateObjectNotFoundException
|
| -from gslib.translation_helper import DEFAULT_CONTENT_TYPE
|
| -from gslib.translation_helper import REMOVE_CORS_CONFIG
|
| -from gslib.util import GetBotoConfigFileList
|
| -from gslib.util import GetCertsFile
|
| -from gslib.util import GetCredentialStoreFilename
|
| -from gslib.util import GetGceCredentialCacheFilename
|
| -from gslib.util import GetJsonResumableChunkSize
|
| -from gslib.util import GetMaxRetryDelay
|
| -from gslib.util import GetNewHttp
|
| -from gslib.util import GetNumRetries
|
| -from gslib.util import UTF8
|
| -
|
| -
|
| -# Implementation supports only 'gs' URLs, so provider is unused.
|
| -# pylint: disable=unused-argument
|
| -
|
| -DEFAULT_GCS_JSON_VERSION = 'v1'
|
| -
|
| -NUM_BUCKETS_PER_LIST_PAGE = 1000
|
| -NUM_OBJECTS_PER_LIST_PAGE = 1000
|
| -
|
| -TRANSLATABLE_APITOOLS_EXCEPTIONS = (apitools_exceptions.HttpError,
|
| - apitools_exceptions.StreamExhausted,
|
| - apitools_exceptions.TransferError,
|
| - apitools_exceptions.TransferInvalidError)
|
| -
|
| -# TODO: Distribute these exceptions better through apitools and here.
|
| -# Right now, apitools is configured not to handle any exceptions on
|
| -# uploads/downloads.
|
| -# oauth2_client tries to JSON-decode the response, which can result
|
| -# in a ValueError if the response was invalid. Until that is fixed in
|
| -# oauth2_client, need to handle it here.
|
| -HTTP_TRANSFER_EXCEPTIONS = (apitools_exceptions.TransferRetryError,
|
| - apitools_exceptions.BadStatusCodeError,
|
| - # TODO: Honor retry-after headers.
|
| - apitools_exceptions.RetryAfterError,
|
| - apitools_exceptions.RequestError,
|
| - httplib.BadStatusLine,
|
| - httplib.IncompleteRead,
|
| - httplib.ResponseNotReady,
|
| - httplib2.ServerNotFoundError,
|
| - socket.error,
|
| - socket.gaierror,
|
| - socket.timeout,
|
| - ssl.SSLError,
|
| - ValueError)
|
| -
|
| -_VALIDATE_CERTIFICATES_503_MESSAGE = (
|
| - """Service Unavailable. If you have recently changed
|
| - https_validate_certificates from True to False in your boto configuration
|
| - file, please delete any cached access tokens in your filesystem (at %s)
|
| - and try again.""" % GetCredentialStoreFilename())
|
| -
|
| -
|
| -class GcsJsonApi(CloudApi):
|
| - """Google Cloud Storage JSON implementation of gsutil Cloud API."""
|
| -
|
| - def __init__(self, bucket_storage_uri_class, logger, provider=None,
|
| - credentials=None, debug=0):
|
| - """Performs necessary setup for interacting with Google Cloud Storage.
|
| -
|
| - Args:
|
| - bucket_storage_uri_class: Unused.
|
| - logger: logging.logger for outputting log messages.
|
| - provider: Unused. This implementation supports only Google Cloud Storage.
|
| - credentials: Credentials to be used for interacting with Google Cloud
|
| - Storage.
|
| - debug: Debug level for the API implementation (0..3).
|
| - """
|
| - # TODO: Plumb host_header for perfdiag / test_perfdiag.
|
| - # TODO: Add jitter to apitools' http_wrapper retry mechanism.
|
| - super(GcsJsonApi, self).__init__(bucket_storage_uri_class, logger,
|
| - provider='gs', debug=debug)
|
| - no_op_credentials = False
|
| - if not credentials:
|
| - loaded_credentials = self._CheckAndGetCredentials(logger)
|
| -
|
| - if not loaded_credentials:
|
| - loaded_credentials = NoOpCredentials()
|
| - no_op_credentials = True
|
| - else:
|
| - if isinstance(credentials, NoOpCredentials):
|
| - no_op_credentials = True
|
| -
|
| - self.credentials = credentials or loaded_credentials
|
| -
|
| - self.certs_file = GetCertsFile()
|
| -
|
| - self.http = GetNewHttp()
|
| - self.http_base = 'https://'
|
| - gs_json_host = config.get('Credentials', 'gs_json_host', None)
|
| - self.host_base = gs_json_host or 'www.googleapis.com'
|
| -
|
| - if not gs_json_host:
|
| - gs_host = config.get('Credentials', 'gs_host', None)
|
| - if gs_host:
|
| - raise ArgumentException(
|
| - 'JSON API is selected but gs_json_host is not configured, '
|
| - 'while gs_host is configured to %s. Please also configure '
|
| - 'gs_json_host and gs_json_port to match your desired endpoint.'
|
| - % gs_host)
|
| -
|
| - gs_json_port = config.get('Credentials', 'gs_json_port', None)
|
| -
|
| - if not gs_json_port:
|
| - gs_port = config.get('Credentials', 'gs_port', None)
|
| - if gs_port:
|
| - raise ArgumentException(
|
| - 'JSON API is selected but gs_json_port is not configured, '
|
| - 'while gs_port is configured to %s. Please also configure '
|
| - 'gs_json_host and gs_json_port to match your desired endpoint.'
|
| - % gs_port)
|
| - self.host_port = ''
|
| - else:
|
| - self.host_port = ':' + config.get('Credentials', 'gs_json_port')
|
| -
|
| - self.api_version = config.get('GSUtil', 'json_api_version',
|
| - DEFAULT_GCS_JSON_VERSION)
|
| - self.url_base = (self.http_base + self.host_base + self.host_port + '/' +
|
| - 'storage/' + self.api_version + '/')
|
| -
|
| - self.credentials.set_store(
|
| - multistore_file.get_credential_storage_custom_string_key(
|
| - GetCredentialStoreFilename(), self.api_version))
|
| -
|
| - self.num_retries = GetNumRetries()
|
| -
|
| - log_request = (debug >= 3)
|
| - log_response = (debug >= 3)
|
| -
|
| - self.api_client = apitools_client.StorageV1(
|
| - url=self.url_base, http=self.http, log_request=log_request,
|
| - log_response=log_response, credentials=self.credentials,
|
| - version=self.api_version)
|
| - self.api_client.num_retries = self.num_retries
|
| -
|
| - if no_op_credentials:
|
| - # This API key is not secret and is used to identify gsutil during
|
| - # anonymous requests.
|
| - self.api_client.AddGlobalParam('key',
|
| - u'AIzaSyDnacJHrKma0048b13sh8cgxNUwulubmJM')
|
| -
|
| - def _CheckAndGetCredentials(self, logger):
|
| - configured_cred_types = []
|
| - try:
|
| - if self._HasOauth2UserAccountCreds():
|
| - configured_cred_types.append(CredTypes.OAUTH2_USER_ACCOUNT)
|
| - if self._HasOauth2ServiceAccountCreds():
|
| - configured_cred_types.append(CredTypes.OAUTH2_SERVICE_ACCOUNT)
|
| - if len(configured_cred_types) > 1:
|
| - # We only allow one set of configured credentials. Otherwise, we're
|
| - # choosing one arbitrarily, which can be very confusing to the user
|
| - # (e.g., if only one is authorized to perform some action) and can
|
| - # also mask errors.
|
| - # Because boto merges config files, GCE credentials show up by default
|
| - # for GCE VMs. We don't want to fail when a user creates a boto file
|
| - # with their own credentials, so in this case we'll use the OAuth2
|
| - # user credentials.
|
| - failed_cred_type = None
|
| - raise CommandException(
|
| - ('You have multiple types of configured credentials (%s), which is '
|
| - 'not supported. One common way this happens is if you run gsutil '
|
| - 'config to create credentials and later run gcloud auth, and '
|
| - 'create a second set of credentials. Your boto config path is: '
|
| - '%s. For more help, see "gsutil help creds".')
|
| - % (configured_cred_types, GetBotoConfigFileList()))
|
| -
|
| - failed_cred_type = CredTypes.OAUTH2_USER_ACCOUNT
|
| - user_creds = self._GetOauth2UserAccountCreds()
|
| - failed_cred_type = CredTypes.OAUTH2_SERVICE_ACCOUNT
|
| - service_account_creds = self._GetOauth2ServiceAccountCreds()
|
| - failed_cred_type = CredTypes.GCE
|
| - gce_creds = self._GetGceCreds()
|
| - failed_cred_type = CredTypes.DEVSHELL
|
| - devshell_creds = self._GetDevshellCreds()
|
| - return user_creds or service_account_creds or gce_creds or devshell_creds
|
| - except: # pylint: disable=bare-except
|
| -
|
| - # If we didn't actually try to authenticate because there were multiple
|
| - # types of configured credentials, don't emit this warning.
|
| - if failed_cred_type:
|
| - if os.environ.get('CLOUDSDK_WRAPPER') == '1':
|
| - logger.warn(
|
| - 'Your "%s" credentials are invalid. Please run\n'
|
| - ' $ gcloud auth login', failed_cred_type)
|
| - else:
|
| - logger.warn(
|
| - 'Your "%s" credentials are invalid. For more help, see '
|
| - '"gsutil help creds", or re-run the gsutil config command (see '
|
| - '"gsutil help config").', failed_cred_type)
|
| -
|
| - # If there's any set of configured credentials, we'll fail if they're
|
| - # invalid, rather than silently falling back to anonymous config (as
|
| - # boto does). That approach leads to much confusion if users don't
|
| - # realize their credentials are invalid.
|
| - raise
|
| -
|
| - def _HasOauth2ServiceAccountCreds(self):
|
| - return config.has_option('Credentials', 'gs_service_key_file')
|
| -
|
| - def _HasOauth2UserAccountCreds(self):
|
| - return config.has_option('Credentials', 'gs_oauth2_refresh_token')
|
| -
|
| - def _HasGceCreds(self):
|
| - return config.has_option('GoogleCompute', 'service_account')
|
| -
|
| - def _GetOauth2ServiceAccountCreds(self):
|
| - if self._HasOauth2ServiceAccountCreds():
|
| - return oauth2_helper.OAuth2ClientFromBotoConfig(
|
| - boto.config,
|
| - cred_type=CredTypes.OAUTH2_SERVICE_ACCOUNT).GetCredentials()
|
| -
|
| - def _GetOauth2UserAccountCreds(self):
|
| - if self._HasOauth2UserAccountCreds():
|
| - return oauth2_helper.OAuth2ClientFromBotoConfig(
|
| - boto.config).GetCredentials()
|
| -
|
| - def _GetGceCreds(self):
|
| - if self._HasGceCreds():
|
| - try:
|
| - return credentials_lib.GceAssertionCredentials(
|
| - cache_filename=GetGceCredentialCacheFilename())
|
| - except apitools_exceptions.ResourceUnavailableError, e:
|
| - if 'service account' in str(e) and 'does not exist' in str(e):
|
| - return None
|
| - raise
|
| -
|
| - def _GetDevshellCreds(self):
|
| - try:
|
| - return devshell.DevshellCredentials()
|
| - except devshell.NoDevshellServer:
|
| - return None
|
| - except:
|
| - raise
|
| -
|
| - def _GetNewDownloadHttp(self, download_stream):
|
| - return GetNewHttp(http_class=HttpWithDownloadStream, stream=download_stream)
|
| -
|
| - def _GetNewUploadHttp(self):
|
| - """Returns an upload-safe Http object (by disabling httplib2 retries)."""
|
| - return GetNewHttp(http_class=HttpWithNoRetries)
|
| -
|
| - def GetBucket(self, bucket_name, provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageBucketsGetRequest
|
| - .ProjectionValueValuesEnum.full)
|
| - apitools_request = apitools_messages.StorageBucketsGetRequest(
|
| - bucket=bucket_name, projection=projection)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| -
|
| - # Here and in list buckets, we have no way of knowing
|
| - # whether we requested a field and didn't get it because it didn't exist
|
| - # or because we didn't have permission to access it.
|
| - try:
|
| - return self.api_client.buckets.Get(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name)
|
| -
|
| - def PatchBucket(self, bucket_name, metadata, canned_acl=None,
|
| - canned_def_acl=None, preconditions=None, provider=None,
|
| - fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageBucketsPatchRequest
|
| - .ProjectionValueValuesEnum.full)
|
| - bucket_metadata = metadata
|
| -
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - # For blank metadata objects, we need to explicitly call
|
| - # them out to apitools so it will send/erase them.
|
| - apitools_include_fields = []
|
| - for metadata_field in ('metadata', 'lifecycle', 'logging', 'versioning',
|
| - 'website'):
|
| - attr = getattr(bucket_metadata, metadata_field, None)
|
| - if attr and not encoding.MessageToDict(attr):
|
| - setattr(bucket_metadata, metadata_field, None)
|
| - apitools_include_fields.append(metadata_field)
|
| -
|
| - if bucket_metadata.cors and bucket_metadata.cors == REMOVE_CORS_CONFIG:
|
| - bucket_metadata.cors = []
|
| - apitools_include_fields.append('cors')
|
| -
|
| - predefined_acl = None
|
| - if canned_acl:
|
| - # Must null out existing ACLs to apply a canned ACL.
|
| - apitools_include_fields.append('acl')
|
| - predefined_acl = (
|
| - apitools_messages.StorageBucketsPatchRequest.
|
| - PredefinedAclValueValuesEnum(
|
| - self._BucketCannedAclToPredefinedAcl(canned_acl)))
|
| -
|
| - predefined_def_acl = None
|
| - if canned_def_acl:
|
| - # Must null out existing default object ACLs to apply a canned ACL.
|
| - apitools_include_fields.append('defaultObjectAcl')
|
| - predefined_def_acl = (
|
| - apitools_messages.StorageBucketsPatchRequest.
|
| - PredefinedDefaultObjectAclValueValuesEnum(
|
| - self._ObjectCannedAclToPredefinedAcl(canned_def_acl)))
|
| -
|
| - apitools_request = apitools_messages.StorageBucketsPatchRequest(
|
| - bucket=bucket_name, bucketResource=bucket_metadata,
|
| - projection=projection,
|
| - ifMetagenerationMatch=preconditions.meta_gen_match,
|
| - predefinedAcl=predefined_acl,
|
| - predefinedDefaultObjectAcl=predefined_def_acl)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| - with self.api_client.IncludeFields(apitools_include_fields):
|
| - try:
|
| - return self.api_client.buckets.Patch(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e)
|
| -
|
| - def CreateBucket(self, bucket_name, project_id=None, metadata=None,
|
| - provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageBucketsInsertRequest
|
| - .ProjectionValueValuesEnum.full)
|
| - if not metadata:
|
| - metadata = apitools_messages.Bucket()
|
| - metadata.name = bucket_name
|
| -
|
| - if metadata.location:
|
| - metadata.location = metadata.location.upper()
|
| - if metadata.storageClass:
|
| - metadata.storageClass = metadata.storageClass.upper()
|
| -
|
| - project_id = PopulateProjectId(project_id)
|
| -
|
| - apitools_request = apitools_messages.StorageBucketsInsertRequest(
|
| - bucket=metadata, project=project_id, projection=projection)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| - try:
|
| - return self.api_client.buckets.Insert(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name)
|
| -
|
| - def DeleteBucket(self, bucket_name, preconditions=None, provider=None):
|
| - """See CloudApi class for function doc strings."""
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - apitools_request = apitools_messages.StorageBucketsDeleteRequest(
|
| - bucket=bucket_name, ifMetagenerationMatch=preconditions.meta_gen_match)
|
| -
|
| - try:
|
| - self.api_client.buckets.Delete(apitools_request)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - if isinstance(
|
| - self._TranslateApitoolsException(e, bucket_name=bucket_name),
|
| - NotEmptyException):
|
| - # If bucket is not empty, check to see if versioning is enabled and
|
| - # signal that in the exception if it is.
|
| - bucket_metadata = self.GetBucket(bucket_name,
|
| - fields=['versioning'])
|
| - if bucket_metadata.versioning and bucket_metadata.versioning.enabled:
|
| - raise NotEmptyException('VersionedBucketNotEmpty',
|
| - status=e.status_code)
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name)
|
| -
|
| - def ListBuckets(self, project_id=None, provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageBucketsListRequest
|
| - .ProjectionValueValuesEnum.full)
|
| - project_id = PopulateProjectId(project_id)
|
| -
|
| - apitools_request = apitools_messages.StorageBucketsListRequest(
|
| - project=project_id, maxResults=NUM_BUCKETS_PER_LIST_PAGE,
|
| - projection=projection)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - if 'nextPageToken' not in fields:
|
| - fields.add('nextPageToken')
|
| - global_params.fields = ','.join(set(fields))
|
| - try:
|
| - bucket_list = self.api_client.buckets.List(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e)
|
| -
|
| - for bucket in self._YieldBuckets(bucket_list):
|
| - yield bucket
|
| -
|
| - while bucket_list.nextPageToken:
|
| - apitools_request = apitools_messages.StorageBucketsListRequest(
|
| - project=project_id, pageToken=bucket_list.nextPageToken,
|
| - maxResults=NUM_BUCKETS_PER_LIST_PAGE, projection=projection)
|
| - try:
|
| - bucket_list = self.api_client.buckets.List(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e)
|
| -
|
| - for bucket in self._YieldBuckets(bucket_list):
|
| - yield bucket
|
| -
|
| - def _YieldBuckets(self, bucket_list):
|
| - """Yields buckets from a list returned by apitools."""
|
| - if bucket_list.items:
|
| - for bucket in bucket_list.items:
|
| - yield bucket
|
| -
|
| - def ListObjects(self, bucket_name, prefix=None, delimiter=None,
|
| - all_versions=None, provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageObjectsListRequest
|
| - .ProjectionValueValuesEnum.full)
|
| - apitools_request = apitools_messages.StorageObjectsListRequest(
|
| - bucket=bucket_name, prefix=prefix, delimiter=delimiter,
|
| - versions=all_versions, projection=projection,
|
| - maxResults=NUM_OBJECTS_PER_LIST_PAGE)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| -
|
| - if fields:
|
| - fields = set(fields)
|
| - if 'nextPageToken' not in fields:
|
| - fields.add('nextPageToken')
|
| - global_params.fields = ','.join(fields)
|
| -
|
| - try:
|
| - object_list = self.api_client.objects.List(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name)
|
| -
|
| - for object_or_prefix in self._YieldObjectsAndPrefixes(object_list):
|
| - yield object_or_prefix
|
| -
|
| - while object_list.nextPageToken:
|
| - apitools_request = apitools_messages.StorageObjectsListRequest(
|
| - bucket=bucket_name, prefix=prefix, delimiter=delimiter,
|
| - versions=all_versions, projection=projection,
|
| - pageToken=object_list.nextPageToken,
|
| - maxResults=NUM_OBJECTS_PER_LIST_PAGE)
|
| - try:
|
| - object_list = self.api_client.objects.List(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name)
|
| -
|
| - for object_or_prefix in self._YieldObjectsAndPrefixes(object_list):
|
| - yield object_or_prefix
|
| -
|
| - def _YieldObjectsAndPrefixes(self, object_list):
|
| - if object_list.items:
|
| - for cloud_obj in object_list.items:
|
| - yield CloudApi.CsObjectOrPrefix(cloud_obj,
|
| - CloudApi.CsObjectOrPrefixType.OBJECT)
|
| - if object_list.prefixes:
|
| - for prefix in object_list.prefixes:
|
| - yield CloudApi.CsObjectOrPrefix(prefix,
|
| - CloudApi.CsObjectOrPrefixType.PREFIX)
|
| -
|
| - def GetObjectMetadata(self, bucket_name, object_name, generation=None,
|
| - provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageObjectsGetRequest
|
| - .ProjectionValueValuesEnum.full)
|
| -
|
| - if generation:
|
| - generation = long(generation)
|
| -
|
| - apitools_request = apitools_messages.StorageObjectsGetRequest(
|
| - bucket=bucket_name, object=object_name, projection=projection,
|
| - generation=generation)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| -
|
| - try:
|
| - return self.api_client.objects.Get(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name,
|
| - object_name=object_name,
|
| - generation=generation)
|
| -
|
| - def GetObjectMedia(
|
| - self, bucket_name, object_name, download_stream,
|
| - provider=None, generation=None, object_size=None,
|
| - download_strategy=CloudApi.DownloadStrategy.ONE_SHOT, start_byte=0,
|
| - end_byte=None, progress_callback=None, serialization_data=None,
|
| - digesters=None):
|
| - """See CloudApi class for function doc strings."""
|
| - # This implementation will get the object metadata first if we don't pass it
|
| - # in via serialization_data.
|
| - if generation:
|
| - generation = long(generation)
|
| -
|
| - outer_total_size = object_size
|
| - if serialization_data:
|
| - outer_total_size = json.loads(serialization_data)['total_size']
|
| -
|
| - if progress_callback:
|
| - if outer_total_size is None:
|
| - raise ArgumentException('Download size is required when callbacks are '
|
| - 'requested for a download, but no size was '
|
| - 'provided.')
|
| - progress_callback(0, outer_total_size)
|
| -
|
| - bytes_downloaded_container = BytesTransferredContainer()
|
| - bytes_downloaded_container.bytes_transferred = start_byte
|
| -
|
| - callback_class_factory = DownloadCallbackConnectionClassFactory(
|
| - bytes_downloaded_container, total_size=outer_total_size,
|
| - progress_callback=progress_callback, digesters=digesters)
|
| - download_http_class = callback_class_factory.GetConnectionClass()
|
| -
|
| - download_http = self._GetNewDownloadHttp(download_stream)
|
| - download_http.connections = {'https': download_http_class}
|
| - authorized_download_http = self.credentials.authorize(download_http)
|
| - WrapDownloadHttpRequest(authorized_download_http)
|
| -
|
| - if serialization_data:
|
| - apitools_download = apitools_transfer.Download.FromData(
|
| - download_stream, serialization_data, self.api_client.http,
|
| - num_retries=self.num_retries)
|
| - else:
|
| - apitools_download = apitools_transfer.Download.FromStream(
|
| - download_stream, auto_transfer=False, total_size=object_size,
|
| - num_retries=self.num_retries)
|
| -
|
| - apitools_download.bytes_http = authorized_download_http
|
| - apitools_request = apitools_messages.StorageObjectsGetRequest(
|
| - bucket=bucket_name, object=object_name, generation=generation)
|
| -
|
| - try:
|
| - if download_strategy == CloudApi.DownloadStrategy.RESUMABLE:
|
| - # Disable retries in apitools. We will handle them explicitly here.
|
| - apitools_download.retry_func = (
|
| - apitools_http_wrapper.RethrowExceptionHandler)
|
| - return self._PerformResumableDownload(
|
| - bucket_name, object_name, download_stream, apitools_request,
|
| - apitools_download, bytes_downloaded_container,
|
| - generation=generation, start_byte=start_byte, end_byte=end_byte,
|
| - serialization_data=serialization_data)
|
| - else:
|
| - return self._PerformDownload(
|
| - bucket_name, object_name, download_stream, apitools_request,
|
| - apitools_download, generation=generation, start_byte=start_byte,
|
| - end_byte=end_byte, serialization_data=serialization_data)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name,
|
| - object_name=object_name,
|
| - generation=generation)
|
| -
|
| - def _PerformResumableDownload(
|
| - self, bucket_name, object_name, download_stream, apitools_request,
|
| - apitools_download, bytes_downloaded_container, generation=None,
|
| - start_byte=0, end_byte=None, serialization_data=None):
|
| - retries = 0
|
| - last_progress_byte = start_byte
|
| - while retries <= self.num_retries:
|
| - try:
|
| - return self._PerformDownload(
|
| - bucket_name, object_name, download_stream, apitools_request,
|
| - apitools_download, generation=generation, start_byte=start_byte,
|
| - end_byte=end_byte, serialization_data=serialization_data)
|
| - except HTTP_TRANSFER_EXCEPTIONS, e:
|
| - start_byte = download_stream.tell()
|
| - bytes_downloaded_container.bytes_transferred = start_byte
|
| - if start_byte > last_progress_byte:
|
| - # We've made progress, so allow a fresh set of retries.
|
| - last_progress_byte = start_byte
|
| - retries = 0
|
| - retries += 1
|
| - if retries > self.num_retries:
|
| - raise ResumableDownloadException(
|
| - 'Transfer failed after %d retries. Final exception: %s' %
|
| - (self.num_retries, unicode(e).encode(UTF8)))
|
| - time.sleep(CalculateWaitForRetry(retries, max_wait=GetMaxRetryDelay()))
|
| - if self.logger.isEnabledFor(logging.DEBUG):
|
| - self.logger.debug(
|
| - 'Retrying download from byte %s after exception: %s. Trace: %s',
|
| - start_byte, unicode(e).encode(UTF8), traceback.format_exc())
|
| - apitools_http_wrapper.RebuildHttpConnections(
|
| - apitools_download.bytes_http)
|
| -
|
| - def _PerformDownload(
|
| - self, bucket_name, object_name, download_stream, apitools_request,
|
| - apitools_download, generation=None, start_byte=0, end_byte=None,
|
| - serialization_data=None):
|
| - if not serialization_data:
|
| - try:
|
| - self.api_client.objects.Get(apitools_request,
|
| - download=apitools_download)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name,
|
| - object_name=object_name,
|
| - generation=generation)
|
| -
|
| - # Disable apitools' default print callbacks.
|
| - def _NoOpCallback(unused_response, unused_download_object):
|
| - pass
|
| -
|
| - # TODO: If we have a resumable download with accept-encoding:gzip
|
| - # on a object that is compressible but not in gzip form in the cloud,
|
| - # on-the-fly compression will gzip the object. In this case if our
|
| - # download breaks, future requests will ignore the range header and just
|
| - # return the object (gzipped) in its entirety. Ideally, we would unzip
|
| - # the bytes that we have locally and send a range request without
|
| - # accept-encoding:gzip so that we can download only the (uncompressed) bytes
|
| - # that we don't yet have.
|
| -
|
| - # Since bytes_http is created in this function, we don't get the
|
| - # user-agent header from api_client's http automatically.
|
| - additional_headers = {
|
| - 'accept-encoding': 'gzip',
|
| - 'user-agent': self.api_client.user_agent
|
| - }
|
| - if start_byte or end_byte:
|
| - apitools_download.GetRange(additional_headers=additional_headers,
|
| - start=start_byte, end=end_byte,
|
| - use_chunks=False)
|
| - else:
|
| - apitools_download.StreamMedia(
|
| - callback=_NoOpCallback, finish_callback=_NoOpCallback,
|
| - additional_headers=additional_headers, use_chunks=False)
|
| - return apitools_download.encoding
|
| -
|
| - def PatchObjectMetadata(self, bucket_name, object_name, metadata,
|
| - canned_acl=None, generation=None, preconditions=None,
|
| - provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageObjectsPatchRequest
|
| - .ProjectionValueValuesEnum.full)
|
| -
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - if generation:
|
| - generation = long(generation)
|
| -
|
| - predefined_acl = None
|
| - apitools_include_fields = []
|
| - if canned_acl:
|
| - # Must null out existing ACLs to apply a canned ACL.
|
| - apitools_include_fields.append('acl')
|
| - predefined_acl = (
|
| - apitools_messages.StorageObjectsPatchRequest.
|
| - PredefinedAclValueValuesEnum(
|
| - self._ObjectCannedAclToPredefinedAcl(canned_acl)))
|
| -
|
| - apitools_request = apitools_messages.StorageObjectsPatchRequest(
|
| - bucket=bucket_name, object=object_name, objectResource=metadata,
|
| - generation=generation, projection=projection,
|
| - ifGenerationMatch=preconditions.gen_match,
|
| - ifMetagenerationMatch=preconditions.meta_gen_match,
|
| - predefinedAcl=predefined_acl)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| -
|
| - try:
|
| - with self.api_client.IncludeFields(apitools_include_fields):
|
| - return self.api_client.objects.Patch(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name,
|
| - object_name=object_name,
|
| - generation=generation)
|
| -
|
| - def _UploadObject(self, upload_stream, object_metadata, canned_acl=None,
|
| - size=None, preconditions=None, provider=None, fields=None,
|
| - serialization_data=None, tracker_callback=None,
|
| - progress_callback=None,
|
| - apitools_strategy=apitools_transfer.SIMPLE_UPLOAD,
|
| - total_size=0):
|
| - # pylint: disable=g-doc-args
|
| - """Upload implementation. Cloud API arguments, plus two more.
|
| -
|
| - Additional args:
|
| - apitools_strategy: SIMPLE_UPLOAD or RESUMABLE_UPLOAD.
|
| - total_size: Total size of the upload; None if it is unknown (streaming).
|
| -
|
| - Returns:
|
| - Uploaded object metadata.
|
| - """
|
| - # pylint: enable=g-doc-args
|
| - ValidateDstObjectMetadata(object_metadata)
|
| - predefined_acl = None
|
| - if canned_acl:
|
| - predefined_acl = (
|
| - apitools_messages.StorageObjectsInsertRequest.
|
| - PredefinedAclValueValuesEnum(
|
| - self._ObjectCannedAclToPredefinedAcl(canned_acl)))
|
| -
|
| - bytes_uploaded_container = BytesTransferredContainer()
|
| -
|
| - if progress_callback and size:
|
| - total_size = size
|
| - progress_callback(0, size)
|
| -
|
| - callback_class_factory = UploadCallbackConnectionClassFactory(
|
| - bytes_uploaded_container, total_size=total_size,
|
| - progress_callback=progress_callback)
|
| -
|
| - upload_http = self._GetNewUploadHttp()
|
| - upload_http_class = callback_class_factory.GetConnectionClass()
|
| - upload_http.connections = {'http': upload_http_class,
|
| - 'https': upload_http_class}
|
| -
|
| - authorized_upload_http = self.credentials.authorize(upload_http)
|
| - WrapUploadHttpRequest(authorized_upload_http)
|
| - # Since bytes_http is created in this function, we don't get the
|
| - # user-agent header from api_client's http automatically.
|
| - additional_headers = {
|
| - 'user-agent': self.api_client.user_agent
|
| - }
|
| -
|
| - try:
|
| - content_type = None
|
| - apitools_request = None
|
| - global_params = None
|
| - if not serialization_data:
|
| - # This is a new upload, set up initial upload state.
|
| - content_type = object_metadata.contentType
|
| - if not content_type:
|
| - content_type = DEFAULT_CONTENT_TYPE
|
| -
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - apitools_request = apitools_messages.StorageObjectsInsertRequest(
|
| - bucket=object_metadata.bucket, object=object_metadata,
|
| - ifGenerationMatch=preconditions.gen_match,
|
| - ifMetagenerationMatch=preconditions.meta_gen_match,
|
| - predefinedAcl=predefined_acl)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| -
|
| - if apitools_strategy == apitools_transfer.SIMPLE_UPLOAD:
|
| - # One-shot upload.
|
| - apitools_upload = apitools_transfer.Upload(
|
| - upload_stream, content_type, total_size=size, auto_transfer=True,
|
| - num_retries=self.num_retries)
|
| - apitools_upload.strategy = apitools_strategy
|
| - apitools_upload.bytes_http = authorized_upload_http
|
| -
|
| - return self.api_client.objects.Insert(
|
| - apitools_request,
|
| - upload=apitools_upload,
|
| - global_params=global_params)
|
| - else: # Resumable upload.
|
| - return self._PerformResumableUpload(
|
| - upload_stream, authorized_upload_http, content_type, size,
|
| - serialization_data, apitools_strategy, apitools_request,
|
| - global_params, bytes_uploaded_container, tracker_callback,
|
| - additional_headers, progress_callback)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=object_metadata.bucket,
|
| - object_name=object_metadata.name)
|
| -
|
| - def _PerformResumableUpload(
|
| - self, upload_stream, authorized_upload_http, content_type, size,
|
| - serialization_data, apitools_strategy, apitools_request, global_params,
|
| - bytes_uploaded_container, tracker_callback, addl_headers,
|
| - progress_callback):
|
| - try:
|
| - if serialization_data:
|
| - # Resuming an existing upload.
|
| - apitools_upload = apitools_transfer.Upload.FromData(
|
| - upload_stream, serialization_data, self.api_client.http,
|
| - num_retries=self.num_retries)
|
| - apitools_upload.chunksize = GetJsonResumableChunkSize()
|
| - apitools_upload.bytes_http = authorized_upload_http
|
| - else:
|
| - # New resumable upload.
|
| - apitools_upload = apitools_transfer.Upload(
|
| - upload_stream, content_type, total_size=size,
|
| - chunksize=GetJsonResumableChunkSize(), auto_transfer=False,
|
| - num_retries=self.num_retries)
|
| - apitools_upload.strategy = apitools_strategy
|
| - apitools_upload.bytes_http = authorized_upload_http
|
| - self.api_client.objects.Insert(
|
| - apitools_request,
|
| - upload=apitools_upload,
|
| - global_params=global_params)
|
| - # Disable retries in apitools. We will handle them explicitly here.
|
| - apitools_upload.retry_func = (
|
| - apitools_http_wrapper.RethrowExceptionHandler)
|
| -
|
| - # Disable apitools' default print callbacks.
|
| - def _NoOpCallback(unused_response, unused_upload_object):
|
| - pass
|
| -
|
| - # If we're resuming an upload, apitools has at this point received
|
| - # from the server how many bytes it already has. Update our
|
| - # callback class with this information.
|
| - bytes_uploaded_container.bytes_transferred = apitools_upload.progress
|
| - if tracker_callback:
|
| - tracker_callback(json.dumps(apitools_upload.serialization_data))
|
| -
|
| - retries = 0
|
| - last_progress_byte = apitools_upload.progress
|
| - while retries <= self.num_retries:
|
| - try:
|
| - # TODO: On retry, this will seek to the bytes that the server has,
|
| - # causing the hash to be recalculated. Make HashingFileUploadWrapper
|
| - # save a digest according to json_resumable_chunk_size.
|
| - if size:
|
| - # If size is known, we can send it all in one request and avoid
|
| - # making a round-trip per chunk.
|
| - http_response = apitools_upload.StreamMedia(
|
| - callback=_NoOpCallback, finish_callback=_NoOpCallback,
|
| - additional_headers=addl_headers)
|
| - else:
|
| - # Otherwise it's a streaming request and we need to ensure that we
|
| - # send the bytes in chunks so that we can guarantee that we never
|
| - # need to seek backwards more than our buffer (and also that the
|
| - # chunks are aligned to 256KB).
|
| - http_response = apitools_upload.StreamInChunks(
|
| - callback=_NoOpCallback, finish_callback=_NoOpCallback,
|
| - additional_headers=addl_headers)
|
| - processed_response = self.api_client.objects.ProcessHttpResponse(
|
| - self.api_client.objects.GetMethodConfig('Insert'), http_response)
|
| - if size is None and progress_callback:
|
| - # Make final progress callback; total size should now be known.
|
| - # This works around the fact the send function counts header bytes.
|
| - # However, this will make the progress appear to go slightly
|
| - # backwards at the end.
|
| - progress_callback(apitools_upload.total_size,
|
| - apitools_upload.total_size)
|
| - return processed_response
|
| - except HTTP_TRANSFER_EXCEPTIONS, e:
|
| - apitools_http_wrapper.RebuildHttpConnections(
|
| - apitools_upload.bytes_http)
|
| - while retries <= self.num_retries:
|
| - try:
|
| - # TODO: Simulate the refresh case in tests. Right now, our
|
| - # mocks are not complex enough to simulate a failure.
|
| - apitools_upload.RefreshResumableUploadState()
|
| - start_byte = apitools_upload.progress
|
| - bytes_uploaded_container.bytes_transferred = start_byte
|
| - break
|
| - except HTTP_TRANSFER_EXCEPTIONS, e2:
|
| - apitools_http_wrapper.RebuildHttpConnections(
|
| - apitools_upload.bytes_http)
|
| - retries += 1
|
| - if retries > self.num_retries:
|
| - raise ResumableUploadException(
|
| - 'Transfer failed after %d retries. Final exception: %s' %
|
| - (self.num_retries, e2))
|
| - time.sleep(
|
| - CalculateWaitForRetry(retries, max_wait=GetMaxRetryDelay()))
|
| - if start_byte > last_progress_byte:
|
| - # We've made progress, so allow a fresh set of retries.
|
| - last_progress_byte = start_byte
|
| - retries = 0
|
| - else:
|
| - retries += 1
|
| - if retries > self.num_retries:
|
| - raise ResumableUploadException(
|
| - 'Transfer failed after %d retries. Final exception: %s' %
|
| - (self.num_retries, unicode(e).encode(UTF8)))
|
| - time.sleep(
|
| - CalculateWaitForRetry(retries, max_wait=GetMaxRetryDelay()))
|
| - if self.logger.isEnabledFor(logging.DEBUG):
|
| - self.logger.debug(
|
| - 'Retrying upload from byte %s after exception: %s. Trace: %s',
|
| - start_byte, unicode(e).encode(UTF8), traceback.format_exc())
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - resumable_ex = self._TranslateApitoolsResumableUploadException(e)
|
| - if resumable_ex:
|
| - raise resumable_ex
|
| - else:
|
| - raise
|
| -
|
| - def UploadObject(self, upload_stream, object_metadata, canned_acl=None,
|
| - size=None, preconditions=None, progress_callback=None,
|
| - provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - return self._UploadObject(
|
| - upload_stream, object_metadata, canned_acl=canned_acl,
|
| - size=size, preconditions=preconditions,
|
| - progress_callback=progress_callback, fields=fields,
|
| - apitools_strategy=apitools_transfer.SIMPLE_UPLOAD)
|
| -
|
| - def UploadObjectStreaming(self, upload_stream, object_metadata,
|
| - canned_acl=None, preconditions=None,
|
| - progress_callback=None, provider=None,
|
| - fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - # Streaming indicated by not passing a size.
|
| - # Resumable capabilities are present up to the resumable chunk size using
|
| - # a buffered stream.
|
| - return self._UploadObject(
|
| - upload_stream, object_metadata, canned_acl=canned_acl,
|
| - preconditions=preconditions, progress_callback=progress_callback,
|
| - fields=fields, apitools_strategy=apitools_transfer.RESUMABLE_UPLOAD,
|
| - total_size=None)
|
| -
|
| - def UploadObjectResumable(
|
| - self, upload_stream, object_metadata, canned_acl=None, preconditions=None,
|
| - provider=None, fields=None, size=None, serialization_data=None,
|
| - tracker_callback=None, progress_callback=None):
|
| - """See CloudApi class for function doc strings."""
|
| - return self._UploadObject(
|
| - upload_stream, object_metadata, canned_acl=canned_acl,
|
| - preconditions=preconditions, fields=fields, size=size,
|
| - serialization_data=serialization_data,
|
| - tracker_callback=tracker_callback, progress_callback=progress_callback,
|
| - apitools_strategy=apitools_transfer.RESUMABLE_UPLOAD)
|
| -
|
| - def CopyObject(self, src_obj_metadata, dst_obj_metadata, src_generation=None,
|
| - canned_acl=None, preconditions=None, progress_callback=None,
|
| - max_bytes_per_call=None, provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - ValidateDstObjectMetadata(dst_obj_metadata)
|
| - predefined_acl = None
|
| - if canned_acl:
|
| - predefined_acl = (
|
| - apitools_messages.StorageObjectsRewriteRequest.
|
| - DestinationPredefinedAclValueValuesEnum(
|
| - self._ObjectCannedAclToPredefinedAcl(canned_acl)))
|
| -
|
| - if src_generation:
|
| - src_generation = long(src_generation)
|
| -
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - projection = (apitools_messages.StorageObjectsRewriteRequest.
|
| - ProjectionValueValuesEnum.full)
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - # Rewrite returns the resultant object under the 'resource' field.
|
| - new_fields = set(['done', 'objectSize', 'rewriteToken',
|
| - 'totalBytesRewritten'])
|
| - for field in fields:
|
| - new_fields.add('resource/' + field)
|
| - global_params.fields = ','.join(set(new_fields))
|
| -
|
| - # Check to see if we are resuming a rewrite.
|
| - tracker_file_name = GetRewriteTrackerFilePath(
|
| - src_obj_metadata.bucket, src_obj_metadata.name, dst_obj_metadata.bucket,
|
| - dst_obj_metadata.name, 'JSON')
|
| - rewrite_params_hash = HashRewriteParameters(
|
| - src_obj_metadata, dst_obj_metadata, projection,
|
| - src_generation=src_generation, gen_match=preconditions.gen_match,
|
| - meta_gen_match=preconditions.meta_gen_match,
|
| - canned_acl=predefined_acl, fields=global_params.fields,
|
| - max_bytes_per_call=max_bytes_per_call)
|
| - resume_rewrite_token = ReadRewriteTrackerFile(tracker_file_name,
|
| - rewrite_params_hash)
|
| -
|
| - progress_cb_with_backoff = None
|
| - try:
|
| - last_bytes_written = 0L
|
| - while True:
|
| - apitools_request = apitools_messages.StorageObjectsRewriteRequest(
|
| - sourceBucket=src_obj_metadata.bucket,
|
| - sourceObject=src_obj_metadata.name,
|
| - destinationBucket=dst_obj_metadata.bucket,
|
| - destinationObject=dst_obj_metadata.name,
|
| - projection=projection, object=dst_obj_metadata,
|
| - sourceGeneration=src_generation,
|
| - ifGenerationMatch=preconditions.gen_match,
|
| - ifMetagenerationMatch=preconditions.meta_gen_match,
|
| - destinationPredefinedAcl=predefined_acl,
|
| - rewriteToken=resume_rewrite_token,
|
| - maxBytesRewrittenPerCall=max_bytes_per_call)
|
| - rewrite_response = self.api_client.objects.Rewrite(
|
| - apitools_request, global_params=global_params)
|
| - bytes_written = long(rewrite_response.totalBytesRewritten)
|
| - if progress_callback and not progress_cb_with_backoff:
|
| - progress_cb_with_backoff = ProgressCallbackWithBackoff(
|
| - long(rewrite_response.objectSize), progress_callback)
|
| - if progress_cb_with_backoff:
|
| - progress_cb_with_backoff.Progress(
|
| - bytes_written - last_bytes_written)
|
| -
|
| - if rewrite_response.done:
|
| - break
|
| - elif not resume_rewrite_token:
|
| - # Save the token and make a tracker file if they don't already exist.
|
| - resume_rewrite_token = rewrite_response.rewriteToken
|
| - WriteRewriteTrackerFile(tracker_file_name, rewrite_params_hash,
|
| - rewrite_response.rewriteToken)
|
| - last_bytes_written = bytes_written
|
| -
|
| - DeleteTrackerFile(tracker_file_name)
|
| - return rewrite_response.resource
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=dst_obj_metadata.bucket,
|
| - object_name=dst_obj_metadata.name)
|
| -
|
| - def DeleteObject(self, bucket_name, object_name, preconditions=None,
|
| - generation=None, provider=None):
|
| - """See CloudApi class for function doc strings."""
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - if generation:
|
| - generation = long(generation)
|
| -
|
| - apitools_request = apitools_messages.StorageObjectsDeleteRequest(
|
| - bucket=bucket_name, object=object_name, generation=generation,
|
| - ifGenerationMatch=preconditions.gen_match,
|
| - ifMetagenerationMatch=preconditions.meta_gen_match)
|
| - try:
|
| - return self.api_client.objects.Delete(apitools_request)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name,
|
| - object_name=object_name,
|
| - generation=generation)
|
| -
|
| - def ComposeObject(self, src_objs_metadata, dst_obj_metadata,
|
| - preconditions=None, provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - ValidateDstObjectMetadata(dst_obj_metadata)
|
| -
|
| - dst_obj_name = dst_obj_metadata.name
|
| - dst_obj_metadata.name = None
|
| - dst_bucket_name = dst_obj_metadata.bucket
|
| - dst_obj_metadata.bucket = None
|
| - if not dst_obj_metadata.contentType:
|
| - dst_obj_metadata.contentType = DEFAULT_CONTENT_TYPE
|
| -
|
| - if not preconditions:
|
| - preconditions = Preconditions()
|
| -
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| -
|
| - src_objs_compose_request = apitools_messages.ComposeRequest(
|
| - sourceObjects=src_objs_metadata, destination=dst_obj_metadata)
|
| -
|
| - apitools_request = apitools_messages.StorageObjectsComposeRequest(
|
| - composeRequest=src_objs_compose_request,
|
| - destinationBucket=dst_bucket_name,
|
| - destinationObject=dst_obj_name,
|
| - ifGenerationMatch=preconditions.gen_match,
|
| - ifMetagenerationMatch=preconditions.meta_gen_match)
|
| - try:
|
| - return self.api_client.objects.Compose(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - # We can't be sure which object was missing in the 404 case.
|
| - if isinstance(e, apitools_exceptions.HttpError) and e.status_code == 404:
|
| - raise NotFoundException('One of the source objects does not exist.')
|
| - else:
|
| - self._TranslateExceptionAndRaise(e)
|
| -
|
| - def WatchBucket(self, bucket_name, address, channel_id, token=None,
|
| - provider=None, fields=None):
|
| - """See CloudApi class for function doc strings."""
|
| - projection = (apitools_messages.StorageObjectsWatchAllRequest
|
| - .ProjectionValueValuesEnum.full)
|
| -
|
| - channel = apitools_messages.Channel(address=address, id=channel_id,
|
| - token=token, type='WEB_HOOK')
|
| -
|
| - apitools_request = apitools_messages.StorageObjectsWatchAllRequest(
|
| - bucket=bucket_name, channel=channel, projection=projection)
|
| -
|
| - global_params = apitools_messages.StandardQueryParameters()
|
| - if fields:
|
| - global_params.fields = ','.join(set(fields))
|
| -
|
| - try:
|
| - return self.api_client.objects.WatchAll(apitools_request,
|
| - global_params=global_params)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e, bucket_name=bucket_name)
|
| -
|
| - def StopChannel(self, channel_id, resource_id, provider=None):
|
| - """See CloudApi class for function doc strings."""
|
| - channel = apitools_messages.Channel(id=channel_id, resourceId=resource_id)
|
| - try:
|
| - self.api_client.channels.Stop(channel)
|
| - except TRANSLATABLE_APITOOLS_EXCEPTIONS, e:
|
| - self._TranslateExceptionAndRaise(e)
|
| -
|
| - def _BucketCannedAclToPredefinedAcl(self, canned_acl_string):
|
| - """Translates the input string to a bucket PredefinedAcl string.
|
| -
|
| - Args:
|
| - canned_acl_string: Canned ACL string.
|
| -
|
| - Returns:
|
| - String that can be used as a query parameter with the JSON API. This
|
| - corresponds to a flavor of *PredefinedAclValueValuesEnum and can be
|
| - used as input to apitools requests that affect bucket access controls.
|
| - """
|
| - # XML : JSON
|
| - translation_dict = {
|
| - None: None,
|
| - 'authenticated-read': 'authenticatedRead',
|
| - 'private': 'private',
|
| - 'project-private': 'projectPrivate',
|
| - 'public-read': 'publicRead',
|
| - 'public-read-write': 'publicReadWrite'
|
| - }
|
| - if canned_acl_string in translation_dict:
|
| - return translation_dict[canned_acl_string]
|
| - raise ArgumentException('Invalid canned ACL %s' % canned_acl_string)
|
| -
|
| - def _ObjectCannedAclToPredefinedAcl(self, canned_acl_string):
|
| - """Translates the input string to an object PredefinedAcl string.
|
| -
|
| - Args:
|
| - canned_acl_string: Canned ACL string.
|
| -
|
| - Returns:
|
| - String that can be used as a query parameter with the JSON API. This
|
| - corresponds to a flavor of *PredefinedAclValueValuesEnum and can be
|
| - used as input to apitools requests that affect object access controls.
|
| - """
|
| - # XML : JSON
|
| - translation_dict = {
|
| - None: None,
|
| - 'authenticated-read': 'authenticatedRead',
|
| - 'bucket-owner-read': 'bucketOwnerRead',
|
| - 'bucket-owner-full-control': 'bucketOwnerFullControl',
|
| - 'private': 'private',
|
| - 'project-private': 'projectPrivate',
|
| - 'public-read': 'publicRead'
|
| - }
|
| - if canned_acl_string in translation_dict:
|
| - return translation_dict[canned_acl_string]
|
| - raise ArgumentException('Invalid canned ACL %s' % canned_acl_string)
|
| -
|
| - def _TranslateExceptionAndRaise(self, e, bucket_name=None, object_name=None,
|
| - generation=None):
|
| - """Translates an HTTP exception and raises the translated or original value.
|
| -
|
| - Args:
|
| - e: Any Exception.
|
| - bucket_name: Optional bucket name in request that caused the exception.
|
| - object_name: Optional object name in request that caused the exception.
|
| - generation: Optional generation in request that caused the exception.
|
| -
|
| - Raises:
|
| - Translated CloudApi exception, or the original exception if it was not
|
| - translatable.
|
| - """
|
| - translated_exception = self._TranslateApitoolsException(
|
| - e, bucket_name=bucket_name, object_name=object_name,
|
| - generation=generation)
|
| - if translated_exception:
|
| - raise translated_exception
|
| - else:
|
| - raise
|
| -
|
| - def _GetMessageFromHttpError(self, http_error):
|
| - if isinstance(http_error, apitools_exceptions.HttpError):
|
| - if getattr(http_error, 'content', None):
|
| - try:
|
| - json_obj = json.loads(http_error.content)
|
| - if 'error' in json_obj and 'message' in json_obj['error']:
|
| - return json_obj['error']['message']
|
| - except Exception: # pylint: disable=broad-except
|
| - # If we couldn't decode anything, just leave the message as None.
|
| - pass
|
| -
|
| - def _TranslateApitoolsResumableUploadException(
|
| - self, e, bucket_name=None, object_name=None, generation=None):
|
| - if isinstance(e, apitools_exceptions.HttpError):
|
| - message = self._GetMessageFromHttpError(e)
|
| - if (e.status_code == 503 and
|
| - self.http.disable_ssl_certificate_validation):
|
| - return ServiceException(_VALIDATE_CERTIFICATES_503_MESSAGE,
|
| - status=e.status_code)
|
| - elif e.status_code >= 500:
|
| - return ResumableUploadException(
|
| - message or 'Server Error', status=e.status_code)
|
| - elif e.status_code == 429:
|
| - return ResumableUploadException(
|
| - message or 'Too Many Requests', status=e.status_code)
|
| - elif e.status_code == 410:
|
| - return ResumableUploadStartOverException(
|
| - message or 'Bad Request', status=e.status_code)
|
| - elif e.status_code == 404:
|
| - return ResumableUploadStartOverException(
|
| - message or 'Bad Request', status=e.status_code)
|
| - elif e.status_code >= 400:
|
| - return ResumableUploadAbortException(
|
| - message or 'Bad Request', status=e.status_code)
|
| - if isinstance(e, apitools_exceptions.StreamExhausted):
|
| - return ResumableUploadAbortException(e.message)
|
| - if (isinstance(e, apitools_exceptions.TransferError) and
|
| - ('Aborting transfer' in e.message or
|
| - 'Not enough bytes in stream' in e.message or
|
| - 'additional bytes left in stream' in e.message)):
|
| - return ResumableUploadAbortException(e.message)
|
| -
|
| - def _TranslateApitoolsException(self, e, bucket_name=None, object_name=None,
|
| - generation=None):
|
| - """Translates apitools exceptions into their gsutil Cloud Api equivalents.
|
| -
|
| - Args:
|
| - e: Any exception in TRANSLATABLE_APITOOLS_EXCEPTIONS.
|
| - bucket_name: Optional bucket name in request that caused the exception.
|
| - object_name: Optional object name in request that caused the exception.
|
| - generation: Optional generation in request that caused the exception.
|
| -
|
| - Returns:
|
| - CloudStorageApiServiceException for translatable exceptions, None
|
| - otherwise.
|
| - """
|
| - if isinstance(e, apitools_exceptions.HttpError):
|
| - message = self._GetMessageFromHttpError(e)
|
| - if e.status_code == 400:
|
| - # It is possible that the Project ID is incorrect. Unfortunately the
|
| - # JSON API does not give us much information about what part of the
|
| - # request was bad.
|
| - return BadRequestException(message or 'Bad Request',
|
| - status=e.status_code)
|
| - elif e.status_code == 401:
|
| - if 'Login Required' in str(e):
|
| - return AccessDeniedException(
|
| - message or 'Access denied: login required.',
|
| - status=e.status_code)
|
| - elif e.status_code == 403:
|
| - if 'The account for the specified project has been disabled' in str(e):
|
| - return AccessDeniedException(message or 'Account disabled.',
|
| - status=e.status_code)
|
| - elif 'Daily Limit for Unauthenticated Use Exceeded' in str(e):
|
| - return AccessDeniedException(
|
| - message or 'Access denied: quota exceeded. '
|
| - 'Is your project ID valid?',
|
| - status=e.status_code)
|
| - elif 'The bucket you tried to delete was not empty.' in str(e):
|
| - return NotEmptyException('BucketNotEmpty (%s)' % bucket_name,
|
| - status=e.status_code)
|
| - elif ('The bucket you tried to create requires domain ownership '
|
| - 'verification.' in str(e)):
|
| - return AccessDeniedException(
|
| - 'The bucket you tried to create requires domain ownership '
|
| - 'verification. Please see '
|
| - 'https://developers.google.com/storage/docs/bucketnaming'
|
| - '?hl=en#verification for more details.', status=e.status_code)
|
| - elif 'User Rate Limit Exceeded' in str(e):
|
| - return AccessDeniedException('Rate limit exceeded. Please retry this '
|
| - 'request later.', status=e.status_code)
|
| - elif 'Access Not Configured' in str(e):
|
| - return AccessDeniedException(
|
| - 'Access Not Configured. Please go to the Google Developers '
|
| - 'Console (https://cloud.google.com/console#/project) for your '
|
| - 'project, select APIs and Auth and enable the '
|
| - 'Google Cloud Storage JSON API.',
|
| - status=e.status_code)
|
| - else:
|
| - return AccessDeniedException(message or e.message,
|
| - status=e.status_code)
|
| - elif e.status_code == 404:
|
| - if bucket_name:
|
| - if object_name:
|
| - return CreateObjectNotFoundException(e.status_code, self.provider,
|
| - bucket_name, object_name,
|
| - generation=generation)
|
| - return CreateBucketNotFoundException(e.status_code, self.provider,
|
| - bucket_name)
|
| - return NotFoundException(e.message, status=e.status_code)
|
| - elif e.status_code == 409 and bucket_name:
|
| - if 'The bucket you tried to delete was not empty.' in str(e):
|
| - return NotEmptyException('BucketNotEmpty (%s)' % bucket_name,
|
| - status=e.status_code)
|
| - return ServiceException(
|
| - 'Bucket %s already exists.' % bucket_name, status=e.status_code)
|
| - elif e.status_code == 412:
|
| - return PreconditionException(message, status=e.status_code)
|
| - elif (e.status_code == 503 and
|
| - not self.http.disable_ssl_certificate_validation):
|
| - return ServiceException(_VALIDATE_CERTIFICATES_503_MESSAGE,
|
| - status=e.status_code)
|
| - return ServiceException(message, status=e.status_code)
|
| - elif isinstance(e, apitools_exceptions.TransferInvalidError):
|
| - return ServiceException('Transfer invalid (possible encoding error: %s)'
|
| - % str(e))
|
|
|