コード例 #1
0
    def get_object_metadata(self,
                            bucket_name,
                            object_name,
                            request_config=None,
                            generation=None,
                            fields_scope=cloud_api.FieldsScope.NO_ACL):
        """See super class."""

        # S3 requires a string, but GCS uses an int for generation.
        if generation:
            generation = int(generation)

        projection = self._get_projection(
            fields_scope, self.messages.StorageObjectsGetRequest)

        decryption_key = getattr(
            getattr(request_config, 'resource_args', None), 'decryption_key',
            None)
        with self._encryption_headers_context(decryption_key):
            object_metadata = self.client.objects.Get(
                self.messages.StorageObjectsGetRequest(bucket=bucket_name,
                                                       object=object_name,
                                                       generation=generation,
                                                       projection=projection))
        return gcs_metadata_util.get_object_resource_from_metadata(
            object_metadata)
コード例 #2
0
    def upload_object(self,
                      source_stream,
                      destination_resource,
                      progress_callback=None,
                      request_config=None,
                      serialization_data=None,
                      tracker_callback=None,
                      upload_strategy=cloud_api.UploadStrategy.SIMPLE):
        """See CloudApi class for function doc strings."""
        del progress_callback  # Unused.
        if self._upload_http_client is None:
            self._upload_http_client = transports.GetApitoolsTransport()

        validated_request_config = cloud_api.get_provider_request_config(
            request_config, GcsRequestConfig)

        if upload_strategy == cloud_api.UploadStrategy.SIMPLE:
            upload = gcs_upload.SimpleUpload(self, self._upload_http_client,
                                             source_stream,
                                             DEFAULT_CONTENT_TYPE,
                                             destination_resource,
                                             validated_request_config)
        elif upload_strategy == cloud_api.UploadStrategy.RESUMABLE:
            upload = gcs_upload.ResumableUpload(
                self, self._upload_http_client, source_stream,
                DEFAULT_CONTENT_TYPE, destination_resource,
                validated_request_config, serialization_data, tracker_callback)
        else:
            raise command_errors.Error('Invalid upload strategy: {}.'.format(
                upload_strategy.value))

        return gcs_metadata_util.get_object_resource_from_metadata(
            upload.run())
コード例 #3
0
    def get_object_metadata(self,
                            bucket_name,
                            object_name,
                            generation=None,
                            fields_scope=cloud_api.FieldsScope.NO_ACL):
        """See super class."""

        # S3 requires a string, but GCS uses an int for generation.
        if generation:
            generation = int(generation)

        projection = self._get_projection(
            fields_scope, self.messages.StorageObjectsGetRequest)

        # TODO(b/160238394) Decrypt metadata fields if necessary.
        try:
            object_metadata = self.client.objects.Get(
                self.messages.StorageObjectsGetRequest(bucket=bucket_name,
                                                       object=object_name,
                                                       generation=generation,
                                                       projection=projection))
        except apitools_exceptions.HttpNotFoundError:
            raise cloud_errors.NotFoundError('Object not found: {}'.format(
                storage_url.CloudUrl(storage_url.ProviderPrefix.GCS,
                                     bucket_name, object_name,
                                     generation).url_string))
        return gcs_metadata_util.get_object_resource_from_metadata(
            object_metadata)
