summaryrefslogtreecommitdiffstats
path: root/test/integration/targets/fetch
diff options
context:
space:
mode:
Diffstat (limited to 'test/integration/targets/fetch')
-rw-r--r--test/integration/targets/fetch/aliases3
-rw-r--r--test/integration/targets/fetch/cleanup.yml16
-rw-r--r--test/integration/targets/fetch/injection/avoid_slurp_return.yml26
-rw-r--r--test/integration/targets/fetch/injection/here.txt1
-rw-r--r--test/integration/targets/fetch/injection/library/slurp.py29
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/defaults/main.yml1
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/handlers/main.yml8
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/meta/main.yml2
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/fail_on_missing.yml53
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml41
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/main.yml5
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/normal.yml38
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/setup.yml46
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/tasks/symlink.yml13
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/vars/Darwin.yml4
-rw-r--r--test/integration/targets/fetch/roles/fetch_tests/vars/default.yml1
-rw-r--r--test/integration/targets/fetch/run_fetch_tests.yml5
-rwxr-xr-xtest/integration/targets/fetch/runme.sh34
-rw-r--r--test/integration/targets/fetch/setup_unreadable_test.yml40
-rw-r--r--test/integration/targets/fetch/test_unreadable_with_stat.yml36
20 files changed, 402 insertions, 0 deletions
diff --git a/test/integration/targets/fetch/aliases b/test/integration/targets/fetch/aliases
new file mode 100644
index 0000000..ff56593
--- /dev/null
+++ b/test/integration/targets/fetch/aliases
@@ -0,0 +1,3 @@
+shippable/posix/group2
+needs/target/setup_remote_tmp_dir
+needs/ssh
diff --git a/test/integration/targets/fetch/cleanup.yml b/test/integration/targets/fetch/cleanup.yml
new file mode 100644
index 0000000..792b603
--- /dev/null
+++ b/test/integration/targets/fetch/cleanup.yml
@@ -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
diff --git a/test/integration/targets/fetch/injection/avoid_slurp_return.yml b/test/integration/targets/fetch/injection/avoid_slurp_return.yml
new file mode 100644
index 0000000..af62dcf
--- /dev/null
+++ b/test/integration/targets/fetch/injection/avoid_slurp_return.yml
@@ -0,0 +1,26 @@
+- name: ensure that 'fake slurp' does not poison fetch source
+ hosts: localhost
+ gather_facts: False
+ tasks:
+ - name: fetch with relative source path
+ fetch: src=../injection/here.txt dest={{output_dir}}
+ become: true
+ register: islurp
+
+ - name: fetch with normal source path
+ fetch: src=here.txt dest={{output_dir}}
+ become: true
+ register: islurp2
+
+ - name: ensure all is good in hollywood
+ assert:
+ that:
+ - "'..' not in islurp['dest']"
+ - "'..' not in islurp2['dest']"
+ - "'foo' not in islurp['dest']"
+ - "'foo' not in islurp2['dest']"
+
+ - name: try to trip dest anyways
+ fetch: src=../injection/here.txt dest={{output_dir}}
+ become: true
+ register: islurp2
diff --git a/test/integration/targets/fetch/injection/here.txt b/test/integration/targets/fetch/injection/here.txt
new file mode 100644
index 0000000..493021b
--- /dev/null
+++ b/test/integration/targets/fetch/injection/here.txt
@@ -0,0 +1 @@
+this is a test file
diff --git a/test/integration/targets/fetch/injection/library/slurp.py b/test/integration/targets/fetch/injection/library/slurp.py
new file mode 100644
index 0000000..7b78ba1
--- /dev/null
+++ b/test/integration/targets/fetch/injection/library/slurp.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+ module: fakeslurp
+ short_desciptoin: fake slurp module
+ description:
+ - this is a fake slurp module
+ options:
+ _notreal:
+ description: really not a real slurp
+ author:
+ - me
+"""
+
+import json
+import random
+
+bad_responses = ['../foo', '../../foo', '../../../foo', '/../../../foo', '/../foo', '//..//foo', '..//..//foo']
+
+
+def main():
+ print(json.dumps(dict(changed=False, content='', encoding='base64', source=random.choice(bad_responses))))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/fetch/roles/fetch_tests/defaults/main.yml b/test/integration/targets/fetch/roles/fetch_tests/defaults/main.yml
new file mode 100644
index 0000000..f0b9cfc
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/defaults/main.yml
@@ -0,0 +1 @@
+skip_cleanup: no
diff --git a/test/integration/targets/fetch/roles/fetch_tests/handlers/main.yml b/test/integration/targets/fetch/roles/fetch_tests/handlers/main.yml
new file mode 100644
index 0000000..c6c296a
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/handlers/main.yml
@@ -0,0 +1,8 @@
+- name: remove test user
+ user:
+ name: fetcher
+ state: absent
+ remove: yes
+ force: yes
+ become: yes
+ when: not skip_cleanup | bool
diff --git a/test/integration/targets/fetch/roles/fetch_tests/meta/main.yml b/test/integration/targets/fetch/roles/fetch_tests/meta/main.yml
new file mode 100644
index 0000000..1810d4b
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_remote_tmp_dir
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/fail_on_missing.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/fail_on_missing.yml
new file mode 100644
index 0000000..d918aae
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/fail_on_missing.yml
@@ -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')
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml
new file mode 100644
index 0000000..8a6b5b7
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/failures.yml
@@ -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')
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/main.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/main.yml
new file mode 100644
index 0000000..eefe95c
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/main.yml
@@ -0,0 +1,5 @@
+- import_tasks: setup.yml
+- import_tasks: normal.yml
+- import_tasks: symlink.yml
+- import_tasks: fail_on_missing.yml
+- import_tasks: failures.yml
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/normal.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/normal.yml
new file mode 100644
index 0000000..6f3ab62
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/normal.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')
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/setup.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/setup.yml
new file mode 100644
index 0000000..83b6093
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/setup.yml
@@ -0,0 +1,46 @@
+- 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: Work-around for locked users on Alpine
+ # see https://github.com/ansible/ansible/issues/68676
+ set_fact:
+ password: '*'
+ when: ansible_distribution == 'Alpine'
+
+- name: Create test user
+ user:
+ name: fetcher
+ create_home: yes
+ group: "{{ _fetch_group | default(omit) }}"
+ groups: "{{ _fetch_additional_groups | default(omit) }}"
+ append: "{{ True if _fetch_additional_groups else False }}"
+ password: "{{ password | default(omit) }}"
+ 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
diff --git a/test/integration/targets/fetch/roles/fetch_tests/tasks/symlink.yml b/test/integration/targets/fetch/roles/fetch_tests/tasks/symlink.yml
new file mode 100644
index 0000000..41d7b35
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/tasks/symlink.yml
@@ -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"'
diff --git a/test/integration/targets/fetch/roles/fetch_tests/vars/Darwin.yml b/test/integration/targets/fetch/roles/fetch_tests/vars/Darwin.yml
new file mode 100644
index 0000000..46fe3af
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/vars/Darwin.yml
@@ -0,0 +1,4 @@
+# macOS requires users to be in an additional group for ssh access
+
+_fetch_group: staff
+_fetch_additional_groups: com.apple.access_ssh
diff --git a/test/integration/targets/fetch/roles/fetch_tests/vars/default.yml b/test/integration/targets/fetch/roles/fetch_tests/vars/default.yml
new file mode 100644
index 0000000..69d7958
--- /dev/null
+++ b/test/integration/targets/fetch/roles/fetch_tests/vars/default.yml
@@ -0,0 +1 @@
+_fetch_additional_groups: []
diff --git a/test/integration/targets/fetch/run_fetch_tests.yml b/test/integration/targets/fetch/run_fetch_tests.yml
new file mode 100644
index 0000000..f2ff1df
--- /dev/null
+++ b/test/integration/targets/fetch/run_fetch_tests.yml
@@ -0,0 +1,5 @@
+- name: call fetch_tests role
+ hosts: testhost
+ gather_facts: false
+ roles:
+ - fetch_tests
diff --git a/test/integration/targets/fetch/runme.sh b/test/integration/targets/fetch/runme.sh
new file mode 100755
index 0000000..a508a0a
--- /dev/null
+++ b/test/integration/targets/fetch/runme.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+set -eux
+
+function cleanup {
+ ansible-playbook -i "${INVENTORY_PATH}" cleanup.yml -e "output_dir=${OUTPUT_DIR}" -b "$@"
+ unset ANSIBLE_CACHE_PLUGIN
+ unset ANSIBLE_CACHE_PLUGIN_CONNECTION
+}
+
+trap 'cleanup "$@"' EXIT
+
+# setup required roles
+ln -s ../../setup_remote_tmp_dir roles/setup_remote_tmp_dir
+
+# run old type role tests
+ansible-playbook -i ../../inventory run_fetch_tests.yml -e "output_dir=${OUTPUT_DIR}" "$@"
+
+# run same test with become
+ansible-playbook -i ../../inventory run_fetch_tests.yml -e "output_dir=${OUTPUT_DIR}" -b "$@"
+
+# run tests to avoid path injection from slurp when fetch uses become
+ansible-playbook -i ../../inventory injection/avoid_slurp_return.yml -e "output_dir=${OUTPUT_DIR}" "$@"
+
+## Test unreadable file with stat. Requires running without become and as a user other than root.
+#
+# Change the known_hosts file to avoid changing the test environment
+export ANSIBLE_CACHE_PLUGIN=jsonfile
+export ANSIBLE_CACHE_PLUGIN_CONNECTION="${OUTPUT_DIR}/cache"
+# Create a non-root user account and configure SSH acccess for that account
+ansible-playbook -i "${INVENTORY_PATH}" setup_unreadable_test.yml -e "output_dir=${OUTPUT_DIR}" "$@"
+
+# Run the tests as the unprivileged user without become to test the use of the stat module from the fetch module
+ansible-playbook -i "${INVENTORY_PATH}" test_unreadable_with_stat.yml -e ansible_user=fetcher -e ansible_become=no -e "output_dir=${OUTPUT_DIR}" "$@"
diff --git a/test/integration/targets/fetch/setup_unreadable_test.yml b/test/integration/targets/fetch/setup_unreadable_test.yml
new file mode 100644
index 0000000..f4cc8c1
--- /dev/null
+++ b/test/integration/targets/fetch/setup_unreadable_test.yml
@@ -0,0 +1,40 @@
+- 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
+ skip_cleanup: 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'
diff --git a/test/integration/targets/fetch/test_unreadable_with_stat.yml b/test/integration/targets/fetch/test_unreadable_with_stat.yml
new file mode 100644
index 0000000..c8a0145
--- /dev/null
+++ b/test/integration/targets/fetch/test_unreadable_with_stat.yml
@@ -0,0 +1,36 @@
+# 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: Check connectivity
+ command: whoami
+ register: whoami
+
+ - name: Verify user
+ assert:
+ that:
+ - whoami.stdout == 'fetcher'
+
+ - 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')