diff --git a/test/integration/targets/file/tasks/directory_as_dest.yml b/test/integration/targets/file/tasks/directory_as_dest.yml new file mode 100644 index 00000000000..decc3835fb7 --- /dev/null +++ b/test/integration/targets/file/tasks/directory_as_dest.yml @@ -0,0 +1,312 @@ +# File module tests for overwriting directories +- name: Initialize the test output dir + include: initialize.yml + +# We need to make this more consistent: +# https://github.com/ansible/proposals/issues/111 +# +# This series of tests document the current inconsistencies. We should not +# break these by accident but if we approve a proposal we can break these on +# purpose. + +# +# Setup +# + +- name: create a test sub-directory + file: + dest: '{{output_dir}}/sub1' + state: directory + +- name: create a file for linking to + copy: + dest: '{{output_dir}}/file_to_link' + content: 'Hello World' + +# +# Error condtion: specify a directory with state={link,file}, force=False +# + +# file raises an error +- name: Try to create a file with directory as dest + file: + dest: '{{output_dir}}/sub1' + state: file + force: False + ignore_errors: True + register: file1_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file1_dir_stat + +- name: verify that the directory was not overwritten + assert: + that: + - 'file1_result is failed' + - 'file1_dir_stat["stat"].isdir' + +# link raises an error +- name: Try to create a symlink with directory as dest + file: + src: '{{ output_dir }}/file_to_link' + dest: '{{output_dir}}/sub1' + state: link + force: False + ignore_errors: True + register: file2_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file2_dir_stat + +- name: verify that the directory was not overwritten + assert: + that: + - 'file2_result is failed' + - 'file2_dir_stat["stat"].isdir' + +# +# Error condition: file and link with non-empty directory +# + +- copy: + content: 'test' + dest: '{{ output_dir }}/sub1/passwd' + +# file raises an error +- name: Try to create a file with directory as dest + file: + dest: '{{output_dir}}/sub1' + state: file + force: True + ignore_errors: True + register: file3_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file3_dir_stat + +- name: verify that the directory was not overwritten + assert: + that: + - 'file3_result is failed' + - 'file3_dir_stat["stat"].isdir' + +# link raises an error +- name: Try to create a symlink with directory as dest + file: + src: '{{ output_dir }}/file_to_link' + dest: '{{output_dir}}/sub1' + state: link + force: True + ignore_errors: True + register: file4_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file4_dir_stat + +- name: verify that the directory was not overwritten + assert: + that: + - 'file4_result is failed' + - 'file4_dir_stat["stat"].isdir' + +# Cleanup the file that made it non-empty +- name: Cleanup the file that made the directory nonempty + file: + state: 'absent' + dest: '{{ output_dir }}/sub1/passwd' + +# +# Error condition: file cannot even overwrite an empty directory with force=True +# + +# file raises an error +- name: Try to create a file with directory as dest + file: + dest: '{{output_dir}}/sub1' + state: file + force: True + ignore_errors: True + register: file5_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file5_dir_stat + +- name: verify that the directory was not overwritten + assert: + that: + - 'file5_result is failed' + - 'file5_dir_stat["stat"].isdir' + +# +# Directory overwriting - link with force=True will overwrite an empty directory +# + +# link can overwrite an empty directory with force=True +- name: Try to create a symlink with directory as dest + file: + src: '{{ output_dir }}/file_to_link' + dest: '{{output_dir}}/sub1' + state: link + force: True + register: file6_result + +- name: Get stat info to show the directory has been overwritten + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file6_dir_stat + +- name: verify that the directory was overwritten + assert: + that: + - 'file6_result is changed' + - 'not file6_dir_stat["stat"].isdir' + - 'file6_dir_stat["stat"].islnk' + +# +# Cleanup from last set of tests +# + +- name: Cleanup the test subdirectory + file: + dest: '{{output_dir}}/sub1' + state: 'absent' + +- name: Re-create the test sub-directory + file: + dest: '{{output_dir}}/sub1' + state: 'directory' + +# +# Hard links have the proposed 111 behaviour already: Place the new file inside the directory +# + +- name: Try to create a hardlink with directory as dest + file: + src: '{{ output_dir }}/file_to_link' + dest: '{{ output_dir }}/sub1' + state: hard + force: False + ignore_errors: True + register: file7_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file7_dir_stat + +- name: Get stat info to show the link has been created + stat: + path: '{{ output_dir }}/sub1/file_to_link' + follow: False + register: file7_link_stat + +- debug: + var: file7_link_stat + +- name: verify that the directory was not overwritten + assert: + that: + - 'file7_result is changed' + - 'file7_dir_stat["stat"].isdir' + - 'file7_link_stat["stat"].isfile' + - 'file7_link_stat["stat"].isfile' + ignore_errors: True + +# +# Touch is a bit different than everything else. +# If we need to set timestamps we should probably add atime, mtime, and ctime parameters +# But I think touch was written because state=file didn't create a file if it +# didn't already exist. We should look at changing that behaviour. +# + +- name: Get initial stat info to compare with later + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file8_initial_dir_stat + +- name: Use touch with directory as dest + file: + dest: '{{output_dir}}/sub1' + state: touch + force: False + register: file8_result + +- name: Get stat info to show the directory has not been changed to a file + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file8_dir_stat + +- name: verify that the directory has been updated + assert: + that: + - 'file8_result is changed' + - 'file8_dir_stat["stat"].isdir' + - 'file8_dir_stat["stat"]["mtime"] != file8_initial_dir_stat["stat"]["mtime"]' + +# +# State=directory realizes that the directory already exists and does nothing +# +- name: Get initial stat info to compare with later + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file9_initial_dir_stat + +- name: Use directory with directory as dest + file: + dest: '{{output_dir}}/sub1' + state: directory + force: False + register: file9_result + +- name: Get stat info to show the directory has not been changed + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file9_dir_stat + +- name: verify that the directory has been updated + assert: + that: + - 'file9_result is not changed' + - 'file9_dir_stat["stat"].isdir' + - 'file9_dir_stat["stat"]["mtime"] == file9_initial_dir_stat["stat"]["mtime"]' + +- name: Use directory with directory as dest and force=True + file: + dest: '{{output_dir}}/sub1' + state: directory + force: True + register: file10_result + +- name: Get stat info to show the directory has not been changed + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file10_dir_stat + +- name: verify that the directory has been updated + assert: + that: + - 'file10_result is not changed' + - 'file10_dir_stat["stat"].isdir' + - 'file10_dir_stat["stat"]["mtime"] == file9_initial_dir_stat["stat"]["mtime"]' diff --git a/test/integration/targets/file/tasks/initialize.yml b/test/integration/targets/file/tasks/initialize.yml new file mode 100644 index 00000000000..dd7d1274865 --- /dev/null +++ b/test/integration/targets/file/tasks/initialize.yml @@ -0,0 +1,15 @@ +# +# Cleanup the output dir and recreate it for the tests to operate on +# +- name: Cleanup the output directory + file: + dest: '{{ output_dir }}' + state: 'absent' + +- name: Recreate the toplevel output dir + file: + dest: '{{ output_dir }}' + state: 'directory' + +- name: prep with a basic file to operate on + copy: src=foo.txt dest={{output_file}} diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml index 4ce5db20b15..9108d7649b7 100644 --- a/test/integration/targets/file/tasks/main.yml +++ b/test/integration/targets/file/tasks/main.yml @@ -25,8 +25,21 @@ - set_fact: remote_file_expanded: '{{ echo.stdout }}' -- name: prep with a basic copy - copy: src=foo.txt dest={{output_file}} +# Include the tests +- name: Run tests for state=link + include: state_link.yml + +- name: Run tests for directory as dest + include: directory_as_dest.yml + +- name: decide to include or not include selinux tests + include: selinux_tests.yml + when: selinux_installed is defined and selinux_installed.stdout != "" and selinux_enabled.stdout != "Disabled" + +- name: Initialize the test output dir + include: initialize.yml + +# These tests need to be organized by state parameter into separate files later - name: verify that we are checking a file and it is present file: path={{output_file}} state=file @@ -111,35 +124,6 @@ - name: clean up file: path=/tmp/worldwritable state=absent -- name: create soft link to file - file: src={{output_file}} dest={{output_dir}}/soft.txt state=link - register: file5_result - -- name: verify that the file was marked as changed - assert: - that: - - "file5_result.changed == true" - -- name: change soft link to relative - file: src={{output_file|basename}} dest={{output_dir}}/soft.txt state=link - register: file5a_result - -- name: verify that the file was marked as changed - assert: - that: - - "file5a_result.changed == true" - - "file5a_result.diff.before.src == remote_file_expanded" - - "file5a_result.diff.after.src == remote_file_expanded|basename" - -- name: soft link idempotency check - file: src={{output_file|basename}} dest={{output_dir}}/soft.txt state=link - register: file5b_result - -- name: verify that the file was not marked as changed - assert: - that: - - "file5b_result.changed == false" - - name: create hard link to file file: src={{output_file}} dest={{output_dir}}/hard.txt state=hard register: file6_result @@ -223,10 +207,6 @@ when: selinux_installed.stdout != "" ignore_errors: true -- name: decide to include or not include selinux tests - include: selinux_tests.yml - when: selinux_installed is defined and selinux_installed.stdout != "" and selinux_enabled.stdout != "Disabled" - - name: remote directory foobar file: path={{output_dir}}/foobar state=absent @@ -284,34 +264,6 @@ that: - "file11_result.uid == 1235" -- name: fail to create soft link to non existent file - file: src=/noneexistent dest={{output_dir}}/soft2.txt state=link force=no - register: file12_result - ignore_errors: true - -- name: verify that link was not created - assert: - that: - - "file12_result.failed == true" - -- name: force creation soft link to non existent - file: src=/noneexistent dest={{output_dir}}/soft2.txt state=link force=yes - register: file13_result - -- name: verify that link was created - assert: - that: - - "file13_result.changed == true" - -- name: Prove idempotence of force creation soft link to non existent - file: src=/noneexistent dest={{output_dir}}/soft2.txt state=link force=yes - register: file13a_result - -- name: verify that the link to nonexistent is idempotent - assert: - that: - - "file13a_result.changed == false" - - name: remove directory foobar file: path={{output_dir}}/foobar state=absent register: file14_result @@ -347,80 +299,6 @@ - 'item.state == "file"' with_items: "{{file16_result.results}}" -- name: try to force the sub-directory to a link - file: src={{output_dir}}/testing dest={{output_dir}}/sub1 state=link force=yes - register: file17_result - ignore_errors: true - -- name: verify the directory was not replaced with a link - assert: - that: - - 'file17_result.failed == true' - - 'file17_result.state == "directory"' - -- name: create soft link to directory using absolute path - file: src=/ dest={{output_dir}}/root state=link - register: file18_result - -- name: verify that the result was marked as changed - assert: - that: - - "file18_result.changed == true" - -- name: create another test sub-directory - file: dest={{output_dir}}/sub2 state=directory - register: file19_result - -- name: verify that the new directory was created - assert: - that: - - 'file19_result.changed == true' - - 'file19_result.state == "directory"' - -- name: create soft link to relative file - file: src=../sub1/file1 dest={{output_dir}}/sub2/link1 state=link - register: file20_result - -- name: Get stat info for the link - stat: - path: '{{ output_dir }}/sub2/link1' - follow: False - register: file20_link_stat - -- name: Get stat info for the pointed to file - stat: - path: '{{ output_dir }}/sub2/link1' - follow: True - register: file20_links_dest_stat - -- name: Get stat info for the file we intend to point to - stat: - path: '{{ output_dir }}/sub1/file1' - follow: False - register: file20_dest_stat - -- debug: var=file20_dest_stat -- name: verify that the link was created correctly - assert: - that: - # file command reports it created something - - "file20_result.changed == true" - # file command created a link - - 'file20_link_stat["stat"]["islnk"]' - # Link points to the right path - - 'file20_link_stat["stat"]["lnk_target"] == "../sub1/file1"' - # The link target and the file we intended to link to have the same inode - - 'file20_links_dest_stat["stat"]["inode"] == file20_dest_stat["stat"]["inode"]' - -- name: create soft link to relative directory - file: src=sub1 dest={{output_dir}}/sub1-link state=link - register: file21_result - -- name: verify that the result was marked as changed - assert: - that: - - "file21_result.changed == true" - - name: test file creation with symbolic mode file: dest={{output_dir}}/test_symbolic state=touch mode=u=rwx,g=rwx,o=rwx register: result @@ -574,49 +452,6 @@ that: - result.mode == '0444' -# test the file module using follow=yes, so that the target of a -# symlink is modified, rather than the link itself - -- name: create a test file - copy: dest={{output_dir}}/test_follow content="this is a test file\n" mode=0666 - -- name: create a symlink to the test file - file: path={{output_dir}}/test_follow_link src="./test_follow" state=link - -- name: modify the permissions on the link using follow=yes - file: path={{output_dir}}/test_follow_link mode=0644 follow=yes - register: result - -- name: assert that the chmod worked - assert: - that: - - result.changed - -- name: stat the link target - stat: path={{output_dir}}/test_follow - register: result - -- name: assert that the link target was modified correctly - assert: - that: - - result.stat.mode == '0644' - -- name: attempt to modify the permissions of the link itself - file: path={{output_dir}}/test_follow_link src="./test_follow" state=link mode=0600 follow=no - register: result - -# Whether the link itself changed is platform dependent! (BSD vs Linux?) -# Just check that the underlying file was not changed -- name: stat the link target - stat: path={{output_dir}}/test_follow - register: result - -- name: assert that the link target was unmodified - assert: - that: - - result.stat.mode == '0644' - ignore_errors: True - # Follow + recursive tests - name: create a toplevel directory file: path={{output_dir}}/test_follow_rec state=directory mode=0755 diff --git a/test/integration/targets/file/tasks/selinux_tests.yml b/test/integration/targets/file/tasks/selinux_tests.yml index 5589ca5dace..6a95c4427a8 100644 --- a/test/integration/targets/file/tasks/selinux_tests.yml +++ b/test/integration/targets/file/tasks/selinux_tests.yml @@ -16,6 +16,9 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . +- name: Initialize the test output dir + include: initialize.yml + - name: touch a file for testing file: path={{output_dir}}/foo-se.txt state=touch register: file_se_result diff --git a/test/integration/targets/file/tasks/state_link.yml b/test/integration/targets/file/tasks/state_link.yml new file mode 100644 index 00000000000..60ea54542c7 --- /dev/null +++ b/test/integration/targets/file/tasks/state_link.yml @@ -0,0 +1,310 @@ +# file module tests for dealing with symlinks (state=link) + +- name: Initialize the test output dir + include: initialize.yml + +# +# Basic absolute symlink to a file +# +- name: create soft link to file + file: src={{output_file}} dest={{output_dir}}/soft.txt state=link + register: file1_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/soft.txt' + follow: False + register: file1_link_stat + +- name: verify that the symlink was created correctly + assert: + that: + - 'file1_result is changed' + - 'file1_link_stat["stat"].islnk' + - 'file1_link_stat["stat"].lnk_target | expanduser == output_file | expanduser' + +# +# Change an absolute soft link into a relative soft link +# +- name: change soft link to relative + file: src={{output_file|basename}} dest={{output_dir}}/soft.txt state=link + register: file2_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/soft.txt' + follow: False + register: file2_link_stat + +- name: verify that the file was marked as changed + assert: + that: + - "file2_result is changed" + - "file2_result.diff.before.src == remote_file_expanded" + - "file2_result.diff.after.src == remote_file_expanded|basename" + - "file2_link_stat['stat'].islnk" + - "file2_link_stat['stat'].lnk_target == remote_file_expanded | basename" + +# +# Check that creating the soft link a second time was idempotent +# +- name: soft link idempotency check + file: src={{output_file|basename}} dest={{output_dir}}/soft.txt state=link + register: file3_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/soft.txt' + follow: False + register: file3_link_stat + +- name: verify that the file was not marked as changed + assert: + that: + - "not file3_result is changed" + - "file3_link_stat['stat'].islnk" + - "file3_link_stat['stat'].lnk_target == remote_file_expanded | basename" + +# +# Test symlink to nonexistent files +# +- name: fail to create soft link to non existent file + file: + src: '/nonexistent' + dest: '{{output_dir}}/soft2.txt' + state: 'link' + force: False + register: file4_result + ignore_errors: true + +- name: verify that link was not created + assert: + that: + - "file4_result is failed" + +- name: force creation soft link to non existent + file: + src: '/nonexistent' + dest: '{{ output_dir}}/soft2.txt' + state: 'link' + force: True + register: file5_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/soft2.txt' + follow: False + register: file5_link_stat + +- name: verify that link was created + assert: + that: + - "file5_result is changed" + - "file5_link_stat['stat'].islnk" + - "file5_link_stat['stat'].lnk_target == '/nonexistent'" + +- name: Prove idempotence of force creation soft link to non existent + file: + src: '/nonexistent' + dest: '{{ output_dir }}/soft2.txt' + state: 'link' + force: True + register: file6a_result + +- name: verify that the link to nonexistent is idempotent + assert: + that: + - "file6a_result.changed == false" + +# +# Test creating a link to a directory https://github.com/ansible/ansible/issues/1369 +# +- name: create soft link to directory using absolute path + file: + src: '/' + dest: '{{ output_dir }}/root' + state: 'link' + register: file6_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/root' + follow: False + register: file6_link_stat + +- name: Get stat info for the pointed to file + stat: + path: '{{ output_dir }}/root' + follow: True + register: file6_links_dest_stat + +- name: Get stat info for the file we intend to point to + stat: + path: '/' + follow: False + register: file6_dest_stat + +- name: verify that the link was created correctly + assert: + that: + # file command reports it created something + - "file6_result.changed == true" + # file command created a link + - 'file6_link_stat["stat"]["islnk"]' + # Link points to the right path + - 'file6_link_stat["stat"]["lnk_target"] == "/"' + # The link target and the file we intended to link to have the same inode + - 'file6_links_dest_stat["stat"]["inode"] == file6_dest_stat["stat"]["inode"]' + +# +# Test creating a relative link +# + +# Relative link to file +- name: create a test sub-directory to link to + file: + dest: '{{ output_dir }}/sub1' + state: 'directory' + +- name: create a file to link to in the test sub-directory + file: + dest: '{{ output_dir }}/sub1/file1' + state: 'touch' + +- name: create another test sub-directory to place links within + file: + dest: '{{output_dir}}/sub2' + state: 'directory' + +- name: create soft link to relative file + file: + src: '../sub1/file1' + dest: '{{ output_dir }}/sub2/link1' + state: 'link' + register: file7_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/sub2/link1' + follow: False + register: file7_link_stat + +- name: Get stat info for the pointed to file + stat: + path: '{{ output_dir }}/sub2/link1' + follow: True + register: file7_links_dest_stat + +- name: Get stat info for the file we intend to point to + stat: + path: '{{ output_dir }}/sub1/file1' + follow: False + register: file7_dest_stat + +- name: verify that the link was created correctly + assert: + that: + # file command reports it created something + - "file7_result.changed == true" + # file command created a link + - 'file7_link_stat["stat"]["islnk"]' + # Link points to the right path + - 'file7_link_stat["stat"]["lnk_target"] == "../sub1/file1"' + # The link target and the file we intended to link to have the same inode + - 'file7_links_dest_stat["stat"]["inode"] == file7_dest_stat["stat"]["inode"]' + +# Relative link to directory +- name: create soft link to relative directory + file: + src: sub1 + dest: '{{ output_dir }}/sub1-link' + state: 'link' + register: file8_result + +- name: Get stat info for the link + stat: + path: '{{ output_dir }}/sub1-link' + follow: False + register: file8_link_stat + +- name: Get stat info for the pointed to file + stat: + path: '{{ output_dir }}/sub1-link' + follow: True + register: file8_links_dest_stat + +- name: Get stat info for the file we intend to point to + stat: + path: '{{ output_dir }}/sub1' + follow: False + register: file8_dest_stat + +- name: verify that the link was created correctly + assert: + that: + # file command reports it created something + - "file8_result.changed == true" + # file command created a link + - 'file8_link_stat["stat"]["islnk"]' + # Link points to the right path + - 'file8_link_stat["stat"]["lnk_target"] == "sub1"' + # The link target and the file we intended to link to have the same inode + - 'file8_links_dest_stat["stat"]["inode"] == file8_dest_stat["stat"]["inode"]' + +# test the file module using follow=yes, so that the target of a +# symlink is modified, rather than the link itself + +- name: create a test file + copy: + dest: '{{output_dir}}/test_follow' + content: 'this is a test file\n' + mode: 0666 + +- name: create a symlink to the test file + file: + path: '{{output_dir}}/test_follow_link' + src: './test_follow' + state: 'link' + +- name: modify the permissions on the link using follow=yes + file: + path: '{{output_dir}}/test_follow_link' + mode: 0644 + follow: yes + register: file9_result + +- name: stat the link target + stat: + path: '{{output_dir}}/test_follow' + register: file9_stat + +- name: assert that the chmod worked + assert: + that: + - 'file9_result is changed' + - 'file9_stat["stat"]["mode"] == "0644"' + +# +# Test modifying the permissions of a link itself +# +- name: attempt to modify the permissions of the link itself + file: + path: '{{output_dir}}/test_follow_link' + src: './test_follow' + state: 'link' + mode: 0600 + follow: False + register: file10_result + +# Whether the link itself changed is platform dependent! (BSD vs Linux?) +# Just check that the underlying file was not changed +- name: stat the link target + stat: + path: '{{output_dir}}/test_follow' + register: file10_target_stat + +- name: assert that the link target was unmodified + assert: + that: + - 'file10_result is changed' + - 'file10_target_stat["stat"]["mode"] == "0644"'