コード例 #4
0
    def upload_object(self,
                      source_stream,
                      destination_resource,
                      request_config,
                      source_resource=None,
                      serialization_data=None,
                      tracker_callback=None,
                      upload_strategy=cloud_api.UploadStrategy.SIMPLE):
        """See CloudApi class for function doc strings."""
        if self._upload_http_client is None:
            self._upload_http_client = transports.GetApitoolsTransport(
                redact_request_body_reason=
                ('Object data is not displayed to keep the log output clean.'
                 ' Set log_http_show_request_body property to True to print the'
                 ' body of this request.'))

        if source_resource:
            source_path = source_resource.storage_url.versionless_url_string
        else:
            source_path = None
        should_gzip_in_flight = gzip_util.should_gzip_in_flight(
            request_config.gzip_settings, source_path)
        if should_gzip_in_flight:
            log.info('Using compressed transport encoding for {}.'.format(
                source_path))
        if upload_strategy == cloud_api.UploadStrategy.SIMPLE:
            upload = gcs_upload.SimpleUpload(self, self._upload_http_client,
                                             source_stream,
                                             destination_resource,
                                             should_gzip_in_flight,
                                             request_config, source_resource)
        elif upload_strategy == cloud_api.UploadStrategy.RESUMABLE:
            upload = gcs_upload.ResumableUpload(
                self, self._upload_http_client, source_stream,
                destination_resource, should_gzip_in_flight, request_config,
                source_resource, serialization_data, tracker_callback)
        else:
            raise command_errors.Error('Invalid upload strategy: {}.'.format(
                upload_strategy.value))

        encryption_key = getattr(request_config.resource_args,
                                 'encryption_key', None)
        try:
            with self._encryption_headers_context(encryption_key):
                metadata = upload.run()
        except (
                apitools_exceptions.StreamExhausted,
                apitools_exceptions.TransferError,
        ) as error:
            raise cloud_errors.ResumableUploadAbortError(
                '{}\n This likely occurred because the file being uploaded changed '
                'size between resumable upload attempts. If this error persists, try '
                'deleting the tracker files present in {}'.format(
                    str(error),
                    properties.VALUES.storage.tracker_files_directory.Get()))

        return gcs_metadata_util.get_object_resource_from_metadata(metadata)
コード例 #5
0
    def list_objects(self,
                     bucket_name,
                     prefix=None,
                     delimiter=None,
                     all_versions=None,
                     fields_scope=cloud_api.FieldsScope.NO_ACL):
        """See super class."""
        projection = self._get_projection(
            fields_scope, self.messages.StorageObjectsListRequest)
        global_params = None
        if fields_scope == cloud_api.FieldsScope.SHORT:
            global_params = self.messages.StandardQueryParameters()
            global_params.fields = (
                'prefixes,items/name,items/size,items/generation,nextPageToken'
            )

        object_list = None
        while True:
            apitools_request = self.messages.StorageObjectsListRequest(
                bucket=bucket_name,
                prefix=prefix,
                delimiter=delimiter,
                versions=all_versions,
                projection=projection,
                pageToken=object_list.nextPageToken if object_list else None,
                maxResults=cloud_api.NUM_ITEMS_PER_LIST_PAGE)

            try:
                object_list = self.client.objects.List(
                    apitools_request, global_params=global_params)
            except apitools_exceptions.HttpError as e:
                core_exceptions.reraise(
                    cloud_errors.translate_error(e, _ERROR_TRANSLATION))

            # Yield objects.
            # TODO(b/160238394) Decrypt metadata fields if necessary.
            for object_metadata in object_list.items:
                object_metadata.bucket = bucket_name
                yield gcs_metadata_util.get_object_resource_from_metadata(
                    object_metadata)

            # Yield prefixes.
            for prefix_string in object_list.prefixes:
                yield resource_reference.PrefixResource(storage_url.CloudUrl(
                    scheme=storage_url.ProviderPrefix.GCS,
                    bucket_name=bucket_name,
                    object_name=prefix_string),
                                                        prefix=prefix_string)

            if not object_list.nextPageToken:
                break
