"""Common s3 functions.""" import boto3 from botocore import exceptions import common_config from constants import errors def copy_object(bucket, source_key, dest_key): """Copy source_ky object to dest_ky inside of S3 bucket. Args: bucket (str): S3 bucket name source_key (str): source object key dest_key (str): destination object key Return: bool: True if copy operation was successful, False otherwise """ s3 = boto3.client('s3') copy_source = { 'Bucket': bucket, 'Key': source_key } try: resp = s3.copy_object( Bucket=bucket, CopySource=copy_source, Key=dest_key) except (exceptions.ClientError, exceptions.ParamValidationError) as e: msg = errors.S3_COPY_ERROR_TEMPLATE.format( src=source_key, dst=dest_key) common_config.logger.error(msg) common_config.logger.exception(str(e)) return False if resp['ResponseMetadata']['HTTPStatusCode'] != 200: msg = errors.S3_COPY_ERROR_TEMPLATE.format( src=source_key, dst=dest_key) common_config.logger.error(msg) return False return True def delete_object(bucket, key): """Delete objects from s3 bucket. Args: bucket (str): S3 bucket name key (str): object key Return: dict: S3 response """ s3 = boto3.client('s3') try: resp = s3.delete_object( Bucket=bucket, Key=key) except (exceptions.ClientError, exceptions.ParamValidationError) as e: msg = errors.S3_DELETE_ERROR_TEMPLATE.format(src=key) common_config.logger.error(msg) common_config.logger.exception(str(e)) return False if resp['ResponseMetadata']['HTTPStatusCode'] not in (200, 204): msg = errors.S3_DELETE_ERROR_TEMPLATE.format(src=key) common_config.logger.error(msg) return False return True def delete_objects(bucket, keys, quiet=True): """Delete objects from s3 bucket. Args: bucket (str): S3 bucket keys (list): List of object keys to delete quiet (bool): enable/disable quiet mode for the request Return: dict: S3 response """ s3 = boto3.client('s3') resp = s3.delete_objects( Bucket=bucket, Delete={ 'Objects': [{'Key': key} for key in keys], 'Quiet': quiet, }) return resp def move_object(bucket, source_key, dest_key): """Move S3 object from one location to another inside of the same bucket. If copy operation has failed - consider move failed, if delete operation has failed - consider move operation successful. This will guarantee that valid attachments are in correct directory. Args: bucket (str): bucket name source_key (str): source object key dest_key (str): destination object key Returns: bool: True if move was successful, False otherwise """ msg = 'Moving {src} to {dst}'.format(src=source_key, dst=dest_key) common_config.logger.info(msg) copy_result = copy_object(bucket, source_key, dest_key) if not copy_result: return False delete_object(bucket, source_key) return True