Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws_s3: Add latest choice on overwrite parameter #595

Merged
merged 9 commits into from
Jan 12, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- aws_s3 - add latest choice on overwrite parameter to get latest object on S3
na4da marked this conversation as resolved.
Show resolved Hide resolved
29 changes: 26 additions & 3 deletions plugins/modules/aws_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@
overwrite:
description:
- Force overwrite either locally on the filesystem or remotely with the object/key. Used with C(PUT) and C(GET) operations.
- Must be a Boolean, C(always), C(never) or C(different).
- Must be a Boolean, C(always), C(never), C(different) or C(latest).
- C(true) is the same as C(always).
- C(false) is equal to C(never).
- When this is set to C(different) the MD5 sum of the local file is compared with the 'ETag' of the object/key in S3.
The ETag may or may not be an MD5 digest of the object data. See the ETag response header here
U(https://docs.aws.amazon.com/AmazonS3/latest/API/RESTCommonResponseHeaders.html).
- (C(GET) mode only) When this is set to C(latest) the last modified timestamp of local file is compared with the 'LastModified' of the object/key in S3.
default: 'always'
aliases: ['force']
type: str
Expand Down Expand Up @@ -421,6 +422,26 @@ def get_etag(s3, bucket, obj, version=None):
return key_check['ETag']


def get_s3_last_modified_timestamp(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:
return None
return key_check['LastModified'].timestamp()


def is_local_object_latest(module, s3, bucket, obj, version=None, local_file=None):
s3_last_modified = get_s3_last_modified_timestamp(s3, bucket, obj, version)
if os.path.exists(local_file) is False:
return False
else:
local_last_modified = os.path.getmtime(local_file)

return s3_last_modified <= local_last_modified


def bucket_check(module, s3, bucket, validate=True):
exists = True
try:
Expand Down Expand Up @@ -940,7 +961,7 @@ def main():

validate_bucket_name(module, bucket)

if overwrite not in ['always', 'never', 'different']:
if overwrite not in ['always', 'never', 'different', 'latest']:
if module.boolean(overwrite):
overwrite = 'always'
else:
Expand Down Expand Up @@ -1014,8 +1035,10 @@ def main():
if dest and path_check(dest) and overwrite != 'always':
if overwrite == 'never':
module.exit_json(msg="Local object already exists and overwrite is disabled.", changed=False)
if etag_compare(module, s3, bucket, obj, version=version, local_file=dest):
if overwrite == 'different' and etag_compare(module, s3, bucket, obj, version=version, local_file=dest):
module.exit_json(msg="Local and remote object are identical, ignoring. Use overwrite=always parameter to force.", changed=False)
if overwrite == 'latest' and is_local_object_latest(module, s3, bucket, obj, version=version, local_file=dest):
module.exit_json(msg="Local object is latest, ignoreing. Use overwrite=always parameter to force.", changed=False)

try:
download_s3file(module, s3, bucket, obj, dest, retries, version=version)
Expand Down
33 changes: 33 additions & 0 deletions tests/integration/targets/aws_s3/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,39 @@
that:
- result is changed

alinabuzachis marked this conversation as resolved.
Show resolved Hide resolved
- name: test get with overwrite=latest and identical files
aws_s3:
bucket: "{{ bucket_name }}"
mode: get
dest: "{{ tmpdir.path }}/download.txt"
object: delete.txt
overwrite: latest
retries: 3
delay: 3
register: result

- assert:
that:
- result is not changed

- name: modify mtime for local file to past
shell: touch -mt 197001010900.00 "{{ tmpdir.path }}/download.txt"

- name: test get with overwrite=latest and files that mtimes are different
aws_s3:
bucket: "{{ bucket_name }}"
mode: get
dest: "{{ tmpdir.path }}/download.txt"
object: delete.txt
overwrite: latest
retries: 3
delay: 3
register: result

- assert:
that:
- result is changed

- name: test geturl of the object
aws_s3:
bucket: "{{ bucket_name }}"
Expand Down