# 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 ) # - set_fact: output_dir_expanded: '{{ output_dir | expanduser }}' - name: record the output directory set_fact: output_file={{output_dir}}/foo.txt - name: locate sha1sum/shasum shell: which sha1sum || which shasum register: sha1sum - name: initiate a basic copy, and also test the mode copy: src=foo.txt dest={{output_file}} mode=0444 register: copy_result - name: check the mode of the output file file: name={{output_file}} state=file register: file_result_check - name: assert the mode is correct assert: that: - "file_result_check.mode == '0444'" #- debug: # var: copy_result - name: assert basic copy worked assert: that: - "'changed' in copy_result" - "'dest' in copy_result" - "'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 == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6'" - name: verify that the legacy md5sum is correct assert: that: - "copy_result.md5sum == 'c47397529fe81ab62ba3f85e9f4c71f2'" when: ansible_fips|bool != True - name: check the stat results of the file stat: path={{output_file}} register: stat_results #- debug: var=stat_results - 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 == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6'" - name: verify that the legacy md5sum is correct assert: that: - "stat_results.stat.md5 == 'c47397529fe81ab62ba3f85e9f4c71f2'" when: ansible_fips|bool != True - name: overwrite the file via same means copy: src=foo.txt dest={{output_file}} register: copy_result2 - name: assert that the file was not changed assert: that: - "not copy_result2|changed" - name: overwrite the file using the content system copy: content="modified" dest={{output_file}} register: copy_result3 - name: check the stat results of the file stat: path={{output_file}} register: stat_results #- debug: var=stat_results - name: assert that the file has changed assert: that: - "copy_result3|changed" - "'content' not in copy_result3" - "stat_results.stat.checksum == '99db324742823c55d975b605e1fc22f4253a9b7d'" - "stat_results.stat.mode != '0700'" - name: overwrite the file again using the content system, also passing along file params copy: content="modified" dest={{output_file}} mode=0700 register: copy_result4 - name: check the stat results of the file stat: path={{output_file}} register: stat_results #- debug: var=stat_results - name: assert that the file has changed assert: that: - "copy_result3|changed" - "'content' not in copy_result3" - "stat_results.stat.checksum == '99db324742823c55d975b605e1fc22f4253a9b7d'" - "stat_results.stat.mode == '0700'" - name: try invalid copy input location fails copy: src=invalid_file_location_does_not_exist dest={{output_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: Clean up file: path: "{{ output_file }}" state: absent - name: Copy source file to destination directory with mode copy: src: foo.txt dest: "{{ output_dir }}" mode: 0500 register: copy_results - name: check the stat results of the file stat: path: '{{ output_file }}' register: stat_results #- debug: var=stat_results - name: assert that the file has changed assert: that: - "copy_results|changed" - "stat_results.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6'" - "stat_results.stat.mode == '0500'" # Test copy with mode=preserve - name: Set file perms to an odd value file: path: '{{ output_file }}' mode: 0547 - name: Copy with mode=preserve copy: src: '{{ output_file }}' dest: '{{ output_dir }}/copy-foo.txt' mode: preserve register: copy_results - name: check the stat results of the file stat: path: '{{ output_dir }}/copy-foo.txt' register: stat_results - name: assert that the file has changed and has correct mode assert: that: - "copy_results|changed" - "copy_results.mode == '0547'" - "stat_results.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6'" - "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 - name: set the output subdirectory set_fact: output_subdir={{output_dir}}/sub - name: make an output subdirectory file: name={{output_subdir}} state=directory - name: setup link target for absolute link copy: dest=/tmp/ansible-test-abs-link content=target - name: setup link target dir for absolute link file: dest=/tmp/ansible-test-abs-link-dir state=directory - name: test recursive copy to directory no trailing slash, local_follow=False copy: src=subdir dest={{output_subdir}} directory_mode=0700 local_follow=False register: recursive_copy_result #- debug: var=recursive_copy_result - name: assert that the recursive copy did something assert: that: - "recursive_copy_result|changed" - name: check that a file in a directory was transferred stat: path={{output_dir}}/sub/subdir/bar.txt register: stat_bar - name: check that a file in a deeper directory was transferred stat: path={{output_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={{output_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: '{{ output_dir }}/sub/subdir/subdir1/ansible-test-abs-link' register: stat_abs_link - name: check symlink to relative path stat: path: '{{ output_dir }}/sub/subdir/subdir1/bar.txt' register: stat_relative_link - name: check symlink to self stat: path: '{{ output_dir }}/sub/subdir/subdir1/invalid' register: stat_self_link - name: check symlink to nonexistent file stat: path: '{{ output_dir }}/sub/subdir/subdir1/invalid2' register: stat_invalid_link - name: check symlink to directory in copy stat: path: '{{ output_dir }}/sub/subdir/subdir1/subdir3' register: stat_dir_in_copy_link - name: check symlink to directory outside of copy stat: path: '{{ output_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={{output_dir}}/sub/{{item}} register: dir_stats with_items: - "subdir" - "subdir/subdira" - "subdir/subdir1" - "subdir/subdir2" - "subdir/subdir2/subdir3" - "subdir/subdir2/subdir3/subdir4" #- debug: var=dir_stats - name: assert recursive copied directories mode assert: that: - "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={{output_subdir}} directory_mode=0700 local_follow=False register: recursive_copy_result - name: assert that the second copy did not change anything assert: that: - "not recursive_copy_result|changed" - name: cleanup the recursive copy subdir file: name={{output_subdir}} state=absent # # Recursive copy with local_follow=False, trailing slash # - name: set the output subdirectory set_fact: output_subdir={{output_dir}}/sub - name: make an output subdirectory file: name={{output_subdir}} state=directory - name: setup link target for absolute link copy: dest=/tmp/ansible-test-abs-link content=target - name: setup link target dir for absolute link file: dest=/tmp/ansible-test-abs-link-dir state=directory - name: test recursive copy to directory trailing slash, local_follow=False copy: src=subdir/ dest={{output_subdir}} directory_mode=0700 local_follow=False register: recursive_copy_result #- debug: var=recursive_copy_result - name: assert that the recursive copy did something assert: that: - "recursive_copy_result|changed" - name: check that a file in a directory was transferred stat: path={{output_dir}}/sub/bar.txt register: stat_bar - name: check that a file in a deeper directory was transferred stat: path={{output_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={{output_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: '{{ output_dir }}/sub/subdir1/ansible-test-abs-link' register: stat_abs_link - name: check symlink to relative path stat: path: '{{ output_dir }}/sub/subdir1/bar.txt' register: stat_relative_link - name: check symlink to self stat: path: '{{ output_dir }}/sub/subdir1/invalid' register: stat_self_link - name: check symlink to nonexistent file stat: path: '{{ output_dir }}/sub/subdir1/invalid2' register: stat_invalid_link - name: check symlink to directory in copy stat: path: '{{ output_dir }}/sub/subdir1/subdir3' register: stat_dir_in_copy_link - name: check symlink to directory outside of copy stat: path: '{{ output_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={{output_dir}}/sub/{{item}} register: dir_stats with_items: - "subdira" - "subdir1" - "subdir2" - "subdir2/subdir3" - "subdir2/subdir3/subdir4" #- debug: var=dir_stats - name: assert recursive copied directories mode 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={{output_subdir}} directory_mode=0700 local_follow=False register: recursive_copy_result - name: assert that the second copy did not change anything assert: that: - "not recursive_copy_result|changed" - name: cleanup the recursive copy subdir file: name={{output_subdir}} state=absent # # test recursive copy local_follow=True, no trailing slash # - name: set the output subdirectory set_fact: output_subdir={{output_dir}}/sub - name: make an output subdirectory file: name={{output_subdir}} state=directory - name: setup link target for absolute link copy: dest=/tmp/ansible-test-abs-link content=target - name: setup link target dir for absolute link file: dest=/tmp/ansible-test-abs-link-dir state=directory - name: test recursive copy to directory no trailing slash, local_follow=True copy: src=subdir dest={{output_subdir}} directory_mode=0700 local_follow=True register: recursive_copy_result #- debug: var=recursive_copy_result - name: assert that the recursive copy did something assert: that: - "recursive_copy_result|changed" - name: check that a file in a directory was transferred stat: path={{output_dir}}/sub/subdir/bar.txt register: stat_bar - name: check that a file in a deeper directory was transferred stat: path={{output_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={{output_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={{output_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: '{{ output_dir }}/sub/subdir/subdir1/ansible-test-abs-link' register: stat_abs_link - name: check symlink to relative path stat: path: '{{ output_dir }}/sub/subdir/subdir1/bar.txt' register: stat_relative_link - name: check symlink to self stat: path: '{{ output_dir }}/sub/subdir/subdir1/invalid' register: stat_self_link - name: check symlink to nonexistent file stat: path: '{{ output_dir }}/sub/subdir/subdir1/invalid2' register: stat_invalid_link - name: check symlink to directory in copy stat: path: '{{ output_dir }}/sub/subdir/subdir1/subdir3' register: stat_dir_in_copy_link - name: check symlink to directory outside of copy stat: path: '{{ output_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 == '0e8a3ad980ec179856012b7eecf4327e99cd44cd'" - "stat_relative_link.stat.exists" - "not stat_relative_link.stat.islnk" - "stat_relative_link.stat.checksum == '6eadeac2dade6347e87c0d24fd455feffa7069f0'" - "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={{output_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 - name: assert recursive copied directories mode 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={{output_subdir}} directory_mode=0700 local_follow=True register: recursive_copy_result - name: assert that the second copy did not change anything assert: that: - "not recursive_copy_result|changed" - name: cleanup the recursive copy subdir file: name={{output_subdir}} state=absent # # Recursive copy of tricky symlinks # - name: Create a directory to copy from file: path: '{{ output_dir }}/source1' state: directory - name: Create a directory outside of the tree file: path: '{{ output_dir }}/source2' state: directory - name: Create a symlink to a directory outside of the tree file: path: '{{ output_dir }}/source1/link' src: '{{ output_dir }}/source2' state: link - name: Create a circular link back to the tree file: path: '{{ output_dir }}/source2/circle' src: '../source1' state: link - name: Create output directory file: path: '{{ output_dir }}/dest1' state: directory - name: Recursive copy the source copy: src: '{{ output_dir }}/source1' dest: '{{ output_dir }}/dest1' local_follow: True register: copy_result - name: Check that the tree link is now a directory stat: path: '{{ output_dir }}/dest1/source1/link' register: link_result - name: Check that the out of tree link is still a link stat: path: '{{ output_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: '{{ output_dir }}/source1' dest: '{{ output_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 output_dir is appropriate for this test (absolute path) assert: that: - '{{ output_dir_expanded[0] == "/" }}' - name: create a directory to copy file: path: '{{ output_dir_expanded }}/source_recursive' state: directory - name: create a file inside of the directory copy: content: "testing" dest: '{{ output_dir_expanded }}/source_recursive/file' - name: Create a directory to place the test output in file: path: '{{ output_dir_expanded }}/destination' state: directory - name: Copy the directory and files within (no trailing slash) copy: src: '{{ output_dir_expanded }}/source_recursive' dest: '{{ output_dir_expanded }}/destination' - name: stat the recursively copied directory stat: path={{output_dir}}/destination/{{item}} register: copied_stat with_items: - "source_recursive" - "source_recursive/file" - "file" #- debug: var=copied_stat - 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: '{{ output_dir_expanded }}/destination' state: absent # Try again with no trailing slash - name: Create a directory to place the test output in file: path: '{{ output_dir_expanded }}/destination' state: directory - name: Copy just the files inside of the directory copy: src: '{{ output_dir_expanded }}/source_recursive/' dest: '{{ output_dir_expanded }}/destination' - name: stat the recursively copied directory stat: path={{output_dir_expanded}}/destination/{{item}} register: copied_stat with_items: - "source_recursive" - "source_recursive/file" - "file" #- debug: var=copied_stat - 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={{output_dir}}/multiline.txt register: copy_result6 #- debug: var=copy_result6 - name: assert the multiline file was created correctly assert: that: - "copy_result6.changed" - "copy_result6.dest == '{{output_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 {{output_dir}} as ~root usually has mode 700 - 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 == 'bbe960a25ea311d21d40669e93df2003ba9b90a2'" - name: clean up file: dest=/tmp/worldwritable state=absent # 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={{output_dir}}/follow_test content="this is the follow test file\n" - name: create a symlink to the test file file: path={{output_dir}}/follow_link src='./follow_test' state=link - name: update the test file using follow=True to preserve the link copy: dest={{output_dir}}/follow_link content="this is the new content\n" follow=yes register: replace_follow_result - name: stat the link path stat: path={{output_dir}}/follow_link register: stat_link_result - name: assert that the link is still a link assert: that: - stat_link_result.stat.islnk - name: get the checksum of the link target shell: "{{ sha1sum.stdout }} {{output_dir}}/follow_test | cut -f1 -sd ' '" register: target_file_result - name: assert that the link target was updated assert: that: - replace_follow_result.checksum == target_file_result.stdout - name: update the test file using follow=False to overwrite the link copy: dest: '{{ output_dir }}/follow_link' content: 'modified' follow: False register: copy_results - name: check the stat results of the file stat: path: '{{output_dir}}/follow_link' register: stat_results #- debug: var=stat_results - name: assert that the file has changed and is not a link assert: that: - "copy_results|changed" - "'content' not in copy_results" - "stat_results.stat.checksum == '99db324742823c55d975b605e1fc22f4253a9b7d'" - "not stat_results.stat.islnk" # # 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 # - name: create a test dir to copy file: path: '{{ output_dir }}/top_dir' state: directory - name: create a test dir to symlink to file: path: '{{ output_dir }}/linked_dir' state: directory - name: create a file in the test dir copy: dest: '{{ output_dir }}/linked_dir/file1' content: 'hello world' - name: create a link to the test dir file: path: '{{ output_dir }}/top_dir/follow_link_dir' src: '{{ output_dir }}/linked_dir' state: link - name: create a circular subdir file: path: '{{ output_dir }}/top_dir/subdir' state: directory ### FIXME: Also add a test for a relative symlink - name: create a circular symlink file: path: '{{ output_dir }}/top_dir/subdir/circle' src: '{{ output_dir }}/top_dir/' state: link - name: copy the directory's link copy: src: '{{ output_dir }}/top_dir' dest: '{{ output_dir }}/new_dir' local_follow: True - name: stat the copied path stat: path: '{{ output_dir }}/new_dir/top_dir/follow_link_dir' register: stat_dir_result - name: stat the copied file stat: path: '{{ output_dir }}/new_dir/top_dir/follow_link_dir/file1' register: stat_file_in_dir_result - name: stat the circular symlink stat: path: '{{ output_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