コード例 #6
0
    def compose_objects(self,
                        source_resources,
                        destination_resource,
                        request_config=None):
        """See CloudApi class for function doc strings."""

        if not source_resources:
            raise cloud_errors.GcsApiError(
                'Compose requires at least one component object.')

        if len(source_resources) > MAX_OBJECTS_PER_COMPOSE_CALL:
            raise cloud_errors.GcsApiError(
                'Compose was called with {} objects. The limit is {}.'.format(
                    len(source_resources), MAX_OBJECTS_PER_COMPOSE_CALL))

        validated_request_config = cloud_api.get_provider_request_config(
            request_config, GcsRequestConfig)

        source_messages = []
        for source in source_resources:
            source_message = self.messages.ComposeRequest.SourceObjectsValueListEntry(
                name=source.storage_url.object_name)
            if source.storage_url.generation is not None:
                generation = int(source.storage_url.generation)
                source_message.generation = generation
            source_messages.append(source_message)

        compose_request_payload = self.messages.ComposeRequest(
            sourceObjects=source_messages,
            destination=gcs_metadata_util.get_apitools_metadata_from_url(
                destination_resource.storage_url))

        compose_request = self.messages.StorageObjectsComposeRequest(
            composeRequest=compose_request_payload,
            destinationBucket=destination_resource.storage_url.bucket_name,
            destinationObject=destination_resource.storage_url.object_name,
            ifGenerationMatch=(
                validated_request_config.precondition_generation_match),
            ifMetagenerationMatch=(
                validated_request_config.precondition_metageneration_match),
        )

        if validated_request_config.predefined_acl_string is not None:
            compose_request.destinationPredefinedAcl = getattr(
                self.messages.StorageObjectsComposeRequest.
                DestinationPredefinedAclValueValuesEnum,
                request_config.predefined_acl_string)

        return gcs_metadata_util.get_object_resource_from_metadata(
            self.client.objects.Compose(compose_request))
コード例 #7
0
    def patch_object_metadata(self,
                              bucket_name,
                              object_name,
                              object_resource,
                              fields_scope=cloud_api.FieldsScope.NO_ACL,
                              generation=None,
                              request_config=None):
        """See super class."""
        # S3 requires a string, but GCS uses an int for generation.
        if generation:
            generation = int(generation)

        if not request_config:
            request_config = GcsRequestConfig()

        predefined_acl = None
        if request_config.predefined_acl_string:
            predefined_acl = getattr(
                self.messages.StorageObjectsPatchRequest.
                PredefinedAclValueValuesEnum,
                request_config.predefined_acl_string)

        projection = self._get_projection(
            fields_scope, self.messages.StorageObjectsPatchRequest)

        # Assume parameters are only for identifying what needs to be patched, and
        # the resource contains the desired patched metadata values.
        patched_metadata = object_resource.metadata
        if not patched_metadata:
            object_resource.metadata = gcs_metadata_util.get_apitools_metadata_from_url(
                object_resource.storage_url)

        request = self.messages.StorageObjectsPatchRequest(
            bucket=bucket_name,
            object=object_name,
            objectResource=object_resource.metadata,
            generation=generation,
            ifGenerationMatch=request_config.precondition_generation_match,
            ifMetagenerationMatch=request_config.
            precondition_metageneration_match,
            predefinedAcl=predefined_acl,
            projection=projection)

        updated_metadata = self.client.objects.Patch(request)
        return gcs_metadata_util.get_object_resource_from_metadata(
            updated_metadata)
コード例 #8
0
    def get_object_metadata(self,
                            bucket_name,
                            object_name,
                            generation=None,
                            fields_scope=cloud_api.FieldsScope.NO_ACL):
        """See super class."""

        # S3 requires a string, but GCS uses an int for generation.
        if generation:
            generation = int(generation)

        projection = self._get_projection(
            fields_scope, self.messages.StorageObjectsGetRequest)

        # TODO(b/160238394) Decrypt metadata fields if necessary.
        object_metadata = self.client.objects.Get(
            self.messages.StorageObjectsGetRequest(bucket=bucket_name,
                                                   object=object_name,
                                                   generation=generation,
                                                   projection=projection))
        return gcs_metadata_util.get_object_resource_from_metadata(
            object_metadata)
