diff options
Diffstat (limited to '')
44 files changed, 840 insertions, 0 deletions
diff --git a/test/integration/targets/connection/aliases b/test/integration/targets/connection/aliases new file mode 100644 index 0000000..136c05e --- /dev/null +++ b/test/integration/targets/connection/aliases @@ -0,0 +1 @@ +hidden diff --git a/test/integration/targets/connection/test.sh b/test/integration/targets/connection/test.sh new file mode 100755 index 0000000..6e16a87 --- /dev/null +++ b/test/integration/targets/connection/test.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -eux + +[ -f "${INVENTORY}" ] + +ansible-playbook test_connection.yml -i "${INVENTORY}" "$@" + +# Check that connection vars do not appear in the output +# https://github.com/ansible/ansible/pull/70853 +trap "rm out.txt" EXIT + +ansible all -i "${INVENTORY}" -m set_fact -a "testing=value" -v | tee out.txt +if grep 'ansible_host' out.txt +then + echo "FAILURE: Connection vars in output" + exit 1 +else + echo "SUCCESS: Connection vars not found" +fi + +ansible-playbook test_reset_connection.yml -i "${INVENTORY}" "$@" diff --git a/test/integration/targets/connection/test_connection.yml b/test/integration/targets/connection/test_connection.yml new file mode 100644 index 0000000..2169942 --- /dev/null +++ b/test/integration/targets/connection/test_connection.yml @@ -0,0 +1,43 @@ +- hosts: "{{ target_hosts }}" + gather_facts: no + serial: 1 + tasks: + + ### raw with unicode arg and output + + - name: raw with unicode arg and output + raw: echo 汉语 + register: command + - name: check output of raw with unicode arg and output + assert: + that: + - "'汉语' in command.stdout" + - command is changed # as of 2.2, raw should default to changed: true for consistency w/ shell/command/script modules + + ### copy local file with unicode filename and content + + - name: create local file with unicode filename and content + local_action: lineinfile dest={{ local_tmp }}-汉语/汉语.txt create=true line=汉语 + - name: remove remote file with unicode filename and content + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语/汉语.txt state=absent" + - name: create remote directory with unicode name + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 state=directory" + - name: copy local file with unicode filename and content + action: "{{ action_prefix }}copy src={{ local_tmp }}-汉语/汉语.txt dest={{ remote_tmp }}-汉语/汉语.txt" + + ### fetch remote file with unicode filename and content + + - name: remove local file with unicode filename and content + local_action: file path={{ local_tmp }}-汉语/汉语.txt state=absent + - name: fetch remote file with unicode filename and content + fetch: src={{ remote_tmp }}-汉语/汉语.txt dest={{ local_tmp }}-汉语/汉语.txt fail_on_missing=true validate_checksum=true flat=true + + ### remove local and remote temp files + + - name: remove local temp file + local_action: file path={{ local_tmp }}-汉语 state=absent + - name: remove remote temp file + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 state=absent" + + ### test wait_for_connection plugin + - wait_for_connection: diff --git a/test/integration/targets/connection/test_reset_connection.yml b/test/integration/targets/connection/test_reset_connection.yml new file mode 100644 index 0000000..2f6cb8d --- /dev/null +++ b/test/integration/targets/connection/test_reset_connection.yml @@ -0,0 +1,5 @@ +- hosts: "{{ target_hosts }}" + gather_facts: no + tasks: + # https://github.com/ansible/ansible/issues/65812 + - meta: reset_connection diff --git a/test/integration/targets/connection_delegation/action_plugins/delegation_action.py b/test/integration/targets/connection_delegation/action_plugins/delegation_action.py new file mode 100644 index 0000000..9d419e7 --- /dev/null +++ b/test/integration/targets/connection_delegation/action_plugins/delegation_action.py @@ -0,0 +1,12 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + return { + 'remote_password': self._connection.get_option('remote_password'), + } diff --git a/test/integration/targets/connection_delegation/aliases b/test/integration/targets/connection_delegation/aliases new file mode 100644 index 0000000..6c96566 --- /dev/null +++ b/test/integration/targets/connection_delegation/aliases @@ -0,0 +1,6 @@ +shippable/posix/group3 +context/controller +skip/freebsd # No sshpass +skip/osx # No sshpass +skip/macos # No sshpass +skip/rhel # No sshpass diff --git a/test/integration/targets/connection_delegation/connection_plugins/delegation_connection.py b/test/integration/targets/connection_delegation/connection_plugins/delegation_connection.py new file mode 100644 index 0000000..f61846c --- /dev/null +++ b/test/integration/targets/connection_delegation/connection_plugins/delegation_connection.py @@ -0,0 +1,45 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +DOCUMENTATION = """ +author: Ansible Core Team +connection: delegation_connection +short_description: Test connection for delegated host check +description: +- Some further description that you don't care about. +options: + remote_password: + description: The remote password + type: str + vars: + - name: ansible_password + # Tests that an aliased key gets the -k option which hardcodes the value to password + aliases: + - password +""" + +from ansible.plugins.connection import ConnectionBase + + +class Connection(ConnectionBase): + + transport = 'delegation_connection' + has_pipelining = True + + def __init__(self, *args, **kwargs): + super(Connection, self).__init__(*args, **kwargs) + + def _connect(self): + super(Connection, self)._connect() + + def exec_command(self, cmd, in_data=None, sudoable=True): + super(Connection, self).exec_command(cmd, in_data, sudoable) + + def put_file(self, in_path, out_path): + super(Connection, self).put_file(in_path, out_path) + + def fetch_file(self, in_path, out_path): + super(Connection, self).fetch_file(in_path, out_path) + + def close(self): + super(Connection, self).close() diff --git a/test/integration/targets/connection_delegation/inventory.ini b/test/integration/targets/connection_delegation/inventory.ini new file mode 100644 index 0000000..e7f846d --- /dev/null +++ b/test/integration/targets/connection_delegation/inventory.ini @@ -0,0 +1 @@ +my_host ansible_host=127.0.0.1 ansible_connection=delegation_connection diff --git a/test/integration/targets/connection_delegation/runme.sh b/test/integration/targets/connection_delegation/runme.sh new file mode 100755 index 0000000..4d50724 --- /dev/null +++ b/test/integration/targets/connection_delegation/runme.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -ux + +echo "Checking if sshpass is present" +command -v sshpass 2>&1 || exit 0 +echo "sshpass is present, continuing with test" + +sshpass -p my_password ansible-playbook -i inventory.ini test.yml -k "$@" diff --git a/test/integration/targets/connection_delegation/test.yml b/test/integration/targets/connection_delegation/test.yml new file mode 100644 index 0000000..678bef5 --- /dev/null +++ b/test/integration/targets/connection_delegation/test.yml @@ -0,0 +1,23 @@ +--- +- hosts: localhost + gather_facts: no + tasks: + - name: test connection receives -k from play_context when delegating + delegation_action: + delegate_to: my_host + register: result + + - assert: + that: + - result.remote_password == 'my_password' + + - name: ensure vars set for that host take precedence over -k + delegation_action: + delegate_to: my_host + vars: + ansible_password: other_password + register: result + + - assert: + that: + - result.remote_password == 'other_password' diff --git a/test/integration/targets/connection_local/aliases b/test/integration/targets/connection_local/aliases new file mode 100644 index 0000000..9390a2b --- /dev/null +++ b/test/integration/targets/connection_local/aliases @@ -0,0 +1,2 @@ +shippable/posix/group5 +needs/target/connection diff --git a/test/integration/targets/connection_local/runme.sh b/test/integration/targets/connection_local/runme.sh new file mode 100755 index 0000000..a2c32ad --- /dev/null +++ b/test/integration/targets/connection_local/runme.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -eux + +group=local + +cd ../connection + +INVENTORY="../connection_${group}/test_connection.inventory" ./test.sh \ + -e target_hosts="${group}" \ + -e action_prefix= \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" diff --git a/test/integration/targets/connection_local/test_connection.inventory b/test/integration/targets/connection_local/test_connection.inventory new file mode 100644 index 0000000..64a2722 --- /dev/null +++ b/test/integration/targets/connection_local/test_connection.inventory @@ -0,0 +1,7 @@ +[local] +local-pipelining ansible_ssh_pipelining=true +local-no-pipelining ansible_ssh_pipelining=false +[local:vars] +ansible_host=localhost +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/connection_paramiko_ssh/aliases b/test/integration/targets/connection_paramiko_ssh/aliases new file mode 100644 index 0000000..3851c95 --- /dev/null +++ b/test/integration/targets/connection_paramiko_ssh/aliases @@ -0,0 +1,5 @@ +needs/ssh +shippable/posix/group5 +needs/target/setup_paramiko +needs/target/connection +destructive # potentially installs/uninstalls OS packages via setup_paramiko diff --git a/test/integration/targets/connection_paramiko_ssh/runme.sh b/test/integration/targets/connection_paramiko_ssh/runme.sh new file mode 100755 index 0000000..123f6e2 --- /dev/null +++ b/test/integration/targets/connection_paramiko_ssh/runme.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -eux + +source ../setup_paramiko/setup.sh + +./test.sh diff --git a/test/integration/targets/connection_paramiko_ssh/test.sh b/test/integration/targets/connection_paramiko_ssh/test.sh new file mode 100755 index 0000000..de1ae67 --- /dev/null +++ b/test/integration/targets/connection_paramiko_ssh/test.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -eux + +group=paramiko_ssh + +cd ../connection + +INVENTORY="../connection_${group}/test_connection.inventory" ./test.sh \ + -e target_hosts="${group}" \ + -e action_prefix= \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" diff --git a/test/integration/targets/connection_paramiko_ssh/test_connection.inventory b/test/integration/targets/connection_paramiko_ssh/test_connection.inventory new file mode 100644 index 0000000..a3f34ab --- /dev/null +++ b/test/integration/targets/connection_paramiko_ssh/test_connection.inventory @@ -0,0 +1,7 @@ +[paramiko_ssh] +paramiko_ssh-pipelining ansible_ssh_pipelining=true +paramiko_ssh-no-pipelining ansible_ssh_pipelining=false +[paramiko_ssh:vars] +ansible_host=localhost +ansible_connection=paramiko_ssh +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/connection_psrp/aliases b/test/integration/targets/connection_psrp/aliases new file mode 100644 index 0000000..b3e9b8b --- /dev/null +++ b/test/integration/targets/connection_psrp/aliases @@ -0,0 +1,4 @@ +windows +shippable/windows/group1 +shippable/windows/smoketest +needs/target/connection diff --git a/test/integration/targets/connection_psrp/files/empty.txt b/test/integration/targets/connection_psrp/files/empty.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/integration/targets/connection_psrp/files/empty.txt diff --git a/test/integration/targets/connection_psrp/runme.sh b/test/integration/targets/connection_psrp/runme.sh new file mode 100755 index 0000000..35984bb --- /dev/null +++ b/test/integration/targets/connection_psrp/runme.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -eux + +# make sure hosts are using psrp connections +ansible -i ../../inventory.winrm localhost \ + -m template \ + -a "src=test_connection.inventory.j2 dest=${OUTPUT_DIR}/test_connection.inventory" \ + "$@" + +python.py -m pip install pypsrp +cd ../connection + +INVENTORY="${OUTPUT_DIR}/test_connection.inventory" ./test.sh \ + -e target_hosts=windows \ + -e action_prefix=win_ \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=c:/windows/temp/ansible-remote \ + "$@" + +cd ../connection_psrp + +ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests.yml \ + "$@" diff --git a/test/integration/targets/connection_psrp/test_connection.inventory.j2 b/test/integration/targets/connection_psrp/test_connection.inventory.j2 new file mode 100644 index 0000000..d2d3a49 --- /dev/null +++ b/test/integration/targets/connection_psrp/test_connection.inventory.j2 @@ -0,0 +1,9 @@ +[windows] +{% for host in vars.groups.windows %} +{{ host }} ansible_host={{ hostvars[host]['ansible_host'] }} ansible_port={{ hostvars[host]['ansible_port'] }} ansible_user={{ hostvars[host]['ansible_user'] }} ansible_password={{ hostvars[host]['ansible_password'] }} +{% endfor %} + +[windows:vars] +ansible_connection=psrp +ansible_psrp_auth=negotiate +ansible_psrp_cert_validation=ignore diff --git a/test/integration/targets/connection_psrp/tests.yml b/test/integration/targets/connection_psrp/tests.yml new file mode 100644 index 0000000..dabbf40 --- /dev/null +++ b/test/integration/targets/connection_psrp/tests.yml @@ -0,0 +1,133 @@ +--- +# these are extra tests for psrp that aren't covered under test/integration/targets/connection/* +- name: test out psrp specific tests + hosts: windows + serial: 1 + gather_facts: no + + tasks: + - name: test complex objects in raw output + # until PyYAML is upgraded to 4.x we need to use the \U escape for a unicode codepoint + # and enclose in a quote to it translates the \U + raw: " + [PSCustomObject]@{string = 'string'}; + [PSCustomObject]@{unicode = 'poo - \U0001F4A9'}; + [PSCustomObject]@{integer = 1}; + [PSCustomObject]@{list = @(1, 2)}; + Get-Service -Name winrm; + Write-Output -InputObject 'string - \U0001F4A9';" + register: raw_out + + - name: assert complex objects in raw output + assert: + that: + - raw_out.stdout_lines|count == 6 + - "raw_out.stdout_lines[0] == 'string: string'" + - "raw_out.stdout_lines[1] == 'unicode: poo - \U0001F4A9'" + - "raw_out.stdout_lines[2] == 'integer: 1'" + - "raw_out.stdout_lines[3] == \"list: [1, 2]\"" + - raw_out.stdout_lines[4] == "winrm" + - raw_out.stdout_lines[5] == "string - \U0001F4A9" + + # Become only works on Server 2008 when running with basic auth, skip this host for now as it is too complicated to + # override the auth protocol in the tests. + - name: check if we running on Server 2008 + win_shell: '[System.Environment]::OSVersion.Version -ge [Version]"6.1"' + register: os_version + + - name: test out become with psrp + win_whoami: + when: os_version|bool + register: whoami_out + become: yes + become_method: runas + become_user: SYSTEM + + - name: assert test out become with psrp + assert: + that: + - whoami_out.account.sid == "S-1-5-18" + when: os_version|bool + + - name: test out async with psrp + win_shell: Start-Sleep -Seconds 2; Write-Output abc + async: 10 + poll: 1 + register: async_out + + - name: assert est out async with psrp + assert: + that: + - async_out.stdout_lines == ["abc"] + + - name: Output unicode characters from Powershell using PSRP + win_command: "powershell.exe -ExecutionPolicy ByPass -Command \"Write-Host '\U0001F4A9'\"" + register: command_unicode_output + + - name: Assert unicode output + assert: + that: + - command_unicode_output is changed + - command_unicode_output.rc == 0 + - "command_unicode_output.stdout == '\U0001F4A9\n'" + - command_unicode_output.stderr == '' + + - name: Output unicode characters from Powershell using PSRP + win_shell: "Write-Host '\U0001F4A9'" + register: shell_unicode_output + + - name: Assert unicode output + assert: + that: + - shell_unicode_output is changed + - shell_unicode_output.rc == 0 + - "shell_unicode_output.stdout == '\U0001F4A9\n'" + - shell_unicode_output.stderr == '' + + - name: copy empty file + win_copy: + src: empty.txt + dest: C:\Windows\TEMP\empty.txt + register: copy_empty + + - name: get result of copy empty file + win_stat: + path: C:\Windows\TEMP\empty.txt + get_checksum: yes + register: copy_empty_actual + + - name: assert copy empty file + assert: + that: + - copy_empty.checksum == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + - copy_empty_actual.stat.checksum == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + - copy_empty_actual.stat.size == 0 + + - block: + - name: fetch empty file + fetch: + src: C:\Windows\TEMP\empty.txt + dest: /tmp/empty.txt + flat: yes + register: fetch_empty + + - name: get result of fetch empty file + stat: + path: /tmp/empty.txt + get_checksum: yes + register: fetch_empty_actual + delegate_to: localhost + + - name: assert fetch empty file + assert: + that: + - fetch_empty.checksum == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + - fetch_empty_actual.stat.checksum == 'da39a3ee5e6b4b0d3255bfef95601890afd80709' + - fetch_empty_actual.stat.size == 0 + + always: + - name: remove tmp file + file: + path: /tmp/empty.txt + state: absent + delegate_to: localhost diff --git a/test/integration/targets/connection_remote_is_local/aliases b/test/integration/targets/connection_remote_is_local/aliases new file mode 100644 index 0000000..1d28bdb --- /dev/null +++ b/test/integration/targets/connection_remote_is_local/aliases @@ -0,0 +1,2 @@ +shippable/posix/group5 +context/controller diff --git a/test/integration/targets/connection_remote_is_local/connection_plugins/remote_is_local.py b/test/integration/targets/connection_remote_is_local/connection_plugins/remote_is_local.py new file mode 100644 index 0000000..818bca4 --- /dev/null +++ b/test/integration/targets/connection_remote_is_local/connection_plugins/remote_is_local.py @@ -0,0 +1,25 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = ''' + name: remote_is_local + short_description: remote is local + description: + - remote_is_local + author: ansible (@core) + version_added: historical + extends_documentation_fragment: + - connection_pipelining + notes: + - The remote user is ignored, the user with which the ansible CLI was executed is used instead. +''' + + +from ansible.plugins.connection.local import Connection as LocalConnection + + +class Connection(LocalConnection): + _remote_is_local = True diff --git a/test/integration/targets/connection_remote_is_local/tasks/main.yml b/test/integration/targets/connection_remote_is_local/tasks/main.yml new file mode 100644 index 0000000..265713a --- /dev/null +++ b/test/integration/targets/connection_remote_is_local/tasks/main.yml @@ -0,0 +1,15 @@ +- command: ansible-playbook {{ role_path }}/test.yml -vvv -i {{ '-i '.join(ansible_inventory_sources) }} + environment: + ANSIBLE_REMOTE_TEMP: /i/dont/exist + ANSIBLE_NOCOLOR: 'true' + register: result + +- assert: + that: + - >- + result.stdout is search('PUT ' ~ ansible_local ~ ' TO ' ~ ansible_local) + - >- + '/i/dont/exist' not in result.stdout + vars: + local_tmp: '{{ q("config", "remote_tmp", plugin_type="shell", plugin_name="sh")|first|expanduser|realpath }}' + ansible_local: '{{ local_tmp }}/ansible-local-\S+' diff --git a/test/integration/targets/connection_remote_is_local/test.yml b/test/integration/targets/connection_remote_is_local/test.yml new file mode 100644 index 0000000..b76ba5f --- /dev/null +++ b/test/integration/targets/connection_remote_is_local/test.yml @@ -0,0 +1,8 @@ +- hosts: testhost + gather_facts: false + tasks: + - ping: + vars: + ansible_connection: remote_is_local + ansible_pipelining: false + ansible_remote_tmp: /i/dont/exist diff --git a/test/integration/targets/connection_ssh/aliases b/test/integration/targets/connection_ssh/aliases new file mode 100644 index 0000000..bd04bed --- /dev/null +++ b/test/integration/targets/connection_ssh/aliases @@ -0,0 +1,3 @@ +needs/ssh +shippable/posix/group3 +needs/target/connection diff --git a/test/integration/targets/connection_ssh/check_ssh_defaults.yml b/test/integration/targets/connection_ssh/check_ssh_defaults.yml new file mode 100644 index 0000000..937f1f7 --- /dev/null +++ b/test/integration/targets/connection_ssh/check_ssh_defaults.yml @@ -0,0 +1,29 @@ +- hosts: ssh + gather_facts: false + vars: + ansible_connection: ssh + ansible_ssh_timeout: 10 + tasks: + - name: contain the maddness + block: + - name: test all is good + ping: + + - name: start the fun + meta: reset_connection + + - name: now test we can use wrong port from ssh/config + ping: + ignore_unreachable: True + vars: + ansible_ssh_args: "-F {{playbook_dir}}/files/port_overrride_ssh.cfg" + register: expected + + - name: check all is as expected + assert: + that: + - expected['unreachable']|bool + - "'2222' in expected['msg']" + always: + - name: make sure we don't cache the bad connection + meta: reset_connection diff --git a/test/integration/targets/connection_ssh/files/port_overrride_ssh.cfg b/test/integration/targets/connection_ssh/files/port_overrride_ssh.cfg new file mode 100644 index 0000000..7f8422e --- /dev/null +++ b/test/integration/targets/connection_ssh/files/port_overrride_ssh.cfg @@ -0,0 +1,2 @@ +Host * + Port 2222 diff --git a/test/integration/targets/connection_ssh/posix.sh b/test/integration/targets/connection_ssh/posix.sh new file mode 100755 index 0000000..8f036fb --- /dev/null +++ b/test/integration/targets/connection_ssh/posix.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -eux + +group=ssh + +cd ../connection + +INVENTORY="../connection_${group}/test_connection.inventory" ./test.sh \ + -e target_hosts="${group}" \ + -e action_prefix= \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=/tmp/ansible-remote \ + "$@" diff --git a/test/integration/targets/connection_ssh/runme.sh b/test/integration/targets/connection_ssh/runme.sh new file mode 100755 index 0000000..ad817c8 --- /dev/null +++ b/test/integration/targets/connection_ssh/runme.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +set -ux + +# We skip this whole section if the test node doesn't have sshpass on it. +if command -v sshpass > /dev/null; then + # Check if our sshpass supports -P + sshpass -P foo > /dev/null + sshpass_supports_prompt=$? + if [[ $sshpass_supports_prompt -eq 0 ]]; then + # If the prompt is wrong, we'll end up hanging (due to sshpass hanging). + # We should probably do something better here, like timing out in Ansible, + # but this has been the behavior for a long time, before we supported custom + # password prompts. + # + # So we search for a custom password prompt that is clearly wrong and call + # ansible with timeout. If we time out, our custom prompt was successfully + # searched for. It's a weird way of doing things, but it does ensure + # that the flag gets passed to sshpass. + timeout 5 ansible -m ping \ + -e ansible_connection=ssh \ + -e ansible_sshpass_prompt=notThis: \ + -e ansible_password=foo \ + -e ansible_user=definitelynotroot \ + -i test_connection.inventory \ + ssh-pipelining + ret=$? + # 124 is EXIT_TIMEDOUT from gnu coreutils + # 143 is 128+SIGTERM(15) from BusyBox + if [[ $ret -ne 124 && $ret -ne 143 ]]; then + echo "Expected to time out and we did not. Exiting with failure." + exit 1 + fi + else + ansible -m ping \ + -e ansible_connection=ssh \ + -e ansible_sshpass_prompt=notThis: \ + -e ansible_password=foo \ + -e ansible_user=definitelynotroot \ + -i test_connection.inventory \ + ssh-pipelining | grep 'customized password prompts' + ret=$? + [[ $ret -eq 0 ]] || exit $ret + fi +fi + +set -e + +if [[ "$(scp -O 2>&1)" == "usage: scp "* ]]; then + # scp supports the -O option (and thus the -T option as well) + # work-around required + # see: https://www.openssh.com/txt/release-9.0 + scp_args=("-e" "ansible_scp_extra_args=-TO") +elif [[ "$(scp -T 2>&1)" == "usage: scp "* ]]; then + # scp supports the -T option + # work-around required + # see: https://github.com/ansible/ansible/issues/52640 + scp_args=("-e" "ansible_scp_extra_args=-T") +else + # scp does not support the -T or -O options + # no work-around required + # however we need to put something in the array to keep older versions of bash happy + scp_args=("-e" "") +fi + +# sftp +./posix.sh "$@" +# scp +ANSIBLE_SCP_IF_SSH=true ./posix.sh "$@" "${scp_args[@]}" +# piped +ANSIBLE_SSH_TRANSFER_METHOD=piped ./posix.sh "$@" + +# test config defaults override +ansible-playbook check_ssh_defaults.yml "$@" -i test_connection.inventory + +# ensure we can load from ini cfg +ANSIBLE_CONFIG=./test_ssh_defaults.cfg ansible-playbook verify_config.yml "$@" + +# ensure we handle cp with spaces correctly, otherwise would fail with +# `"Failed to connect to the host via ssh: command-line line 0: keyword controlpath extra arguments at end of line"` +ANSIBLE_SSH_CONTROL_PATH='/tmp/ssh cp with spaces' ansible -m ping all -e ansible_connection=ssh -i test_connection.inventory "$@" diff --git a/test/integration/targets/connection_ssh/test_connection.inventory b/test/integration/targets/connection_ssh/test_connection.inventory new file mode 100644 index 0000000..a1a4ff1 --- /dev/null +++ b/test/integration/targets/connection_ssh/test_connection.inventory @@ -0,0 +1,7 @@ +[ssh] +ssh-pipelining ansible_ssh_pipelining=true +ssh-no-pipelining ansible_ssh_pipelining=false +[ssh:vars] +ansible_host=localhost +ansible_connection=ssh +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/test/integration/targets/connection_ssh/test_ssh_defaults.cfg b/test/integration/targets/connection_ssh/test_ssh_defaults.cfg new file mode 100644 index 0000000..362f946 --- /dev/null +++ b/test/integration/targets/connection_ssh/test_ssh_defaults.cfg @@ -0,0 +1,5 @@ +[ssh_connection] +ssh_common_args=fromconfig +ssh_extra_args=fromconfig +scp_extra_args=fromconfig +sftp_extra_args=fromconfig diff --git a/test/integration/targets/connection_ssh/verify_config.yml b/test/integration/targets/connection_ssh/verify_config.yml new file mode 100644 index 0000000..0bf7958 --- /dev/null +++ b/test/integration/targets/connection_ssh/verify_config.yml @@ -0,0 +1,21 @@ +- hosts: localhost + gather_facts: false + vars: + ssh_configs: + - ssh_common_args + - ssh_extra_args + - sftp_extra_args + - scp_extra_args + tasks: + - debug: + msg: '{{item ~ ": " ~ lookup("config", item, plugin_type="connection", plugin_name="ssh")}}' + verbosity: 3 + loop: '{{ssh_configs}}' + tags: [ configfile ] + + - name: check config from file + assert: + that: + - 'lookup("config", item, plugin_type="connection", plugin_name="ssh") == "fromconfig"' + loop: '{{ssh_configs}}' + tags: [ configfile ] diff --git a/test/integration/targets/connection_windows_ssh/aliases b/test/integration/targets/connection_windows_ssh/aliases new file mode 100644 index 0000000..af3f193 --- /dev/null +++ b/test/integration/targets/connection_windows_ssh/aliases @@ -0,0 +1,5 @@ +windows +shippable/windows/group1 +shippable/windows/smoketest +needs/target/connection +needs/target/setup_remote_tmp_dir diff --git a/test/integration/targets/connection_windows_ssh/runme.sh b/test/integration/targets/connection_windows_ssh/runme.sh new file mode 100755 index 0000000..766193f --- /dev/null +++ b/test/integration/targets/connection_windows_ssh/runme.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +set -eux + +# We need to run these tests with both the powershell and cmd shell type + +### cmd tests - no DefaultShell set ### +ansible -i ../../inventory.winrm localhost \ + -m template \ + -a "src=test_connection.inventory.j2 dest=${OUTPUT_DIR}/test_connection.inventory" \ + -e "test_shell_type=cmd" \ + "$@" + +# https://github.com/PowerShell/Win32-OpenSSH/wiki/DefaultShell +ansible -i ../../inventory.winrm windows \ + -m win_regedit \ + -a "path=HKLM:\\\\SOFTWARE\\\\OpenSSH name=DefaultShell state=absent" \ + "$@" + +# Need to flush the connection to ensure we get a new shell for the next tests +ansible -i "${OUTPUT_DIR}/test_connection.inventory" windows \ + -m meta -a "reset_connection" \ + "$@" + +# sftp +./windows.sh "$@" +# scp +ANSIBLE_SSH_TRANSFER_METHOD=scp ./windows.sh "$@" +# other tests not part of the generic connection test framework +ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests.yml \ + "$@" + +### powershell tests - explicit DefaultShell set ### +# we do this last as the default shell on our CI instances is set to PowerShell +ansible -i ../../inventory.winrm localhost \ + -m template \ + -a "src=test_connection.inventory.j2 dest=${OUTPUT_DIR}/test_connection.inventory" \ + -e "test_shell_type=powershell" \ + "$@" + +# ensure the default shell is set to PowerShell +ansible -i ../../inventory.winrm windows \ + -m win_regedit \ + -a "path=HKLM:\\\\SOFTWARE\\\\OpenSSH name=DefaultShell data=C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe" \ + "$@" + +ansible -i "${OUTPUT_DIR}/test_connection.inventory" windows \ + -m meta -a "reset_connection" \ + "$@" + +./windows.sh "$@" +ANSIBLE_SSH_TRANSFER_METHOD=scp ./windows.sh "$@" +ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests.yml \ + "$@" diff --git a/test/integration/targets/connection_windows_ssh/test_connection.inventory.j2 b/test/integration/targets/connection_windows_ssh/test_connection.inventory.j2 new file mode 100644 index 0000000..5893eaf --- /dev/null +++ b/test/integration/targets/connection_windows_ssh/test_connection.inventory.j2 @@ -0,0 +1,12 @@ +[windows] +{% for host in vars.groups.windows %} +{{ host }} ansible_host={{ hostvars[host]['ansible_host'] }} ansible_user={{ hostvars[host]['ansible_user'] }}{{ ' ansible_ssh_private_key_file=' ~ hostvars[host]['ansible_ssh_private_key_file'] if (hostvars[host]['ansible_ssh_private_key_file']|default()) else '' }} +{% endfor %} + +[windows:vars] +ansible_shell_type={{ test_shell_type }} +ansible_connection=ssh +ansible_port=22 +# used to preserve the existing environment and not touch existing files +ansible_ssh_extra_args="-o UserKnownHostsFile=/dev/null" +ansible_ssh_host_key_checking=False diff --git a/test/integration/targets/connection_windows_ssh/tests.yml b/test/integration/targets/connection_windows_ssh/tests.yml new file mode 100644 index 0000000..e9b538b --- /dev/null +++ b/test/integration/targets/connection_windows_ssh/tests.yml @@ -0,0 +1,32 @@ +--- +- name: test out Windows SSH specific tests + hosts: windows + serial: 1 + gather_facts: no + + tasks: + - name: test out become with Windows SSH + win_whoami: + register: win_ssh_become + become: yes + become_method: runas + become_user: SYSTEM + + - name: assert test out become with Windows SSH + assert: + that: + - win_ssh_become.account.sid == "S-1-5-18" + + - name: test out async with Windows SSH + win_shell: Write-Host café + async: 20 + poll: 3 + register: win_ssh_async + + - name: assert test out async with Windows SSH + assert: + that: + - win_ssh_async is changed + - win_ssh_async.rc == 0 + - win_ssh_async.stdout == "café\n" + - win_ssh_async.stderr == "" diff --git a/test/integration/targets/connection_windows_ssh/tests_fetch.yml b/test/integration/targets/connection_windows_ssh/tests_fetch.yml new file mode 100644 index 0000000..0b4fe94 --- /dev/null +++ b/test/integration/targets/connection_windows_ssh/tests_fetch.yml @@ -0,0 +1,41 @@ +# This must be a play as we need to invoke it with the ANSIBLE_SCP_IF_SSH env +# to control the mechanism used. Unfortunately while ansible_scp_if_ssh is +# documented, it isn't actually used hence the separate invocation +--- +- name: further fetch tests with metachar characters in filename + hosts: windows + force_handlers: yes + serial: 1 + gather_facts: no + + tasks: + - name: setup remote tmp dir + import_role: + name: ../../setup_remote_tmp_dir + + - name: create remote file with metachar in name + win_copy: + content: some content + dest: '{{ remote_tmp_dir }}\file ^with &whoami' + + - name: test fetch against a file with cmd metacharacters + block: + - name: fetch file with metachar in name + fetch: + src: '{{ remote_tmp_dir }}\file ^with &whoami' + dest: ansible-test.txt + flat: yes + register: fetch_res + + - name: assert fetch file with metachar in name + assert: + that: + - fetch_res is changed + - fetch_res.checksum == '94e66df8cd09d410c62d9e0dc59d3a884e458e05' + + always: + - name: remove local copy of file + file: + path: ansible-test.txt + state: absent + delegate_to: localhost diff --git a/test/integration/targets/connection_windows_ssh/windows.sh b/test/integration/targets/connection_windows_ssh/windows.sh new file mode 100755 index 0000000..d2db50f --- /dev/null +++ b/test/integration/targets/connection_windows_ssh/windows.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -eux + +cd ../connection + +# A recent patch to OpenSSH causes a validation error when running through Ansible. It seems like if the path is quoted +# then it will fail with 'protocol error: filename does not match request'. We currently ignore this by setting +# 'ansible_scp_extra_args=-T' to ignore this check but this should be removed once that bug is fixed and our test +# container has been updated. +# https://unix.stackexchange.com/questions/499958/why-does-scps-strict-filename-checking-reject-quoted-last-component-but-not-oth +# https://github.com/openssh/openssh-portable/commit/391ffc4b9d31fa1f4ad566499fef9176ff8a07dc +INVENTORY="${OUTPUT_DIR}/test_connection.inventory" ./test.sh \ + -e target_hosts=windows \ + -e action_prefix=win_ \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=c:/windows/temp/ansible-remote \ + -e ansible_scp_extra_args=-T \ + "$@" + +cd ../connection_windows_ssh + +ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests_fetch.yml \ + -e ansible_scp_extra_args=-T \ + "$@" diff --git a/test/integration/targets/connection_winrm/aliases b/test/integration/targets/connection_winrm/aliases new file mode 100644 index 0000000..af3f193 --- /dev/null +++ b/test/integration/targets/connection_winrm/aliases @@ -0,0 +1,5 @@ +windows +shippable/windows/group1 +shippable/windows/smoketest +needs/target/connection +needs/target/setup_remote_tmp_dir diff --git a/test/integration/targets/connection_winrm/runme.sh b/test/integration/targets/connection_winrm/runme.sh new file mode 100755 index 0000000..36a7aa8 --- /dev/null +++ b/test/integration/targets/connection_winrm/runme.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -eux + +# make sure hosts are using winrm connections +ansible -i ../../inventory.winrm localhost \ + -m template \ + -a "src=test_connection.inventory.j2 dest=${OUTPUT_DIR}/test_connection.inventory" \ + "$@" + +cd ../connection + +INVENTORY="${OUTPUT_DIR}/test_connection.inventory" ./test.sh \ + -e target_hosts=windows \ + -e action_prefix=win_ \ + -e local_tmp=/tmp/ansible-local \ + -e remote_tmp=c:/windows/temp/ansible-remote \ + "$@" + +cd ../connection_winrm + +ansible-playbook -i "${OUTPUT_DIR}/test_connection.inventory" tests.yml \ + "$@" diff --git a/test/integration/targets/connection_winrm/test_connection.inventory.j2 b/test/integration/targets/connection_winrm/test_connection.inventory.j2 new file mode 100644 index 0000000..7c4f3dc --- /dev/null +++ b/test/integration/targets/connection_winrm/test_connection.inventory.j2 @@ -0,0 +1,10 @@ +[windows] +{% for host in vars.groups.windows %} +{{ host }} ansible_host={{ hostvars[host]['ansible_host'] }} ansible_port={{ hostvars[host]['ansible_port'] }} ansible_user={{ hostvars[host]['ansible_user'] }} ansible_password={{ hostvars[host]['ansible_password'] }} +{% endfor %} + +[windows:vars] +ansible_connection=winrm +# we don't know if we're using an encrypted connection or not, so we'll use message encryption +ansible_winrm_transport=ntlm +ansible_winrm_server_cert_validation=ignore diff --git a/test/integration/targets/connection_winrm/tests.yml b/test/integration/targets/connection_winrm/tests.yml new file mode 100644 index 0000000..78f92a4 --- /dev/null +++ b/test/integration/targets/connection_winrm/tests.yml @@ -0,0 +1,28 @@ +--- +- name: test out Windows WinRM specific tests + hosts: windows + force_handlers: yes + serial: 1 + gather_facts: no + + tasks: + - name: setup remote tmp dir + import_role: + name: ../../setup_remote_tmp_dir + + - name: copy across empty file + win_copy: + content: '' + dest: '{{ remote_tmp_dir }}\empty.txt' + register: winrm_copy_empty + + - name: get result of copy across empty file + win_stat: + path: '{{ remote_tmp_dir }}\empty.txt' + register: winrm_copy_empty_actual + + - name: assert copy across empty file + assert: + that: + - winrm_copy_empty is changed + - winrm_copy_empty_actual.stat.size == 0 |