diff --git a/changelogs/fragments/unarchive_differs.yml b/changelogs/fragments/unarchive_differs.yml new file mode 100644 index 00000000000..c95af7e2573 --- /dev/null +++ b/changelogs/fragments/unarchive_differs.yml @@ -0,0 +1,4 @@ +--- +bugfixes: + - unarchive - trigger change when size and content differ when other + properties are unchanged (https://github.com/ansible/ansible/pull/83454). diff --git a/lib/ansible/modules/unarchive.py b/lib/ansible/modules/unarchive.py index a523b1d9ce2..0d56da53a40 100644 --- a/lib/ansible/modules/unarchive.py +++ b/lib/ansible/modules/unarchive.py @@ -275,6 +275,8 @@ ZIP_FILE_MODE_RE = re.compile(r'([r-][w-][SsTtx-]){3}') INVALID_OWNER_RE = re.compile(r': Invalid owner') INVALID_GROUP_RE = re.compile(r': Invalid group') SYMLINK_DIFF_RE = re.compile(r': Symlink differs$') +CONTENT_DIFF_RE = re.compile(r': Contents differ$') +SIZE_DIFF_RE = re.compile(r': Size differs$') def crc32(path, buffer_size): @@ -891,16 +893,15 @@ class TgzArchive(object): out += line + '\n' if not self.file_args['mode'] and MODE_DIFF_RE.search(line): out += line + '\n' - if MOD_TIME_DIFF_RE.search(line): - out += line + '\n' - if MISSING_FILE_RE.search(line): - out += line + '\n' - if INVALID_OWNER_RE.search(line): - out += line + '\n' - if INVALID_GROUP_RE.search(line): - out += line + '\n' - if SYMLINK_DIFF_RE.search(line): - out += line + '\n' + differ_regexes = [ + MOD_TIME_DIFF_RE, MISSING_FILE_RE, INVALID_OWNER_RE, + INVALID_GROUP_RE, SYMLINK_DIFF_RE, CONTENT_DIFF_RE, + SIZE_DIFF_RE + ] + for regex in differ_regexes: + if regex.search(line): + out += line + '\n' + if out: unarchived = False return dict(unarchived=unarchived, rc=rc, out=out, err=err, cmd=cmd) diff --git a/test/integration/targets/unarchive/files/content_differs.tar.gz b/test/integration/targets/unarchive/files/content_differs.tar.gz new file mode 100644 index 00000000000..86d0891be5a Binary files /dev/null and b/test/integration/targets/unarchive/files/content_differs.tar.gz differ diff --git a/test/integration/targets/unarchive/files/content_differs_2.tar.gz b/test/integration/targets/unarchive/files/content_differs_2.tar.gz new file mode 100644 index 00000000000..d048e50f8cc Binary files /dev/null and b/test/integration/targets/unarchive/files/content_differs_2.tar.gz differ diff --git a/test/integration/targets/unarchive/files/size_differs.tar.gz b/test/integration/targets/unarchive/files/size_differs.tar.gz new file mode 100644 index 00000000000..7f76808dc73 Binary files /dev/null and b/test/integration/targets/unarchive/files/size_differs.tar.gz differ diff --git a/test/integration/targets/unarchive/files/size_differs_2.tar.gz b/test/integration/targets/unarchive/files/size_differs_2.tar.gz new file mode 100644 index 00000000000..0d53561b84b Binary files /dev/null and b/test/integration/targets/unarchive/files/size_differs_2.tar.gz differ diff --git a/test/integration/targets/unarchive/tasks/main.yml b/test/integration/targets/unarchive/tasks/main.yml index b07c2fe7fbe..278642a1066 100644 --- a/test/integration/targets/unarchive/tasks/main.yml +++ b/test/integration/targets/unarchive/tasks/main.yml @@ -5,6 +5,8 @@ - import_tasks: test_tar_gz_creates.yml - import_tasks: test_tar_gz_owner_group.yml - import_tasks: test_tar_gz_keep_newer.yml +- import_tasks: test_tar_gz_size_differs.yml +- import_tasks: test_tar_gz_content_differs.yml - import_tasks: test_tar_zst.yml - import_tasks: test_zip.yml - import_tasks: test_exclude.yml diff --git a/test/integration/targets/unarchive/tasks/test_tar_gz_content_differs.yml b/test/integration/targets/unarchive/tasks/test_tar_gz_content_differs.yml new file mode 100644 index 00000000000..79a0be23983 --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_tar_gz_content_differs.yml @@ -0,0 +1,56 @@ +- set_fact: + dest: '{{remote_tmp_dir}}/test-unarchive-tar-gz-content-differs' + +- name: create our tar.gz unarchive destination + file: + path: "{{ dest }}" + state: directory + +- name: unarchive a tar.gz file + unarchive: + src: 'content_differs.tar.gz' + dest: '{{ dest }}' + remote_src: no + register: unarchive_content_differs_01 + +- name: verify that the task was marked as changed + assert: + that: + - unarchive_content_differs_01.changed + +- name: checksum the file after first unarchive + stat: + path: '{{ dest }}/hello.txt' + checksum_algorithm: sha256 + get_checksum: yes + register: first_unarchive_content + +- name: unarchive a tar.gz file with same name, size but different content + unarchive: + src: 'content_differs_2.tar.gz' + dest: '{{ dest }}' + remote_src: no + register: unarchive_content_differs_01 + +- name: verify that the task was marked as changed + assert: + that: + - unarchive_content_differs_01.changed + +- name: checksum the file after second unarchive + stat: + path: '{{ dest }}/hello.txt' + checksum_algorithm: sha256 + get_checksum: yes + register: second_unarchive_content + +- name: verify that unarchive extracted file with new content + assert: + that: + - first_unarchive_content.stat.checksum != second_unarchive_content.stat.checksum + - first_unarchive_content.stat.size == second_unarchive_content.stat.size + +- name: remove our tar.gz unarchive destination + file: + path: '{{ dest }}' + state: absent diff --git a/test/integration/targets/unarchive/tasks/test_tar_gz_size_differs.yml b/test/integration/targets/unarchive/tasks/test_tar_gz_size_differs.yml new file mode 100644 index 00000000000..c4a0d2af80c --- /dev/null +++ b/test/integration/targets/unarchive/tasks/test_tar_gz_size_differs.yml @@ -0,0 +1,52 @@ +- set_fact: + dest: '{{remote_tmp_dir}}/test-unarchive-tar-gz-size-differs' + +- name: create our tar.gz unarchive destination + file: + path: "{{ dest }}" + state: directory + +- name: unarchive a tar.gz file + unarchive: + src: 'size_differs.tar.gz' + dest: '{{ dest }}' + remote_src: no + register: unarchive_size_differs_01 + +- name: verify that the task was marked as changed + assert: + that: + - unarchive_size_differs_01.changed + +- name: Check size after first unarchive + stat: + path: '{{ dest }}/hello.txt' + register: first_unarchive + +- name: unarchive a tar.gz file with same name but different size + unarchive: + src: 'size_differs_2.tar.gz' + dest: '{{ dest }}' + remote_src: no + register: unarchive_size_differs_02 + +- name: verify that the task was marked as changed + assert: + that: + - unarchive_size_differs_02.changed + +- name: Check size after unarchive + stat: + path: '{{ dest }}/hello.txt' + register: second_unarchive + +- name: verify that unarchive extracted new sized file + assert: + that: + - first_unarchive.stat.size != second_unarchive.stat.size + - first_unarchive.stat.size < second_unarchive.stat.size + +- name: remove our tar.gz unarchive destination + file: + path: '{{ dest }}' + state: absent