Skip to content

Commit

Permalink
aws_s3 - fix issue when copy missing key from bucket (#603)
Browse files Browse the repository at this point in the history
aws_s3 - fix issue when copy missing key from bucket

SUMMARY

#602

ISSUE TYPE


Bugfix Pull Request

COMPONENT NAME

aws_s3
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: None <None>
  • Loading branch information
abikouo authored Jan 7, 2022
1 parent b27f516 commit 7bdad10
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 35 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/593-aws_s3-fix-copy-when-missing-key.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- aws_s3 - fix exception raised when using module to copy from source to destination and key is missing from source (https://github.com/ansible-collections/amazon.aws/issues/602).
76 changes: 41 additions & 35 deletions plugins/modules/aws_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,13 +412,16 @@ def etag_compare(module, s3, bucket, obj, version=None, local_file=None, content


def get_etag(s3, bucket, obj, version=None):
if version:
key_check = s3.head_object(Bucket=bucket, Key=obj, VersionId=version)
else:
key_check = s3.head_object(Bucket=bucket, Key=obj)
if not key_check:
try:
if version:
key_check = s3.head_object(Bucket=bucket, Key=obj, VersionId=version)
else:
key_check = s3.head_object(Bucket=bucket, Key=obj)
if not key_check:
return None
return key_check['ETag']
except is_boto3_error_code('404'):
return None
return key_check['ETag']


def bucket_check(module, s3, bucket, validate=True):
Expand Down Expand Up @@ -721,40 +724,43 @@ def copy_object_to_bucket(module, s3, bucket, obj, encrypt, metadata, validate,
if module.params['copy_src'].get('version_id') is not None:
version = module.params['copy_src'].get('version_id')
bucketsrc.update({'VersionId': version})
keyrtn = key_check(module, s3, bucketsrc['Bucket'], bucketsrc['Key'], version=version, validate=validate)
if keyrtn:
s_etag = get_etag(s3, bucketsrc['Bucket'], bucketsrc['Key'], version=version)
if s_etag == d_etag:
# Tags
tags, changed = ensure_tags(s3, module, bucket, obj)
if not changed:
module.exit_json(msg="ETag from source and destination are the same", changed=False)
if not key_check(module, s3, bucketsrc['Bucket'], bucketsrc['Key'], version=version, validate=validate):
# Key does not exist in source bucket
module.exit_json(msg="Key %s does not exist in bucket %s." % (bucketsrc['Key'], bucketsrc['Bucket']), changed=False)

s_etag = get_etag(s3, bucketsrc['Bucket'], bucketsrc['Key'], version=version)
if s_etag == d_etag:
# Tags
tags, changed = ensure_tags(s3, module, bucket, obj)
if not changed:
module.exit_json(msg="ETag from source and destination are the same", changed=False)
else:
params.update({'CopySource': bucketsrc})
if encrypt:
params['ServerSideEncryption'] = module.params['encryption_mode']
if module.params['encryption_kms_key_id'] and module.params['encryption_mode'] == 'aws:kms':
params['SSEKMSKeyId'] = module.params['encryption_kms_key_id']
if metadata:
params['Metadata'] = {}
# determine object metadata and extra arguments
for option in metadata:
extra_args_option = option_in_extra_args(option)
if extra_args_option is not None:
params[extra_args_option] = metadata[option]
else:
params['Metadata'][option] = metadata[option]

copy_result = s3.copy_object(**params)
for acl in module.params.get('permission'):
s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj)
# Tags
tags, changed = ensure_tags(s3, module, bucket, obj)
module.exit_json(msg="tags successfully updated.", changed=changed, tags=tags)
else:
params.update({'CopySource': bucketsrc})
if encrypt:
params['ServerSideEncryption'] = module.params['encryption_mode']
if module.params['encryption_kms_key_id'] and module.params['encryption_mode'] == 'aws:kms':
params['SSEKMSKeyId'] = module.params['encryption_kms_key_id']
if metadata:
params['Metadata'] = {}
# determine object metadata and extra arguments
for option in metadata:
extra_args_option = option_in_extra_args(option)
if extra_args_option is not None:
params[extra_args_option] = metadata[option]
else:
params['Metadata'][option] = metadata[option]
copy_result = s3.copy_object(**params)
for acl in module.params.get('permission'):
s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj)
# Tags
tags, changed = ensure_tags(s3, module, bucket, obj)
module.exit_json(msg="Object copied from bucket %s to bucket %s." % (bucketsrc['Bucket'], bucket), tags=tags, changed=True)
except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS):
module.warn("PutObjectAcl is not implemented by your storage provider. Set the permissions parameters to the empty list to avoid this warning")
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Failed while copying object %s from bucket %s." % (obj, module.params['copy_src'].get('Bucket')))
module.exit_json(msg="Object copied from bucket %s to bucket %s." % (bucketsrc['Bucket'], bucket), tags=tags, changed=True)


def is_fakes3(s3_url):
Expand Down
16 changes: 16 additions & 0 deletions tests/integration/targets/aws_s3/tasks/copy_object.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@
that:
- copy_result is not changed

- name: Copy from unexisting key should not succeed
aws_s3:
bucket: "{{ copy_bucket.dst }}"
mode: copy
object: missing_key.txt
copy_src:
bucket: "{{ copy_bucket.src }}"
object: this_key_does_not_exist.txt
register: result

- name: Validate result when copying missing key
assert:
that:
- result is not changed
- 'result.msg == "Key this_key_does_not_exist.txt does not exist in bucket {{ copy_bucket.src }}."'

always:
- include_tasks: delete_bucket.yml
with_items:
Expand Down

0 comments on commit 7bdad10

Please sign in to comment.