From eb740e97d4238d441fa33e8f8b2dfcdfd720d262 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Wed, 5 Mar 2025 00:33:11 +0000 Subject: [PATCH 1/6] add check mode tests, add comments --- .../integration/targets/mount/tasks/main.yml | 346 +++++++++++++++++- 1 file changed, 339 insertions(+), 7 deletions(-) diff --git a/tests/integration/targets/mount/tasks/main.yml b/tests/integration/targets/mount/tasks/main.yml index bb91648..49fa003 100644 --- a/tests/integration/targets/mount/tasks/main.yml +++ b/tests/integration/targets/mount/tasks/main.yml @@ -1,3 +1,4 @@ +# SETUP ################################################################################ - name: Install dependencies (Linux) ansible.builtin.package: name: e2fsprogs @@ -110,6 +111,46 @@ mode: '0644' register: orig_info +# BIND MOUNT ################################################################################ +# bind mount check mode +- name: Bind mount a filesystem (Linux) (check mode) + ansible.posix.mount: + src: '{{ output_dir }}/mount_source' + name: '{{ output_dir }}/mount_dest' + state: mounted + fstype: None + opts: bind + when: ansible_system == 'Linux' + register: bind_result_linux_dry_run + check_mode: true + +- name: Bind mount a filesystem (FreeBSD) (check mode) + ansible.posix.mount: + src: '{{ output_dir }}/mount_source' + name: '{{ output_dir }}/mount_dest' + state: mounted + fstype: nullfs + when: ansible_system == 'FreeBSD' + register: bind_result_freebsd_dry_run + check_mode: true + +- name: attempt to stat bind mounted file + ansible.builtin.stat: + path: '{{ output_dir }}/mount_dest/test_file' + when: ansible_system in ('FreeBSD', 'Linux') + register: dest_stat + +- name: Assert the bind mount did not take place + ansible.builtin.assert: + that: + - linux_and_changed or freebsd_and_changed + - not dest_stat['stat']['exists'] + vars: + linux_and_changed: "{{ ansible_system == 'Linux' and bind_result_linux_dry_run['changed'] }}" + freebsd_and_changed: "{{ ansible_system == 'FreeBSD' and bind_result_freebsd['changed'] }}" + when: ansible_system in ('FreeBSD', 'Linux') + +# bind mount - name: Bind mount a filesystem (Linux) ansible.posix.mount: src: '{{ output_dir }}/mount_source' @@ -168,6 +209,48 @@ - (ansible_system == 'Linux' and not bind_result_linux['changed']) or (ansible_system == 'FreeBSD' and not bind_result_freebsd['changed']) when: ansible_system in ('FreeBSD', 'Linux') +# remount check mode +- name: Remount filesystem with different opts (Linux) (check mode) + ansible.posix.mount: + src: '{{ output_dir }}/mount_source' + name: '{{ output_dir }}/mount_dest' + state: mounted + fstype: None + opts: bind,ro + when: ansible_system == 'Linux' + register: bind_result_linux + check_mode: true + +- name: Remount filesystem with different opts (FreeBSD) (check mode) + ansible.posix.mount: + src: '{{ output_dir }}/mount_source' + name: '{{ output_dir }}/mount_dest' + state: mounted + fstype: nullfs + opts: ro + when: ansible_system == 'FreeBSD' + register: bind_result_freebsd + check_mode: true + +- name: Get mount options + ansible.builtin.shell: + cmd: set -o pipefail && mount | grep mount_dest | grep -c -E -w '(ro|read-only)' + executable: "{{ shell_executable }}" + changed_when: false + failed_when: false + register: new_options_count + +- name: Make sure the filesystem does not have the new opts + ansible.builtin.assert: + that: + - linux_and_changed or freebsd_and_changed + - new_options_count.stdout | int == 0 + vars: + linux_and_changed: "{{ ansible_system == 'Linux' and bind_result_linux_dry_run['changed'] }}" + freebsd_and_changed: "{{ ansible_system == 'FreeBSD' and bind_result_freebsd['changed'] }}" + when: ansible_system in ('FreeBSD', 'Linux') + +# remount - name: Remount filesystem with different opts (Linux) ansible.posix.mount: src: '{{ output_dir }}/mount_source' @@ -203,6 +286,29 @@ - 1 == remount_options.stdout_lines | length when: ansible_system in ('FreeBSD', 'Linux') +# unmount check mode +- name: Unmount the bind mount (check mode) + ansible.posix.mount: + name: '{{ output_dir }}/mount_dest' + state: absent + when: ansible_system in ('Linux', 'FreeBSD') + register: unmount_result + check_mode: true + +- name: Make sure the file still exists in dest + ansible.builtin.stat: + path: '{{ output_dir }}/mount_dest/test_file' + when: ansible_system in ('FreeBSD', 'Linux') + register: dest_stat + +- name: Check that we did not unmount + ansible.builtin.assert: + that: + - unmount_result['changed'] + - dest_stat['stat']['exists'] + when: ansible_system in ('FreeBSD', 'Linux') + +# unmount - name: Unmount the bind mount ansible.posix.mount: name: '{{ output_dir }}/mount_dest' @@ -223,9 +329,36 @@ - not dest_stat['stat']['exists'] when: ansible_system in ('FreeBSD', 'Linux') -- name: Block to test remounted option +# SWAP ############################################################# +- name: swap when: ansible_system in ('Linux') block: + # mount swap check mode + - name: Stat /etc/fstab + stat: + path: /etc/fstab + register: stat_fstab_before + + - name: Create fstab record for the first swap file (check mode) + ansible.posix.mount: + name: none + src: /tmp/swap1 + opts: sw + fstype: swap + state: present + check_mode: true + + - name: Stat /etc/fstab + stat: + path: /etc/fstab + register: stat_fstab_after + + - name: Assert that fstab checksum did not change + ansible.builtin.assert: + that: + - stat_fstab_before.stat.checksum == stat_fstab_after.stat.checksum + + # mount swap1 - name: Create fstab record for the first swap file ansible.posix.mount: name: none @@ -250,6 +383,7 @@ - swap1_created['changed'] - not swap1_created_again['changed'] + # mount swap2 - name: Create fstab record for the second swap file ansible.posix.mount: name: none @@ -274,6 +408,30 @@ - swap2_created['changed'] - not swap2_created_again['changed'] + # remove swap check mode + - name: Stat /etc/fstab + stat: + path: /etc/fstab + register: stat_fstab_before + + - name: Remove the fstab record for the first swap file (check mode) + ansible.posix.mount: + name: none + src: /tmp/swap1 + state: absent + check_mode: true + + - name: Stat /etc/fstab + stat: + path: /etc/fstab + register: stat_fstab_after + + - name: Assert that fstab checksum did not change + ansible.builtin.assert: + that: + - stat_fstab_before.stat.checksum == stat_fstab_after.stat.checksum + + # remove swap1 - name: Remove the fstab record for the first swap file ansible.posix.mount: name: none @@ -294,6 +452,7 @@ - swap1_removed['changed'] - not swap1_removed_again['changed'] + # remove swap2 - name: Remove the fstab record for the second swap file ansible.posix.mount: name: none @@ -314,6 +473,10 @@ - swap2_removed['changed'] - not swap2_removed_again['changed'] +# FIXUP ############################################################# +- name: fix incomplete entry already present in fstab + when: ansible_system == 'Linux' + block: - name: Create fstab record with missing last two fields ansible.builtin.copy: dest: /etc/fstab @@ -343,6 +506,11 @@ - ''' 0 0'' in optional_fields_content.stdout' - 1 == optional_fields_content.stdout_lines | length +# REMOUNTED ############################################################# +- name: Block to test remounted option + when: ansible_system in ('Linux') + block: + # setup - name: Create empty file community.general.filesize: path: /tmp/myfs.img @@ -372,6 +540,26 @@ ansible.builtin.pause: seconds: 2 + # remount check mode + - name: Remount (check mode) + ansible.posix.mount: + path: /tmp/myfs + state: remounted + + - name: Get again the last write time + ansible.builtin.shell: + cmd: >- + set -o pipefail && dumpe2fs /tmp/myfs.img 2>/dev/null | grep -i "last write time:" |cut -d: -f2- + executable: "{{ shell_executable }}" + changed_when: false + register: last_write_time_check + + - name: Fail if they are different + ansible.builtin.fail: + msg: Filesytem was remounted, testing of the module failed! + when: last_write_time.stdout != last_write_time_check.stdout + + # remount - name: Test if the FS is remounted ansible.posix.mount: path: /tmp/myfs @@ -390,6 +578,28 @@ msg: Filesytem was not remounted, testing of the module failed! when: last_write is defined and last_write_time2 is defined and last_write_time.stdout == last_write_time2.stdout + # remount different options check mode + - name: Remount filesystem with different opts using remounted option (Linux only) + ansible.posix.mount: + path: /tmp/myfs + state: remounted + opts: rw,noexec + check_mode: true + + - name: Get remounted options (Linux only) + ansible.builtin.shell: + cmd: set -o pipefail && mount | grep myfs | grep -E -w 'noexec' | wc -l + executable: "{{ shell_executable }}" + changed_when: false + register: remounted_options + + - name: Make sure the filesystem now has the new opts after using remounted (Linux only) + ansible.builtin.assert: + that: + - "'0' in remounted_options.stdout" + - "1 == remounted_options.stdout_lines | length" + + # remount different options - name: Remount filesystem with different opts using remounted option (Linux only) ansible.posix.mount: path: /tmp/myfs @@ -409,6 +619,7 @@ - "'1' in remounted_options.stdout" - "1 == remounted_options.stdout_lines | length" + # backup - name: Mount the FS again to test backup ansible.posix.mount: path: /tmp/myfs @@ -439,9 +650,11 @@ - /tmp/myfs.img - /tmp/myfs +# BOOT ############################################################# - name: Block to test boot option for Linux when: ansible_system in ('Linux') block: + # setup - name: Create empty file community.general.filesize: path: /tmp/myfs.img @@ -452,6 +665,7 @@ fstype: ext3 dev: /tmp/myfs.img + # noauto - name: Mount the FS with noauto option ansible.posix.mount: path: /tmp/myfs @@ -472,6 +686,7 @@ path: /tmp/myfs state: absent + # noauto + defaults - name: Mount the FS with noauto option and defaults ansible.posix.mount: path: /tmp/myfs @@ -499,6 +714,7 @@ - /tmp/myfs.img - /tmp/myfs +# NEWLINE END OF FILE ############################################ - name: Block to test missing newline at the EOF of fstab when: ansible_system in ('Linux') block: @@ -537,6 +753,7 @@ - /tmp/myfs1 - /tmp/test_fstab +# EPHEMERAL ################################################ - name: Block to test ephemeral option environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin @@ -552,8 +769,7 @@ path: /tmp/myfs_B.img size: 20M - ##### FORMAT FS ON LINUX - + ##### FORMAT FS ON LINUX - name: Block to format FS on Linux when: ansible_system == 'Linux' block: @@ -567,8 +783,7 @@ fstype: ext3 dev: /tmp/myfs_B.img - ##### FORMAT FS ON SOLARIS AND BSD - + ##### FORMAT FS ON SOLARIS AND BSD - name: Create loop devices on Solaris and BSD ansible.builtin.shell: cmd: "set -o pipefail && {{ ephemeral_create_loop_dev_cmd }}" @@ -583,14 +798,54 @@ changed_when: true when: ephemeral_format_fs_cmd is defined - ##### TESTS - - name: Create fstab if it does not exist ansible.builtin.file: path: "{{ ephemeral_fstab }}" state: touch mode: '0644' + # normal ephemeral mount check mode + - name: Get checksum of /etc/fstab before mounting anything + ansible.builtin.stat: + path: '{{ ephemeral_fstab }}' + register: fstab_stat_before_mount + + - name: Mount the FS A with ephemeral state + ansible.posix.mount: + path: /tmp/myfs + src: '{{ ephemeral_device_a }}' + fstype: '{{ ephemeral_fstype }}' + opts: rw + state: ephemeral + register: ephemeral_mount_info + + - name: Put something in the directory so we can do additional checks later on + ansible.builtin.copy: + content: 'Testing' + dest: /tmp/myfs/test_file + mode: '0644' + + - name: Get checksum of /etc/fstab after an ephemeral mount + ansible.builtin.stat: + path: '{{ ephemeral_fstab }}' + register: fstab_stat_after_mount + + - name: Get mountinfo + ansible.builtin.shell: + cmd: grep -c '/tmp/myfs' <(mount -v) + executable: "{{ shell_executable }}" + register: check_mountinfo + failed_when: false + changed_when: false + + - name: Assert the mount occured and the fstab is unchanged + ansible.builtin.assert: + that: + - check_mountinfo.stdout|int == 0 + - ephemeral_mount_info['changed'] + - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_mount['stat']['checksum'] + + # normal ephemeral mount - name: Get checksum of /etc/fstab before mounting anything ansible.builtin.stat: path: '{{ ephemeral_fstab }}' @@ -631,6 +886,48 @@ - ephemeral_mount_info['changed'] - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_mount['stat']['checksum'] + # remount different options check mode + - name: Get first mount record + ansible.builtin.shell: + cmd: grep '/tmp/myfs' <(mount -v) + executable: "{{ shell_executable }}" + register: ephemeral_mount_record_1 + changed_when: false + + - name: Try to mount FS A where FS A is already mounted (should trigger remount and changed) + ansible.posix.mount: + path: /tmp/myfs + src: '{{ ephemeral_device_a }}' + fstype: '{{ ephemeral_fstype }}' + opts: ro + state: ephemeral + register: ephemeral_mount_info + check_mode: true + + - name: Get second mount record (should be different than the first) + ansible.builtin.shell: + cmd: grep '/tmp/myfs' <(mount -v) + executable: "{{ shell_executable }}" + register: ephemeral_mount_record_2 + changed_when: false + + - name: Get mountinfo + ansible.builtin.shell: + cmd: grep -c '/tmp/myfs' <(mount -v) + executable: "{{ shell_executable }}" + failed_when: false + register: check_mountinfo + changed_when: false + + - name: Assert the FS A is still mounted, the options changed and the fstab unchanged + ansible.builtin.assert: + that: + - check_mountinfo.stdout|int == 0 + - ephemeral_mount_record_1.stdout != ephemeral_mount_record_2.stdout + - ephemeral_mount_info['changed'] + - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_mount['stat']['checksum'] + + # remount different options - name: Get first mount record ansible.builtin.shell: cmd: grep '/tmp/myfs' <(mount -v) @@ -670,6 +967,7 @@ - ephemeral_mount_info['changed'] - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_mount['stat']['checksum'] + # conflicting mount - name: Try to mount file B on file A mountpoint (should fail) ansible.posix.mount: path: /tmp/myfs @@ -707,6 +1005,39 @@ - test_file_stat['stat']['exists'] - ephemeral_mount_b_info is failed + # unmount check mode + - name: Unmount FS with state = unmounted + ansible.posix.mount: + path: /tmp/myfs + state: unmounted + check_mode: true + + - name: Get fstab checksum after unmounting an ephemeral mount with state = unmounted + ansible.builtin.stat: + path: '{{ ephemeral_fstab }}' + register: fstab_stat_after_unmount + + - name: Get mountinfo + ansible.builtin.shell: + cmd: grep -c '/tmp/myfs' <(mount -v) + executable: "{{ shell_executable }}" + register: check_mountinfo + failed_when: false + changed_when: false + + - name: Try to stat our test file + ansible.builtin.stat: + path: /tmp/myfs/test_file + register: test_file_stat + + - name: Assert that fstab is unchanged after unmounting an ephemeral mount with state = unmounted + ansible.builtin.assert: + that: + - check_mountinfo.stdout|int == 0 + - not test_file_stat['stat']['exists'] + - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_unmount['stat']['checksum'] + + # unmount - name: Unmount FS with state = unmounted ansible.posix.mount: path: /tmp/myfs @@ -759,6 +1090,7 @@ - /tmp/myfs_B.img - /tmp/myfs +# OPTS_NO_LOG ###################################### - name: Block to test opts_no_log option when: ansible_system == 'Linux' block: From f087d58cbe6fe9b9e831592e14dd4ffa3b3f1c3f Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Wed, 5 Mar 2025 00:39:46 +0000 Subject: [PATCH 2/6] fix bugs --- .../integration/targets/mount/tasks/main.yml | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/integration/targets/mount/tasks/main.yml b/tests/integration/targets/mount/tasks/main.yml index 49fa003..7aa65f5 100644 --- a/tests/integration/targets/mount/tasks/main.yml +++ b/tests/integration/targets/mount/tasks/main.yml @@ -590,6 +590,7 @@ ansible.builtin.shell: cmd: set -o pipefail && mount | grep myfs | grep -E -w 'noexec' | wc -l executable: "{{ shell_executable }}" + failed_when: false changed_when: false register: remounted_options @@ -818,12 +819,7 @@ opts: rw state: ephemeral register: ephemeral_mount_info - - - name: Put something in the directory so we can do additional checks later on - ansible.builtin.copy: - content: 'Testing' - dest: /tmp/myfs/test_file - mode: '0644' + check_mode: true - name: Get checksum of /etc/fstab after an ephemeral mount ansible.builtin.stat: @@ -919,11 +915,11 @@ register: check_mountinfo changed_when: false - - name: Assert the FS A is still mounted, the options changed and the fstab unchanged + - name: Assert the FS A is still mounted, the options unchanged and the fstab unchanged ansible.builtin.assert: that: - - check_mountinfo.stdout|int == 0 - - ephemeral_mount_record_1.stdout != ephemeral_mount_record_2.stdout + - check_mountinfo.stdout|int == 1 + - ephemeral_mount_record_1.stdout == ephemeral_mount_record_2.stdout - ephemeral_mount_info['changed'] - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_mount['stat']['checksum'] @@ -1030,11 +1026,11 @@ path: /tmp/myfs/test_file register: test_file_stat - - name: Assert that fstab is unchanged after unmounting an ephemeral mount with state = unmounted + - name: Assert that unmount did not take place and fstab unchanged ansible.builtin.assert: that: - - check_mountinfo.stdout|int == 0 - - not test_file_stat['stat']['exists'] + - check_mountinfo.stdout|int == 1 + - test_file_stat['stat']['exists'] - fstab_stat_before_mount['stat']['checksum'] == fstab_stat_after_unmount['stat']['checksum'] # unmount From 7d8da00f792649a3c7dd0dfc8664c315be6218fe Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Thu, 6 Mar 2025 10:46:57 -0500 Subject: [PATCH 3/6] add (check mode) to task name --- tests/integration/targets/mount/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/mount/tasks/main.yml b/tests/integration/targets/mount/tasks/main.yml index 7aa65f5..c35f31d 100644 --- a/tests/integration/targets/mount/tasks/main.yml +++ b/tests/integration/targets/mount/tasks/main.yml @@ -811,7 +811,7 @@ path: '{{ ephemeral_fstab }}' register: fstab_stat_before_mount - - name: Mount the FS A with ephemeral state + - name: Mount the FS A with ephemeral state (check mode) ansible.posix.mount: path: /tmp/myfs src: '{{ ephemeral_device_a }}' From 979f00ce757095fccb49364ef2606396894fab9b Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Wed, 26 Mar 2025 15:18:35 -0400 Subject: [PATCH 4/6] fix lint errors --- tests/integration/targets/mount/tasks/main.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/integration/targets/mount/tasks/main.yml b/tests/integration/targets/mount/tasks/main.yml index c35f31d..bc903b8 100644 --- a/tests/integration/targets/mount/tasks/main.yml +++ b/tests/integration/targets/mount/tasks/main.yml @@ -134,7 +134,7 @@ register: bind_result_freebsd_dry_run check_mode: true -- name: attempt to stat bind mounted file +- name: Attempt to stat bind mounted file ansible.builtin.stat: path: '{{ output_dir }}/mount_dest/test_file' when: ansible_system in ('FreeBSD', 'Linux') @@ -330,12 +330,12 @@ when: ansible_system in ('FreeBSD', 'Linux') # SWAP ############################################################# -- name: swap +- name: Swap when: ansible_system in ('Linux') block: # mount swap check mode - name: Stat /etc/fstab - stat: + ansible.builtin.stat: path: /etc/fstab register: stat_fstab_before @@ -349,7 +349,7 @@ check_mode: true - name: Stat /etc/fstab - stat: + ansible.builtin.stat: path: /etc/fstab register: stat_fstab_after @@ -410,7 +410,7 @@ # remove swap check mode - name: Stat /etc/fstab - stat: + ansible.builtin.stat: path: /etc/fstab register: stat_fstab_before @@ -422,7 +422,7 @@ check_mode: true - name: Stat /etc/fstab - stat: + ansible.builtin.stat: path: /etc/fstab register: stat_fstab_after @@ -474,7 +474,7 @@ - not swap2_removed_again['changed'] # FIXUP ############################################################# -- name: fix incomplete entry already present in fstab +- name: Fix incomplete entry already present in fstab when: ansible_system == 'Linux' block: - name: Create fstab record with missing last two fields From 4ff0e3aa139118c912b77053323489d9202142f4 Mon Sep 17 00:00:00 2001 From: Simon Leary Date: Wed, 26 Mar 2025 15:20:42 -0400 Subject: [PATCH 5/6] remove assertion of changed --- tests/integration/targets/mount/tasks/main.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/integration/targets/mount/tasks/main.yml b/tests/integration/targets/mount/tasks/main.yml index bc903b8..c3bca6c 100644 --- a/tests/integration/targets/mount/tasks/main.yml +++ b/tests/integration/targets/mount/tasks/main.yml @@ -143,11 +143,7 @@ - name: Assert the bind mount did not take place ansible.builtin.assert: that: - - linux_and_changed or freebsd_and_changed - not dest_stat['stat']['exists'] - vars: - linux_and_changed: "{{ ansible_system == 'Linux' and bind_result_linux_dry_run['changed'] }}" - freebsd_and_changed: "{{ ansible_system == 'FreeBSD' and bind_result_freebsd['changed'] }}" when: ansible_system in ('FreeBSD', 'Linux') # bind mount From eead50b287f3dc4ca05cb16525ed08a78717b897 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 26 Mar 2025 12:36:08 -0700 Subject: [PATCH 6/6] Apply suggestions from code review --- tests/integration/targets/mount/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/mount/tasks/main.yml b/tests/integration/targets/mount/tasks/main.yml index c3bca6c..d96f865 100644 --- a/tests/integration/targets/mount/tasks/main.yml +++ b/tests/integration/targets/mount/tasks/main.yml @@ -830,7 +830,7 @@ failed_when: false changed_when: false - - name: Assert the mount occured and the fstab is unchanged + - name: Assert the mount occurred and the fstab is unchanged ansible.builtin.assert: that: - check_mountinfo.stdout|int == 0