コード例 #9
0
    def copy_object(self,
                    source_resource,
                    destination_resource,
                    progress_callback=None,
                    request_config=None):
        """See super class."""
        # TODO(b/161898251): Implement encryption and decryption.
        if not request_config:
            request_config = GcsRequestConfig()

        destination_metadata = getattr(destination_resource, 'metadata', None)
        if not destination_metadata:
            destination_metadata = gcs_metadata_util.get_apitools_metadata_from_url(
                destination_resource.storage_url)
            if source_resource.metadata:
                gcs_metadata_util.copy_select_object_metadata(
                    source_resource.metadata, destination_metadata)

        if request_config.max_bytes_per_call:
            max_bytes_per_call = request_config.max_bytes_per_call
        else:
            max_bytes_per_call = scaled_integer.ParseInteger(
                properties.VALUES.storage.copy_chunk_size.Get())

        if request_config.predefined_acl_string:
            predefined_acl = getattr(
                self.messages.StorageObjectsRewriteRequest.
                DestinationPredefinedAclValueValuesEnum,
                request_config.predefined_acl_string)
        else:
            predefined_acl = None

        if source_resource.generation is None:
            source_generation = None
        else:
            source_generation = int(source_resource.generation)

        tracker_file_path = tracker_file_util.get_tracker_file_path(
            destination_resource.storage_url,
            tracker_file_util.TrackerFileType.REWRITE,
            source_resource.storage_url)
        rewrite_parameters_hash = tracker_file_util.hash_gcs_rewrite_parameters_for_tracker_file(
            source_resource,
            destination_resource,
            destination_metadata,
            request_config=request_config)
        try:
            resume_rewrite_token = tracker_file_util.read_rewrite_tracker_file(
                tracker_file_path, rewrite_parameters_hash)
            log.debug('Found rewrite token. Resuming copy.')
        except files.MissingFileError:
            resume_rewrite_token = None
            log.debug('No rewrite token found. Starting copy from scratch.')

        while True:
            request = self.messages.StorageObjectsRewriteRequest(
                sourceBucket=source_resource.storage_url.bucket_name,
                sourceObject=source_resource.storage_url.object_name,
                destinationBucket=destination_resource.storage_url.bucket_name,
                destinationObject=destination_resource.storage_url.object_name,
                object=destination_metadata,
                sourceGeneration=source_generation,
                ifGenerationMatch=request_config.precondition_generation_match,
                ifMetagenerationMatch=(
                    request_config.precondition_metageneration_match),
                destinationPredefinedAcl=predefined_acl,
                rewriteToken=resume_rewrite_token,
                maxBytesRewrittenPerCall=max_bytes_per_call)
            rewrite_response = self.client.objects.Rewrite(request)
            processed_bytes = rewrite_response.totalBytesRewritten
            if progress_callback:
                progress_callback(processed_bytes)

            if rewrite_response.done:
                break
            elif not resume_rewrite_token:
                resume_rewrite_token = rewrite_response.rewriteToken
                tracker_file_util.write_rewrite_tracker_file(
                    tracker_file_path, rewrite_parameters_hash,
                    rewrite_response.rewriteToken)

        tracker_file_util.delete_tracker_file(tracker_file_path)
        return gcs_metadata_util.get_object_resource_from_metadata(
            rewrite_response.resource)
