# test code for the copy module and action plugin # (c) 2014, Michael DeHaan # (c) 2017, Ansible Project # # GNU General Public License v3 or later (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt ) # - name: Record the output directory set_fact: remote_file: "{{ remote_dir }}/foo.txt" - name: Initiate a basic copy, and also test the mode copy: src: foo.txt dest: "{{ remote_file }}" mode: 0444 register: copy_result - name: Record the sha of the test file for later tests set_fact: remote_file_hash: "{{ copy_result['checksum'] }}" - name: Check the mode of the output file file: name: "{{ remote_file }}" state: file register: file_result_check - name: Assert the mode is correct assert: that: - "file_result_check.mode == '0444'" # same as expanduser & expandvars - command: 'echo {{ remote_dir }}' register: echo - set_fact: remote_dir_expanded: '{{ echo.stdout }}' remote_file_expanded: '{{ echo.stdout }}/foo.txt' - debug: var: copy_result verbosity: 1 - name: Assert basic copy worked assert: that: - "'changed' in copy_result" - copy_result.dest == remote_file_expanded - "'group' in copy_result" - "'gid' in copy_result" - "'md5sum' in copy_result" - "'checksum' in copy_result" - "'owner' in copy_result" - "'size' in copy_result" - "'src' in copy_result" - "'state' in copy_result" - "'uid' in copy_result" - name: Verify that the file was marked as changed assert: that: - "copy_result.changed == true" - name: Verify that the file checksums are correct assert: that: - "copy_result.checksum == ('foo.txt\n'|hash('sha1'))" - name: Verify that the legacy md5sum is correct assert: that: - "copy_result.md5sum == ('foo.txt\n'|hash('md5'))" when: ansible_fips|bool != True - name: Check the stat results of the file stat: path: "{{ remote_file }}" get_md5: yes register: stat_results - debug: var: stat_results verbosity: 1 - name: Assert the stat results are correct assert: that: - "stat_results.stat.exists == true" - "stat_results.stat.isblk == false" - "stat_results.stat.isfifo == false" - "stat_results.stat.isreg == true" - "stat_results.stat.issock == false" - "stat_results.stat.checksum == ('foo.txt\n'|hash('sha1'))" - name: Verify that the legacy md5sum is correct assert: that: - "stat_results.stat.md5 == ('foo.txt\n'|hash('md5'))" when: ansible_fips|bool != True - name: Overwrite the file via same means copy: src: foo.txt dest: "{{ remote_file }}" decrypt: no register: copy_result2 - name: Assert that the file was not changed assert: that: - "copy_result2 is not changed" - name: Assert basic copy worked assert: that: - "'changed' in copy_result2" - copy_result2.dest == remote_file_expanded - "'group' in copy_result2" - "'gid' in copy_result2" - "'checksum' in copy_result2" - "'owner' in copy_result2" - "'size' in copy_result2" - "'state' in copy_result2" - "'uid' in copy_result2" - name: Overwrite the file using the content system copy: content: "modified" dest: "{{ remote_file }}" decrypt: no register: copy_result3 - name: Check the stat results of the file stat: path: "{{ remote_file }}" register: stat_results - debug: var: stat_results verbosity: 1 - name: Assert that the file has changed assert: that: - "copy_result3 is changed" - "'content' not in copy_result3" - "stat_results.stat.checksum == ('modified'|hash('sha1'))" - "stat_results.stat.mode != '0700'" - name: Overwrite the file again using the content system, also passing along file params copy: content: "modified" dest: "{{ remote_file }}" mode: 0700 decrypt: no register: copy_result4 - name: Check the stat results of the file stat: path: "{{ remote_file }}" register: stat_results - debug: var: stat_results verbosity: 1 - name: Assert that the file has changed assert: that: - "copy_result3 is changed" - "'content' not in copy_result3" - "stat_results.stat.checksum == ('modified'|hash('sha1'))" - "stat_results.stat.mode == '0700'" - name: Create a hardlink to the file file: src: '{{ remote_file }}' dest: '{{ remote_dir }}/hard.lnk' state: hard - name: copy the same contents into place copy: content: 'modified' dest: '{{ remote_file }}' mode: 0700 decrypt: no register: copy_results - name: Check the stat results of the file stat: path: "{{ remote_file }}" register: stat_results - name: Check the stat results of the hard link stat: path: "{{ remote_dir }}/hard.lnk" register: hlink_results - name: Check that the file did not change assert: that: - 'stat_results.stat.inode == hlink_results.stat.inode' - 'copy_results.changed == False' - "stat_results.stat.checksum == ('modified'|hash('sha1'))" - name: copy the same contents into place but change mode copy: content: 'modified' dest: '{{ remote_file }}' mode: 0404 decrypt: no register: copy_results - name: Check the stat results of the file stat: path: "{{ remote_file }}" register: stat_results - name: Check the stat results of the hard link stat: path: "{{ remote_dir }}/hard.lnk" register: hlink_results - name: Check that the file changed permissions but is still the same assert: that: - 'stat_results.stat.inode == hlink_results.stat.inode' - 'copy_results.changed == True' - 'stat_results.stat.mode == hlink_results.stat.mode' - 'stat_results.stat.mode == "0404"' - "stat_results.stat.checksum == ('modified'|hash('sha1'))" - name: copy the different contents into place copy: content: 'adjusted' dest: '{{ remote_file }}' mode: 0404 register: copy_results - name: Check the stat results of the file stat: path: "{{ remote_file }}" register: stat_results - name: Check the stat results of the hard link stat: path: "{{ remote_dir }}/hard.lnk" register: hlink_results - name: Check that the file changed and hardlink was broken assert: that: - 'stat_results.stat.inode != hlink_results.stat.inode' - 'copy_results.changed == True' - "stat_results.stat.checksum == ('adjusted'|hash('sha1'))" - "hlink_results.stat.checksum == ('modified'|hash('sha1'))" - name: Try invalid copy input location fails copy: src: invalid_file_location_does_not_exist dest: "{{ remote_dir }}/file.txt" ignore_errors: True register: failed_copy - name: Assert that invalid source failed assert: that: - "failed_copy.failed" - "'invalid_file_location_does_not_exist' in failed_copy.msg" - name: Try empty source to ensure it fails copy: src: '' dest: "{{ remote_dir }}" ignore_errors: True register: failed_copy - debug: var: failed_copy verbosity: 1 - name: Assert that empty source failed assert: that: - failed_copy is failed - "'src (or content) is required' in failed_copy.msg" - name: Try without destination to ensure it fails copy: src: foo.txt ignore_errors: True register: failed_copy - debug: var: failed_copy verbosity: 1 - name: Assert that missing destination failed assert: that: - failed_copy is failed - "'dest is required' in failed_copy.msg" - name: Try without source to ensure it fails copy: dest: "{{ remote_file }}" ignore_errors: True register: failed_copy - debug: var: failed_copy verbosity: 1 - name: Assert that missing source failed assert: that: - failed_copy is failed - "'src (or content) is required' in failed_copy.msg" - name: Try with both src and content to ensure it fails copy: src: foo.txt content: testing dest: "{{ remote_file }}" ignore_errors: True register: failed_copy - name: Assert that mutually exclusive parameters failed assert: that: - failed_copy is failed - "'mutually exclusive' in failed_copy.msg" - name: Try with content and directory as destination to ensure it fails copy: content: testing dest: "{{ remote_dir }}" ignore_errors: True register: failed_copy - debug: var: failed_copy verbosity: 1 - name: Assert that content and directory as destination failed assert: that: - failed_copy is failed - "'can not use content with a dir as dest' in failed_copy.msg" - name: Clean up file: path: "{{ remote_file }}" state: absent - name: Copy source file to destination directory with mode copy: src: foo.txt dest: "{{ remote_dir }}" mode: 0500 register: copy_results - name: Check the stat results of the file stat: path: '{{ remote_file }}' register: stat_results - debug: var: stat_results verbosity: 1 - name: Assert that the file has changed assert: that: - "copy_results is changed" - "stat_results.stat.checksum == ('foo.txt\n'|hash('sha1'))" - "stat_results.stat.mode == '0500'" # Test copy with mode=preserve - name: Create file and set perms to an odd value copy: content: "foo.txt\n" dest: '{{ local_temp_dir }}/foo.txt' mode: 0547 connection: local - name: Copy with mode=preserve copy: src: '{{ local_temp_dir }}/foo.txt' dest: '{{ remote_dir }}/copy-foo.txt' mode: preserve register: copy_results - name: Check the stat results of the file stat: path: '{{ remote_dir }}/copy-foo.txt' register: stat_results - name: Assert that the file has changed and has correct mode assert: that: - "copy_results is changed" - "copy_results.mode == '0547'" - "stat_results.stat.checksum == ('foo.txt\n'|hash('sha1'))" - "stat_results.stat.mode == '0547'" # # test recursive copy local_follow=False, no trailing slash # - name: Create empty directory in the role we're copying from (git can't store empty dirs) file: path: '{{ role_path }}/files/subdir/subdira' state: directory connection: local - name: Set the output subdirectory set_fact: remote_subdir: "{{ remote_dir }}/sub" - name: Make an output subdirectory file: name: "{{ remote_subdir }}" state: directory - name: Setup link target for absolute link copy: dest: /tmp/ansible-test-abs-link content: target connection: local - name: Setup link target dir for absolute link file: dest: /tmp/ansible-test-abs-link-dir state: directory connection: local - name: Test recursive copy to directory no trailing slash, local_follow=False copy: src: subdir dest: "{{ remote_subdir }}" directory_mode: 0700 local_follow: False register: recursive_copy_result - debug: var: recursive_copy_result verbosity: 1 - name: Assert that the recursive copy did something assert: that: - "recursive_copy_result is changed" - name: Check that a file in a directory was transferred stat: path: "{{ remote_dir }}/sub/subdir/bar.txt" register: stat_bar - name: Check that a file in a deeper directory was transferred stat: path: "{{ remote_dir }}/sub/subdir/subdir2/baz.txt" register: stat_bar2 - name: Check that a file in a directory whose parent contains a directory alone was transferred stat: path: "{{ remote_dir }}/sub/subdir/subdir2/subdir3/subdir4/qux.txt" register: stat_bar3 - name: Assert recursive copy files assert: that: - "stat_bar.stat.exists" - "stat_bar2.stat.exists" - "stat_bar3.stat.exists" - name: Check symlink to absolute path stat: path: '{{ remote_dir }}/sub/subdir/subdir1/ansible-test-abs-link' register: stat_abs_link - name: Check symlink to relative path stat: path: '{{ remote_dir }}/sub/subdir/subdir1/bar.txt' register: stat_relative_link - name: Check symlink to self stat: path: '{{ remote_dir }}/sub/subdir/subdir1/invalid' register: stat_self_link - name: Check symlink to nonexistent file stat: path: '{{ remote_dir }}/sub/subdir/subdir1/invalid2' register: stat_invalid_link - name: Check symlink to directory in copy stat: path: '{{ remote_dir }}/sub/subdir/subdir1/subdir3' register: stat_dir_in_copy_link - name: Check symlink to directory outside of copy stat: path: '{{ remote_dir }}/sub/subdir/subdir1/ansible-test-abs-link-dir' register: stat_dir_outside_copy_link - name: Assert recursive copy symlinks local_follow=False assert: that: - "stat_abs_link.stat.exists" - "stat_abs_link.stat.islnk" - "'/tmp/ansible-test-abs-link' == stat_abs_link.stat.lnk_target" - "stat_relative_link.stat.exists" - "stat_relative_link.stat.islnk" - "'../bar.txt' == stat_relative_link.stat.lnk_target" - "stat_self_link.stat.exists" - "stat_self_link.stat.islnk" - "'invalid' in stat_self_link.stat.lnk_target" - "stat_invalid_link.stat.exists" - "stat_invalid_link.stat.islnk" - "'../invalid' in stat_invalid_link.stat.lnk_target" - "stat_dir_in_copy_link.stat.exists" - "stat_dir_in_copy_link.stat.islnk" - "'../subdir2/subdir3' in stat_dir_in_copy_link.stat.lnk_target" - "stat_dir_outside_copy_link.stat.exists" - "stat_dir_outside_copy_link.stat.islnk" - "'/tmp/ansible-test-abs-link-dir' == stat_dir_outside_copy_link.stat.lnk_target" - name: Stat the recursively copied directories stat: path: "{{ remote_dir }}/sub/{{ item }}" register: dir_stats with_items: - "subdir" - "subdir/subdira" - "subdir/subdir1" - "subdir/subdir2" - "subdir/subdir2/subdir3" - "subdir/subdir2/subdir3/subdir4" - debug: var: stat_results verbosity: 1 - name: Assert recursive copied directories mode (1) assert: that: - "item.stat.exists" - "item.stat.mode == '0700'" with_items: "{{dir_stats.results}}" - name: Test recursive copy to directory no trailing slash, local_follow=False second time copy: src: subdir dest: "{{ remote_subdir }}" directory_mode: 0700 local_follow: False register: recursive_copy_result - name: Assert that the second copy did not change anything assert: that: - "recursive_copy_result is not changed" - name: Cleanup the recursive copy subdir file: name: "{{ remote_subdir }}" state: absent # # Recursive copy with local_follow=False, trailing slash # - name: Set the output subdirectory set_fact: remote_subdir: "{{ remote_dir }}/sub" - name: Make an output subdirectory file: name: "{{ remote_subdir }}" state: directory - name: Setup link target for absolute link copy: dest: /tmp/ansible-test-abs-link content: target connection: local - name: Setup link target dir for absolute link file: dest: /tmp/ansible-test-abs-link-dir state: directory connection: local - name: Test recursive copy to directory trailing slash, local_follow=False copy: src: subdir/ dest: "{{ remote_subdir }}" directory_mode: 0700 local_follow: False register: recursive_copy_result - debug: var: recursive_copy_result verbosity: 1 - name: Assert that the recursive copy did something assert: that: - "recursive_copy_result is changed" - name: Check that a file in a directory was transferred stat: path: "{{ remote_dir }}/sub/bar.txt" register: stat_bar - name: Check that a file in a deeper directory was transferred stat: path: "{{ remote_dir }}/sub/subdir2/baz.txt" register: stat_bar2 - name: Check that a file in a directory whose parent contains a directory alone was transferred stat: path: "{{ remote_dir }}/sub/subdir2/subdir3/subdir4/qux.txt" register: stat_bar3 - name: Assert recursive copy files assert: that: - "stat_bar.stat.exists" - "stat_bar2.stat.exists" - "stat_bar3.stat.exists" - name: Check symlink to absolute path stat: path: '{{ remote_dir }}/sub/subdir1/ansible-test-abs-link' register: stat_abs_link - name: Check symlink to relative path stat: path: '{{ remote_dir }}/sub/subdir1/bar.txt' register: stat_relative_link - name: Check symlink to self stat: path: '{{ remote_dir }}/sub/subdir1/invalid' register: stat_self_link - name: Check symlink to nonexistent file stat: path: '{{ remote_dir }}/sub/subdir1/invalid2' register: stat_invalid_link - name: Check symlink to directory in copy stat: path: '{{ remote_dir }}/sub/subdir1/subdir3' register: stat_dir_in_copy_link - name: Check symlink to directory outside of copy stat: path: '{{ remote_dir }}/sub/subdir1/ansible-test-abs-link-dir' register: stat_dir_outside_copy_link - name: Assert recursive copy symlinks local_follow=False trailing slash assert: that: - "stat_abs_link.stat.exists" - "stat_abs_link.stat.islnk" - "'/tmp/ansible-test-abs-link' == stat_abs_link.stat.lnk_target" - "stat_relative_link.stat.exists" - "stat_relative_link.stat.islnk" - "'../bar.txt' == stat_relative_link.stat.lnk_target" - "stat_self_link.stat.exists" - "stat_self_link.stat.islnk" - "'invalid' in stat_self_link.stat.lnk_target" - "stat_invalid_link.stat.exists" - "stat_invalid_link.stat.islnk" - "'../invalid' in stat_invalid_link.stat.lnk_target" - "stat_dir_in_copy_link.stat.exists" - "stat_dir_in_copy_link.stat.islnk" - "'../subdir2/subdir3' in stat_dir_in_copy_link.stat.lnk_target" - "stat_dir_outside_copy_link.stat.exists" - "stat_dir_outside_copy_link.stat.islnk" - "'/tmp/ansible-test-abs-link-dir' == stat_dir_outside_copy_link.stat.lnk_target" - name: Stat the recursively copied directories stat: path: "{{ remote_dir }}/sub/{{ item }}" register: dir_stats with_items: - "subdira" - "subdir1" - "subdir2" - "subdir2/subdir3" - "subdir2/subdir3/subdir4" - debug: var: dir_stats verbosity: 1 - name: Assert recursive copied directories mode (2) assert: that: - "item.stat.mode == '0700'" with_items: "{{dir_stats.results}}" - name: Test recursive copy to directory trailing slash, local_follow=False second time copy: src: subdir/ dest: "{{ remote_subdir }}" directory_mode: 0700 local_follow: False register: recursive_copy_result - name: Assert that the second copy did not change anything assert: that: - "recursive_copy_result is not changed" - name: Cleanup the recursive copy subdir file: name: "{{ remote_subdir }}" state: absent # # test recursive copy local_follow=True, no trailing slash # - name: Set the output subdirectory set_fact: remote_subdir: "{{ remote_dir }}/sub" - name: Make an output subdirectory file: name: "{{ remote_subdir }}" state: directory - name: Setup link target for absolute link copy: dest: /tmp/ansible-test-abs-link content: target connection: local - name: Setup link target dir for absolute link file: dest: /tmp/ansible-test-abs-link-dir state: directory connection: local - name: Test recursive copy to directory no trailing slash, local_follow=True copy: src: subdir dest: "{{ remote_subdir }}" directory_mode: 0700 local_follow: True register: recursive_copy_result - debug: var: recursive_copy_result verbosity: 1 - name: Assert that the recursive copy did something assert: that: - "recursive_copy_result is changed" - name: Check that a file in a directory was transferred stat: path: "{{ remote_dir }}/sub/subdir/bar.txt" register: stat_bar - name: Check that a file in a deeper directory was transferred stat: path: "{{ remote_dir }}/sub/subdir/subdir2/baz.txt" register: stat_bar2 - name: Check that a file in a directory whose parent contains a directory alone was transferred stat: path: "{{ remote_dir }}/sub/subdir/subdir2/subdir3/subdir4/qux.txt" register: stat_bar3 - name: Check that a file in a directory whose parent is a symlink was transferred stat: path: "{{ remote_dir }}/sub/subdir/subdir1/subdir3/subdir4/qux.txt" register: stat_bar4 - name: Assert recursive copy files assert: that: - "stat_bar.stat.exists" - "stat_bar2.stat.exists" - "stat_bar3.stat.exists" - "stat_bar4.stat.exists" - name: Check symlink to absolute path stat: path: '{{ remote_dir }}/sub/subdir/subdir1/ansible-test-abs-link' register: stat_abs_link - name: Check symlink to relative path stat: path: '{{ remote_dir }}/sub/subdir/subdir1/bar.txt' register: stat_relative_link - name: Check symlink to self stat: path: '{{ remote_dir }}/sub/subdir/subdir1/invalid' register: stat_self_link - name: Check symlink to nonexistent file stat: path: '{{ remote_dir }}/sub/subdir/subdir1/invalid2' register: stat_invalid_link - name: Check symlink to directory in copy stat: path: '{{ remote_dir }}/sub/subdir/subdir1/subdir3' register: stat_dir_in_copy_link - name: Check symlink to directory outside of copy stat: path: '{{ remote_dir }}/sub/subdir/subdir1/ansible-test-abs-link-dir' register: stat_dir_outside_copy_link - name: Assert recursive copy symlinks local_follow=True assert: that: - "stat_abs_link.stat.exists" - "not stat_abs_link.stat.islnk" - "stat_abs_link.stat.checksum == ('target'|hash('sha1'))" - "stat_relative_link.stat.exists" - "not stat_relative_link.stat.islnk" - "stat_relative_link.stat.checksum == ('baz\n'|hash('sha1'))" - "stat_self_link.stat.exists" - "stat_self_link.stat.islnk" - "'invalid' in stat_self_link.stat.lnk_target" - "stat_invalid_link.stat.exists" - "stat_invalid_link.stat.islnk" - "'../invalid' in stat_invalid_link.stat.lnk_target" - "stat_dir_in_copy_link.stat.exists" - "not stat_dir_in_copy_link.stat.islnk" - "stat_dir_in_copy_link.stat.isdir" - - "stat_dir_outside_copy_link.stat.exists" - "not stat_dir_outside_copy_link.stat.islnk" - "stat_dir_outside_copy_link.stat.isdir" - name: Stat the recursively copied directories stat: path: "{{ remote_dir }}/sub/{{ item }}" register: dir_stats with_items: - "subdir" - "subdir/subdira" - "subdir/subdir1" - "subdir/subdir1/subdir3" - "subdir/subdir1/subdir3/subdir4" - "subdir/subdir2" - "subdir/subdir2/subdir3" - "subdir/subdir2/subdir3/subdir4" - debug: var: dir_stats verbosity: 1 - name: Assert recursive copied directories mode (3) assert: that: - "item.stat.mode == '0700'" with_items: "{{dir_stats.results}}" - name: Test recursive copy to directory no trailing slash, local_follow=True second time copy: src: subdir dest: "{{ remote_subdir }}" directory_mode: 0700 local_follow: True register: recursive_copy_result - name: Assert that the second copy did not change anything assert: that: - "recursive_copy_result is not changed" - name: Cleanup the recursive copy subdir file: name: "{{ remote_subdir }}" state: absent # # Recursive copy of tricky symlinks # - block: - name: Create a directory to copy from file: path: '{{ local_temp_dir }}/source1' state: directory - name: Create a directory outside of the tree file: path: '{{ local_temp_dir }}/source2' state: directory - name: Create a symlink to a directory outside of the tree file: path: '{{ local_temp_dir }}/source1/link' src: '{{ local_temp_dir }}/source2' state: link - name: Create a circular link back to the tree file: path: '{{ local_temp_dir }}/source2/circle' src: '../source1' state: link - name: Create output directory file: path: '{{ local_temp_dir }}/dest1' state: directory connection: local - name: Recursive copy the source copy: src: '{{ local_temp_dir }}/source1' dest: '{{ remote_dir }}/dest1' local_follow: True register: copy_result - name: Check that the tree link is now a directory stat: path: '{{ remote_dir }}/dest1/source1/link' register: link_result - name: Check that the out of tree link is still a link stat: path: '{{ remote_dir }}/dest1/source1/link/circle' register: circle_result - name: Verify that the recursive copy worked assert: that: - 'copy_result.changed' - 'link_result.stat.isdir' - 'not link_result.stat.islnk' - 'circle_result.stat.islnk' - '"../source1" == circle_result.stat.lnk_target' - name: Recursive copy the source a second time copy: src: '{{ local_temp_dir }}/source1' dest: '{{ remote_dir }}/dest1' local_follow: True register: copy_result - name: Verify that the recursive copy made no changes assert: that: - 'not copy_result.changed' # # Recursive copy with absolute paths (#27439) # - name: Test that remote_dir is appropriate for this test (absolute path) assert: that: - '{{ remote_dir_expanded[0] == "/" }}' - block: - name: Create a directory to copy file: path: '{{ local_temp_dir }}/source_recursive' state: directory - name: Create a file inside of the directory copy: content: "testing" dest: '{{ local_temp_dir }}/source_recursive/file' - name: Create a directory to place the test output in file: path: '{{ local_temp_dir }}/destination' state: directory connection: local - name: Copy the directory and files within (no trailing slash) copy: src: '{{ local_temp_dir }}/source_recursive' dest: '{{ remote_dir }}/destination' - name: Stat the recursively copied directory stat: path: "{{ remote_dir }}/destination/{{ item }}" register: copied_stat with_items: - "source_recursive" - "source_recursive/file" - "file" - debug: var: copied_stat verbosity: 1 - name: Assert with no trailing slash, directory and file is copied assert: that: - "copied_stat.results[0].stat.exists" - "copied_stat.results[1].stat.exists" - "not copied_stat.results[2].stat.exists" - name: Cleanup file: path: '{{ remote_dir }}/destination' state: absent # Try again with no trailing slash - name: Create a directory to place the test output in file: path: '{{ remote_dir }}/destination' state: directory - name: Copy just the files inside of the directory copy: src: '{{ local_temp_dir }}/source_recursive/' dest: '{{ remote_dir }}/destination' - name: Stat the recursively copied directory stat: path: "{{ remote_dir }}/destination/{{ item }}" register: copied_stat with_items: - "source_recursive" - "source_recursive/file" - "file" - debug: var: copied_stat verbosity: 1 - name: Assert with trailing slash, only the file is copied assert: that: - "not copied_stat.results[0].stat.exists" - "not copied_stat.results[1].stat.exists" - "copied_stat.results[2].stat.exists" # # issue 8394 # - name: Create a file with content and a literal multiline block copy: content: | this is the first line this is the second line this line is after an empty line this line is the last line dest: "{{ remote_dir }}/multiline.txt" register: copy_result6 - debug: var: copy_result6 verbosity: 1 - name: Assert the multiline file was created correctly assert: that: - "copy_result6.changed" - "copy_result6.dest == '{{remote_dir_expanded}}/multiline.txt'" - "copy_result6.checksum == '9cd0697c6a9ff6689f0afb9136fa62e0b3fee903'" # test overwriting a file as an unprivileged user (pull request #8624) # this can't be relative to {{remote_dir}} as ~root usually has mode 700 - block: - name: Create world writable directory file: dest: /tmp/worldwritable state: directory mode: 0777 - name: Create world writable file copy: dest: /tmp/worldwritable/file.txt content: "bar" mode: 0666 - name: Overwrite the file as user nobody copy: dest: /tmp/worldwritable/file.txt content: "baz" become: yes become_user: nobody register: copy_result7 - name: Assert the file was overwritten assert: that: - "copy_result7.changed" - "copy_result7.dest == '/tmp/worldwritable/file.txt'" - "copy_result7.checksum == ('baz'|hash('sha1'))" - name: Clean up file: dest: /tmp/worldwritable state: absent remote_user: root # # Follow=True tests # # test overwriting a link using "follow=yes" so that the link # is preserved and the link target is updated - name: Create a test file to symlink to copy: dest: "{{ remote_dir }}/follow_test" content: "this is the follow test file\n" - name: Create a symlink to the test file file: path: "{{ remote_dir }}/follow_link" src: './follow_test' state: link - name: Update the test file using follow=True to preserve the link copy: dest: "{{ remote_dir }}/follow_link" src: foo.txt follow: yes register: replace_follow_result - name: Stat the link path stat: path: "{{ remote_dir }}/follow_link" register: stat_link_result - name: Assert that the link is still a link and contents were changed assert: that: - stat_link_result['stat']['islnk'] - stat_link_result['stat']['lnk_target'] == './follow_test' - replace_follow_result['changed'] - "replace_follow_result['checksum'] == remote_file_hash" # Symlink handling when the dest is already there # https://github.com/ansible/ansible-modules-core/issues/1568 - name: test idempotency by trying to copy to the symlink with the same contents copy: dest: "{{ remote_dir }}/follow_link" src: foo.txt follow: yes register: replace_follow_result - name: Stat the link path stat: path: "{{ remote_dir }}/follow_link" register: stat_link_result - name: Assert that the link is still a link and contents were changed assert: that: - stat_link_result['stat']['islnk'] - stat_link_result['stat']['lnk_target'] == './follow_test' - not replace_follow_result['changed'] - replace_follow_result['checksum'] == remote_file_hash - name: Update the test file using follow=False to overwrite the link copy: dest: '{{ remote_dir }}/follow_link' content: 'modified' follow: False register: copy_results - name: Check the stat results of the file stat: path: '{{remote_dir}}/follow_link' register: stat_results - debug: var: stat_results verbosity: 1 - name: Assert that the file has changed and is not a link assert: that: - "copy_results is changed" - "'content' not in copy_results" - "stat_results.stat.checksum == ('modified'|hash('sha1'))" - "not stat_results.stat.islnk" # test overwriting a link using "follow=yes" so that the link # is preserved and the link target is updated when the thing being copied is a link # # File mode tests # - name: setup directory for test file: state=directory dest={{remote_dir }}/directory mode=0755 - name: set file mode when the destination is a directory copy: src=foo.txt dest={{remote_dir}}/directory/ mode=0705 - name: set file mode when the destination is a directory copy: src=foo.txt dest={{remote_dir}}/directory/ mode=0604 register: file_result - name: check that the file has the correct attributes stat: path={{ remote_dir }}/directory/foo.txt register: file_attrs - assert: that: - "file_attrs.stat.uid == 0" - "file_attrs.stat.pw_name == 'root'" - "file_attrs.stat.mode == '0604'" - name: check that the containing directory did not change attributes stat: path={{ remote_dir }}/directory/ register: dir_attrs - assert: that: - "dir_attrs.stat.mode == '0755'" # # I believe the below section is now covered in the recursive copying section. # Hold on for now as an original test case but delete once confirmed that # everything is passing # # Recursive copying with symlinks tests # - block: - name: Create a test dir to copy file: path: '{{ local_temp_dir }}/top_dir' state: directory - name: Create a test dir to symlink to file: path: '{{ local_temp_dir }}/linked_dir' state: directory - name: Create a file in the test dir copy: dest: '{{ local_temp_dir }}/linked_dir/file1' content: 'hello world' - name: Create a link to the test dir file: path: '{{ local_temp_dir }}/top_dir/follow_link_dir' src: '{{ local_temp_dir }}/linked_dir' state: link - name: Create a circular subdir file: path: '{{ local_temp_dir }}/top_dir/subdir' state: directory ### FIXME: Also add a test for a relative symlink - name: Create a circular symlink file: path: '{{ local_temp_dir }}/top_dir/subdir/circle' src: '{{ local_temp_dir }}/top_dir/' state: link connection: local - name: Copy the directory's link copy: src: '{{ local_temp_dir }}/top_dir' dest: '{{ remote_dir }}/new_dir' local_follow: True - name: Stat the copied path stat: path: '{{ remote_dir }}/new_dir/top_dir/follow_link_dir' register: stat_dir_result - name: Stat the copied file stat: path: '{{ remote_dir }}/new_dir/top_dir/follow_link_dir/file1' register: stat_file_in_dir_result - name: Stat the circular symlink stat: path: '{{ remote_dir }}/new_dir/top_dir/subdir/circle' register: stat_circular_symlink_result - name: Assert that the directory exists assert: that: - stat_dir_result.stat.exists - stat_dir_result.stat.isdir - stat_file_in_dir_result.stat.exists - stat_file_in_dir_result.stat.isreg - stat_circular_symlink_result.stat.exists - stat_circular_symlink_result.stat.islnk # Relative paths in dest: - name: Smoketest that copying content to an implicit relative path works copy: content: 'testing' dest: 'ansible-testing.txt' register: relative_results - name: Assert that copying to an implicit relative path reported changed assert: that: - 'relative_results["changed"]' - 'relative_results["checksum"] == "dc724af18fbdd4e59189f5fe768a5f8311527050"' - name: Test that copying the same content with an implicit relative path reports no change copy: content: 'testing' dest: 'ansible-testing.txt' register: relative_results - name: Assert that copying the same content with an implicit relative path reports no change assert: that: - 'not relative_results["changed"]' - 'relative_results["checksum"] == "dc724af18fbdd4e59189f5fe768a5f8311527050"' - name: Test that copying different content with an implicit relative path reports change copy: content: 'testing2' dest: 'ansible-testing.txt' register: relative_results - name: Assert that copying different content with an implicit relative path reports changed assert: that: - 'relative_results["changed"]' - 'relative_results["checksum"] == "596b29ec9afea9e461a20610d150939b9c399d93"' - name: Smoketest that explicit relative path works copy: content: 'testing' dest: './ansible-testing.txt' register: relative_results - name: Assert that explicit relative paths reports change assert: that: - 'relative_results["changed"]' - 'relative_results["checksum"] == "dc724af18fbdd4e59189f5fe768a5f8311527050"' - name: Cleanup relative path tests file: path: 'ansible-testing.txt' state: absent # src is a file, dest is a non-existent directory (2 levels of directories): # checks that dest is created - include: dest_in_non_existent_directories.yml with_items: - { src: 'foo.txt', dest: 'new_sub_dir1/sub_dir2/', check: 'new_sub_dir1/sub_dir2/foo.txt' } - { src: 'subdir', dest: 'new_sub_dir1/sub_dir2/', check: 'new_sub_dir1/sub_dir2/subdir/bar.txt' } - { src: 'subdir/', dest: 'new_sub_dir1/sub_dir2/', check: 'new_sub_dir1/sub_dir2/bar.txt' } - { src: 'subdir', dest: 'new_sub_dir1/sub_dir2', check: 'new_sub_dir1/sub_dir2/subdir/bar.txt' } - { src: 'subdir/', dest: 'new_sub_dir1/sub_dir2', check: 'new_sub_dir1/sub_dir2/bar.txt' } # src is a file, dest is file in a non-existent directory: checks that a failure occurs - include: src_file_dest_file_in_non_existent_dir.yml with_items: - 'new_sub_dir1/sub_dir2/foo.txt' - 'new_sub_dir1/foo.txt' loop_control: loop_var: 'dest'