ansible/test/integration/targets/copy/tasks/tests.yml
Toshio Kuratomi 7b325baa22 Bkprt recursive copy fix (#40268)
* Fixes #34893 (#40166)

Fixes several bugs exposed in #34893
* Fixes relative path handling in copy so that it splits directories and
  reconstructs the correct file path
* Return failed in the proper circumstances

(cherry picked from commit ca4147f2cc)

* Add changelog for recursive copy fix

(cherry picked from commit cab0f21564)
2018-07-03 11:49:45 -07:00

1455 lines
37 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# test code for the copy module and action plugin
# (c) 2014, Michael DeHaan <michael.dehaan@gmail.com>
# (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 }}'
- debug:
var: copy_result
verbosity: 1
- 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 == ('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: 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"
#
# Recursive copy with relative paths (#34893)
#
- name: Create a directory to copy
file:
path: 'source_recursive'
state: directory
- name: Create a file inside of the directory
copy:
content: "testing"
dest: 'source_recursive/file'
- name: Create a directory to place the test output in
file:
path: 'destination'
state: directory
- name: Copy the directory and files within (no trailing slash)
copy:
src: 'source_recursive'
dest: 'destination'
- name: Stat the recursively copied directory
stat:
path: "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: 'destination'
state: absent
# Try again with no trailing slash
- name: Create a directory to place the test output in
file:
path: 'destination'
state: directory
- name: Copy just the files inside of the directory
copy:
src: 'source_recursive/'
dest: 'destination'
- name: Stat the recursively copied directory
stat:
path: "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"
- name: Cleanup
file:
path: 'destination'
state: absent
- name: Cleanup
file:
path: 'source_recursive'
state: absent
#
# 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'