mirror of https://github.com/ansible/ansible.git
deprecate `_remote_checksum()` and remove use in fetch (#74848)
* Remove use of _remote_checksum from fetch module * Add deprecation message displayed during runtime * Increase test coverage for fetch * Add tests covering the use of stat from the fetch module This required creating an unpriveleged user account and connecting as that user remotely since it is not possible to create a file that the root user cannot stat. * Use fact caching to persist remote tmp dir across playbook runs * Add variables to setup_remote_tmp test role to allow caching of the remote temp dir fact and preventing removal of the remote_tmp_dirpull/75035/head
parent
5a5a1882d4
commit
be0cdc0ea2
@ -0,0 +1,16 @@
|
||||
- name: Cleanup user account
|
||||
hosts: testhost
|
||||
|
||||
tasks:
|
||||
- name: remove test user
|
||||
user:
|
||||
name: fetcher
|
||||
state: absent
|
||||
remove: yes
|
||||
force: yes
|
||||
|
||||
- name: delete temporary directory
|
||||
file:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: absent
|
||||
no_log: yes
|
@ -0,0 +1,8 @@
|
||||
all:
|
||||
hosts:
|
||||
testhost:
|
||||
ansible_host: localhost
|
||||
ansible_connection: ssh
|
||||
ansible_python_interpreter: "{{ ansible_playbook_python }}"
|
||||
ansible_host_key_checking: no
|
||||
ansible_ssh_common_args: -o UserKnownHostsFile={{ output_dir }}/known_hosts -o StrictHostKeyChecking=no
|
@ -0,0 +1 @@
|
||||
skip_cleanup: no
|
@ -0,0 +1,8 @@
|
||||
- name: remove test user
|
||||
user:
|
||||
name: fetcher
|
||||
state: absent
|
||||
remove: yes
|
||||
force: yes
|
||||
become: yes
|
||||
when: not skip_cleanup | bool
|
@ -0,0 +1,53 @@
|
||||
- name: Attempt to fetch a non-existent file - do not fail on missing
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/doesnotexist"
|
||||
dest: "{{ output_dir }}/fetched"
|
||||
fail_on_missing: no
|
||||
register: fetch_missing_nofail
|
||||
|
||||
- name: Attempt to fetch a non-existent file - fail on missing
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/doesnotexist"
|
||||
dest: "{{ output_dir }}/fetched"
|
||||
fail_on_missing: yes
|
||||
register: fetch_missing
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Attempt to fetch a non-existent file - fail on missing implicit
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/doesnotexist"
|
||||
dest: "{{ output_dir }}/fetched"
|
||||
register: fetch_missing_implicit
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Attempt to fetch a directory - should not fail but return a message
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}"
|
||||
dest: "{{ output_dir }}/somedir"
|
||||
fail_on_missing: no
|
||||
register: fetch_dir
|
||||
|
||||
- name: Attempt to fetch a directory - should fail
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}"
|
||||
dest: "{{ output_dir }}/somedir"
|
||||
fail_on_missing: yes
|
||||
register: failed_fetch_dir
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check fetch missing with failure with implicit fail
|
||||
assert:
|
||||
that:
|
||||
- fetch_missing_nofail.msg is search('ignored')
|
||||
- fetch_missing_nofail is not changed
|
||||
- fetch_missing is failed
|
||||
- fetch_missing is not changed
|
||||
- fetch_missing.msg is search ('remote file does not exist')
|
||||
- fetch_missing_implicit is failed
|
||||
- fetch_missing_implicit is not changed
|
||||
- fetch_missing_implicit.msg is search ('remote file does not exist')
|
||||
- fetch_dir is not changed
|
||||
- fetch_dir.msg is search('is a directory')
|
||||
- failed_fetch_dir is failed
|
||||
- failed_fetch_dir is not changed
|
||||
- failed_fetch_dir.msg is search('is a directory')
|
@ -0,0 +1,41 @@
|
||||
- name: Fetch with no parameters
|
||||
fetch:
|
||||
register: fetch_no_params
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Fetch with incorrect source type
|
||||
fetch:
|
||||
src: [1, 2]
|
||||
dest: "{{ output_dir }}/fetched"
|
||||
register: fetch_incorrect_src
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Try to fetch a file inside an inaccessible directory
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/noaccess/file1"
|
||||
dest: "{{ output_dir }}"
|
||||
register: failed_fetch_no_access
|
||||
become: yes
|
||||
become_user: fetcher
|
||||
become_method: su
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Dest is an existing directory name without trailing slash and flat=yes, should fail
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
dest: "{{ output_dir }}"
|
||||
flat: yes
|
||||
register: failed_fetch_dest_dir
|
||||
ignore_errors: true
|
||||
|
||||
- name: Ensure fetch failed
|
||||
assert:
|
||||
that:
|
||||
- fetch_no_params is failed
|
||||
- fetch_no_params.msg is search('src and dest are required')
|
||||
- fetch_incorrect_src is failed
|
||||
- fetch_incorrect_src.msg is search('Invalid type supplied for source')
|
||||
- failed_fetch_no_access is failed
|
||||
- failed_fetch_no_access.msg is search('file is not readable')
|
||||
- failed_fetch_dest_dir is failed
|
||||
- failed_fetch_dest_dir.msg is search('dest is an existing directory')
|
@ -1,141 +1,5 @@
|
||||
# test code for the pip module
|
||||
# (c) 2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: create a file that we can use to fetch
|
||||
copy: content="test" dest={{ remote_tmp_dir }}/orig
|
||||
|
||||
- name: fetch the test file
|
||||
fetch: src={{ remote_tmp_dir }}/orig dest={{ output_dir }}/fetched
|
||||
register: fetched
|
||||
|
||||
- debug: var=fetched
|
||||
|
||||
- name: Assert that we fetched correctly
|
||||
assert:
|
||||
that:
|
||||
- 'fetched["changed"] == True'
|
||||
- 'fetched["checksum"] == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"'
|
||||
- 'fetched["remote_checksum"] == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"'
|
||||
- 'lookup("file", output_dir + "/fetched/" + inventory_hostname + remote_tmp_dir + "/orig") == "test"'
|
||||
|
||||
# TODO: check the become and non-become forms of fetch because in one form we'll do
|
||||
# the get method of the connection plugin and in the become case we'll use the
|
||||
# fetch module.
|
||||
|
||||
- name: fetch a second time to show idempotence
|
||||
fetch: src={{ remote_tmp_dir }}/orig dest={{ output_dir }}/fetched
|
||||
register: fetched
|
||||
|
||||
- name: Assert that the file was not fetched the second time
|
||||
assert:
|
||||
that:
|
||||
- 'fetched["changed"] == False'
|
||||
- 'fetched["checksum"] == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"'
|
||||
|
||||
- name: attempt to fetch a non-existent file - do not fail on missing
|
||||
fetch: src={{ remote_tmp_dir }}/doesnotexist dest={{ output_dir }}/fetched fail_on_missing=False
|
||||
register: fetch_missing_nofail
|
||||
|
||||
- name: check fetch missing no fail result
|
||||
assert:
|
||||
that:
|
||||
- "fetch_missing_nofail.msg"
|
||||
- "fetch_missing_nofail is not changed"
|
||||
|
||||
- name: attempt to fetch a non-existent file - fail on missing
|
||||
fetch: src={{ remote_tmp_dir }}/doesnotexist dest={{ output_dir }}/fetched fail_on_missing=yes
|
||||
register: fetch_missing
|
||||
ignore_errors: true
|
||||
|
||||
- name: check fetch missing with failure
|
||||
assert:
|
||||
that:
|
||||
- "fetch_missing is failed"
|
||||
- "fetch_missing.msg"
|
||||
- "fetch_missing is not changed"
|
||||
|
||||
- name: attempt to fetch a non-existent file - fail on missing implicit
|
||||
fetch: src={{ remote_tmp_dir }}/doesnotexist dest={{ output_dir }}/fetched
|
||||
register: fetch_missing_implicit
|
||||
ignore_errors: true
|
||||
|
||||
- name: check fetch missing with failure with implicit fail
|
||||
assert:
|
||||
that:
|
||||
- "fetch_missing_implicit is failed"
|
||||
- "fetch_missing_implicit.msg"
|
||||
- "fetch_missing_implicit is not changed"
|
||||
|
||||
- name: attempt to fetch a directory - should not fail but return a message
|
||||
fetch: src={{ remote_tmp_dir }} dest={{ output_dir }}/somedir fail_on_missing=False
|
||||
register: fetch_dir
|
||||
|
||||
- name: check fetch directory result
|
||||
assert:
|
||||
that:
|
||||
- "fetch_dir is not changed"
|
||||
- "fetch_dir.msg"
|
||||
|
||||
- name: attempt to fetch a directory - should fail
|
||||
fetch: src={{ remote_tmp_dir }} dest={{ output_dir }}/somedir fail_on_missing=True
|
||||
register: failed_fetch_dir
|
||||
ignore_errors: true
|
||||
|
||||
- name: check fetch directory result
|
||||
assert:
|
||||
that:
|
||||
- "failed_fetch_dir is failed"
|
||||
- "fetch_dir.msg"
|
||||
|
||||
- name: create symlink to a file that we can fetch
|
||||
file:
|
||||
path: "{{ remote_tmp_dir }}/link"
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
state: "link"
|
||||
|
||||
- name: fetch the file via a symlink
|
||||
fetch: src={{ remote_tmp_dir }}/link dest={{ output_dir }}/fetched-link
|
||||
register: fetched
|
||||
|
||||
- debug: var=fetched
|
||||
|
||||
- name: Assert that we fetched correctly
|
||||
assert:
|
||||
that:
|
||||
- 'fetched["changed"] == True'
|
||||
- 'fetched["checksum"] == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"'
|
||||
- 'fetched["remote_checksum"] == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"'
|
||||
- 'lookup("file", output_dir + "/fetched-link/" + inventory_hostname + remote_tmp_dir + "/link") == "test"'
|
||||
|
||||
# TODO: check the become and non-become forms of fetch because in one form we'll do
|
||||
# the get method of the connection plugin and in the become case we'll use the
|
||||
# fetch module.
|
||||
|
||||
- name: dest is an existing directory name without trailing slash and flat=yes, should fail
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
dest: "{{ output_dir }}"
|
||||
flat: yes
|
||||
register: failed_fetch_dest_dir
|
||||
ignore_errors: true
|
||||
|
||||
- name: check that it indeed failed
|
||||
assert:
|
||||
that:
|
||||
- "failed_fetch_dest_dir is failed"
|
||||
- "failed_fetch_dest_dir.msg"
|
||||
- import_tasks: setup.yml
|
||||
- import_tasks: normal.yml
|
||||
- import_tasks: symlink.yml
|
||||
- import_tasks: fail_on_missing.yml
|
||||
- import_tasks: failures.yml
|
||||
|
@ -0,0 +1,38 @@
|
||||
- name: Fetch the test file
|
||||
fetch: src={{ remote_tmp_dir }}/orig dest={{ output_dir }}/fetched
|
||||
register: fetched
|
||||
|
||||
- name: Fetch a second time to show no changes
|
||||
fetch: src={{ remote_tmp_dir }}/orig dest={{ output_dir }}/fetched
|
||||
register: fetched_again
|
||||
|
||||
- name: Fetch the test file in check mode
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
dest: "{{ output_dir }}/fetched"
|
||||
check_mode: yes
|
||||
register: fetch_check_mode
|
||||
|
||||
- name: Fetch with dest ending in path sep
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
dest: "{{ output_dir }}/"
|
||||
flat: yes
|
||||
|
||||
- name: Fetch with dest with relative path
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
dest: "{{ output_dir[1:] }}"
|
||||
flat: yes
|
||||
|
||||
- name: Assert that we fetched correctly
|
||||
assert:
|
||||
that:
|
||||
- fetched is changed
|
||||
- fetched.checksum == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
|
||||
- fetched_again is not changed
|
||||
- fetched_again.checksum == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
|
||||
- fetched.remote_checksum == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
|
||||
- lookup("file", output_dir + "/fetched/" + inventory_hostname + remote_tmp_dir + "/orig") == "test"
|
||||
- fetch_check_mode is skipped
|
||||
- fetch_check_mode.msg is search('not \(yet\) supported')
|
@ -0,0 +1,38 @@
|
||||
- name: Include system specific variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_facts.system }}.yml"
|
||||
- default.yml
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
|
||||
- name: Create test user
|
||||
user:
|
||||
name: fetcher
|
||||
create_home: yes
|
||||
groups: "{{ _fetch_additional_groups | default(omit) }}"
|
||||
append: "{{ True if _fetch_additional_groups else False }}"
|
||||
become: yes
|
||||
notify:
|
||||
- remove test user
|
||||
|
||||
- name: Create a file that we can use to fetch
|
||||
copy:
|
||||
content: "test"
|
||||
dest: "{{ remote_tmp_dir }}/orig"
|
||||
|
||||
- name: Create symlink to a file that we can fetch
|
||||
file:
|
||||
path: "{{ remote_tmp_dir }}/link"
|
||||
src: "{{ remote_tmp_dir }}/orig"
|
||||
state: "link"
|
||||
|
||||
- name: Create an inaccessible directory
|
||||
file:
|
||||
path: "{{ remote_tmp_dir }}/noaccess"
|
||||
state: directory
|
||||
mode: '0600'
|
||||
owner: root
|
||||
become: yes
|
@ -0,0 +1,13 @@
|
||||
- name: Fetch the file via a symlink
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/link"
|
||||
dest: "{{ output_dir }}/fetched-link"
|
||||
register: fetched_symlink
|
||||
|
||||
- name: Assert that we fetched correctly
|
||||
assert:
|
||||
that:
|
||||
- fetched_symlink is changed
|
||||
- fetched_symlink.checksum == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
|
||||
- fetched_symlink.remote_checksum == "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
|
||||
- 'lookup("file", output_dir + "/fetched-link/" + inventory_hostname + remote_tmp_dir + "/link") == "test"'
|
@ -0,0 +1,3 @@
|
||||
# macOS requires users to be in an additional group for ssh access
|
||||
|
||||
_fetch_additional_groups: com.apple.access_ssh
|
@ -0,0 +1 @@
|
||||
_fetch_additional_groups: []
|
@ -0,0 +1,39 @@
|
||||
- name: Create a user account and configure ssh access
|
||||
hosts: testhost
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- import_role:
|
||||
name: fetch_tests
|
||||
tasks_from: setup.yml
|
||||
vars:
|
||||
# Keep the remote temp dir and cache the remote_tmp_dir fact. The directory itself
|
||||
# and the fact that contains the path are needed in a separate ansible-playbook run.
|
||||
setup_remote_tmp_dir_skip_cleanup: yes
|
||||
setup_remote_tmp_dir_cache_path: yes
|
||||
|
||||
# This prevents ssh access. It is fixed in some container images but not all.
|
||||
# https://github.com/ansible/distro-test-containers/pull/70
|
||||
- name: Remove /run/nologin
|
||||
file:
|
||||
path: /run/nologin
|
||||
state: absent
|
||||
|
||||
# Setup ssh access for the unprivileged user.
|
||||
- name: Get home directory for temporary user
|
||||
command: echo ~fetcher
|
||||
register: fetcher_home
|
||||
|
||||
- name: Create .ssh dir
|
||||
file:
|
||||
path: "{{ fetcher_home.stdout }}/.ssh"
|
||||
state: directory
|
||||
owner: fetcher
|
||||
mode: '0700'
|
||||
|
||||
- name: Configure authorized_keys
|
||||
copy:
|
||||
src: "~root/.ssh/authorized_keys"
|
||||
dest: "{{ fetcher_home.stdout }}/.ssh/authorized_keys"
|
||||
owner: fetcher
|
||||
mode: '0600'
|
@ -0,0 +1,27 @@
|
||||
# This playbook needs to be run as a non-root user without become. Under
|
||||
# those circumstances, the fetch module uses stat and not slurp.
|
||||
|
||||
- name: Test unreadable file using stat
|
||||
hosts: testhost
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: Try to fetch a file inside an inaccessible directory
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/noaccess/file1"
|
||||
dest: "{{ output_dir }}"
|
||||
register: failed_fetch_no_access
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Try to fetch a file inside an inaccessible directory without fail_on_missing
|
||||
fetch:
|
||||
src: "{{ remote_tmp_dir }}/noaccess/file1"
|
||||
dest: "{{ output_dir }}"
|
||||
fail_on_missing: no
|
||||
register: failed_fetch_no_access_fail_on_missing
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- failed_fetch_no_access is failed
|
||||
- failed_fetch_no_access.msg is search('Permission denied')
|
||||
- failed_fetch_no_access_fail_on_missing.msg is search(', ignored')
|
@ -0,0 +1,2 @@
|
||||
setup_remote_tmp_dir_skip_cleanup: no
|
||||
setup_remote_tmp_dir_cache_path: no
|
@ -1,5 +1,7 @@
|
||||
- name: delete temporary directory
|
||||
include_tasks: default-cleanup.yml
|
||||
when: not setup_remote_tmp_dir_skip_cleanup | bool
|
||||
|
||||
- name: delete temporary directory (windows)
|
||||
include_tasks: windows-cleanup.yml
|
||||
when: not setup_remote_tmp_dir_skip_cleanup | bool
|
||||
|
Loading…
Reference in New Issue