コード例 #10
0
    def compose_objects(self,
                        source_resources,
                        destination_resource,
                        request_config,
                        original_source_resource=None):
        """See CloudApi class for function doc strings."""

        if not source_resources:
            raise cloud_errors.GcsApiError(
                'Compose requires at least one component object.')

        if len(source_resources) > MAX_OBJECTS_PER_COMPOSE_CALL:
            raise cloud_errors.GcsApiError(
                'Compose was called with {} objects. The limit is {}.'.format(
                    len(source_resources), MAX_OBJECTS_PER_COMPOSE_CALL))

        source_messages = []
        for source in source_resources:
            source_message = self.messages.ComposeRequest.SourceObjectsValueListEntry(
                name=source.storage_url.object_name)
            if source.storage_url.generation is not None:
                generation = int(source.storage_url.generation)
                source_message.generation = generation
            source_messages.append(source_message)

        destination_metadata = gcs_metadata_util.get_apitools_metadata_from_url(
            destination_resource.storage_url)
        if original_source_resource and isinstance(
                original_source_resource,
                resource_reference.FileObjectResource):
            original_source_file_path = (
                original_source_resource.storage_url.object_name)
        else:
            original_source_file_path = None
        gcs_metadata_util.update_object_metadata_from_request_config(
            destination_metadata, request_config, original_source_file_path)

        compose_request_payload = self.messages.ComposeRequest(
            sourceObjects=source_messages, destination=destination_metadata)

        compose_request = self.messages.StorageObjectsComposeRequest(
            composeRequest=compose_request_payload,
            destinationBucket=destination_resource.storage_url.bucket_name,
            destinationObject=destination_resource.storage_url.object_name,
            ifGenerationMatch=request_config.precondition_generation_match,
            ifMetagenerationMatch=request_config.
            precondition_metageneration_match)

        if request_config.resource_args:
            encryption_key = request_config.resource_args.encryption_key
            if encryption_key and encryption_key.type == encryption_util.KeyType.CMEK:
                compose_request.kmsKeyName = encryption_key.key

        if request_config.predefined_acl_string is not None:
            compose_request.destinationPredefinedAcl = getattr(
                self.messages.StorageObjectsComposeRequest.
                DestinationPredefinedAclValueValuesEnum,
                request_config.predefined_acl_string)

        encryption_key = getattr(request_config.resource_args,
                                 'encryption_key', None)
        with self._encryption_headers_context(encryption_key):
            return gcs_metadata_util.get_object_resource_from_metadata(
                self.client.objects.Compose(compose_request))
コード例 #11
0
    def copy_object(self,
                    source_resource,
                    destination_resource,
                    request_config,
                    progress_callback=None):
        """See super class."""
        destination_metadata = getattr(destination_resource, 'metadata', None)
        if not destination_metadata:
            destination_metadata = gcs_metadata_util.get_apitools_metadata_from_url(
                destination_resource.storage_url)
        if source_resource.metadata:
            gcs_metadata_util.copy_select_object_metadata(
                source_resource.metadata, destination_metadata, request_config)
        gcs_metadata_util.update_object_metadata_from_request_config(
            destination_metadata, request_config)

        if request_config.max_bytes_per_call:
            max_bytes_per_call = request_config.max_bytes_per_call
        else:
            max_bytes_per_call = scaled_integer.ParseInteger(
                properties.VALUES.storage.copy_chunk_size.Get())

        if request_config.predefined_acl_string:
            predefined_acl = getattr(
                self.messages.StorageObjectsRewriteRequest.
                DestinationPredefinedAclValueValuesEnum,
                request_config.predefined_acl_string)
        else:
            predefined_acl = None

        if source_resource.generation is None:
            source_generation = None
        else:
            source_generation = int(source_resource.generation)

        tracker_file_path = tracker_file_util.get_tracker_file_path(
            destination_resource.storage_url,
            tracker_file_util.TrackerFileType.REWRITE,
            source_url=source_resource.storage_url)
        rewrite_parameters_hash = tracker_file_util.hash_gcs_rewrite_parameters_for_tracker_file(
            source_resource,
            destination_resource,
            destination_metadata,
            request_config=request_config)
        try:
            resume_rewrite_token = tracker_file_util.read_rewrite_tracker_file(
                tracker_file_path, rewrite_parameters_hash)
            log.debug('Found rewrite token. Resuming copy.')
        except files.MissingFileError:
            resume_rewrite_token = None
            log.debug('No rewrite token found. Starting copy from scratch.')

        with self._encryption_headers_for_rewrite_call_context(request_config):
            while True:
                request = self.messages.StorageObjectsRewriteRequest(
                    sourceBucket=source_resource.storage_url.bucket_name,
                    sourceObject=source_resource.storage_url.object_name,
                    destinationBucket=destination_resource.storage_url.
                    bucket_name,
                    destinationObject=destination_resource.storage_url.
                    object_name,
                    object=destination_metadata,
                    sourceGeneration=source_generation,
                    ifGenerationMatch=copy_util.get_generation_match_value(
                        request_config),
                    ifMetagenerationMatch=request_config.
                    precondition_metageneration_match,
                    destinationPredefinedAcl=predefined_acl,
                    rewriteToken=resume_rewrite_token,
                    maxBytesRewrittenPerCall=max_bytes_per_call)

                encryption_key = getattr(request_config.resource_args,
                                         'encryption_key', None)
                if encryption_key and encryption_key.type == encryption_util.KeyType.CMEK:
                    # This key is also provided in destination_metadata.kmsKeyName by
                    # update_object_metadata_from_request_config. This has no effect on
                    # the copy object request, which references the field below, and is a
                    # side-effect of logic required for uploads and compose operations.
                    request.destinationKmsKeyName = encryption_key.key

                rewrite_response = self.client.objects.Rewrite(request)
                processed_bytes = rewrite_response.totalBytesRewritten
                if progress_callback:
                    progress_callback(processed_bytes)

                if rewrite_response.done:
                    break

                if not resume_rewrite_token:
                    resume_rewrite_token = rewrite_response.rewriteToken
                    if source_resource.size >= scaled_integer.ParseInteger(
                            properties.VALUES.storage.resumable_threshold.Get(
                            )):
                        tracker_file_util.write_rewrite_tracker_file(
                            tracker_file_path, rewrite_parameters_hash,
                            rewrite_response.rewriteToken)

        tracker_file_util.delete_tracker_file(tracker_file_path)
        return gcs_metadata_util.get_object_resource_from_metadata(
            rewrite_response.resource)
