From b9dfd1f77ac6705a07212f5ead4f6b3aa678c500 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Mon, 30 Sep 2024 12:56:48 -0400 Subject: [PATCH] fix copy module update atime/mtime (#83235) (#83792) Ensure we force mtime/atime update when using copystat Co-authored-by: Sloane Hertel <19572925+s-hertel@users.noreply.github.com> Co-authored-by: Brian Coca (cherry picked from commit 26375e7f12d476b5db5441a89a7b0d6356781140) Co-authored-by: dkuji --- .../83235-copy-module-update-mtime.yml | 2 + lib/ansible/module_utils/basic.py | 1 + test/integration/targets/copy/tasks/tests.yml | 42 +++++++++++++++++++ .../module_utils/basic/test_atomic_move.py | 1 + 4 files changed, 46 insertions(+) create mode 100644 changelogs/fragments/83235-copy-module-update-mtime.yml diff --git a/changelogs/fragments/83235-copy-module-update-mtime.yml b/changelogs/fragments/83235-copy-module-update-mtime.yml new file mode 100644 index 00000000000..7dd36aff642 --- /dev/null +++ b/changelogs/fragments/83235-copy-module-update-mtime.yml @@ -0,0 +1,2 @@ +bugfixes: + - copy - mtime/atime not updated. Fix now update mtime/atime(https://github.com/ansible/ansible/issues/83013) diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py index 03bdefc477d..0528ea043f5 100644 --- a/lib/ansible/module_utils/basic.py +++ b/lib/ansible/module_utils/basic.py @@ -1598,6 +1598,7 @@ class AnsibleModule(object): dest_stat = os.stat(b_dest) os.chown(b_src, dest_stat.st_uid, dest_stat.st_gid) shutil.copystat(b_dest, b_src) + os.utime(b_src, times=(time.time(), time.time())) except OSError as e: if e.errno != errno.EPERM: raise diff --git a/test/integration/targets/copy/tasks/tests.yml b/test/integration/targets/copy/tasks/tests.yml index 50a346526b1..5515d4fad96 100644 --- a/test/integration/targets/copy/tasks/tests.yml +++ b/test/integration/targets/copy/tasks/tests.yml @@ -2448,3 +2448,45 @@ loop: - '{{ src }}' - '{{ src }}_dest' + +- name: Verify atime and mtime update on content change (same partition) + vars: + remote_file: "{{ remote_tmp_dir }}/foo.txt" + ansible_remote_tmp: "{{ remote_tmp_dir }}" + block: + - name: Create a dest file + shell: "echo Test content > {{ remote_file }}" + register: create_dest_result + + - name: Check the stat results of the file before copying + stat: + path: "{{ remote_file }}" + register: stat_results_before_copy + + - name: Overwrite the file using the content system + copy: + content: "modified" + dest: "{{ remote_file }}" + decrypt: no + register: copy_result + + - name: Check the stat results of the file after copying + stat: + path: "{{ remote_file }}" + register: stat_results_after_copy + + - name: Assert that the file has changed + assert: + that: + - "create_dest_result is changed" + - "copy_result is changed" + - "'content' not in copy_result" + - "stat_results_before_copy.stat.atime < stat_results_after_copy.stat.atime" + - "stat_results_before_copy.stat.mtime < stat_results_after_copy.stat.mtime" + always: + - name: clean up dest file + file: + path: '{{ item }}' + state: absent + loop: + - '{{ remote_file }}' diff --git a/test/units/module_utils/basic/test_atomic_move.py b/test/units/module_utils/basic/test_atomic_move.py index 927ed3ee032..d49588d47d5 100644 --- a/test/units/module_utils/basic/test_atomic_move.py +++ b/test/units/module_utils/basic/test_atomic_move.py @@ -48,6 +48,7 @@ def atomic_mocks(mocker, monkeypatch): 'copyfileobj': mocker.patch('shutil.copyfileobj'), 'move': mocker.patch('shutil.move'), 'mkstemp': mocker.patch('tempfile.mkstemp'), + 'utime': mocker.patch('os.utime'), } mocks['getlogin'].return_value = 'root'