コード例 #12
0
    def _upload_object(self,
                       source_stream,
                       object_metadata,
                       request_config,
                       apitools_strategy=apitools_transfer.SIMPLE_UPLOAD,
                       progress_callback=None,
                       serialization_data=None,
                       total_size=0,
                       tracker_callback=None):
        # pylint: disable=g-doc-args
        """GCS-specific upload implementation. Adds args to Cloud API interface.

    Additional args:
      object_metadata (messages.Object): Apitools metadata for object to
          upload.
      apitools_strategy (str): SIMPLE_UPLOAD or RESUMABLE_UPLOAD constant in
          apitools.base.py.transfer.
      serialization_data (str): Implementation-specific JSON string of a dict
          containing serialization information for the download.
      total_size (int): Total size of the upload in bytes.
          If streaming, total size is None.
      tracker_callback (function): Callback that keeps track of upload progress.

    Returns:
      Uploaded object metadata in an ObjectResource.

    Raises:
      ValueError if an object can't be uploaded with the provided metadata.
    """
        predefined_acl = None
        if request_config.predefined_acl_string:
            predefined_acl = getattr(
                self.messages.StorageObjectsInsertRequest.
                PredefinedAclValueValuesEnum,
                request_config.predefined_acl_string)

        # TODO(b/160998052): Use encryption_wrapper to generate encryption headers.

        # Fresh upload. Prepare arguments.
        if not serialization_data:
            content_type = object_metadata.contentType

            if not content_type:
                content_type = DEFAULT_CONTENT_TYPE

            request = self.messages.StorageObjectsInsertRequest(
                bucket=object_metadata.bucket,
                object=object_metadata,
                ifGenerationMatch=request_config.precondition_generation_match,
                ifMetagenerationMatch=(
                    request_config.precondition_metageneration_match),
                predefinedAcl=predefined_acl)

        if apitools_strategy == apitools_transfer.SIMPLE_UPLOAD:
            apitools_upload = apitools_transfer.Upload(
                source_stream,
                content_type,
                auto_transfer=True,
                chunksize=properties.VALUES.storage.chunk_size.GetInt(),
                gzip_encoded=request_config.gzip_encoded,
                num_retries=properties.VALUES.storage.max_retries.GetInt(),
                progress_callback=progress_callback,
                total_size=request_config.size)

            result_object_metadata = self.client.objects.Insert(
                request, upload=apitools_upload)

            return gcs_metadata_util.get_object_resource_from_metadata(
                result_object_metadata)
        else:
            # TODO(b/160998556): Implement resumable upload.
            pass