diff options
Diffstat (limited to 'test/integration/targets')
260 files changed, 11645 insertions, 0 deletions
diff --git a/test/integration/targets/add_host/tasks/main.yml b/test/integration/targets/add_host/tasks/main.yml new file mode 100644 index 0000000000..cafd6bd4eb --- /dev/null +++ b/test/integration/targets/add_host/tasks/main.yml @@ -0,0 +1,39 @@ +# test code for the add_host action +# (c) 2015, Matt Davis <mdavis@ansible.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: add a host to the runtime inventory + add_host: + name: newdynamichost + groups: newdynamicgroup + a_var: from add_host + +- debug: msg={{hostvars['newdynamichost'].group_names}} + +- name: ensure that dynamically-added host is visible via hostvars, groups, etc (there are several caches that could break this) + assert: + that: + - hostvars['bogushost'] is not defined # there was a bug where an undefined host was a "type" instead of an instance- ensure this works before we rely on it + - hostvars['newdynamichost'] is defined + - hostvars['newdynamichost'].group_names is defined + - "'newdynamicgroup' in hostvars['newdynamichost'].group_names" + - hostvars['newdynamichost']['bogusvar'] is not defined + - hostvars['newdynamichost']['a_var'] is defined + - hostvars['newdynamichost']['a_var'] == 'from add_host' + - groups['bogusgroup'] is not defined # same check as above to ensure that bogus groups are undefined... + - groups['newdynamicgroup'] is defined + - "'newdynamichost' in groups['newdynamicgroup']" diff --git a/test/integration/targets/apache2_module/meta/main.yml b/test/integration/targets/apache2_module/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/apache2_module/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/apache2_module/tasks/actualtest.yml b/test/integration/targets/apache2_module/tasks/actualtest.yml new file mode 100644 index 0000000000..5b02a1c2ff --- /dev/null +++ b/test/integration/targets/apache2_module/tasks/actualtest.yml @@ -0,0 +1,61 @@ +# 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: install apache via apt + apt: name=apache2 state=present + when: "ansible_os_family == 'Debian'" + +- name: install apache via zypper + zypper: name=apache2 state=present + when: "ansible_os_family == 'Suse'" + +- name: disable userdir module + apache2_module: name=userdir state=absent + +- name: disable userdir module, second run + apache2_module: name=userdir state=absent + register: disable + +- name: ensure apache2_module is idempotent + assert: + that: + - 'not disable.changed' + +- name: enable userdir module + apache2_module: name=userdir state=present + register: enable + +- name: ensure changed on successful enable + assert: + that: + - 'enable.changed' + +- name: enable userdir module, second run + apache2_module: name=userdir state=present + register: enabletwo + +- name: ensure apache2_module is idempotent + assert: + that: + - 'not enabletwo.changed' + +- name: disable userdir module, final run + apache2_module: name=userdir state=absent + register: disablefinal + +- name: ensure changed on successful disable + assert: + that: + - 'disablefinal.changed' diff --git a/test/integration/targets/apache2_module/tasks/main.yml b/test/integration/targets/apache2_module/tasks/main.yml new file mode 100644 index 0000000000..590fc726e3 --- /dev/null +++ b/test/integration/targets/apache2_module/tasks/main.yml @@ -0,0 +1,6 @@ +--- + +- name: include only on supported systems + include: actualtest.yml + when: ansible_os_family in ['Debian', 'Suse'] + # centos/RHEL does not have a2enmod/a2dismod diff --git a/test/integration/targets/apt/meta/main.yml b/test/integration/targets/apt/meta/main.yml new file mode 100644 index 0000000000..07faa21776 --- /dev/null +++ b/test/integration/targets/apt/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/apt/tasks/apt-builddep.yml b/test/integration/targets/apt/tasks/apt-builddep.yml new file mode 100644 index 0000000000..e3f9357b12 --- /dev/null +++ b/test/integration/targets/apt/tasks/apt-builddep.yml @@ -0,0 +1,55 @@ +# test installing build-deps using netcat and quilt as test victims. +# +# Deps can be discovered like so (taken from ubuntu 12.04) +# ==== +# root@localhost:~ # apt-rdepends --build-depends --follow=DEPENDS netcat +# Reading package lists... Done +# Building dependency tree +# Reading state information... Done +# netcat +# Build-Depends: debhelper (>= 8.0.0) +# Build-Depends: quilt +# root@localhost:~ # +# ==== +# Since many things depend on debhelper, let's just uninstall quilt, then +# install build-dep for netcat to get it back. build-dep doesn't have an +# uninstall, so we don't need to test for reverse actions (eg, uninstall +# build-dep and ensure things are clean) + +# uninstall quilt +- name: check quilt with dpkg + shell: dpkg -s quilt + register: dpkg_result + ignore_errors: true + tags: ['test_apt_builddep'] + +- name: uninstall quilt with apt + apt: pkg=quilt state=absent purge=yes + register: apt_result + when: dpkg_result|success + tags: ['test_apt_builddep'] + +# install build-dep for netcat +- name: install netcat build-dep with apt + apt: pkg=netcat state=build-dep + register: apt_result + tags: ['test_apt_builddep'] + +- name: verify build_dep of netcat + assert: + that: + - "'changed' in apt_result" + tags: ['test_apt_builddep'] + +# ensure debhelper and qilt are installed +- name: check build_deps with dpkg + shell: dpkg --get-selections | egrep '(debhelper|quilt)' + failed_when: False + register: dpkg_result + tags: ['test_apt_builddep'] + +- name: verify build_deps are really there + assert: + that: + - "dpkg_result.rc == 0" + tags: ['test_apt_builddep'] diff --git a/test/integration/targets/apt/tasks/apt.yml b/test/integration/targets/apt/tasks/apt.yml new file mode 100644 index 0000000000..d293f845fa --- /dev/null +++ b/test/integration/targets/apt/tasks/apt.yml @@ -0,0 +1,150 @@ +- name: show python version + debug: var=ansible_python_version + +- name: use python-apt + set_fact: + python_apt: python-apt + when: ansible_python_version | version_compare('3', '<') + +- name: use python3-apt + set_fact: + python_apt: python3-apt + when: ansible_python_version | version_compare('3', '>=') + +# UNINSTALL 'python-apt' +# The `apt` module has the smarts to auto-install `python-apt`. To test, we +# will first uninstall `python-apt`. +- name: check {{ python_apt }} with dpkg + shell: dpkg -s {{ python_apt }} + register: dpkg_result + ignore_errors: true + +- name: uninstall {{ python_apt }} with apt + apt: pkg={{ python_apt }} state=absent purge=yes + register: apt_result + when: dpkg_result|success + +# UNINSTALL 'hello' +# With 'python-apt' uninstalled, the first call to 'apt' should install +# python-apt. +- name: uninstall hello with apt + apt: pkg=hello state=absent purge=yes + register: apt_result + +- name: check hello with dpkg + shell: dpkg-query -l hello + failed_when: False + register: dpkg_result + +- name: verify uninstallation of hello + assert: + that: + - "'changed' in apt_result" + - "dpkg_result.rc == 1" + +# UNINSTALL AGAIN +- name: uninstall hello with apt + apt: pkg=hello state=absent purge=yes + register: apt_result + +- name: verify no change on re-uninstall + assert: + that: + - "not apt_result.changed" + +# INSTALL +- name: install hello with apt + apt: name=hello state=present + register: apt_result + +- name: check hello with dpkg + shell: dpkg-query -l hello + failed_when: False + register: dpkg_result + +- debug: var=apt_result +- debug: var=dpkg_result + +- name: verify installation of hello + assert: + that: + - "apt_result.changed" + - "dpkg_result.rc == 0" + +- name: verify apt module outputs + assert: + that: + - "'changed' in apt_result" + - "'stderr' in apt_result" + - "'stdout' in apt_result" + - "'stdout_lines' in apt_result" + +# INSTALL AGAIN +- name: install hello with apt + apt: name=hello state=present + register: apt_result + +- name: verify no change on re-install + assert: + that: + - "not apt_result.changed" + +# UNINSTALL AGAIN +- name: uninstall hello with apt + apt: pkg=hello state=absent purge=yes + register: apt_result + +# INSTALL WITH VERSION WILDCARD +- name: install hello with apt + apt: name=hello=2.* state=present + register: apt_result + +- name: check hello with wildcard with dpkg + shell: dpkg-query -l hello + failed_when: False + register: dpkg_result + +- debug: var=apt_result +- debug: var=dpkg_result + +- name: verify installation of hello + assert: + that: + - "apt_result.changed" + - "dpkg_result.rc == 0" + +- name: check hello version + shell: dpkg -s hello | grep Version | awk '{print $2}' + register: hello_version +- name: check hello architecture + shell: dpkg -s hello | grep Architecture | awk '{print $2}' + register: hello_architecture + +- name: uninstall hello with apt + apt: pkg=hello state=absent purge=yes + +- name: install deb file + apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ hello_architecture.stdout }}.deb" + register: apt_initial + +- name: install deb file again + apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ hello_architecture.stdout }}.deb" + register: apt_secondary + +- name: verify installation of hello + assert: + that: + - "apt_initial.changed" + - "not apt_secondary.changed" + +- name: uninstall hello with apt + apt: pkg=hello state=absent purge=yes + +- name: force install of deb + apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ hello_architecture.stdout }}.deb" force=true + register: dpkg_force + +- name: verify installation of hello + assert: + that: + - "dpkg_force.changed" diff --git a/test/integration/targets/apt/tasks/main.yml b/test/integration/targets/apt/tasks/main.yml new file mode 100644 index 0000000000..552b543d2d --- /dev/null +++ b/test/integration/targets/apt/tasks/main.yml @@ -0,0 +1,22 @@ +# (c) 2014, James Tanner <tanner.jc@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/>. + +- include: 'apt.yml' + when: ansible_distribution in ('Ubuntu', 'Debian') + +- include: 'apt-builddep.yml' + when: ansible_distribution in ('Ubuntu', 'Debian') diff --git a/test/integration/targets/apt_repository/meta/main.yml b/test/integration/targets/apt_repository/meta/main.yml new file mode 100644 index 0000000000..07faa21776 --- /dev/null +++ b/test/integration/targets/apt_repository/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/apt_repository/tasks/apt.yml b/test/integration/targets/apt_repository/tasks/apt.yml new file mode 100644 index 0000000000..4295badb16 --- /dev/null +++ b/test/integration/targets/apt_repository/tasks/apt.yml @@ -0,0 +1,205 @@ +--- + +- set_fact: + test_ppa_name: 'ppa:git-core/ppa' + test_ppa_filename: 'git-core' + test_ppa_spec: 'deb http://ppa.launchpad.net/git-core/ppa/ubuntu {{ansible_distribution_release}} main' + test_ppa_key: 'E1DF1F24' # http://keyserver.ubuntu.com:11371/pks/lookup?search=0xD06AAF4C11DAB86DF421421EFE6B20ECA7AD98A1&op=index + +- name: show python version + debug: var=ansible_python_version + +- name: use python-apt + set_fact: + python_apt: python-apt + when: ansible_python_version | version_compare('3', '<') + +- name: use python3-apt + set_fact: + python_apt: python3-apt + when: ansible_python_version | version_compare('3', '>=') + +# UNINSTALL 'python-apt' +# The `apt_repository` module has the smarts to auto-install `python-apt`. To +# test, we will first uninstall `python-apt`. +- name: check {{ python_apt }} with dpkg + shell: dpkg -s {{ python_apt }} + register: dpkg_result + ignore_errors: true + +- name: uninstall {{ python_apt }} with apt + apt: pkg={{ python_apt }} state=absent purge=yes + register: apt_result + when: dpkg_result|success + +# +# TEST: apt_repository: repo=<name> +# +- include: 'cleanup.yml' + +- name: 'record apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_before + +- name: 'name=<name> (expect: pass)' + apt_repository: repo='{{test_ppa_name}}' state=present + register: result + +- name: 'assert the apt cache did *NOT* change' + assert: + that: + - 'result.changed' + - 'result.state == "present"' + - 'result.repo == "{{test_ppa_name}}"' + +- name: 'examine apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_after + +- name: 'assert the apt cache did change' + assert: + that: + - 'cache_before.stat.mtime != cache_after.stat.mtime' + +- name: 'ensure ppa key is installed (expect: pass)' + apt_key: id='{{test_ppa_key}}' state=present + +# +# TEST: apt_repository: repo=<name> update_cache=no +# +- include: 'cleanup.yml' + +- name: 'record apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_before + +- name: 'name=<name> update_cache=no (expect: pass)' + apt_repository: repo='{{test_ppa_name}}' state=present update_cache=no + register: result + +- assert: + that: + - 'result.changed' + - 'result.state == "present"' + - 'result.repo == "{{test_ppa_name}}"' + +- name: 'examine apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_after + +- name: 'assert the apt cache did *NOT* change' + assert: + that: + - 'cache_before.stat.mtime == cache_after.stat.mtime' + +- name: 'ensure ppa key is installed (expect: pass)' + apt_key: id='{{test_ppa_key}}' state=present + +# +# TEST: apt_repository: repo=<name> update_cache=yes +# +- include: 'cleanup.yml' + +- name: 'record apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_before + +- name: 'name=<name> update_cache=yes (expect: pass)' + apt_repository: repo='{{test_ppa_name}}' state=present update_cache=yes + register: result + +- assert: + that: + - 'result.changed' + - 'result.state == "present"' + - 'result.repo == "{{test_ppa_name}}"' + +- name: 'examine apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_after + +- name: 'assert the apt cache did change' + assert: + that: + - 'cache_before.stat.mtime != cache_after.stat.mtime' + +- name: 'ensure ppa key is installed (expect: pass)' + apt_key: id='{{test_ppa_key}}' state=present + +# +# TEST: apt_repository: repo=<spec> +# +- include: 'cleanup.yml' + +- name: 'record apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_before + +- name: 'name=<spec> (expect: pass)' + apt_repository: repo='{{test_ppa_spec}}' state=present + register: result + +- assert: + that: + - 'result.changed' + - 'result.state == "present"' + - 'result.repo == "{{test_ppa_spec}}"' + +- name: 'examine apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_after + +- name: 'assert the apt cache did change' + assert: + that: + - 'cache_before.stat.mtime != cache_after.stat.mtime' + +# When installing a repo with the spec, the key is *NOT* added +- name: 'ensure ppa key is absent (expect: pass)' + apt_key: id='{{test_ppa_key}}' state=absent + +# +# TEST: apt_repository: repo=<spec> filename=<filename> +# +- include: 'cleanup.yml' + +- name: 'record apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_before + +- name: 'name=<spec> filename=<filename> (expect: pass)' + apt_repository: repo='{{test_ppa_spec}}' filename='{{test_ppa_filename}}' state=present + register: result + +- assert: + that: + - 'result.changed' + - 'result.state == "present"' + - 'result.repo == "{{test_ppa_spec}}"' + +- name: 'examine source file' + stat: path='/etc/apt/sources.list.d/{{test_ppa_filename}}.list' + register: source_file + +- name: 'assert source file exists' + assert: + that: + - 'source_file.stat.exists == True' + +- name: 'examine apt cache mtime' + stat: path='/var/cache/apt/pkgcache.bin' + register: cache_after + +- name: 'assert the apt cache did change' + assert: + that: + - 'cache_before.stat.mtime != cache_after.stat.mtime' + +# When installing a repo with the spec, the key is *NOT* added +- name: 'ensure ppa key is absent (expect: pass)' + apt_key: id='{{test_ppa_key}}' state=absent + +# +# TEARDOWN +# +- include: 'cleanup.yml' diff --git a/test/integration/targets/apt_repository/tasks/cleanup.yml b/test/integration/targets/apt_repository/tasks/cleanup.yml new file mode 100644 index 0000000000..86a09dd5ae --- /dev/null +++ b/test/integration/targets/apt_repository/tasks/cleanup.yml @@ -0,0 +1,18 @@ +--- +# tasks to cleanup a repo and assert it is gone + +- name: remove existing ppa + apt_repository: repo={{test_ppa_name}} state=absent + ignore_errors: true + +- name: test that ppa does not exist (expect pass) + shell: cat /etc/apt/sources.list /etc/apt/sources.list.d/* | grep "{{test_ppa_spec}}" + register: command + failed_when: command.rc == 0 + changed_when: false + +# Should this use apt-key, maybe? +- name: remove ppa key + apt_key: id={{test_ppa_key}} state=absent + ignore_errors: true + diff --git a/test/integration/targets/apt_repository/tasks/main.yml b/test/integration/targets/apt_repository/tasks/main.yml new file mode 100644 index 0000000000..38ae8f0447 --- /dev/null +++ b/test/integration/targets/apt_repository/tasks/main.yml @@ -0,0 +1,21 @@ +# test code for the apt_repository module +# (c) 2014, James Laska <jlaska@ansible.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/>. + +- include: 'apt.yml' + when: ansible_distribution in ('Ubuntu') + diff --git a/test/integration/targets/assemble/files/fragment1 b/test/integration/targets/assemble/files/fragment1 new file mode 100644 index 0000000000..a00d3ea04a --- /dev/null +++ b/test/integration/targets/assemble/files/fragment1 @@ -0,0 +1 @@ +this is fragment 1 diff --git a/test/integration/targets/assemble/files/fragment2 b/test/integration/targets/assemble/files/fragment2 new file mode 100644 index 0000000000..860f760388 --- /dev/null +++ b/test/integration/targets/assemble/files/fragment2 @@ -0,0 +1 @@ +this is fragment 2 diff --git a/test/integration/targets/assemble/files/fragment3 b/test/integration/targets/assemble/files/fragment3 new file mode 100644 index 0000000000..df95b24bb6 --- /dev/null +++ b/test/integration/targets/assemble/files/fragment3 @@ -0,0 +1 @@ +this is fragment 3 diff --git a/test/integration/targets/assemble/files/fragment4 b/test/integration/targets/assemble/files/fragment4 new file mode 100644 index 0000000000..c83252bb8e --- /dev/null +++ b/test/integration/targets/assemble/files/fragment4 @@ -0,0 +1 @@ +this is fragment 4 diff --git a/test/integration/targets/assemble/files/fragment5 b/test/integration/targets/assemble/files/fragment5 new file mode 100644 index 0000000000..8a527d15f7 --- /dev/null +++ b/test/integration/targets/assemble/files/fragment5 @@ -0,0 +1 @@ +this is fragment 5 diff --git a/test/integration/targets/assemble/meta/main.yml b/test/integration/targets/assemble/meta/main.yml new file mode 100644 index 0000000000..a9d0b4681e --- /dev/null +++ b/test/integration/targets/assemble/meta/main.yml @@ -0,0 +1,20 @@ +# test code for the assemble module +# (c) 2014, James Cammarata <jcammarata@ansible.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/>. + +dependencies: + - prepare_tests diff --git a/test/integration/targets/assemble/tasks/main.yml b/test/integration/targets/assemble/tasks/main.yml new file mode 100644 index 0000000000..d0c1f15e56 --- /dev/null +++ b/test/integration/targets/assemble/tasks/main.yml @@ -0,0 +1,93 @@ +# test code for the assemble module +# (c) 2014, James Cammarata <jcammarata@ansible.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 new directory for file source + file: dest="{{output_dir}}/src" state=directory + register: result + +- name: assert the directory was created + assert: + that: + - "result.state == 'directory'" + +- name: copy the files to a new directory + copy: src="./" dest="{{output_dir}}/src" + register: result + +- name: test assemble with all fragments + assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled1" + register: result + +- name: assert the fragments were assembled + assert: + that: + - "result.state == 'file'" + - "result.changed == True" + - "result.checksum == '048a1bd1951aa5ccc427eeb4ca19aee45e9c68b3'" + +- name: test assemble with all fragments + assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled1" + register: result + +- name: assert that the same assemble made no changes + assert: + that: + - "result.state == 'file'" + - "result.changed == False" + - "result.checksum == '048a1bd1951aa5ccc427eeb4ca19aee45e9c68b3'" + +- name: test assemble with fragments matching a regex + assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled2" regexp="^fragment[1-3]$" + register: result + +- name: assert the fragments were assembled with a regex + assert: + that: + - "result.state == 'file'" + - "result.checksum == 'edfe2d7487ef8f5ebc0f1c4dc57ba7b70a7b8e2b'" + +- name: test assemble with a delimiter + assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled3" delimiter="#--- delimiter ---#" + register: result + +- name: assert the fragments were assembled with a delimiter + assert: + that: + - "result.state == 'file'" + - "result.checksum == '505359f48c65b3904127cf62b912991d4da7ed6d'" + +- name: test assemble with remote_src=False + assemble: src="./" dest="{{output_dir}}/assembled4" remote_src=no + register: result + +- name: assert the fragments were assembled without remote + assert: + that: + - "result.state == 'file'" + - "result.checksum == '048a1bd1951aa5ccc427eeb4ca19aee45e9c68b3'" + +- name: test assemble with remote_src=False and a delimiter + assemble: src="./" dest="{{output_dir}}/assembled5" remote_src=no delimiter="#--- delimiter ---#" + register: result + +- name: assert the fragments were assembled without remote + assert: + that: + - "result.state == 'file'" + - "result.checksum == '505359f48c65b3904127cf62b912991d4da7ed6d'" + diff --git a/test/integration/targets/async/library/async_test.py b/test/integration/targets/async/library/async_test.py new file mode 100644 index 0000000000..5c77a27c8d --- /dev/null +++ b/test/integration/targets/async/library/async_test.py @@ -0,0 +1,39 @@ +import sys +import json +from ansible.module_utils.basic import AnsibleModule + +def main(): + if "--interactive" in sys.argv: + import ansible.module_utils.basic + ansible.module_utils.basic._ANSIBLE_ARGS = json.dumps(dict( + ANSIBLE_MODULE_ARGS=dict( + fail_mode="graceful" + ) + )) + + module = AnsibleModule(argument_spec = dict( + fail_mode = dict(type='list', default=['success']) + ) + ) + + result = dict(changed=True) + + fail_mode = module.params['fail_mode'] + + try: + if 'leading_junk' in fail_mode: + print("leading junk before module output") + + if 'graceful' in fail_mode: + module.fail_json(msg="failed gracefully") + + if 'exception' in fail_mode: + raise Exception('failing via exception') + + module.exit_json(**result) + + finally: + if 'trailing_junk' in fail_mode: + print("trailing junk after module output") + +main()
\ No newline at end of file diff --git a/test/integration/targets/async/meta/main.yml b/test/integration/targets/async/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/async/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/async/tasks/main.yml b/test/integration/targets/async/tasks/main.yml new file mode 100644 index 0000000000..c6739dc256 --- /dev/null +++ b/test/integration/targets/async/tasks/main.yml @@ -0,0 +1,154 @@ +# test code for the async keyword +# (c) 2014, James Tanner <tanner.jc@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: run a 2 second loop + shell: for i in $(seq 1 2); do echo $i ; sleep 1; done; + async: 10 + poll: 1 + register: async_result + + +- debug: var=async_result + +- name: validate async returns + assert: + that: + - "'ansible_job_id' in async_result" + - "'changed' in async_result" + - "'cmd' in async_result" + - "'delta' in async_result" + - "'end' in async_result" + - "'finished' in async_result or async_result.finished == 1" + - "'rc' in async_result" + - "'start' in async_result" + - "'stderr' in async_result" + - "'stdout' in async_result" + - "'stdout_lines' in async_result" + - "async_result.rc == 0" + +- name: test async without polling + command: sleep 5 + async: 30 + poll: 0 + register: async_result + +- debug: var=async_result + +- name: validate async without polling returns + assert: + that: + - "'ansible_job_id' in async_result" + - "'started' in async_result" + - "'finished' not in async_result or async_result.finished == 0" + +- name: test skipped task handling + command: /bin/true + async: 15 + poll: 0 + when: False + +# test async "fire and forget, but check later" + +- name: 'start a task with "fire-and-forget"' + command: sleep 3 + async: 30 + poll: 0 + register: fnf_task + +- name: assert task was successfully started + assert: + that: + - fnf_task.started + - "'ansible_job_id' in fnf_task" + +- name: 'check on task started as a "fire-and-forget"' + async_status: jid={{ fnf_task.ansible_job_id }} + register: fnf_result + until: fnf_result.finished + retries: 10 + delay: 1 + +- name: assert task was successfully checked + assert: + that: + - fnf_result.finished + +- name: test graceful module failure + async_test: + fail_mode: graceful + async: 30 + poll: 1 + register: async_result + ignore_errors: true + +- name: assert task failed correctly + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result | changed == false + - async_result | failed + - async_result.msg == 'failed gracefully' + +- name: test exception module failure + async_test: + fail_mode: exception + async: 5 + poll: 1 + register: async_result + ignore_errors: true + +- name: validate response + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result.changed == false + - async_result | failed == true + - async_result.stderr is search('failing via exception', multiline=True) + +- name: test leading junk before JSON + async_test: + fail_mode: leading_junk + async: 5 + poll: 1 + register: async_result + +- name: validate response + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result.changed == true + - async_result | success + +- name: test trailing junk after JSON + async_test: + fail_mode: trailing_junk + async: 5 + poll: 1 + register: async_result + +- name: validate response + assert: + that: + - async_result.ansible_job_id is match('\d+\.\d+') + - async_result.finished == 1 + - async_result.changed == true + - async_result | success + - async_result.warnings[0] is search('trailing junk after module output') diff --git a/test/integration/targets/authorized_key/defaults/main.yml b/test/integration/targets/authorized_key/defaults/main.yml new file mode 100644 index 0000000000..e3a7606e01 --- /dev/null +++ b/test/integration/targets/authorized_key/defaults/main.yml @@ -0,0 +1,15 @@ +--- +dss_key_basic: > + ssh-dss DATA_BASIC root@testing +dss_key_unquoted_option: > + idle-timeout=5m ssh-dss DATA_UNQUOTED_OPTION root@testing +dss_key_command: > + command="/bin/true" ssh-dss DATA_COMMAND root@testing +dss_key_complex_command: > + command="echo foo 'bar baz'" ssh-dss DATA_COMPLEX_COMMAND root@testing +dss_key_command_single_option: > + no-port-forwarding,command="/bin/true" ssh-dss DATA_COMMAND_SINGLE_OPTIONS root@testing +dss_key_command_multiple_options: > + no-port-forwarding,idle-timeout=5m,command="/bin/true" ssh-dss DATA_COMMAND_MULTIPLE_OPTIONS root@testing +dss_key_trailing: > + ssh-dss DATA_TRAILING root@testing foo bar baz diff --git a/test/integration/targets/authorized_key/meta/main.yml b/test/integration/targets/authorized_key/meta/main.yml new file mode 100644 index 0000000000..145d4f7ca1 --- /dev/null +++ b/test/integration/targets/authorized_key/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/authorized_key/tasks/main.yml b/test/integration/targets/authorized_key/tasks/main.yml new file mode 100644 index 0000000000..9b2c245082 --- /dev/null +++ b/test/integration/targets/authorized_key/tasks/main.yml @@ -0,0 +1,272 @@ +# test code for the authorized_key module +# (c) 2014, James Cammarata <jcammarata@ansible.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/>. + + +# ------------------------------------------------------------- +# Setup steps + +- name: touch the authorized_keys file + file: dest="{{output_dir}}/authorized_keys" state=touch + register: result + +- name: assert that the authorized_keys file was created + assert: + that: + - 'result.changed == True' + - 'result.state == "file"' + +# ------------------------------------------------------------- +# basic ssh-dss key + +- name: add basic ssh-dss key + authorized_key: user=root key="{{ dss_key_basic }}" state=present path="{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_basic' + - 'result.key_options == None' + +- name: re-add basic ssh-dss key + authorized_key: user=root key="{{ dss_key_basic }}" state=present path="{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# ssh-dss key with an unquoted option + +- name: add ssh-dss key with an unquoted option + authorized_key: + user: root + key: "{{ dss_key_unquoted_option }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_unquoted_option' + - 'result.key_options == None' + +- name: re-add ssh-dss key with an unquoted option + authorized_key: + user: root + key: "{{ dss_key_unquoted_option }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# ssh-dss key with a leading command="/bin/foo" + +- name: add ssh-dss key with a leading command + authorized_key: + user: root + key: "{{ dss_key_command }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_command' + - 'result.key_options == None' + +- name: re-add ssh-dss key with a leading command + authorized_key: + user: root + key: "{{ dss_key_command }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# ssh-dss key with a complex quoted leading command +# ie. command="/bin/echo foo 'bar baz'" + +- name: add ssh-dss key with a complex quoted leading command + authorized_key: + user: root + key: "{{ dss_key_complex_command }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_complex_command' + - 'result.key_options == None' + +- name: re-add ssh-dss key with a complex quoted leading command + authorized_key: + user: root + key: "{{ dss_key_complex_command }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# ssh-dss key with a command and a single option, which are +# in a comma-separated list + +- name: add ssh-dss key with a command and a single option + authorized_key: + user: root + key: "{{ dss_key_command_single_option }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_command_single_option' + - 'result.key_options == None' + +- name: re-add ssh-dss key with a command and a single option + authorized_key: + user: root + key: "{{ dss_key_command_single_option }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# ssh-dss key with a command and multiple other options + +- name: add ssh-dss key with a command and multiple options + authorized_key: + user: root + key: "{{ dss_key_command_multiple_options }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_command_multiple_options' + - 'result.key_options == None' + +- name: re-add ssh-dss key with a command and multiple options + authorized_key: + user: root + key: "{{ dss_key_command_multiple_options }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# ssh-dss key with multiple trailing parts, which are space- +# separated and not quoted in any way + +- name: add ssh-dss key with trailing parts + authorized_key: + user: root + key: "{{ dss_key_trailing }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_trailing' + - 'result.key_options == None' + +- name: re-add ssh-dss key with trailing parts + authorized_key: + user: root + key: "{{ dss_key_trailing }}" + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that nothing changed + assert: + that: + - 'result.changed == False' + +# ------------------------------------------------------------- +# basic ssh-dss key with mutliple permit-open options +# https://github.com/ansible/ansible-modules-core/issues/1715 + +- name: add basic ssh-dss key with multi-opts + authorized_key: + user: root + key: "{{ dss_key_basic }}" + key_options: 'no-agent-forwarding,no-X11-forwarding,permitopen="10.9.8.1:8080",permitopen="10.9.8.1:9001"' + state: present + path: "{{output_dir|expanduser}}/authorized_keys" + register: result + +- name: assert that the key with multi-opts was added + assert: + that: + - 'result.changed == True' + - 'result.key == dss_key_basic' + - 'result.key_options == "no-agent-forwarding,no-X11-forwarding,permitopen=\"10.9.8.1:8080\",permitopen=\"10.9.8.1:9001\""' + +- name: get the file content + shell: cat "{{output_dir|expanduser}}/authorized_keys" | fgrep DATA_BASIC + register: content + +- name: validate content + assert: + that: + - 'content.stdout == "no-agent-forwarding,no-X11-forwarding,permitopen=\"10.9.8.1:8080\",permitopen=\"10.9.8.1:9001\" ssh-dss DATA_BASIC root@testing"' diff --git a/test/integration/targets/become/files/baz.txt b/test/integration/targets/become/files/baz.txt new file mode 100644 index 0000000000..b8d834daa4 --- /dev/null +++ b/test/integration/targets/become/files/baz.txt @@ -0,0 +1 @@ +testing tilde expansion with become diff --git a/test/integration/targets/become/tasks/main.yml b/test/integration/targets/become/tasks/main.yml new file mode 100644 index 0000000000..86462d4eea --- /dev/null +++ b/test/integration/targets/become/tasks/main.yml @@ -0,0 +1,83 @@ +- include_vars: default.yml + +- name: Create test user + become: True + become_user: root + user: + name: "{{ become_test_user }}" + +- name: test becoming user + shell: whoami + become: True + become_user: "{{ become_test_user }}" + register: results + +- assert: + that: + - "results.stdout == '{{ become_test_user }}'" + +- name: tilde expansion honors become in file + become: True + become_user: "{{ become_test_user }}" + file: + path: "~/foo.txt" + state: touch + +- name: check that the path in the user's home dir was created + become: True + become_user: "{{ become_test_user }}" + stat: + path: "~{{ become_test_user }}/foo.txt" + register: results + +- assert: + that: + - "results.stat.exists == True" + - "results.stat.path|dirname|basename == '{{ become_test_user }}'" + +- name: tilde expansion honors become in template + become: True + become_user: "{{ become_test_user }}" + template: + src: "bar.j2" + dest: "~/bar.txt" + +- name: check that the path in the user's home dir was created + become: True + become_user: "{{ become_test_user }}" + stat: + path: "~{{ become_test_user }}/bar.txt" + register: results + +- assert: + that: + - "results.stat.exists == True" + - "results.stat.path|dirname|basename == '{{ become_test_user }}'" + +- name: tilde expansion honors become in copy + become: True + become_user: "{{ become_test_user }}" + copy: + src: baz.txt + dest: "~/baz.txt" + +- name: check that the path in the user's home dir was created + become: True + become_user: "{{ become_test_user }}" + stat: + path: "~{{ become_test_user }}/baz.txt" + register: results + +- assert: + that: + - "results.stat.exists == True" + - "results.stat.path|dirname|basename == '{{ become_test_user }}'" + +- name: Remove test user and their home dir + become: True + become_user: root + user: + name: "{{ become_test_user }}" + state: "absent" + remove: "yes" + diff --git a/test/integration/targets/become/templates/bar.j2 b/test/integration/targets/become/templates/bar.j2 new file mode 100644 index 0000000000..7c5fe0ab49 --- /dev/null +++ b/test/integration/targets/become/templates/bar.j2 @@ -0,0 +1 @@ +{{ become_test_user }} diff --git a/test/integration/targets/become/vars/default.yml b/test/integration/targets/become/vars/default.yml new file mode 100644 index 0000000000..223d44ed24 --- /dev/null +++ b/test/integration/targets/become/vars/default.yml @@ -0,0 +1 @@ +become_test_user: ansibletest1 diff --git a/test/integration/targets/binary/files/b64_latin1 b/test/integration/targets/binary/files/b64_latin1 new file mode 100644 index 0000000000..c7fbdeb632 --- /dev/null +++ b/test/integration/targets/binary/files/b64_latin1 @@ -0,0 +1 @@ +Café Eñe diff --git a/test/integration/targets/binary/files/b64_utf8 b/test/integration/targets/binary/files/b64_utf8 new file mode 100644 index 0000000000..c7fbdeb632 --- /dev/null +++ b/test/integration/targets/binary/files/b64_utf8 @@ -0,0 +1 @@ +Café Eñe diff --git a/test/integration/targets/binary/files/from_playbook b/test/integration/targets/binary/files/from_playbook new file mode 100644 index 0000000000..c7fbdeb632 --- /dev/null +++ b/test/integration/targets/binary/files/from_playbook @@ -0,0 +1 @@ +Café Eñe diff --git a/test/integration/targets/binary/meta/main.yml b/test/integration/targets/binary/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/binary/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/binary/tasks/main.yml b/test/integration/targets/binary/tasks/main.yml new file mode 100644 index 0000000000..486ee6d6b0 --- /dev/null +++ b/test/integration/targets/binary/tasks/main.yml @@ -0,0 +1,131 @@ +--- +# Various ways users want to use binary data +# Could integrate into individual modules but currently these don't all work. +# Probably easier to see them all in a single block to know what we're testing. +# When we can start testing v2 we should test that all of these work. + +# In v1: The following line will traceback if it's the first task in the role. +# Does not traceback if it's the second or third etc task. +- debug: msg="{{ utf8_simple_accents|b64decode}}" + +# Expected values of the written files +- name: get checksums that we expect later files to have + copy: + src: from_playbook + dest: "{{ output_dir }}" + +- copy: + src: b64_utf8 + dest: "{{ output_dir }}" + +- copy: + src: b64_latin1 + dest: "{{ output_dir }}" + +- stat: + path: "{{ output_dir }}/from_playbook" + register: from_playbook + +- stat: + path: "{{ output_dir }}/b64_utf8" + register: b64_utf8 + +- stat: + path: "{{ output_dir }}/b64_latin1" + register: b64_latin1 + +# Tests themselves +- name: copy with utf-8 content in a playbook + copy: + content: "{{ simple_accents }}\n" + dest: "{{ output_dir }}/from_playbook.txt" + +- name: Check that copying utf-8 content matches + stat: + path: "{{ output_dir }}/from_playbook.txt" + register: results + +- assert: + that: + - 'results.stat.checksum == from_playbook.stat.checksum' + +- name: copy with utf8 in a base64 encoded string + copy: + content: "{{ utf8_simple_accents|b64decode }}\n" + dest: "{{ output_dir }}/b64_utf8.txt" + +- name: Check that utf8 in a base64 string matches + stat: + path: "{{ output_dir }}/b64_utf8.txt" + register: results + +- assert: + that: + - 'results.stat.checksum == b64_utf8.stat.checksum' + +- name: copy with latin1 in a base64 encoded string + copy: + content: "{{ latin1_simple_accents|b64decode }}\n" + dest: "{{ output_dir }}/b64_latin1.txt" + +- name: Check that latin1 in a base64 string matches + stat: + path: "{{ output_dir }}/b64_latin1.txt" + register: results + +- assert: + that: + - 'results.stat.checksum == b64_latin1.stat.checksum' + # This one depends on being able to pass binary data through + # Might be a while before we find a solution for this + ignore_errors: True + +- name: Template with a unicode string from the playbook + template: + src: "from_playbook_template.j2" + dest: "{{ output_dir }}/from_playbook_template.txt" + +- name: Check that writing a template from a playbook var matches + stat: + path: "{{ output_dir }}/from_playbook_template.txt" + register: results + +- assert: + that: + - 'results.stat.checksum == from_playbook.stat.checksum' + +- name: Template with utf8 in a base64 encoded string + template: + src: "b64_utf8_template.j2" + dest: "{{ output_dir }}/b64_utf8_template.txt" + +- name: Check that writing a template from a base64 encoded utf8 string matches + stat: + path: "{{ output_dir }}/b64_utf8_template.txt" + register: results + +- assert: + that: + - 'results.stat.checksum == b64_utf8.stat.checksum' + +- name: Template with latin1 in a base64 encoded string + template: + src: "b64_latin1_template.j2" + dest: "{{ output_dir }}/b64_latin1_template.txt" + +- name: Check that writing a template from a base64 encoded latin1 string matches + stat: + path: "{{ output_dir }}/b64_latin1_template.txt" + register: results + +- assert: + that: + - 'results.stat.checksum == b64_latin1.stat.checksum' + # This one depends on being able to pass binary data through + # Might be a while before we find a solution for this + ignore_errors: True + +# These might give garbled output but none of them should traceback +- debug: var=simple_accents +- debug: msg="{{ utf8_simple_accents|b64decode}}" +- debug: msg="{{ latin1_simple_accents|b64decode}}" diff --git a/test/integration/targets/binary/templates/b64_latin1_template.j2 b/test/integration/targets/binary/templates/b64_latin1_template.j2 new file mode 100644 index 0000000000..ee2fc1b19c --- /dev/null +++ b/test/integration/targets/binary/templates/b64_latin1_template.j2 @@ -0,0 +1 @@ +{{ latin1_simple_accents|b64decode }} diff --git a/test/integration/targets/binary/templates/b64_utf8_template.j2 b/test/integration/targets/binary/templates/b64_utf8_template.j2 new file mode 100644 index 0000000000..9fd3ed48b1 --- /dev/null +++ b/test/integration/targets/binary/templates/b64_utf8_template.j2 @@ -0,0 +1 @@ +{{ utf8_simple_accents|b64decode }} diff --git a/test/integration/targets/binary/templates/from_playbook_template.j2 b/test/integration/targets/binary/templates/from_playbook_template.j2 new file mode 100644 index 0000000000..3be6dd4f0b --- /dev/null +++ b/test/integration/targets/binary/templates/from_playbook_template.j2 @@ -0,0 +1 @@ +{{ simple_accents }} diff --git a/test/integration/targets/binary/vars/main.yml b/test/integration/targets/binary/vars/main.yml new file mode 100644 index 0000000000..f6d40232c3 --- /dev/null +++ b/test/integration/targets/binary/vars/main.yml @@ -0,0 +1,3 @@ +simple_accents: 'Café Eñe' +utf8_simple_accents: 'Q2Fmw6kgRcOxZQ==' +latin1_simple_accents: 'Q2Fm6SBF8WU=' diff --git a/test/integration/targets/changed_when/meta/main.yml b/test/integration/targets/changed_when/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/changed_when/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/changed_when/tasks/main.yml b/test/integration/targets/changed_when/tasks/main.yml new file mode 100644 index 0000000000..0e87b90340 --- /dev/null +++ b/test/integration/targets/changed_when/tasks/main.yml @@ -0,0 +1,42 @@ +# test code for the changed_when parameter +# (c) 2014, James Tanner <tanner.jc@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: ensure shell is always changed + shell: ls -al /tmp + register: shell_result + +- debug: var=shell_result + +- name: changed should always be true for shell + assert: + that: + - "shell_result.changed" + +- name: test changed_when override for shell + shell: ls -al /tmp + changed_when: False + register: shell_result + +- debug: var=shell_result + +- name: changed should be false + assert: + that: + - "not shell_result.changed" + + diff --git a/test/integration/targets/command_shell/files/create_afile.sh b/test/integration/targets/command_shell/files/create_afile.sh new file mode 100755 index 0000000000..e6fae448b2 --- /dev/null +++ b/test/integration/targets/command_shell/files/create_afile.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "win" > "$1"
\ No newline at end of file diff --git a/test/integration/targets/command_shell/files/remove_afile.sh b/test/integration/targets/command_shell/files/remove_afile.sh new file mode 100755 index 0000000000..4a7fea6617 --- /dev/null +++ b/test/integration/targets/command_shell/files/remove_afile.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +rm "$1"
\ No newline at end of file diff --git a/test/integration/targets/command_shell/files/test.sh b/test/integration/targets/command_shell/files/test.sh new file mode 100755 index 0000000000..ade17e9b8c --- /dev/null +++ b/test/integration/targets/command_shell/files/test.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo -n "win"
\ No newline at end of file diff --git a/test/integration/targets/command_shell/meta/main.yml b/test/integration/targets/command_shell/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/command_shell/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/command_shell/tasks/main.yml b/test/integration/targets/command_shell/tasks/main.yml new file mode 100644 index 0000000000..226f8df622 --- /dev/null +++ b/test/integration/targets/command_shell/tasks/main.yml @@ -0,0 +1,219 @@ +# Test code for the command and shell modules. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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/>. + +- set_fact: output_dir_test={{output_dir}}/test_command_shell + +- name: make sure our testing sub-directory does not exist + file: path="{{ output_dir_test }}" state=absent + +- name: create our testing sub-directory + file: path="{{ output_dir_test }}" state=directory + +- name: prep our test script + copy: src=test.sh dest="{{ output_dir_test }}" mode=0755 + +- name: prep our test script + copy: src=create_afile.sh dest="{{ output_dir_test }}" mode=0755 + +- name: prep our test script + copy: src=remove_afile.sh dest="{{ output_dir_test }}" mode=0755 + +- name: locate bash + shell: which bash + register: bash + +- name: locate sha1sum/shasum + shell: which sha1sum || which shasum + register: sha1sum + +## +## command +## + +- name: execute the test.sh script via command + command: "{{output_dir_test | expanduser}}/test.sh" + register: command_result0 + +- name: assert that the script executed correctly + assert: + that: + - "command_result0.rc == 0" + - "command_result0.stderr == ''" + - "command_result0.stdout == 'win'" + +# executable + +# FIXME doesn't have the expected stdout. + +#- name: execute the test.sh script with executable via command +# command: "{{output_dir_test | expanduser}}/test.sh executable={{ bash.stdout }}" +# register: command_result1 +# +#- name: assert that the script executed correctly with command +# assert: +# that: +# - "command_result1.rc == 0" +# - "command_result1.stderr == ''" +# - "command_result1.stdout == 'win'" + +# chdir + +- name: execute the test.sh script with chdir via command + command: ./test.sh chdir="{{output_dir_test | expanduser}}" + register: command_result2 + +- name: assert that the script executed correctly with chdir + assert: + that: + - "command_result2.rc == 0" + - "command_result2.stderr == ''" + - "command_result2.stdout == 'win'" + +# creates + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + +- name: create afile.txt with create_afile.sh via command + command: "{{output_dir_test | expanduser}}/create_afile.sh {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.txt" + +- name: verify that afile.txt is present + file: path={{output_dir_test}}/afile.txt state=file + +- name: re-run previous command using creates with globbing + command: "{{output_dir_test | expanduser}}/create_afile.sh {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.*" + register: command_result3 + +- name: assert that creates with globbing is working + assert: + that: + - "command_result3.changed != True" + +# removes + +- name: remove afile.txt with remote_afile.sh via command + command: "{{output_dir_test | expanduser}}/remove_afile.sh {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.txt" + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + +- name: re-run previous command using removes with globbing + command: "{{output_dir_test | expanduser}}/remove_afile.sh {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.*" + register: command_result4 + +- name: assert that removes with globbing is working + assert: + that: + - "command_result4.changed != True" + +## +## shell +## + +- name: execute the test.sh script + shell: "{{output_dir_test | expanduser}}/test.sh" + register: shell_result0 + +- name: assert that the script executed correctly + assert: + that: + - "shell_result0.rc == 0" + - "shell_result0.stderr == ''" + - "shell_result0.stdout == 'win'" + +# executable + +# FIXME doesn't pass the expected stdout + +#- name: execute the test.sh script +# shell: "{{output_dir_test | expanduser}}/test.sh executable={{ bash.stdout }}" +# register: shell_result1 +# +#- name: assert that the shell executed correctly +# assert: +# that: +# - "shell_result1.rc == 0" +# - "shell_result1.stderr == ''" +# - "shell_result1.stdout == 'win'" + +# chdir + +- name: execute the test.sh script with chdir + shell: ./test.sh chdir="{{output_dir_test | expanduser}}" + register: shell_result2 + +- name: assert that the shell executed correctly with chdir + assert: + that: + - "shell_result2.rc == 0" + - "shell_result2.stderr == ''" + - "shell_result2.stdout == 'win'" + +# creates + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + +- name: execute the test.sh script with chdir + shell: "{{output_dir_test | expanduser}}/test.sh > {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.txt" + +- name: verify that afile.txt is present + file: path={{output_dir_test}}/afile.txt state=file + +# multiline + +- name: remove test file previously created + file: path={{output_dir_test | expanduser}}/afile.txt state=absent + +- name: execute a shell command using a literal multiline block + args: + executable: "{{ bash.stdout }}" + shell: | + echo this is a \ + "multiline echo" \ + "with a new line + in quotes" \ + | {{ sha1sum.stdout }} \ + | tr -s ' ' \ + | cut -f1 -d ' ' + echo "this is a second line" + register: shell_result5 + +- debug: var=shell_result5 + +- name: assert the multiline shell command ran as expected + assert: + that: + - "shell_result5.changed" + - "shell_result5.stdout == '5575bb6b71c9558db0b6fbbf2f19909eeb4e3b98\nthis is a second line'" + +- name: execute a shell command using a literal multiline block with arguments in it + shell: | + executable="{{ bash.stdout }}" + creates={{output_dir_test | expanduser}}/afile.txt + echo "test" + register: shell_result6 + +- name: assert the multiline shell command with arguments in it run as expected + assert: + that: + - "shell_result6.changed" + - "shell_result6.stdout == 'test'" + +- name: remove the previously created file + file: path={{output_dir_test}}/afile.txt state=absent diff --git a/test/integration/targets/conditionals/tasks/main.yml b/test/integration/targets/conditionals/tasks/main.yml new file mode 100644 index 0000000000..0908b910fb --- /dev/null +++ b/test/integration/targets/conditionals/tasks/main.yml @@ -0,0 +1,309 @@ +# test code for conditional statements +# (c) 2014, James Cammarata <jcammarata@ansible.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: test conditional '==' + shell: echo 'testing' + when: 1 == 1 + register: result + +- name: assert conditional '==' ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional '==' + shell: echo 'testing' + when: 0 == 1 + register: result + +- name: assert bad conditional '==' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test conditional '!=' + shell: echo 'testing' + when: 0 != 1 + register: result + +- name: assert conditional '!=' ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional '!=' + shell: echo 'testing' + when: 1 != 1 + register: result + +- name: assert bad conditional '!=' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test conditional 'in' + shell: echo 'testing' + when: 1 in [1,2,3] + register: result + +- name: assert conditional 'in' ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional 'in' + shell: echo 'testing' + when: 1 in [7,8,9] + register: result + +- name: assert bad conditional 'in' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test conditional 'not in' + shell: echo 'testing' + when: 0 not in [1,2,3] + register: result + +- name: assert conditional 'not in' ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional 'not in' + shell: echo 'testing' + when: 1 not in [1,2,3] + register: result + +- name: assert bad conditional 'not in' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test conditional 'is defined' + shell: echo 'testing' + when: test_bare is defined + register: result + +- name: assert conditional 'is defined' ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional 'is defined' + shell: echo 'testing' + when: foo_asdf_xyz is defined + register: result + +- name: assert bad conditional 'is defined' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test conditional 'is not defined' + shell: echo 'testing' + when: foo_asdf_xyz is not defined + register: result + +- name: assert conditional 'is not defined' ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional 'is not defined' + shell: echo 'testing' + when: test_bare is not defined + register: result + +- name: assert bad conditional 'is not defined' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test bad conditional 'is undefined' + shell: echo 'testing' + when: test_bare is undefined + register: result + +- name: assert bad conditional 'is undefined' did NOT run + assert: + that: + - "result.skipped == true" + +- name: test bare conditional + shell: echo 'testing' + when: test_bare + register: result + +- name: assert bare conditional ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test conditional using a variable + shell: echo 'testing' + when: test_bare_var == 123 + register: result + +- name: assert conditional using a variable ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test good conditional based on nested variables + shell: echo 'testing' + when: test_bare_nested_good + register: result + +- name: assert good conditional based on nested var ran + assert: + that: + - "result.changed == true" + - "result.stdout == 'testing'" + - "result.rc == 0" + +- name: test bad conditional based on nested variables + shell: echo 'testing' + when: test_bare_nested_bad + register: result + +- name: assert that the bad nested conditional did NOT run + assert: + that: + - "result.skipped == true" + +#----------------------------------------------------------------------- +# proper booleanification tests (issue #8629) + +- name: set fact to string 'false' + set_fact: bool_test1=false + +- name: set fact to string 'False' + set_fact: bool_test2=False + +- name: set fact to a proper boolean using complex args + set_fact: + bool_test3: false + +- name: "test boolean value 'false' string using 'when: var'" + command: echo 'hi' + when: bool_test1 + register: result + +- name: assert that the task did not run for 'false' + assert: + that: + - "result.skipped == true" + +- name: "test boolean value 'false' string using 'when: not var'" + command: echo 'hi' + when: not bool_test1 + register: result + +- name: assert that the task DID run for not 'false' + assert: + that: + - "result.changed" + +- name: "test boolean value of 'False' string using 'when: var'" + command: echo 'hi' + when: bool_test2 + register: result + +- name: assert that the task did not run for 'False' + assert: + that: + - "result.skipped == true" + +- name: "test boolean value 'False' string using 'when: not var'" + command: echo 'hi' + when: not bool_test2 + register: result + +- name: assert that the task DID run for not 'False' + assert: + that: + - "result.changed" + +- name: "test proper boolean value of complex arg using 'when: var'" + command: echo 'hi' + when: bool_test3 + register: result + +- name: assert that the task did not run for proper boolean false + assert: + that: + - "result.skipped == true" + +- name: "test proper boolean value of complex arg using 'when: not var'" + command: echo 'hi' + when: not bool_test3 + register: result + +- name: assert that the task DID run for not false + assert: + that: + - "result.changed" + +- set_fact: skipped_bad_attribute=True +- block: + - name: test a with_items loop using a variable with a missing attribute + debug: var=item + with_items: "{{cond_bad_attribute.results | default('')}}" + register: result + - set_fact: skipped_bad_attribute=False + - name: assert the task was skipped + assert: + that: + - skipped_bad_attribute + when: cond_bad_attribute is defined and 'results' in cond_bad_attribute + +- name: test a with_items loop skipping a single item + debug: var=item + with_items: "{{cond_list_of_items.results}}" + when: item != 'b' + register: result + +- debug: var=result + +- name: assert only a single item was skipped + assert: + that: + - result.results|length == 3 + - result.results[1].skipped + +- name: test complex templated condition + debug: msg="it works" + when: vars_file_var in things1|union([vars_file_var]) diff --git a/test/integration/targets/conditionals/vars/main.yml b/test/integration/targets/conditionals/vars/main.yml new file mode 100644 index 0000000000..a02b87168e --- /dev/null +++ b/test/integration/targets/conditionals/vars/main.yml @@ -0,0 +1,17 @@ +--- +# foo is a dictionary that will be used to check that +# a conditional passes a with_items loop on a variable +# with a missing attribute (ie. foo.results) +cond_bad_attribute: + bar: a + +cond_list_of_items: + results: + - a + - b + - c + +things1: + - 1 + - 2 +vars_file_var: 321 diff --git a/test/integration/targets/copy/files/foo.txt b/test/integration/targets/copy/files/foo.txt new file mode 100644 index 0000000000..7c6ded14ec --- /dev/null +++ b/test/integration/targets/copy/files/foo.txt @@ -0,0 +1 @@ +foo.txt diff --git a/test/integration/targets/copy/files/subdir/bar.txt b/test/integration/targets/copy/files/subdir/bar.txt new file mode 100644 index 0000000000..76018072e0 --- /dev/null +++ b/test/integration/targets/copy/files/subdir/bar.txt @@ -0,0 +1 @@ +baz diff --git a/test/integration/targets/copy/files/subdir/subdir2/baz.txt b/test/integration/targets/copy/files/subdir/subdir2/baz.txt new file mode 100644 index 0000000000..76018072e0 --- /dev/null +++ b/test/integration/targets/copy/files/subdir/subdir2/baz.txt @@ -0,0 +1 @@ +baz diff --git a/test/integration/targets/copy/files/subdir/subdir2/subdir3/subdir4/qux.txt b/test/integration/targets/copy/files/subdir/subdir2/subdir3/subdir4/qux.txt new file mode 100644 index 0000000000..78df5b06bd --- /dev/null +++ b/test/integration/targets/copy/files/subdir/subdir2/subdir3/subdir4/qux.txt @@ -0,0 +1 @@ +qux
\ No newline at end of file diff --git a/test/integration/targets/copy/meta/main.yml b/test/integration/targets/copy/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/copy/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/copy/tasks/main.yml b/test/integration/targets/copy/tasks/main.yml new file mode 100644 index 0000000000..128c0a793e --- /dev/null +++ b/test/integration/targets/copy/tasks/main.yml @@ -0,0 +1,260 @@ +# test code for the copy module and action plugin +# (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: record the output directory + set_fact: output_file={{output_dir}}/foo.txt + +- name: locate sha1sum/shasum + shell: which sha1sum || which shasum + register: sha1sum + +- name: initiate a basic copy, and also test the mode + copy: src=foo.txt dest={{output_file}} mode=0444 + register: copy_result + +- name: check the mode of the output file + file: name={{output_file}} state=file + register: file_result_check + +- name: assert the mode is correct + assert: + that: + - "file_result_check.mode == '0444'" + +- name: assert basic copy worked + assert: + that: + - "'changed' in copy_result" + - "'dest' in copy_result" + - "'group' in copy_result" + - "'gid' in copy_result" + - "'md5sum' in copy_result" + - "'checksum' in copy_result" + - "'owner' in copy_result" + - "'size' in copy_result" + - "'src' in copy_result" + - "'state' in copy_result" + - "'uid' in copy_result" + +- name: verify that the file was marked as changed + assert: + that: + - "copy_result.changed == true" + +- name: verify that the file checksums are correct + assert: + that: + - "copy_result.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6'" + +- name: verify that the legacy md5sum is correct + assert: + that: + - "copy_result.md5sum == 'c47397529fe81ab62ba3f85e9f4c71f2'" + when: ansible_fips|bool != True + +- name: check the stat results of the file + stat: path={{output_file}} + register: stat_results + +- debug: var=stat_results + +- name: assert the stat results are correct + assert: + that: + - "stat_results.stat.exists == true" + - "stat_results.stat.isblk == false" + - "stat_results.stat.isfifo == false" + - "stat_results.stat.isreg == true" + - "stat_results.stat.issock == false" + - "stat_results.stat.checksum == 'c79a6506c1c948be0d456ab5104d5e753ab2f3e6'" + +- name: verify that the legacy md5sum is correct + assert: + that: + - "stat_results.stat.md5 == 'c47397529fe81ab62ba3f85e9f4c71f2'" + when: ansible_fips|bool != True + +- name: overwrite the file via same means + copy: src=foo.txt dest={{output_file}} + register: copy_result2 + +- name: assert that the file was not changed + assert: + that: + - "not copy_result2|changed" + +- name: overwrite the file using the content system + copy: content="modified" dest={{output_file}} + register: copy_result3 + +- name: assert that the file has changed + assert: + that: + - "copy_result3|changed" + - "'content' not in copy_result3" + +# test recursive copy + +- name: set the output subdirectory + set_fact: output_subdir={{output_dir}}/sub + +- name: make an output subdirectory + file: name={{output_subdir}} state=directory + +- name: test recursive copy to directory + copy: src=subdir dest={{output_subdir}} directory_mode=0700 + register: recursive_copy_result + +- debug: var=recursive_copy_result +- name: assert that the recursive copy did something + assert: + that: + - "recursive_copy_result|changed" + +- name: check that a file in a directory was transferred + stat: path={{output_dir}}/sub/subdir/bar.txt + register: stat_bar + +- name: check that a file in a deeper directory was transferred + stat: path={{output_dir}}/sub/subdir/subdir2/baz.txt + register: stat_bar2 + +- name: check that a file in a directory whose parent contains a directory alone was transferred + stat: path={{output_dir}}/sub/subdir/subdir2/subdir3/subdir4/qux.txt + register: stat_bar3 + +- name: assert recursive copy things + assert: + that: + - "stat_bar.stat.exists" + - "stat_bar2.stat.exists" + - "stat_bar3.stat.exists" + +- name: stat the recursively copied directories + stat: path={{output_dir}}/sub/{{item}} + register: dir_stats + with_items: + - "subdir" + - "subdir/subdir2" + - "subdir/subdir2/subdir3" + - "subdir/subdir2/subdir3/subdir4" + +- name: assert recursive copied directories mode + assert: + that: + - "{{item.stat.mode}} == 0700" + with_items: "{{dir_stats.results}}" + + +# errors on this aren't presently ignored so this test is commented out. But it would be nice to fix. +# + +- name: overwrite the file again using the content system, also passing along file params + copy: content="modified" dest={{output_file}} + register: copy_result4 + +#- name: assert invalid copy input location fails +# copy: src=invalid_file_location_does_not_exist dest={{output_dir}}/file.txt +# ignore_errors: True +# register: failed_copy + +- name: copy already copied directory again + copy: src=subdir dest={{output_subdir | expanduser}} owner={{ansible_ssh_user|default(omit)}} + register: copy_result5 + +- name: assert that the directory was not changed + assert: + that: + - "not copy_result5|changed" + +# issue 8394 +- name: create a file with content and a literal multiline block + copy: | + content='this is the first line + this is the second line + + this line is after an empty line + this line is the last line + ' + dest={{output_dir}}/multiline.txt + register: copy_result6 + +- debug: var=copy_result6 + +- name: assert the multiline file was created correctly + assert: + that: + - "copy_result6.changed" + - "copy_result6.dest == '{{output_dir|expanduser}}/multiline.txt'" + - "copy_result6.checksum == '9cd0697c6a9ff6689f0afb9136fa62e0b3fee903'" + +# test overwriting a file as an unprivileged user (pull request #8624) +# this can't be relative to {{output_dir}} as ~root usually has mode 700 + +- name: create world writable directory + file: dest=/tmp/worldwritable state=directory mode=0777 + +- name: create world writable file + copy: dest=/tmp/worldwritable/file.txt content="bar" mode=0666 + +- name: overwrite the file as user nobody + copy: dest=/tmp/worldwritable/file.txt content="baz" + become: yes + become_user: nobody + register: copy_result7 + +- name: assert the file was overwritten + assert: + that: + - "copy_result7.changed" + - "copy_result7.dest == '/tmp/worldwritable/file.txt'" + - "copy_result7.checksum == 'bbe960a25ea311d21d40669e93df2003ba9b90a2'" + +- name: clean up + file: dest=/tmp/worldwritable state=absent + +# test overwriting a link using "follow=yes" so that the link +# is preserved and the link target is updated + +- name: create a test file to symlink to + copy: dest={{output_dir}}/follow_test content="this is the follow test file\n" + +- name: create a symlink to the test file + file: path={{output_dir}}/follow_link src='./follow_test' state=link + +- name: update the test file using follow=True to preserve the link + copy: dest={{output_dir}}/follow_link content="this is the new content\n" follow=yes + register: replace_follow_result + +- name: stat the link path + stat: path={{output_dir}}/follow_link + register: stat_link_result + +- name: assert that the link is still a link + assert: + that: + - stat_link_result.stat.islnk + +- name: get the checksum of the link target + shell: "{{ sha1sum.stdout }} {{output_dir}}/follow_test | cut -f1 -sd ' '" + register: target_file_result + +- name: assert that the link target was updated + assert: + that: + - replace_follow_result.checksum == target_file_result.stdout diff --git a/test/integration/targets/docker/files/devdockerCA.crt b/test/integration/targets/docker/files/devdockerCA.crt new file mode 100644 index 0000000000..14f1b2f7ee --- /dev/null +++ b/test/integration/targets/docker/files/devdockerCA.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIJAPczDjnFOjH/MA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD +VQQGEwJVUzELMAkGA1UECAwCTkMxDzANBgNVBAcMBkR1cmhhbTEQMA4GA1UECgwH +QW5zaWJsZTEfMB0GA1UEAwwWZG9ja2VydGVzdC5hbnNpYmxlLmNvbTEkMCIGCSqG +SIb3DQEJARYVdGt1cmF0b21pQGFuc2libGUuY29tMB4XDTE1MDMxNzIyMjc1OVoX +DTQyMDgwMjIyMjc1OVowgYQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzEPMA0G +A1UEBwwGRHVyaGFtMRAwDgYDVQQKDAdBbnNpYmxlMR8wHQYDVQQDDBZkb2NrZXJ0 +ZXN0LmFuc2libGUuY29tMSQwIgYJKoZIhvcNAQkBFhV0a3VyYXRvbWlAYW5zaWJs +ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIk4D0+QY3obQM +I/BPmI4pFFu734HHz98ce6Qat7WYiGUHsnt3LHw2a6zMsgP3siD1zqGHtk1IipWR +IwZbXm1spww/8YNUEE8wbXlLGI8IPUpg2J7NS2SdYIuN/TrQMqCUt7fFb+7OQjaH +RtR0LtXhP96al3E8BR9G6AiS67XuwdTL4vrXLUWISjNyF2Vj7xQsp8KRrq0qnXhq +pefeBi1fD9DG5f76j3s8lqGiOg9FHegvfodonNGcqE16T/vBhQcf+NjenlFvR2Lh +3wb/RCo/b1IhZHKNx32fJ/WpiKXkrLYFvwtIWtLw6XIwwarc+n7AfGqKnt4h4bAG +a+5aNnlFAgMBAAGjUDBOMB0GA1UdDgQWBBRZpu6oomSlpCvy2VgOHbWwDwVl1jAf +BgNVHSMEGDAWgBRZpu6oomSlpCvy2VgOHbWwDwVl1jAMBgNVHRMEBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQCqOSFzTgQDww5bkNRCQrg7lTKzXW9bJpJ5NZdTLwh6 +b+e+XouRH+lBe7Cnn2RTtuFYVfm8hQ1Ra7GDM3v2mJns/s3zDkRINZMMVXddzl5S +M8QxsFJK41PaL9wepizslkcg19yQkdWJQYPDeFurlFvwtakhZE7ttawYi5bFkbCd +4fchMNBBmcigpSfoWb/L2lK2vVKBcfOdUl+V6k49lpf8u7WZD0Xi2cbBhw17tPj4 +ulKZaVNdzj0GFfhpQe/MtDoqxStRpHamdk0Y6fN+CvoW7RPDeVsqkIgCu30MOFuG +A53ZtOc3caYRyGYJtIIl0Rd5uIApscec/6RGiFX6Gab8 +-----END CERTIFICATE----- diff --git a/test/integration/targets/docker/files/devdockerCA.key b/test/integration/targets/docker/files/devdockerCA.key new file mode 100644 index 0000000000..0c8c0ee7b0 --- /dev/null +++ b/test/integration/targets/docker/files/devdockerCA.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAyJOA9PkGN6G0DCPwT5iOKRRbu9+Bx8/fHHukGre1mIhlB7J7 +dyx8NmuszLID97Ig9c6hh7ZNSIqVkSMGW15tbKcMP/GDVBBPMG15SxiPCD1KYNie +zUtknWCLjf060DKglLe3xW/uzkI2h0bUdC7V4T/empdxPAUfRugIkuu17sHUy+L6 +1y1FiEozchdlY+8ULKfCka6tKp14aqXn3gYtXw/QxuX++o97PJahojoPRR3oL36H +aJzRnKhNek/7wYUHH/jY3p5Rb0di4d8G/0QqP29SIWRyjcd9nyf1qYil5Ky2Bb8L +SFrS8OlyMMGq3Pp+wHxqip7eIeGwBmvuWjZ5RQIDAQABAoIBAQCVOumfWgf+LBlB +TxvknKRoe/Ukes6cU1S0ZGlcV4KM0i4Y4/poWHiyJLqUMX4yNB3BxNL5nfEyH6nY +Ki74m/Dd/gtnJ9GGIfxJE6pC7Sq9/pvwIjtEkutxC/vI0LeJX6GKBIZ+JyGN5EWd +sF0xdAc9Z7+/VR2ygj0bDFgUt7rMv6fLaXh6i5Ms0JV7I/HkIi0Lmy9FncJPOTjP +/Wb3Rj5twDppBqSiqU2JNQHysWzNbp8nzBGeR0+WU6xkWjjGzVyQZJq4XJQhqqot +t+v+/lF+jObujcRxPRStaA5IoQdmls3l+ubkoFeNp3j6Nigz40wjTJArMu/Q9xQ5 +A+kHYNgBAoGBAPVNku0eyz1SyMM8FNoB+AfSpkslTnqfmehn1GCOOS9JPimGWS3A +UlAs/PAPW/H/FTM38eC89GsKKVV8zvwkERNwf+PIGzkQrJgYLxGwoflAKsvFoQi9 +PVbIn0TBDZ3TWyNfGul62fEgNen4B46d7kG6l/C3p9eKKCo3sCBgWl8FAoGBANFS +n9YWyAYmHQAWy5R0YeTsdtiRpZWkB0Is9Jr8Zm/DQDNnsKgvXw//qxuWYMi68teK +6o8t5mgDQNWBu3rXrU73f8mMVJNmzSHFbyQEyFOJ9yvI5qMRbJfvdURUje6d3ZUw +G7olKjX0fec4cAG7hbT8sMDvIbnATdhh3VppiEVBAoGBAJKidJnaNpPJ0MkkOTK4 +ypOikFWLT4ZtsYsDxiiR3A0wM0CPVu/Kb2oN+oVmKQhX+0xKvQQi79iskljP6ss+ +pBaCwXBgRiWumf2xNzHT7H8apHp7APBAb1JZSxvGa2VU2r4iM+wty+of3xqlcZ8H +OU2BRSJYJrTpmWjjMR2pe1whAoGAfMTbMSlzIPcm4h60SlD06Rdp370xDfkvumpB +gwBfrs6bPgjYa+eQqmCjBValagDFL2VGWwHpDKajxqAFuDtGuoMcUG6tGw9zxmWA +0d9n6SObiSW/FAQWzpmVNJ2R3GGM6pg6bsIoXvDU+zXQzbeRA0h7swTW/Xl67Teo +UXQGHgECgYEAjckqv2e39AgBvjxvj9SylVbFNSERrbpmiIRH31MnAHpTXbxRf7K+ +/79vUsRfQun9F/+KVfjUyMqRj0PE2tS4ATIjqQsa18RCB4mAE3sNsKz8HbJfzIFq +eEqAWmURm6gRmLmaTMlXS0ZtZaw/A2Usa/DJumu9CsfBu7ZJbDnrQIY= +-----END RSA PRIVATE KEY----- diff --git a/test/integration/targets/docker/files/devdockerCA.srl b/test/integration/targets/docker/files/devdockerCA.srl new file mode 100644 index 0000000000..78f0162afe --- /dev/null +++ b/test/integration/targets/docker/files/devdockerCA.srl @@ -0,0 +1 @@ +D96F3E552F279F46 diff --git a/test/integration/targets/docker/files/docker-registry.htpasswd b/test/integration/targets/docker/files/docker-registry.htpasswd new file mode 100644 index 0000000000..7cee295817 --- /dev/null +++ b/test/integration/targets/docker/files/docker-registry.htpasswd @@ -0,0 +1 @@ +testdocker:$apr1$6cYd3tA9$4Dc9/I5Z.bl8/br8O/6B41 diff --git a/test/integration/targets/docker/files/dockertest.ansible.com.crt b/test/integration/targets/docker/files/dockertest.ansible.com.crt new file mode 100644 index 0000000000..e89327c3fa --- /dev/null +++ b/test/integration/targets/docker/files/dockertest.ansible.com.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYTCCAkkCCQDZbz5VLyefRjANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgMAk5DMQ8wDQYDVQQHDAZEdXJoYW0xEDAOBgNVBAoMB0Fuc2li +bGUxHzAdBgNVBAMMFmRvY2tlcnRlc3QuYW5zaWJsZS5jb20xJDAiBgkqhkiG9w0B +CQEWFXRrdXJhdG9taUBhbnNpYmxlLmNvbTAgFw0xNTAzMTcyMjMxNTBaGA8yMjg4 +MTIzMDIyMzE1MFowXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5DMQ8wDQYDVQQH +DAZEdXJoYW0xEDAOBgNVBAoMB0Fuc2libGUxHzAdBgNVBAMMFmRvY2tlcnRlc3Qu +YW5zaWJsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7WpI3 +QuuARgPufAA0JkGCGIUNWqFyTEngOWvBVEuk5TnDB4x78OCE9j7rr75OxZaSc6Y7 +oFTl+hhlgt6sqj+GXehgCHLA97CCc8eUqGv3bwdIIg/hahCPjEWfYzocX1xmUdzN +6klbV9lSO7FGSuk7W4DNga/weRfZmVoPi6jqTvx0tFsGrHVb1evholUKpxaOEYQZ +2NJ22+UXpUyVzN/mw5TAGNG0/yR7sIgCjKYCsYF8k79SfNDMJ1VcCPy3aag45jaz +WoA+OIJJFRkAaPSM5VtnbGBv/slpDVaKfl2ei7Ey3mKx1b7jYMzRz07Gw+zqr1gJ +kBWvfjR7ioxXcN7jAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAJyF24tCq5R8SJto +EMln0m9dMoJTC5usaBYBUMMe6hV2ikUGaXVDIqY+Yypt1sIcjGnLRmehJbej8iS7 +4aypuLc8Fgb4CvW+gY3I3W1iF7ZxIN/4yr237Z9KH1d1uGi+066Sk94OCXlqgsb+ +RzU6XOg+PMIjYC/us5VRv8a2qfjIA8getR+19nP+hR6NgIQcEyRKG2FmhkUSAwd8 +60FhpW4UmPQmn0ErZmRwdp2hNPj5g3my5iOSi7DzdK4CwZJAASOoWsbQIxP0k4JE +PMo7Ad1YxXlOvNWIA8FLMkRsq3li6KJ17WBdEYgFeuxWpf1/x1WA+WpwEIfC5cuR +A5LkaNI= +-----END CERTIFICATE----- diff --git a/test/integration/targets/docker/files/dockertest.ansible.com.csr b/test/integration/targets/docker/files/dockertest.ansible.com.csr new file mode 100644 index 0000000000..62b1f8535a --- /dev/null +++ b/test/integration/targets/docker/files/dockertest.ansible.com.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICozCCAYsCAQAwXjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5DMQ8wDQYDVQQH +DAZEdXJoYW0xEDAOBgNVBAoMB0Fuc2libGUxHzAdBgNVBAMMFmRvY2tlcnRlc3Qu +YW5zaWJsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7WpI3 +QuuARgPufAA0JkGCGIUNWqFyTEngOWvBVEuk5TnDB4x78OCE9j7rr75OxZaSc6Y7 +oFTl+hhlgt6sqj+GXehgCHLA97CCc8eUqGv3bwdIIg/hahCPjEWfYzocX1xmUdzN +6klbV9lSO7FGSuk7W4DNga/weRfZmVoPi6jqTvx0tFsGrHVb1evholUKpxaOEYQZ +2NJ22+UXpUyVzN/mw5TAGNG0/yR7sIgCjKYCsYF8k79SfNDMJ1VcCPy3aag45jaz +WoA+OIJJFRkAaPSM5VtnbGBv/slpDVaKfl2ei7Ey3mKx1b7jYMzRz07Gw+zqr1gJ +kBWvfjR7ioxXcN7jAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAoPgw9dlA3Ys2 +oahtr2KMNFnHnab6hUr/CuDIygkOft+MCX1cPXY1c0R72NQq42TjAFO5UnriJ0Jg +rcWgBAw8TCOHH77ZWawQFjWWoxNTy+bfXNJ002tzc4S/A4s8ytcFQN7E2irbGtUB +ratVaE+c6RvD/o48N4YLUyJbJK84FZ1xMnJI0z5R6XzDWEqYbobzkM/aUWvDTT9F ++F9H5W/3sIhNFVGLygSKbhgrb6eaC8R36fcmTRfYYdT4GrpXFePoZ4LJGCKiiaGV +p8gZzYQ9xjRYDP2OUMacBDlX1Mu5IJ2SCfjavD1hMhB54tWiiw3CRMJcNMql7ob/ +ZHH8UDMqgA== +-----END CERTIFICATE REQUEST----- diff --git a/test/integration/targets/docker/files/dockertest.ansible.com.key b/test/integration/targets/docker/files/dockertest.ansible.com.key new file mode 100644 index 0000000000..bda2bb6126 --- /dev/null +++ b/test/integration/targets/docker/files/dockertest.ansible.com.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAu1qSN0LrgEYD7nwANCZBghiFDVqhckxJ4DlrwVRLpOU5wweM +e/DghPY+66++TsWWknOmO6BU5foYZYLerKo/hl3oYAhywPewgnPHlKhr928HSCIP +4WoQj4xFn2M6HF9cZlHczepJW1fZUjuxRkrpO1uAzYGv8HkX2ZlaD4uo6k78dLRb +Bqx1W9Xr4aJVCqcWjhGEGdjSdtvlF6VMlczf5sOUwBjRtP8ke7CIAoymArGBfJO/ +UnzQzCdVXAj8t2moOOY2s1qAPjiCSRUZAGj0jOVbZ2xgb/7JaQ1Win5dnouxMt5i +sdW+42DM0c9OxsPs6q9YCZAVr340e4qMV3De4wIDAQABAoIBABjczxSIS+pM4E6w +o/JHtV/HUzjPcydQ2mjoFdWlExjB1qV8BfeYoqLibr0mKFIZxH6Q3FmDUGDojH5E +HLq7KQzyv1inJltXQ1Q8exrOMu22DThUVNksEyCJk9+v8lE7km59pJiq46s8gDl6 +dG8Il+TporEi6a820qRsxlfTx8m4EUbyPIhf2e2wYdqiscLwj49ZzMs3TFJxN3j4 +lLP3QDHz9n8q+XXpUT9+rsePe4D4DVVRLhg8w35zkys36xfvBZrI+9SytSs+r1/e +X4gVhxeX9q3FkvXiw1IDGPr0l5X7SH+5zk7JWuLfFbNBK02zR/Bd2OIaYAOmyIFk +ZzsVfokCgYEA8Cj04S32Tga7lOAAUEuPjgXbCtGYqBUJ/9mlMHJBtyl4vaBRm1Z3 +1YQqlL3yGM1F6ZStPWs86vsVaScypr7+RnmQ/uPjz1g2jNI9vomqRkzpzd8/bBwW +J3FCaKFIfl9uQx4ac7piAYdhNXswjQ7Kzn5xgG24i8EkUm6+UxarA38CgYEAx7X+ +qOVT+kA5WU1EDIc2x3Au0PhNIXiHOGRLW0MC7Vy1xBrgxfVrz6J8flBXOxmWYjRq +3dFiHA9S7WPQStkgTjzE91sthLefJ8DKXE4IrRkvYXIIX8DqkcFxTHS/OzckTcK/ +z79jNOPYA1s+z2jzgd24sslXbqxNz1LqZ/PlRp0CgYEAik8cEF72/aK0/x0uMRAD +IcjPiGCDKTHMq3M9xjPXEtQofBTLSsm2g9n05+qodY4qmEYOq1OKJs3pW8C+U/ek +2xOB5Ll75lqoN9uQwZ3o2UnMUMskbG+UdqyskTNpW5Y8Gx1IIKQTc0vzOOi0YlhF +hjydw1ftM1dNQsgShimE3aMCgYEAwITwFk7kcoTBBBZY+B7Mrtu1Ndt3N0HiUHlW +r4Zc5waNbptefVbF9GY1zuqR/LYA43CWaHj1NAmNrqye2diPrPwmADHUInGEqqTO +LsdG099Ibo6oBe6J8bJiDwsoYeQZSiDoGVPtRcoyraGjXfxVaaac6zTu5RCS/b53 +m3hhWH0CgYAqi3x10NpJHInU/zNa1GhI9UVJzabE2APdbPHvoE/yyfpCGhExiXZw +MDImUzc59Ro0pCZ9Bk7pd5LwdjjeJXih7jaRZQlPD1BeM6dKdmJps1KMaltOOJ4J +W0FE34E+Kt5JeIix8zmhxgaAU9NVilaNx5tI/D65Y0inMBZpqedrtg== +-----END RSA PRIVATE KEY----- diff --git a/test/integration/targets/docker/files/nginx-docker-registry.conf b/test/integration/targets/docker/files/nginx-docker-registry.conf new file mode 100644 index 0000000000..99c7802e1b --- /dev/null +++ b/test/integration/targets/docker/files/nginx-docker-registry.conf @@ -0,0 +1,40 @@ +# For versions of Nginx > 1.3.9 that include chunked transfer encoding support +# Replace with appropriate values where necessary + +upstream docker-registry { + server localhost:5000; +} + +server { + listen 8080; + server_name dockertest.ansible.com; + + ssl on; + ssl_certificate /etc/pki/tls/certs/dockertest.ansible.com.crt; + ssl_certificate_key /etc/pki/tls/private/dockertest.ansible.com.key; + + proxy_set_header Host $http_host; # required for Docker client sake + proxy_set_header X-Real-IP $remote_addr; # pass on real client IP + + client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads + + # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486) + chunked_transfer_encoding on; + + location / { + # let Nginx know about our auth file + auth_basic "Restricted"; + auth_basic_user_file /etc/nginx/docker-registry.htpasswd; + + proxy_pass http://docker-registry; + } + location /_ping { + auth_basic off; + proxy_pass http://docker-registry; + } + location /v1/_ping { + auth_basic off; + proxy_pass http://docker-registry; + } + +} diff --git a/test/integration/targets/docker/meta/main.yml b/test/integration/targets/docker/meta/main.yml new file mode 100644 index 0000000000..399f3fb6e7 --- /dev/null +++ b/test/integration/targets/docker/meta/main.yml @@ -0,0 +1,20 @@ +# test code for the service module +# (c) 2014, James Cammarata <jcammarata@ansible.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/>. + +dependencies: + - prepare_tests diff --git a/test/integration/targets/docker/tasks/docker-setup-debian.yml b/test/integration/targets/docker/tasks/docker-setup-debian.yml new file mode 100644 index 0000000000..068011a093 --- /dev/null +++ b/test/integration/targets/docker/tasks/docker-setup-debian.yml @@ -0,0 +1,6 @@ +- name: Install docker packages (apt) + apt: + state: present + # Note: add docker-registry when available + name: docker.io,python-docker,netcat-openbsd,nginx + diff --git a/test/integration/targets/docker/tasks/docker-setup-rht.yml b/test/integration/targets/docker/tasks/docker-setup-rht.yml new file mode 100644 index 0000000000..c25821c3be --- /dev/null +++ b/test/integration/targets/docker/tasks/docker-setup-rht.yml @@ -0,0 +1,17 @@ +- name: Install docker packages (rht family) + package: + state: present + name: docker-io,docker-registry,python-docker-py,nginx + +- name: Install netcat (Fedora) + package: + state: present + name: nmap-ncat + when: ansible_distribution == 'Fedora' or (ansible_os_family == 'RedHat' and ansible_distribution_version|version_compare('>=', 7)) + +- name: Install netcat (RHEL) + package: + state: present + name: nc + when: ansible_distribution != 'Fedora' and (ansible_os_family == 'RedHat' and ansible_distribution_version|version_compare('<', 7)) + diff --git a/test/integration/targets/docker/tasks/docker-tests.yml b/test/integration/targets/docker/tasks/docker-tests.yml new file mode 100644 index 0000000000..14e23f72dd --- /dev/null +++ b/test/integration/targets/docker/tasks/docker-tests.yml @@ -0,0 +1,58 @@ +- name: Start docker daemon + service: + name: docker + state: started + +- name: Download busybox image + docker: + image: busybox + state: present + pull: missing + +- name: Run a small script in busybox + docker: + image: busybox + state: reloaded + pull: always + command: "nc -l -p 2000 -e xargs -n1 echo hello" + detach: True + +- name: Get the docker container ip + set_fact: container_ip="{{docker_containers[0].NetworkSettings.IPAddress}}" + +- name: Try to access the server + shell: "echo 'world' | nc {{ container_ip }} 2000" + register: docker_output + +- name: check that the script ran + assert: + that: + - "'hello world' in docker_output.stdout_lines" + +- name: Run a script that sets environment in busybox + docker: + image: busybox + state: reloaded + pull: always + env: + TEST: hello + command: '/bin/sh -c "nc -l -p 2000 -e xargs -n1 echo $TEST"' + detach: True + +- name: Get the docker container ip + set_fact: container_ip="{{docker_containers[0].NetworkSettings.IPAddress}}" + +- name: Try to access the server + shell: "echo 'world' | nc {{ container_ip }} 2000" + register: docker_output + +- name: check that the script ran + assert: + that: + - "'hello world' in docker_output.stdout_lines" + +- name: Remove containers + shell: "docker rm -f $(docker ps -aq)" + +- name: Remove all images from the local docker + shell: "docker rmi -f $(docker images -q)" diff --git a/test/integration/targets/docker/tasks/main.yml b/test/integration/targets/docker/tasks/main.yml new file mode 100644 index 0000000000..3689599523 --- /dev/null +++ b/test/integration/targets/docker/tasks/main.yml @@ -0,0 +1,23 @@ +#- include: docker-setup-rht.yml +# when: ansible_distribution in ['Fedora'] +#- include: docker-setup-rht.yml + # Packages on RHEL and CentOS 7 are broken, broken, broken. Revisit when + # they've got that sorted out + # CentOS 6 currently broken by conflicting files in python-backports and python-backports-ssl_match_hostname + #when: ansible_distribution in ['RedHat', 'CentOS'] and ansible_lsb.major_release|int == 6 + +# python-docker isn't available until 14.10. Revist at the next Ubuntu LTS +#- include: docker-setup-debian.yml +# when: ansible_distribution in ['Ubuntu'] + +#- include: docker-tests.yml + # Add other distributions as the proper packages become available +# when: ansible_distribution in ['Fedora'] + +#- include: docker-tests.yml +# when: ansible_distribution in ['RedHat', 'CentOS'] and ansible_lsb.major_release|int == 6 + +#- include: registry-tests.yml + # Add other distributions as the proper packages become available +# when: ansible_distribution in ['Fedora'] + diff --git a/test/integration/targets/docker/tasks/registry-tests.yml b/test/integration/targets/docker/tasks/registry-tests.yml new file mode 100644 index 0000000000..1ef330da5f --- /dev/null +++ b/test/integration/targets/docker/tasks/registry-tests.yml @@ -0,0 +1,189 @@ +- name: Configure a private docker registry + service: + name: docker-registry + state: started + +- name: Retrieve busybox image from docker hub + docker: + image: busybox + state: present + pull: missing + +- name: Get busybox image id + shell: "docker images | grep busybox | awk '{ print $3 }'" + register: image_id + +- name: Tag docker image into the local registry + command: "docker tag {{ image_id.stdout_lines[0] }} localhost:5000/mine" + +- name: Push docker image into the private registry + command: "docker push localhost:5000/mine" + +- name: Remove all images from the local docker + shell: "docker rmi -f {{image_id.stdout_lines[0]}}" + +- name: Get number of images in docker + command: "docker images" + register: docker_output + +# docker prints a header so the header should be all that's present +- name: Check that there are no images in docker + assert: + that: + - "{{ docker_output.stdout_lines| length }} <= 1 " + +- name: Retrieve the image from private docker registry + docker: + image: "localhost:5000/mine" + state: present + pull: missing + insecure_registry: True + +- name: Run a small script in the new image + docker: + image: "localhost:5000/mine" + state: reloaded + pull: always + command: "nc -l -p 2000 -e xargs -n1 echo hello" + detach: True + insecure_registry: True + +- name: Get the docker container id + shell: "docker ps | grep mine | awk '{ print $1 }'" + register: container_id + +- name: Get the docker container ip + shell: "docker inspect {{ container_id.stdout_lines[0] }} | grep IPAddress | awk -F '\"' '{ print $4 }'" + register: container_ip + +- name: Pause a few moments because docker is not reliable + pause: + seconds: 40 + +- name: Try to access the server + shell: "echo 'world' | nc {{ container_ip.stdout_lines[0] }} 2000" + register: docker_output + +- name: check that the script ran + assert: + that: + - "'hello world' in docker_output.stdout_lines" + + +- name: Remove containers + shell: "docker rm -f $(docker ps -aq)" + +- shell: docker images -q +- name: Remove all images from the local docker + shell: "docker rmi -f $(docker images -q)" + +- name: Get number of images in docker + command: "docker images" + register: docker_output + +- name: Check that there are no images in docker + assert: + that: + - "{{ docker_output.stdout_lines| length }} <= 1" + +# +# Private registry secured with an SSL proxy +# + +- name: Set selinux to allow docker to connect to nginx + seboolean: + name: docker_connect_any + state: yes + +- name: Set selinux to allow nginx to connect to docker + seboolean: + name: httpd_can_network_connect + state: yes + +- name: Setup nginx with a user/password + copy: + src: docker-registry.htpasswd + dest: /etc/nginx/docker-registry.htpasswd + +- name: Setup nginx with a config file + copy: + src: nginx-docker-registry.conf + dest: /etc/nginx/conf.d/nginx-docker-registry.conf + +- name: Setup nginx docker cert + copy: + src: dockertest.ansible.com.crt + dest: /etc/pki/tls/certs/dockertest.ansible.com.crt + +- name: Setup nginx docker key + copy: + src: dockertest.ansible.com.key + dest: /etc/pki/tls/private/dockertest.ansible.com.key + +- name: Setup the ca keys + copy: + src: devdockerCA.crt + dest: /etc/pki/ca-trust/source/anchors/devdockerCA.crt + +- name: Update the ca bundle + command: update-ca-trust extract + +- name: Restart docker daemon + service: + name: docker + state: restarted + +- name: Start nginx + service: + name: nginx + state: restarted + +- name: Add domain name to hosts + lineinfile: + line: "127.0.0.1 dockertest.ansible.com" + dest: /etc/hosts + state: present + +- name: Start a container after getting it from a secured private registry + docker: + image: dockertest.ansible.com:8080/mine + registry: dockertest.ansible.com:8080 + username: "testdocker" + password: "testdocker" + state: running + command: "nc -l -p 2000 -e xargs -n1 echo hello" + detach: True + +- name: Get the docker container id + shell: "docker ps | grep mine | awk '{ print $1 }'" + register: container_id + +- name: Get the docker container ip + shell: "docker inspect {{ container_id.stdout_lines[0] }} | grep IPAddress | awk -F '\"' '{ print $4 }'" + register: container_ip + +- name: Pause a few moments because docker is not reliable + pause: + seconds: 40 + +- name: Try to access the server + shell: "echo 'world' | nc {{ container_ip.stdout_lines[0] }} 2000" + register: docker_output + +- name: check that the script ran + assert: + that: + - "'hello world' in docker_output.stdout_lines" + +- name: Remove containers + shell: "docker rm $(docker ps -aq)" + +- name: Remove all images from the local docker + shell: "docker rmi -f $(docker images -q)" + +- name: Remove domain name to hosts + lineinfile: + line: "127.0.0.1 dockertest.ansible.com" + dest: /etc/hosts + state: absent + diff --git a/test/integration/targets/embedded_module/library/test_integration_module b/test/integration/targets/embedded_module/library/test_integration_module new file mode 100644 index 0000000000..f564619470 --- /dev/null +++ b/test/integration/targets/embedded_module/library/test_integration_module @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +print('{"changed":false, "msg":"this is the embedded module"}') diff --git a/test/integration/targets/embedded_module/tasks/main.yml b/test/integration/targets/embedded_module/tasks/main.yml new file mode 100644 index 0000000000..6a6d6485fc --- /dev/null +++ b/test/integration/targets/embedded_module/tasks/main.yml @@ -0,0 +1,9 @@ +- name: run the embedded dummy module + test_integration_module: + register: result + +- name: assert the embedded module ran + assert: + that: + - "'msg' in result" + - result.msg == "this is the embedded module" diff --git a/test/integration/targets/facts_d/meta/main.yml b/test/integration/targets/facts_d/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/facts_d/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/facts_d/tasks/main.yml b/test/integration/targets/facts_d/tasks/main.yml new file mode 100644 index 0000000000..3b5bba4075 --- /dev/null +++ b/test/integration/targets/facts_d/tasks/main.yml @@ -0,0 +1,39 @@ +# Test code for facts.d and setup filters +# (c) 2014, James Tanner <tanner.jc@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/>. + +- set_fact: fact_dir={{output_dir}}/facts.d + +- file: path={{ fact_dir }} state=directory +- shell: echo "[general]" > {{ fact_dir }}/preferences.fact +- shell: echo "bar=loaded" >> {{ fact_dir }}/preferences.fact + +- setup: + fact_path: "{{ fact_dir | expanduser }}" + filter: "*local*" + register: setup_result + +- debug: var=setup_result + +- assert: + that: + - "'ansible_facts' in setup_result" + - "'ansible_local' in setup_result.ansible_facts" + - "'preferences' in setup_result.ansible_facts['ansible_local']" + - "'general' in setup_result.ansible_facts['ansible_local']['preferences']" + - "'bar' in setup_result.ansible_facts['ansible_local']['preferences']['general']" + - "setup_result.ansible_facts['ansible_local']['preferences']['general']['bar'] == 'loaded'" diff --git a/test/integration/targets/failed_when/tasks/main.yml b/test/integration/targets/failed_when/tasks/main.yml new file mode 100644 index 0000000000..4a5617e142 --- /dev/null +++ b/test/integration/targets/failed_when/tasks/main.yml @@ -0,0 +1,69 @@ +# Test code for failed_when. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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: command rc 0 failed_when_result undef + shell: exit 0 + ignore_errors: True + register: result + +- assert: + that: + - "'failed' not in result" + +- name: command rc 0 failed_when_result False + shell: exit 0 + failed_when: false + ignore_errors: true + register: result + +- assert: + that: + - "'failed' in result and not result.failed" + - "'failed_when_result' in result and not result.failed_when_result" + +- name: command rc 1 failed_when_result True + shell: exit 1 + failed_when: true + ignore_errors: true + register: result + +- assert: + that: + - "'failed' in result and result.failed" + - "'failed_when_result' in result and result.failed_when_result" + +- name: command rc 1 failed_when_result undef + shell: exit 1 + ignore_errors: true + register: result + +- assert: + that: + - "'failed' in result and result.failed" + +- name: command rc 1 failed_when_result False + shell: exit 1 + failed_when: false + ignore_errors: true + register: result + +- assert: + that: + - "'failed' in result and not result.failed" + - "'failed_when_result' in result and not result.failed_when_result" + diff --git a/test/integration/targets/fetch/meta/main.yml b/test/integration/targets/fetch/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/fetch/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/fetch/tasks/main.yml b/test/integration/targets/fetch/tasks/main.yml new file mode 100644 index 0000000000..4b6fb611e2 --- /dev/null +++ b/test/integration/targets/fetch/tasks/main.yml @@ -0,0 +1,97 @@ +# 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={{ output_dir }}/orig + +- name: fetch the motd + fetch: src={{ output_dir }}/orig dest={{ output_dir }}/fetched + register: fetched + +- debug: var=fetched + +# 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: diff what we fetched with the original file + shell: diff {{ output_dir }}/orig {{ output_dir }}/fetched/{{inventory_hostname}}{{ output_dir | expanduser }}/orig + register: diff + +- name: check the diff to make sure they are the same + assert: + that: + 'diff.stdout == ""' + +- name: attempt to fetch a non-existent file - do not fail on missing + fetch: src={{ output_dir }}/doesnotexist dest={{ output_dir }}/fetched + register: fetch_missing_nofail + +- name: check fetch missing no fail result + assert: + that: + - "fetch_missing_nofail.msg" + - "not fetch_missing_nofail|changed" + +- name: attempt to fetch a non-existent file - fail on missing + fetch: src={{ output_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|failed" + - "fetch_missing.msg" + - "not fetch_missing|changed" + +- name: attempt to fetch a directory - should not fail but return a message + fetch: src={{ output_dir }} dest={{ output_dir }}/somedir + register: fetch_dir + +- name: check fetch directory result + assert: + that: + - "not fetch_dir|changed" + - "fetch_dir.msg" + +- name: create symlink to a file that we can fetch + file: + path: "{{ output_dir }}/link" + src: "{{ output_dir }}/orig" + state: "link" + +- name: fetch the file via a symlink + fetch: src={{ output_dir }}/link dest={{ output_dir }}/fetched-link + register: fetched + +- debug: var=fetched + +# 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: diff what we fetched with the original file + shell: diff {{ output_dir }}/orig {{ output_dir }}/fetched-link/{{inventory_hostname}}{{ output_dir | expanduser }}/link + register: diff + +- name: check the diff to make sure they are the same + assert: + that: + 'diff.stdout == ""' + diff --git a/test/integration/targets/file/files/foo.txt b/test/integration/targets/file/files/foo.txt new file mode 100644 index 0000000000..7c6ded14ec --- /dev/null +++ b/test/integration/targets/file/files/foo.txt @@ -0,0 +1 @@ +foo.txt diff --git a/test/integration/targets/file/files/foobar/directory/fileC b/test/integration/targets/file/files/foobar/directory/fileC new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/file/files/foobar/directory/fileC diff --git a/test/integration/targets/file/files/foobar/directory/fileD b/test/integration/targets/file/files/foobar/directory/fileD new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/file/files/foobar/directory/fileD diff --git a/test/integration/targets/file/files/foobar/fileA b/test/integration/targets/file/files/foobar/fileA new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/file/files/foobar/fileA diff --git a/test/integration/targets/file/files/foobar/fileB b/test/integration/targets/file/files/foobar/fileB new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/file/files/foobar/fileB diff --git a/test/integration/targets/file/meta/main.yml b/test/integration/targets/file/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/file/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/file/tasks/main.yml b/test/integration/targets/file/tasks/main.yml new file mode 100644 index 0000000000..c6cbd6f6ad --- /dev/null +++ b/test/integration/targets/file/tasks/main.yml @@ -0,0 +1,564 @@ +# Test code for the file module. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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/>. + +- set_fact: output_file={{output_dir}}/foo.txt + +- name: prep with a basic copy + copy: src=foo.txt dest={{output_file}} + +- name: verify that we are checking a file and it is present + file: path={{output_file}} state=file + register: file_result + +- name: verify that the file was marked as changed + assert: + that: + - "file_result.changed == false" + - "file_result.state == 'file'" + +- name: verify that we are checking an absent file + file: path={{output_dir}}/bar.txt state=absent + register: file2_result + +- name: verify that the file was marked as changed + assert: + that: + - "file2_result.changed == false" + - "file2_result.state == 'absent'" + +- name: verify we can touch a file + file: path={{output_dir}}/baz.txt state=touch + register: file3_result + +- name: verify that the file was marked as changed + assert: + that: + - "file3_result.changed == true" + - "file3_result.state == 'file'" + - "file3_result.mode == '0644'" + +- name: change file mode + file: path={{output_dir}}/baz.txt mode=0600 + register: file4_result + +- name: verify that the file was marked as changed + assert: + that: + - "file4_result.changed == true" + - "file4_result.mode == '0600'" + +- name: change ownership and group + file: path={{output_dir}}/baz.txt owner=1234 group=1234 + +- name: setup a tmp-like directory for ownership test + file: path=/tmp/worldwritable mode=1777 state=directory + +- name: Ask to create a file without enough perms to change ownership + file: path=/tmp/worldwritable/baz.txt state=touch owner=root + become: yes + become_user: nobody + register: chown_result + ignore_errors: True + +- name: Ask whether the new file exists + stat: path=/tmp/worldwritable/baz.txt + register: file_exists_result + +- name: Verify that the file doesn't exist on failure + assert: + that: + - "chown_result.failed == True" + - "file_exists_result.stat.exists == False" + +- name: clean up + file: path=/tmp/worldwritable state=absent + +- name: create soft link to file + file: src={{output_file}} dest={{output_dir}}/soft.txt state=link + register: file5_result + +- name: verify that the file was marked as changed + assert: + that: + - "file5_result.changed == true" + +- name: create hard link to file + file: src={{output_file}} dest={{output_dir}}/hard.txt state=hard + register: file6_result + +- name: verify that the file was marked as changed + assert: + that: + - "file6_result.changed == true" + +- name: touch a hard link + file: src={{output_file}} dest={{output_dir}}/hard.txt state=touch + register: file6_touch_result + +- name: verify that the hard link was touched + assert: + that: + - "file6_touch_result.changed == true" + +- name: create a directory + file: path={{output_dir}}/foobar state=directory + register: file7_result + +- name: verify that the file was marked as changed + assert: + that: + - "file7_result.changed == true" + - "file7_result.state == 'directory'" + +- name: determine if selinux is installed + shell: which getenforce || exit 0 + register: selinux_installed + +- name: determine if selinux is enabled + shell: getenforce + register: selinux_enabled + when: selinux_installed.stdout != "" + ignore_errors: true + +- name: decide to include or not include selinux tests + include: selinux_tests.yml + when: selinux_installed is defined and selinux_installed.stdout != "" and selinux_enabled.stdout != "Disabled" + +- name: remote directory foobar + file: path={{output_dir}}/foobar state=absent + +- name: remove file foo.txt + file: path={{output_dir}}/foo.txt state=absent + +- name: remove file bar.txt + file: path={{output_dir}}/foo.txt state=absent + +- name: remove file baz.txt + file: path={{output_dir}}/foo.txt state=absent + +- name: copy directory structure over + copy: src=foobar dest={{output_dir}} + +- name: Change ownership of a directory with recurse=no(default) + file: path={{output_dir}}/foobar owner=1234 + +- name: verify that the permission of the directory was set + file: path={{output_dir}}/foobar state=directory + register: file8_result + +- name: assert that the directory has changed to have owner 1234 + assert: + that: + - "file8_result.uid == 1234" + +- name: verify that the permission of a file under the directory was not set + file: path={{output_dir}}/foobar/fileA state=file + register: file9_result + +- name: assert the file owner has not changed to 1234 + assert: + that: + - "file9_result.uid != 1234" + +- name: change the ownership of a directory with recurse=yes + file: path={{output_dir}}/foobar owner=1235 recurse=yes + +- name: verify that the permission of the directory was set + file: path={{output_dir}}/foobar state=directory + register: file10_result + +- name: assert that the directory has changed to have owner 1235 + assert: + that: + - "file10_result.uid == 1235" + +- name: verify that the permission of a file under the directory was not set + file: path={{output_dir}}/foobar/fileA state=file + register: file11_result + +- name: assert that the file has changed to have owner 1235 + assert: + that: + - "file11_result.uid == 1235" + +- name: fail to create soft link to non existent file + file: src=/noneexistent dest={{output_dir}}/soft2.txt state=link force=no + register: file12_result + ignore_errors: true + +- name: verify that link was not created + assert: + that: + - "file12_result.failed == true" + +- name: force creation soft link to non existent + file: src=/noneexistent dest={{output_dir}}/soft2.txt state=link force=yes + register: file13_result + +- name: verify that link was created + assert: + that: + - "file13_result.changed == true" + +- name: remove directory foobar + file: path={{output_dir}}/foobar state=absent + register: file14_result + +- name: verify that the directory was removed + assert: + that: + - 'file14_result.changed == true' + - 'file14_result.state == "absent"' + +- name: create a test sub-directory + file: dest={{output_dir}}/sub1 state=directory + register: file15_result + +- name: verify that the new directory was created + assert: + that: + - 'file15_result.changed == true' + - 'file15_result.state == "directory"' + +- name: create test files in the sub-directory + file: dest={{output_dir}}/sub1/{{item}} state=touch + with_items: + - file1 + - file2 + - file3 + register: file16_result + +- name: verify the files were created + assert: + that: + - 'item.changed == true' + - 'item.state == "file"' + with_items: "{{file16_result.results}}" + +- name: try to force the sub-directory to a link + file: src={{output_dir}}/testing dest={{output_dir}}/sub1 state=link force=yes + register: file17_result + ignore_errors: true + +- name: verify the directory was not replaced with a link + assert: + that: + - 'file17_result.failed == true' + - 'file17_result.state == "directory"' + +- name: create soft link to directory using absolute path + file: src=/ dest={{output_dir}}/root state=link + register: file18_result + +- name: verify that the result was marked as changed + assert: + that: + - "file18_result.changed == true" + +- name: create another test sub-directory + file: dest={{output_dir}}/sub2 state=directory + register: file19_result + +- name: verify that the new directory was created + assert: + that: + - 'file19_result.changed == true' + - 'file19_result.state == "directory"' + +- name: create soft link to relative file + file: src=../sub1/file1 dest={{output_dir}}/sub2/link1 state=link + register: file20_result + +- name: verify that the result was marked as changed + assert: + that: + - "file20_result.changed == true" + +- name: create soft link to relative directory + file: src=sub1 dest={{output_dir}}/sub1-link state=link + register: file21_result + +- name: verify that the result was marked as changed + assert: + that: + - "file21_result.changed == true" + +- name: test file creation with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=u=rwx,g=rwx,o=rwx + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0777' + +- name: modify symbolic mode for all + file: dest={{output_dir}}/test_symbolic state=touch mode=a=r + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0444' + +- name: modify symbolic mode for owner + file: dest={{output_dir}}/test_symbolic state=touch mode=u+w + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0644' + +- name: modify symbolic mode for group + file: dest={{output_dir}}/test_symbolic state=touch mode=g+w + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0664' + +- name: modify symbolic mode for world + file: dest={{output_dir}}/test_symbolic state=touch mode=o+w + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0666' + +- name: modify symbolic mode for owner + file: dest={{output_dir}}/test_symbolic state=touch mode=u+x + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0766' + +- name: modify symbolic mode for group + file: dest={{output_dir}}/test_symbolic state=touch mode=g+x + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0776' + +- name: modify symbolic mode for world + file: dest={{output_dir}}/test_symbolic state=touch mode=o+x + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0777' + +- name: remove symbolic mode for world + file: dest={{output_dir}}/test_symbolic state=touch mode=o-wx + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0774' + +- name: remove symbolic mode for group + file: dest={{output_dir}}/test_symbolic state=touch mode=g-wx + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0744' + +- name: remove symbolic mode for owner + file: dest={{output_dir}}/test_symbolic state=touch mode=u-wx + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0444' + +- name: set sticky bit with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=o+t + register: result + +- name: assert file mode + assert: + that: + - result.mode == '01444' + +- name: remove sticky bit with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=o-t + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0444' + +- name: add setgid with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=g+s + register: result + +- name: assert file mode + assert: + that: + - result.mode == '02444' + +- name: remove setgid with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=g-s + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0444' + +- name: add setuid with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=u+s + register: result + +- name: assert file mode + assert: + that: + - result.mode == '04444' + +- name: remove setuid with symbolic mode + file: dest={{output_dir}}/test_symbolic state=touch mode=u-s + register: result + +- name: assert file mode + assert: + that: + - result.mode == '0444' + +# test the file module using follow=yes, so that the target of a +# symlink is modified, rather than the link itself + +- name: create a test file + copy: dest={{output_dir}}/test_follow content="this is a test file\n" mode=0666 + +- name: create a symlink to the test file + file: path={{output_dir}}/test_follow_link src="./test_follow" state=link + +- name: modify the permissions on the link using follow=yes + file: path={{output_dir}}/test_follow_link mode=0644 follow=yes + register: result + +- name: assert that the chmod worked + assert: + that: + - result.changed + +- name: stat the link target + stat: path={{output_dir}}/test_follow + register: result + +- name: assert that the link target was modified correctly + assert: + that: + - result.stat.mode == '0644' + +- name: attempt to modify the permissions of the link itself + file: path={{output_dir}}/test_follow_link src="./test_follow" state=link mode=0600 follow=no + register: result + +# Whether the link itself changed is platform dependent! (BSD vs Linux?) +# Just check that the underlying file was not changed +- name: stat the link target + stat: path={{output_dir}}/test_follow + register: result + +- name: assert that the link target was unmodified + assert: + that: + - result.stat.mode == '0644' + ignore_errors: True + +# Follow + recursive tests +- name: create a toplevel directory + file: path={{output_dir}}/test_follow_rec state=directory mode=0755 + +- name: create a file outside of the toplevel + file: path={{output_dir}}/test_follow_rec_target_file state=touch mode=0700 + +- name: create a directory outside of the toplevel + file: path={{output_dir}}/test_follow_rec_target_dir state=directory mode=0700 + +- name: create a file inside of the link target directory + file: path={{output_dir}}/test_follow_rec_target_dir/foo state=touch mode=0700 + +- name: create a symlink to the file + file: path={{output_dir}}/test_follow_rec/test_link state=link src="../test_follow_rec_target_file" + +- name: create a symlink to the directory + file: path={{output_dir}}/test_follow_rec/test_link_dir state=link src="../test_follow_rec_target_dir" + +- name: try to change permissions without following symlinks + file: path={{output_dir}}/test_follow_rec follow=False mode="a-x" recurse=True + +- name: stat the link file target + stat: path={{output_dir}}/test_follow_rec_target_file + register: file_result + +- name: stat the link dir target + stat: path={{output_dir}}/test_follow_rec_target_dir + register: dir_result + +- name: stat the file inside the link dir target + stat: path={{output_dir}}/test_follow_rec_target_dir/foo + register: file_in_dir_result + +- debug: var=file_result.stat.mode +- debug: var=dir_result.stat.mode +- debug: var=file_in_dir_result.stat.mode +- name: assert that the link targets were unmodified + assert: + that: + - file_result.stat.mode == '0700' + - dir_result.stat.mode == '0700' + - file_in_dir_result.stat.mode == '0700' + +- name: try to change permissions with following symlinks + file: path={{output_dir}}/test_follow_rec follow=True mode="a-x" recurse=True + +- name: stat the link file target + stat: path={{output_dir}}/test_follow_rec_target_file + register: file_result + +- name: stat the link dir target + stat: path={{output_dir}}/test_follow_rec_target_dir + register: dir_result + +- name: stat the file inside the link dir target + stat: path={{output_dir}}/test_follow_rec_target_dir/foo + register: file_in_dir_result + +- debug: var=file_result.stat.mode +- debug: var=dir_result.stat.mode +- debug: var=file_in_dir_result.stat.mode +- name: assert that the link targets were modified + assert: + that: + - file_result.stat.mode == '0600' + - dir_result.stat.mode == '0600' + - file_in_dir_result.stat.mode == '0600' diff --git a/test/integration/targets/file/tasks/selinux_tests.yml b/test/integration/targets/file/tasks/selinux_tests.yml new file mode 100644 index 0000000000..b0f6e17de2 --- /dev/null +++ b/test/integration/targets/file/tasks/selinux_tests.yml @@ -0,0 +1,30 @@ +# Test code for the file module - selinux subtasks. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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: touch a file for testing + file: path={{output_dir}}/foo-se.txt state=touch + register: file_se_result + +- name: verify that the file was marked as changed + assert: + that: + - "file_se_result.changed == true" + - "file_se_result.secontext == 'unconfined_u:object_r:admin_home_t:s0'" + +- name: remove the file used for testing + file: path={{output_dir}}/foo-se.txt state=absent
\ No newline at end of file diff --git a/test/integration/targets/filters/files/9851.txt b/test/integration/targets/filters/files/9851.txt new file mode 100644 index 0000000000..70b12793e1 --- /dev/null +++ b/test/integration/targets/filters/files/9851.txt @@ -0,0 +1,3 @@ + [{ + "k": "Quotes \"'\n" +}] diff --git a/test/integration/targets/filters/files/foo.txt b/test/integration/targets/filters/files/foo.txt new file mode 100644 index 0000000000..c5af545d3a --- /dev/null +++ b/test/integration/targets/filters/files/foo.txt @@ -0,0 +1,61 @@ +This is a test of various filter plugins found in Ansible (ex: core.py), and +not so much a test of the core filters in Jinja2. + +Dumping the same structure to YAML + +- this is a list element +- this: is a hash element in a list + warp: 9 + where: endor + + +Dumping the same structure to JSON, but don't pretty print + +["this is a list element", {"this": "is a hash element in a list", "where": "endor", "warp": 9}] + +Dumping the same structure to YAML, but don't pretty print + +- this is a list element +- {this: is a hash element in a list, warp: 9, where: endor} + + +From a recorded task, the changed, failed, success, and skipped +filters are shortcuts to ask if those tasks produced changes, failed, +succeeded, or skipped (as one might guess). + +Changed = True +Failed = False +Success = True +Skipped = False + +The mandatory filter fails if a variable is not defined and returns the value. +To avoid breaking this test, this variable is already defined. + +a = 1 + +There are various casts available + +int = 1 +bool = True + +String quoting + +quoted = quoted + +The fileglob module returns the list of things matching a pattern. + +fileglob = [] + +There are also various string operations that work on paths. These do not require +files to exist and are passthrus to the python os.path functions + +/etc/motd with basename = motd +/etc/motd with dirname = /etc + +TODO: realpath follows symlinks. There isn't a test for this just now. + +TODO: add tests for set theory operations like union + +TODO: add tests for regex, match, and search + + diff --git a/test/integration/targets/filters/meta/main.yml b/test/integration/targets/filters/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/filters/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/filters/tasks/main.yml b/test/integration/targets/filters/tasks/main.yml new file mode 100644 index 0000000000..03566220e5 --- /dev/null +++ b/test/integration/targets/filters/tasks/main.yml @@ -0,0 +1,112 @@ +# test code +# (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: a dummy task to test the changed and success filters + shell: echo hi + register: some_registered_var + +- debug: var=some_registered_var + +- name: Verify that we workaround a py26 json bug + template: src=py26json.j2 dest={{output_dir}}/py26json.templated mode=0644 + +- name: 9851 - Verify that we don't trigger https://github.com/ansible/ansible/issues/9851 + copy: + content: " [{{item|to_nice_json}}]" + dest: "{{output_dir}}/9851.out" + with_items: + - {"k": "Quotes \"'\n"} + +- name: 9851 - copy known good output into place + copy: src=9851.txt dest={{output_dir}}/9851.txt + +- name: 9851 - Compare generated json to known good + shell: diff -w {{output_dir}}/9851.out {{output_dir}}/9851.txt + register: diff_result_9851 + +- name: 9851 - verify generated file matches known good + assert: + that: + - 'diff_result_9851.stdout == ""' + +- name: fill in a basic template + template: src=foo.j2 dest={{output_dir}}/foo.templated mode=0644 + register: template_result + +- name: copy known good into place + copy: src=foo.txt dest={{output_dir}}/foo.txt + +- name: compare templated file to known good + shell: diff -w {{output_dir}}/foo.templated {{output_dir}}/foo.txt + register: diff_result + +- name: verify templated file matches known good + assert: + that: + - 'diff_result.stdout == ""' + +- name: Verify human_readable + tags: "human_readable" + assert: + that: + - '"1.00 Bytes" == 1|human_readable' + - '"1.00 bits" == 1|human_readable(isbits=True)' + - '"10.00 KB" == 10240|human_readable' + - '"97.66 MB" == 102400000|human_readable' + - '"0.10 GB" == 102400000|human_readable(unit="G")' + - '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")' + +- name: Verify human_to_bytes + tags: "human_to_bytes" + assert: + that: + - "{{'0'|human_to_bytes}} == 0" + - "{{'0.1'|human_to_bytes}} == 0" + - "{{'0.9'|human_to_bytes}} == 1" + - "{{'1'|human_to_bytes}} == 1" + - "{{'10.00 KB'|human_to_bytes}} == 10240" + - "{{ '11 MB'|human_to_bytes}} == 11534336" + - "{{ '1.1 GB'|human_to_bytes}} == 1181116006" + - "{{'10.00 Kb'|human_to_bytes(isbits=True)}} == 10240" + +- name: Verify human_to_bytes (bad string) + tags: "human_to_bytes" + set_fact: bad_string="{{'10.00 foo'|human_to_bytes}}" + ignore_errors: yes + register: _ + +- name: Verify human_to_bytes (bad string) + tags: "human_to_bytes" + assert: + that: "{{_.failed}}" + +- name: Container lookups with extract + assert: + that: + - "'x' == [0]|map('extract',['x','y'])|list|first" + - "'y' == [1]|map('extract',['x','y'])|list|first" + - "42 == ['x']|map('extract',{'x':42,'y':31})|list|first" + - "31 == ['x','y']|map('extract',{'x':42,'y':31})|list|last" + - "'local' == ['localhost']|map('extract',hostvars,'ansible_connection')|list|first" + - "'local' == ['localhost']|map('extract',hostvars,['ansible_connection'])|list|first" + +- name: Test json_query filter + assert: + that: + - "users | json_query('[*].hosts[].host') == ['host_a', 'host_b', 'host_c', 'host_d']" + diff --git a/test/integration/targets/filters/templates/foo.j2 b/test/integration/targets/filters/templates/foo.j2 new file mode 100644 index 0000000000..cf592f98cc --- /dev/null +++ b/test/integration/targets/filters/templates/foo.j2 @@ -0,0 +1,55 @@ +This is a test of various filter plugins found in Ansible (ex: core.py), and +not so much a test of the core filters in Jinja2. + +Dumping the same structure to YAML + +{{ some_structure | to_nice_yaml }} + +Dumping the same structure to JSON, but don't pretty print + +{{ some_structure | to_json }} + +Dumping the same structure to YAML, but don't pretty print + +{{ some_structure | to_yaml }} + +From a recorded task, the changed, failed, success, and skipped +filters are shortcuts to ask if those tasks produced changes, failed, +succeeded, or skipped (as one might guess). + +Changed = {{ some_registered_var | changed }} +Failed = {{ some_registered_var | failed }} +Success = {{ some_registered_var | success }} +Skipped = {{ some_registered_var | skipped }} + +The mandatory filter fails if a variable is not defined and returns the value. +To avoid breaking this test, this variable is already defined. + +a = {{ a | mandatory }} + +There are various casts available + +int = {{ a | int }} +bool = {{ 1 | bool }} + +String quoting + +quoted = {{ 'quoted' | quote }} + +The fileglob module returns the list of things matching a pattern. + +fileglob = {{ (output_dir + '/*') | fileglob }} + +There are also various string operations that work on paths. These do not require +files to exist and are passthrus to the python os.path functions + +/etc/motd with basename = {{ '/etc/motd' | basename }} +/etc/motd with dirname = {{ '/etc/motd' | dirname }} + +TODO: realpath follows symlinks. There isn't a test for this just now. + +TODO: add tests for set theory operations like union + +TODO: add tests for regex, match, and search + + diff --git a/test/integration/targets/filters/templates/py26json.j2 b/test/integration/targets/filters/templates/py26json.j2 new file mode 100644 index 0000000000..dba62ad17c --- /dev/null +++ b/test/integration/targets/filters/templates/py26json.j2 @@ -0,0 +1,2 @@ +Provoke a python2.6 json bug +{{ hostvars[inventory_hostname] | to_nice_json }} diff --git a/test/integration/targets/filters/vars/main.yml b/test/integration/targets/filters/vars/main.yml new file mode 100644 index 0000000000..7b9f609c95 --- /dev/null +++ b/test/integration/targets/filters/vars/main.yml @@ -0,0 +1,18 @@ +some_structure: + - "this is a list element" + - + this: "is a hash element in a list" + warp: 9 + where: endor + +users: + - name: steve + hosts: + - host: host_a + password: abc + - host: host_b + - name: bill + hosts: + - host: host_c + password: default + - host: host_d diff --git a/test/integration/targets/gem/meta/main.yml b/test/integration/targets/gem/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/gem/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/gem/tasks/main.yml b/test/integration/targets/gem/tasks/main.yml new file mode 100644 index 0000000000..476e602634 --- /dev/null +++ b/test/integration/targets/gem/tasks/main.yml @@ -0,0 +1,40 @@ +# test code for the gem module +# (c) 2014, James Tanner <tanner.jc@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: remove a gem + gem: name=gist state=absent + +- name: verify gist is not installed + shell: gem list | egrep '^gist ' + register: uninstall + failed_when: "uninstall.rc != 1" + +- name: install a gem + gem: name=gist state=present + register: gem_result + +- name: verify module output properties + assert: + that: + - "'name' in gem_result" + - "'changed' in gem_result" + - "'state' in gem_result" + +- name: verify gist is installed + shell: gem list | egrep '^gist ' + diff --git a/test/integration/targets/get_url/meta/main.yml b/test/integration/targets/get_url/meta/main.yml new file mode 100644 index 0000000000..b5f2416aed --- /dev/null +++ b/test/integration/targets/get_url/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - prepare_tests + - prepare_http_tests + diff --git a/test/integration/targets/get_url/tasks/main.yml b/test/integration/targets/get_url/tasks/main.yml new file mode 100644 index 0000000000..521dd9efab --- /dev/null +++ b/test/integration/targets/get_url/tasks/main.yml @@ -0,0 +1,213 @@ +# Test code for the file module. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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: Determine if python looks like it will support modern ssl features like SNI + command: "{{ ansible_python.executable }} -c 'from ssl import SSLContext'" + ignore_errors: True + register: python_test + +- name: Set python_has_sslcontext if we have it + set_fact: + python_has_ssl_context: True + when: python_test.rc == 0 + +- name: Set python_has_sslcontext False if we don't have it + set_fact: + python_has_ssl_context: False + when: python_test.rc != 0 + +- name: Define test files for file schema + set_fact: + geturl_srcfile: "{{ output_dir | expanduser }}/aurlfile.txt" + geturl_dstfile: "{{ output_dir | expanduser }}/aurlfile_copy.txt" + +- name: Create source file + copy: + dest: "{{ geturl_srcfile }}" + content: "foobar" + +- name: test file fetch + get_url: + url: "{{ 'file://' + geturl_srcfile }}" + dest: "{{ geturl_dstfile }}" + register: result + +- name: assert success and change + assert: + that: + - result.changed + - '"OK" in result.msg' + +- name: test nonexisting file fetch + get_url: + url: "{{ 'file://' + geturl_srcfile + 'NOFILE' }}" + dest: "{{ geturl_dstfile + 'NOFILE' }}" + register: result + ignore_errors: True + +- name: assert success and change + assert: + that: + - result.failed + +- name: test https fetch + get_url: url="https://{{ httpbin_host }}/get" dest={{output_dir}}/get_url.txt force=yes + register: result + +- name: assert the get_url call was successful + assert: + that: + - result.changed + - '"OK" in result.msg' + +- name: test https fetch to a site with mismatched hostname and certificate + get_url: + url: "https://{{ badssl_host }}/" + dest: "{{ output_dir }}/shouldnotexist.html" + ignore_errors: True + register: result + +- stat: + path: "{{ output_dir }}/shouldnotexist.html" + register: stat_result + +- name: Assert that the file was not downloaded + assert: + that: + - "result.failed == true" + - "'Failed to validate the SSL certificate' in result.msg" + - "stat_result.stat.exists == false" + +- name: test https fetch to a site with mismatched hostname and certificate and validate_certs=no + get_url: + url: "https://{{ badssl_host }}/" + dest: "{{ output_dir }}/get_url_no_validate.html" + validate_certs: no + register: result + +- stat: + path: "{{ output_dir }}/get_url_no_validate.html" + register: stat_result + +- name: Assert that the file was downloaded + assert: + that: + - "result.changed == true" + - "stat_result.stat.exists == true" + +# SNI Tests +# SNI is only built into the stdlib from python-2.7.9 onwards +- name: Test that SNI works + get_url: + url: 'https://{{ sni_host }}/' + dest: "{{ output_dir }}/sni.html" + register: get_url_result + ignore_errors: True + +- command: "grep '{{ sni_host }}' {{ output_dir}}/sni.html" + register: data_result + when: "{{ python_has_ssl_context }}" + +- debug: var=get_url_result +- name: Assert that SNI works with this python version + assert: + that: + - 'data_result.rc == 0' + - '"failed" not in get_url_result' + when: "{{ python_has_ssl_context }}" + +# If the client doesn't support SNI then get_url should have failed with a certificate mismatch +- name: Assert that hostname verification failed because SNI is not supported on this version of python + assert: + that: + - 'get_url_result["failed"]' + when: "{{ not python_has_ssl_context }}" + +# These tests are just side effects of how the site is hosted. It's not +# specifically a test site. So the tests may break due to the hosting changing +- name: Test that SNI works + get_url: + url: 'https://{{ sni_host }}/' + dest: "{{ output_dir }}/sni.html" + register: get_url_result + ignore_errors: True + +- command: "grep '{{ sni_host }}' {{ output_dir}}/sni.html" + register: data_result + when: "{{ python_has_ssl_context }}" + +- debug: var=get_url_result +- name: Assert that SNI works with this python version + assert: + that: + - 'data_result.rc == 0' + - '"failed" not in get_url_result' + when: "{{ python_has_ssl_context }}" + +# If the client doesn't support SNI then get_url should have failed with a certificate mismatch +- name: Assert that hostname verification failed because SNI is not supported on this version of python + assert: + that: + - 'get_url_result["failed"]' + when: "{{ not python_has_ssl_context }}" +# End hacky SNI test section + +- name: Test get_url with redirect + get_url: + url: 'http://{{ httpbin_host }}/redirect/6' + dest: "{{ output_dir }}/redirect.json" + +- name: Test that setting file modes work + get_url: + url: 'http://{{ httpbin_host }}/' + dest: '{{ output_dir }}/test' + mode: '0707' + register: result + +- stat: + path: "{{ output_dir }}/test" + register: stat_result + +- name: Assert that the file has the right permissions + assert: + that: + - "result.changed == true" + - "stat_result.stat.mode == '0707'" + +- name: Test that setting file modes on an already downlaoded file work + get_url: + url: 'http://{{ httpbin_host }}/' + dest: '{{ output_dir }}/test' + mode: '0070' + register: result + +- stat: + path: "{{ output_dir }}/test" + register: stat_result + +- name: Assert that the file has the right permissions + assert: + that: + - "result.changed == true" + - "stat_result.stat.mode == '0070'" + +#https://github.com/ansible/ansible/issues/16191 +- name: Test url split with no filename + get_url: + url: https://{{ httpbin_host }} + dest: "{{ output_dir }}" diff --git a/test/integration/targets/git/meta/main.yml b/test/integration/targets/git/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/git/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/git/tasks/main.yml b/test/integration/targets/git/tasks/main.yml new file mode 100644 index 0000000000..ec842c8407 --- /dev/null +++ b/test/integration/targets/git/tasks/main.yml @@ -0,0 +1,661 @@ +# test code for the git module +# (c) 2014, James Tanner <tanner.jc@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: set role facts + set_fact: + checkout_dir: '{{ output_dir }}/git' + repo_dir: '{{ output_dir }}/local_repos' + repo_format1: 'https://github.com/jimi-c/test_role' + repo_format2: 'git@github.com:jimi-c/test_role.git' + repo_format3: 'ssh://git@github.com/jimi-c/test_role.git' + repo_submodules: 'https://github.com/abadger/test_submodules.git' + repo_submodules_newer: 'https://github.com/abadger/test_submodules_newer.git' + repo_submodule1: 'https://github.com/abadger/test_submodules_subm1.git' + repo_submodule1_newer: 'https://github.com/abadger/test_submodules_subm1_newer.git' + repo_submodule2: 'https://github.com/abadger/test_submodules_subm2.git' + repo_update_url_1: 'https://github.com/ansible-test-robinro/git-test-old' + repo_update_url_2: 'https://github.com/ansible-test-robinro/git-test-new' + repo_depth_url: 'https://github.com/ansible-test-robinro/git-test-shallow-depth' + known_host_files: + - "{{ lookup('env','HOME') }}/.ssh/known_hosts" + - '/etc/ssh/ssh_known_hosts' + git_version_supporting_depth: 1.9.1 + +- name: clean out the output_dir + shell: rm -rf {{ output_dir }}/* + +- name: verify that git is installed so this test can continue + shell: which git + +- name: get git version, only newer than {{git_version_supporting_depth}} has fixed git depth + shell: git --version | grep 'git version' | sed 's/git version //' + register: git_version + +- name: set dummy git config + shell: git config --global user.email "noreply@example.com"; git config --global user.name "Ansible Test Runner" + +- name: create repo_dir + file: path={{repo_dir}} state=directory + +# +# Test repo=https://github.com/... +# + + +- name: initial checkout + git: repo={{ repo_format1 }} dest={{ checkout_dir }} + register: git_result + +- name: verify information about the initial clone + assert: + that: + - "'before' in git_result" + - "'after' in git_result" + - "not git_result.before" + - "git_result.changed" + +- name: repeated checkout + git: repo={{ repo_format1 }} dest={{ checkout_dir }} + register: git_result2 + +- name: check for tags + stat: path={{ checkout_dir }}/.git/refs/tags + register: tags + +- name: check for HEAD + stat: path={{ checkout_dir }}/.git/HEAD + register: head + +- name: assert presence of tags/trunk/branches + assert: + that: + - "tags.stat.isdir" + - "head.stat.isreg" + +- name: verify on a reclone things are marked unchanged + assert: + that: + - "not git_result2.changed" + +# +# Test repo=git@github.com:/... +# Requires variable: github_ssh_private_key +# + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: remove known_host files + file: state=absent path={{ item }} + with_items: "{{known_host_files}}" + +- name: checkout ssh://git@github.com repo without accept_hostkey (expected fail) + git: repo={{ repo_format2 }} dest={{ checkout_dir }} + register: git_result + ignore_errors: true + +- assert: + that: + - 'git_result.failed' + - 'git_result.msg == "github.com has an unknown hostkey. Set accept_hostkey to True or manually add the hostkey prior to running the git module"' + +- name: checkout git@github.com repo with accept_hostkey (expected pass) + git: + repo: '{{ repo_format2 }}' + dest: '{{ checkout_dir }}' + accept_hostkey: true + key_file: '{{ github_ssh_private_key }}' + register: git_result + when: github_ssh_private_key is defined + +- assert: + that: + - 'git_result.changed' + when: not git_result|skipped + +# +# Test repo=ssh://git@github.com/... +# Requires variable: github_ssh_private_key +# + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: checkout ssh://git@github.com repo with accept_hostkey (expected pass) + git: + repo: '{{ repo_format3 }}' + dest: '{{ checkout_dir }}' + version: 'master' + accept_hostkey: false # should already have been accepted + key_file: '{{ github_ssh_private_key }}' + register: git_result + when: github_ssh_private_key is defined + +- assert: + that: + - 'git_result.changed' + when: not git_result|skipped + +# Test a non-updating repo query with no destination specified + +- name: get info on a repo without updating and with no destination specified + git: + repo: '{{ repo_format1 }}' + update: no + clone: no + accept_hostkey: yes + register: git_result + +- assert: + that: + - 'git_result.changed' + +# Test that a specific revision can be checked out + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: clone to specific revision + git: + repo: "{{ repo_format1 }}" + dest: "{{ checkout_dir }}" + version: df4612ba925fbc1b3c51cbb006f51a0443bd2ce9 + +- name: check HEAD after clone to revision + command: git rev-parse HEAD chdir="{{ checkout_dir }}" + register: git_result + +- assert: + that: + - 'git_result.stdout == "df4612ba925fbc1b3c51cbb006f51a0443bd2ce9"' + +- name: update to specific revision + git: + repo: "{{ repo_format1 }}" + dest: "{{ checkout_dir }}" + version: 4e739a34719654db7b04896966e2354e1256ea5d + register: git_result + +- assert: + that: + - 'git_result.changed' + +- name: check HEAD after update to revision + command: git rev-parse HEAD chdir="{{ checkout_dir }}" + register: git_result + +- assert: + that: + - 'git_result.stdout == "4e739a34719654db7b04896966e2354e1256ea5d"' + +# Test a revision not available under refs/heads/ or refs/tags/ + +- name: attempt to get unavailable revision + git: + repo: "{{ repo_format1 }}" + dest: "{{ checkout_dir }}" + version: 2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b + ignore_errors: true + register: git_result + +- assert: + that: + - 'git_result.failed' + +# Same as the previous test, but this time we specify which ref +# contains the SHA1 +- name: update to revision by specifying the refspec + git: + repo: https://github.com/ansible/ansible-examples.git + dest: '{{ checkout_dir }}' + version: 2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b + refspec: refs/pull/7/merge + +- name: check HEAD after update with refspec + command: git rev-parse HEAD chdir="{{ checkout_dir }}" + register: git_result + +- assert: + that: + - 'git_result.stdout == "2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b"' + +# try out combination of refspec and depth +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: update to revision by specifying the refspec with depth=1 + git: + repo: https://github.com/ansible/ansible-examples.git + dest: '{{ checkout_dir }}' + version: 2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b + refspec: refs/pull/7/merge + depth: 1 + +- name: check HEAD after update with refspec + command: git rev-parse HEAD chdir="{{ checkout_dir }}" + register: git_result + +- assert: + that: + - 'git_result.stdout == "2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b"' + +- name: try to access other commit + shell: git checkout 0ce1096 + register: checkout_shallow + failed_when: False + args: + chdir: '{{ checkout_dir }}' + +- name: make sure the old commit was not fetched + assert: + that: + - checkout_shallow.rc == 1 + - checkout_shallow|failed + when: git_version.stdout | version_compare("{{git_version_supporting_depth}}", '>=') + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: clone to revision by specifying the refspec + git: + repo: https://github.com/ansible/ansible-examples.git + dest: '{{ checkout_dir }}' + version: 2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b + refspec: refs/pull/7/merge + +- name: check HEAD after update with refspec + command: git rev-parse HEAD chdir="{{ checkout_dir }}" + register: git_result + +- assert: + that: + - 'git_result.stdout == "2cfde3668b8bb10fbe2b9d5cec486025ad8cc51b"' + +# +# Submodule tests +# + +# Repository A with submodules defined (repo_submodules) +# .gitmodules file points to Repository I +# Repository B forked from A that has newer commits (repo_submodules_newer) +# .gitmodules file points to Repository II instead of I +# .gitmodules file also points to Repository III +# Repository I for submodule1 (repo_submodule1) +# Has 1 file checked in +# Repository II forked from I that has newer commits (repo_submodule1_newer) +# Has 2 files checked in +# Repository III for a second submodule (repo_submodule2) +# Has 1 file checked in + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: Test that clone without recursive does not retrieve submodules + git: + repo: '{{ repo_submodules }}' + dest: '{{ checkout_dir }}' + recursive: no + +- command: 'ls -1a {{ checkout_dir }}/submodule1' + register: submodule1 + +- assert: + that: '{{ submodule1.stdout_lines|length }} == 2' + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + + +- name: Test that clone with recursive retrieves submodules + git: + repo: '{{ repo_submodules }}' + dest: '{{ checkout_dir }}' + recursive: yes + +- command: 'ls -1a {{ checkout_dir }}/submodule1' + register: submodule1 + +- assert: + that: '{{ submodule1.stdout_lines|length }} == 4' + +- name: Copy the checkout so we can run several different tests on it + command: 'cp -pr {{ checkout_dir }} {{ checkout_dir }}.bak' + + + +- name: Test that update without recursive does not change submodules + command: 'git config --replace-all remote.origin.url {{ repo_submodules_newer }}' + args: + chdir: '{{ checkout_dir }}' + +- git: + repo: '{{ repo_submodules_newer }}' + dest: '{{ checkout_dir }}' + recursive: no + update: yes + track_submodules: yes + +- command: 'ls -1a {{ checkout_dir }}/submodule1' + register: submodule1 + +- stat: + path: '{{ checkout_dir }}/submodule2' + register: submodule2 + +- command: 'ls -1a {{ checkout_dir }}/submodule2' + register: submodule2 + +- assert: + that: '{{ submodule1.stdout_lines|length }} == 4' +- assert: + that: '{{ submodule2.stdout_lines|length }} == 2' + + + +- name: Restore checkout to prior state + file: state=absent path={{ checkout_dir }} +- command: 'cp -pr {{ checkout_dir }}.bak {{ checkout_dir }}' + +- name: Test that update with recursive updated existing submodules + command: 'git config --replace-all remote.origin.url {{ repo_submodules_newer }}' + args: + chdir: '{{ checkout_dir }}' + +- git: + repo: '{{ repo_submodules_newer }}' + dest: '{{ checkout_dir }}' + update: yes + recursive: yes + track_submodules: yes + +- command: 'ls -1a {{ checkout_dir }}/submodule1' + register: submodule1 + +- assert: + that: '{{ submodule1.stdout_lines|length }} == 5' + + +- name: Test that update with recursive found new submodules + command: 'ls -1a {{ checkout_dir }}/submodule2' + register: submodule2 + +- assert: + that: '{{ submodule2.stdout_lines|length }} == 4' +# test change of repo url +# see https://github.com/ansible/ansible-modules-core/pull/721 + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: Clone example git repo + git: + repo: '{{ repo_update_url_1 }}' + dest: '{{ checkout_dir }}' + +- name: Clone repo with changed url to the same place + git: + repo: '{{ repo_update_url_2 }}' + dest: '{{ checkout_dir }}' + register: clone2 + +- assert: + that: "clone2|success" + +- name: check url updated + shell: git remote show origin | grep Fetch + register: remote_url + args: + chdir: '{{ checkout_dir }}' + environment: + LC_ALL: C + +- assert: + that: + - "'git-test-new' in remote_url.stdout" + - "'git-test-old' not in remote_url.stdout" + +- name: check for new content in git-test-new + stat: path={{ checkout_dir }}/newfilename + register: repo_content + +- name: assert presence of new file in repo (i.e. working copy updated) + assert: + that: "repo_content.stat.exists" + +# Test that checkout by branch works when the branch is not in our current repo but the sha is + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: Clone example git repo that we're going to modify + git: + repo: '{{ repo_update_url_1 }}' + dest: '{{ checkout_dir }}/repo' + +- name: Clone the repo again - this is what we test + git: + repo: '{{ checkout_dir }}/repo' + dest: '{{ checkout_dir }}/checkout' + +- name: Add a branch to the repo + command: git branch new-branch + args: + chdir: '{{ checkout_dir }}/repo' + +- name: Checkout the new branch in the checkout + git: + repo: '{{ checkout_dir}}/repo' + version: 'new-branch' + dest: '{{ checkout_dir }}/checkout' + + +# Test the depth option and fetching revisions that were ignored first + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: Clone example git repo with depth=1 + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + +- name: try to access earlier commit + shell: git checkout 79624b4 + register: checkout_early + failed_when: False + args: + chdir: '{{ checkout_dir }}' + +- name: make sure the old commit was not fetched + assert: + that: checkout_early.rc == 1 + when: git_version.stdout | version_compare("{{git_version_supporting_depth}}", '>=') + +# tests https://github.com/ansible/ansible/issues/14954 +- name: fetch repo again with depth=1 + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + register: checkout2 + +- assert: + that: "not checkout2|changed" + when: git_version.stdout | version_compare("{{git_version_supporting_depth}}", '>=') + +- name: again try to access earlier commit + shell: git checkout 79624b4 + register: checkout_early + failed_when: False + args: + chdir: '{{ checkout_dir }}' + +- name: again make sure the old commit was not fetched + assert: + that: checkout_early.rc == 1 + when: git_version.stdout | version_compare("{{git_version_supporting_depth}}", '>=') + +# make sure we are still able to fetch other versions +- name: Clone same repo with older version + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + version: earlytag + register: cloneold + +- assert: + that: "cloneold|success" + +- name: try to access earlier commit + shell: git checkout 79624b4 + args: + chdir: '{{ checkout_dir }}' + +# test for https://github.com/ansible/ansible-modules-core/issues/527 +# clone a repo, add a tag to the same commit and try to checkout the new commit + + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: checkout example repo + git: repo={{ repo_format1 }} dest={{ checkout_dir }} + +- name: clone example repo locally + git: repo={{ checkout_dir }} dest={{checkout_dir}}.copy + +- name: get tags of head + command: git tag --contains chdir="{{ checkout_dir }}.copy" + register: listoftags + +- name: make sure the tag does not yet exist + assert: + that: + - "'newtag' not in listoftags.stdout_lines" + +- name: add tag in orig repo + command: git tag newtag chdir="{{ checkout_dir }}" + +- name: update copy with new tag + git: repo={{ checkout_dir }} dest={{checkout_dir}}.copy version=newtag + register: update_new_tag + +- name: get tags of new head + command: git tag --contains chdir="{{ checkout_dir }}.copy" + register: listoftags + +- name: check new head + assert: + that: + - not update_new_tag|changed + - "'newtag' in listoftags.stdout_lines" + + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + + + # Test for https://github.com/ansible/ansible-modules-core/issues/3456 + # clone a repo with depth and version specified + +- name: clone repo with both version and depth specified + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + version: master + +- name: run a second time (now fetch, not clone) + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + version: master + register: git_fetch + +- name: ensure the fetch succeeded + assert: + that: git_fetch|success + + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +- name: clone repo with both version and depth specified + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + version: master + +- name: switch to older branch with depth=1 (uses fetch) + git: + repo: '{{ repo_depth_url }}' + dest: '{{ checkout_dir }}' + depth: 1 + version: earlybranch + register: git_fetch + +- name: ensure the fetch succeeded + assert: + that: git_fetch|success + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + +# test for https://github.com/ansible/ansible-modules-core/issues/3782 +# make sure shallow fetch works when no version is specified + +- name: prepare old git repo + shell: git init; echo "1" > a; git add a; git commit -m "1" + args: + chdir: "{{repo_dir}}" + +- name: checkout old repo + git: + repo: '{{ repo_dir }}' + dest: '{{ checkout_dir }}' + depth: 1 + +- name: "update repo" + shell: echo "2" > a; git commit -a -m "2" + args: + chdir: "{{repo_dir}}" + +- name: fetch updated repo + git: + repo: '{{ repo_dir }}' + dest: '{{ checkout_dir }}' + depth: 1 + register: git_fetch + ignore_errors: yes + +- name: read file + shell: cat {{ checkout_dir }}/a + +- name: check update arrived + assert: + that: + - "{{ lookup('file', checkout_dir+'/a' )}} == 2" + - git_fetch|changed + +- name: clear checkout_dir + file: state=absent path={{ checkout_dir }} + diff --git a/test/integration/targets/hg/meta/main.yml b/test/integration/targets/hg/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/hg/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/hg/tasks/main.yml b/test/integration/targets/hg/tasks/main.yml new file mode 100644 index 0000000000..4eee22e4c7 --- /dev/null +++ b/test/integration/targets/hg/tasks/main.yml @@ -0,0 +1,78 @@ +# test code for the hg module +# (c) 2014, James Tanner <tanner.jc@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: set where to extract the repo + set_fact: checkout_dir={{ output_dir }}/epdb + +- name: set what repo to use + set_fact: repo=https://bitbucket.org/rpathsync/epdb + +- name: clean out the output_dir + shell: rm -rf {{ output_dir }}/* + +- name: verify that mercurial is installed so this test can continue + shell: which hg + +- name: initial checkout + hg: repo={{ repo }} dest={{ checkout_dir }} + register: hg_result + +- debug: var=hg_result + +#- shell: ls ~/ansible_testing/epdb +- shell: ls {{ checkout_dir }} + +- name: verify information about the initial clone + assert: + that: + - "'before' in hg_result" + - "'after' in hg_result" + - "not hg_result.before" + - "hg_result.changed" + +- name: repeated checkout + hg: repo={{ repo }} dest={{ checkout_dir }} + register: hg_result2 + +- debug: var=hg_result2 + +- name: check for tags + stat: path={{ checkout_dir }}/.hgtags + register: tags + +- name: check for remotes + stat: path={{ checkout_dir }}/.hg/branch + register: branches + +- debug: var=tags +- debug: var=branches + +- name: assert presence of tags/trunk/branches + assert: + that: + - "tags.stat.isreg" + - "branches.stat.isreg" + +- name: verify on a reclone things are marked unchanged + assert: + that: + - "not hg_result2.changed" + + + + diff --git a/test/integration/targets/ignore_errors/meta/main.yml b/test/integration/targets/ignore_errors/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/ignore_errors/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/ignore_errors/tasks/main.yml b/test/integration/targets/ignore_errors/tasks/main.yml new file mode 100644 index 0000000000..e0d6ae2f16 --- /dev/null +++ b/test/integration/targets/ignore_errors/tasks/main.yml @@ -0,0 +1,23 @@ +# test code +# (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: this will not stop the playbook + shell: /bin/false + register: failed + ignore_errors: True + diff --git a/test/integration/targets/include_vars/defaults/main.yml b/test/integration/targets/include_vars/defaults/main.yml new file mode 100644 index 0000000000..901fb220d2 --- /dev/null +++ b/test/integration/targets/include_vars/defaults/main.yml @@ -0,0 +1,3 @@ +--- +testing: 1 +base_dir: defaults diff --git a/test/integration/targets/include_vars/tasks/main.yml b/test/integration/targets/include_vars/tasks/main.yml new file mode 100644 index 0000000000..5ef6b3ef05 --- /dev/null +++ b/test/integration/targets/include_vars/tasks/main.yml @@ -0,0 +1,86 @@ +--- +- name: verify that the default value is indeed 1 + assert: + that: + - "testing == 1" + - "base_dir == 'defaults'" + +- name: include the vars/environments/development/all.yml + include_vars: + file: environments/development/all.yml + +- name: verify that the default value is indeed 789 + assert: + that: + - "testing == 789" + - "base_dir == 'environments/development'" + +- name: include the vars/environments/development/all.yml and save results in all + include_vars: + file: environments/development/all.yml + name: all + +- name: verify that the values are stored in the all variable + assert: + that: + - "all['testing'] == 789" + - "all['base_dir'] == 'environments/development'" + +- name: include the all directory in vars + include_vars: + dir: all + depth: 1 + +- name: verify that the default value is indeed 123 + assert: + that: + - "testing == 123" + - "base_dir == 'all'" + +- name: include every directory in vars + include_vars: + dir: vars + +- name: verify that the variable overwrite based on alphabetical order + assert: + that: + - "testing == 456" + - "base_dir == 'services'" + - "webapp_containers == 10" + +- name: include every directory in vars except files matching webapp.yml + include_vars: + dir: vars + ignore_files: + - webapp.yml + +- name: verify that the webapp.yml file was not included + assert: + that: + - "testing == 789" + - "base_dir == 'environments/development'" + +- name: include only files matching webapp.yml + include_vars: + dir: environments + files_matching: webapp.yml + +- name: verify that only files matching webapp.yml and in the environments directory get loaded. + assert: + that: + - "testing == 101112" + - "base_dir == 'development/services'" + - "webapp_containers == 20" + +- name: include only files matching webapp.yml and store results in webapp + include_vars: + dir: environments + files_matching: webapp.yml + name: webapp + +- name: verify that only files matching webapp.yml and in the environments directory get loaded into stored variable webapp. + assert: + that: + - "webapp['testing'] == 101112" + - "webapp['base_dir'] == 'development/services'" + - "webapp['webapp_containers'] == 20" diff --git a/test/integration/targets/include_vars/vars/all/all.yml b/test/integration/targets/include_vars/vars/all/all.yml new file mode 100644 index 0000000000..14c3e92b8e --- /dev/null +++ b/test/integration/targets/include_vars/vars/all/all.yml @@ -0,0 +1,3 @@ +--- +testing: 123 +base_dir: all diff --git a/test/integration/targets/include_vars/vars/environments/development/all.yml b/test/integration/targets/include_vars/vars/environments/development/all.yml new file mode 100644 index 0000000000..9f370de549 --- /dev/null +++ b/test/integration/targets/include_vars/vars/environments/development/all.yml @@ -0,0 +1,3 @@ +--- +testing: 789 +base_dir: 'environments/development' diff --git a/test/integration/targets/include_vars/vars/environments/development/services/webapp.yml b/test/integration/targets/include_vars/vars/environments/development/services/webapp.yml new file mode 100644 index 0000000000..a0a809c9e5 --- /dev/null +++ b/test/integration/targets/include_vars/vars/environments/development/services/webapp.yml @@ -0,0 +1,4 @@ +--- +testing: 101112 +base_dir: 'development/services' +webapp_containers: 20 diff --git a/test/integration/targets/include_vars/vars/services/webapp.yml b/test/integration/targets/include_vars/vars/services/webapp.yml new file mode 100644 index 0000000000..f0dcc8b517 --- /dev/null +++ b/test/integration/targets/include_vars/vars/services/webapp.yml @@ -0,0 +1,4 @@ +--- +testing: 456 +base_dir: services +webapp_containers: 10 diff --git a/test/integration/targets/iterators/meta/main.yml b/test/integration/targets/iterators/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/iterators/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/iterators/tasks/main.yml b/test/integration/targets/iterators/tasks/main.yml new file mode 100644 index 0000000000..584144af77 --- /dev/null +++ b/test/integration/targets/iterators/tasks/main.yml @@ -0,0 +1,257 @@ +# test code for iterating with lookup plugins +# (c) 2014, James Tanner <tanner.jc@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/>. + +# WITH_ITEMS + +- name: test with_items + set_fact: "{{ item }}=moo" + with_items: + - 'foo' + - 'bar' + +- debug: var=foo +- debug: var=bar + +- name: verify with_items results + assert: + that: + - "foo == 'moo'" + - "bar == 'moo'" + +# WITH_NESTED + +- name: test with_nested + set_fact: "{{ item.0 + item.1 }}=x" + with_nested: + - [ 'a', 'b' ] + - [ 'c', 'd' ] + +- debug: var=ac +- debug: var=ad +- debug: var=bc +- debug: var=bd + +- name: verify with_nested results + assert: + that: + - "ac == 'x'" + - "ad == 'x'" + - "bc == 'x'" + - "bd == 'x'" + +# WITH_SEQUENCE + +- name: test with_sequence + set_fact: "{{ 'x' + item }}={{ item }}" + with_sequence: start=0 end=3 + +- name: test with_sequence backwards + set_fact: "{{ 'y' + item }}={{ item }}" + with_sequence: start=3 end=0 stride=-1 + +- name: verify with_sequence + assert: + that: + - "x0 == '0'" + - "x1 == '1'" + - "x2 == '2'" + - "x3 == '3'" + - "y3 == '3'" + - "y2 == '2'" + - "y1 == '1'" + - "y0 == '0'" + +- name: test with_sequence not failing on count == 0 + debug: msg='previously failed with backward counting error' + with_sequence: count=0 + register: count_of_zero + +- name: test with_sequence does 1 when start == end + debug: msg='should run once' + with_sequence: start=1 end=1 + register: start_equal_end + +- name: test with_sequence count 1 + set_fact: "{{ 'x' + item }}={{ item }}" + with_sequence: count=1 + register: count_of_one + +- assert: + that: + - not start_equal_end| skipped + - count_of_zero | skipped + - not count_of_one | skipped + +# WITH_RANDOM_CHOICE + +- name: test with_random_choice + set_fact: "random={{ item }}" + with_random_choice: + - "foo" + - "bar" + +- name: verify with_random_choice + assert: + that: + - "random in ['foo', 'bar']" + +# WITH_SUBELEMENTS + +- name: test with_subelements + set_fact: "{{ '_'+ item.0.id + item.1 }}={{ item.1 }}" + with_subelements: + - "{{element_data}}" + - the_list + +- name: verify with_subelements results + assert: + that: + - "_xf == 'f'" + - "_xd == 'd'" + - "_ye == 'e'" + - "_yf == 'f'" + +- name: test with_subelements in subkeys + set_fact: "{{ '_'+ item.0.id + item.1 }}={{ item.1 }}" + with_subelements: + - "{{element_data}}" + - the.sub.key.list + +- name: verify with_subelements in subkeys results + assert: + that: + - "_xq == 'q'" + - "_xr == 'r'" + - "_yi == 'i'" + - "_yo == 'o'" + +- name: test with_subelements with missing key or subkey + set_fact: "{{ '_'+ item.0.id + item.1 }}={{ item.1 }}" + with_subelements: + - "{{element_data_missing}}" + - the.sub.key.list + - skip_missing: yes + register: _subelements_missing_subkeys + +- debug: var=_subelements_missing_subkeys +- debug: var=_subelements_missing_subkeys.results|length +- name: verify with_subelements in subkeys results + assert: + that: + - _subelements_missing_subkeys.skipped is not defined + - _subelements_missing_subkeys.results|length == 2 + - "_xk == 'k'" + - "_xl == 'l'" + + +# WITH_TOGETHER + +- name: test with_together + #shell: echo {{ item }} + set_fact: "{{ item.0 }}={{ item.1 }}" + with_together: + - [ 'a', 'b', 'c', 'd' ] + - [ '1', '2', '3', '4' ] + +- name: verify with_together results + assert: + that: + - "a == '1'" + - "b == '2'" + - "c == '3'" + - "d == '4'" + +# WITH_FIRST_FOUND + +- name: create file for test + shell: echo "foo" > {{ output_dir }}/foo1 + +- name: create file for test + shell: echo "bar" > {{ output_dir }}/bar1 + +- name: test with_first_found + #shell: echo {{ item }} + set_fact: "first_found={{ item }}" + with_first_found: + - "{{ output_dir + '/does_not_exist' }}" + - "{{ output_dir + '/foo1' }}" + - "{{ output_dir + '/bar1' }}" + +- name: set expected + set_fact: first_expected="{{ output_dir | expanduser + '/foo1' }}" + +- name: set unexpected + set_fact: first_unexpected="{{ output_dir | expanduser + '/bar1' }}" + +- name: verify with_first_found results + assert: + that: + - "first_found == first_expected" + - "first_found != first_unexpected" + +# WITH_LINES + +- name: test with_lines + #shell: echo "{{ item }}" + set_fact: "{{ item }}=set" + with_lines: for i in $(seq 1 5); do echo "l$i" ; done; + +- name: verify with_lines results + assert: + that: + - "l1 == 'set'" + - "l2 == 'set'" + - "l3 == 'set'" + - "l4 == 'set'" + - "l5 == 'set'" + +# WITH_INDEX +- name: create unindexed list + shell: for i in $(seq 1 5); do echo "x" ; done; + register: list_data + +- name: create indexed list + set_fact: "{{ item[1] + item[0]|string }}=set" + with_indexed_items: "{{list_data.stdout_lines}}" + +- name: verify with_indexed_items result + assert: + that: + - "x0 == 'set'" + - "x1 == 'set'" + - "x2 == 'set'" + - "x3 == 'set'" + - "x4 == 'set'" + +# WITH_FLATTENED + +- name: test with_flattened + set_fact: "{{ item }}=flattened" + with_flattened: + - [ 'a__' ] + - [ 'b__', ['c__', 'd__'] ] + +- name: verify with_flattened results + assert: + that: + - "a__ == 'flattened'" + - "b__ == 'flattened'" + - "c__ == 'flattened'" + - "d__ == 'flattened'" + + diff --git a/test/integration/targets/iterators/vars/main.yml b/test/integration/targets/iterators/vars/main.yml new file mode 100644 index 0000000000..f7ef50f57a --- /dev/null +++ b/test/integration/targets/iterators/vars/main.yml @@ -0,0 +1,43 @@ +element_data: + - id: x + the_list: + - "f" + - "d" + the: + sub: + key: + list: + - "q" + - "r" + - id: y + the_list: + - "e" + - "f" + the: + sub: + key: + list: + - "i" + - "o" +element_data_missing: + - id: x + the_list: + - "f" + - "d" + the: + sub: + key: + list: + - "k" + - "l" + - id: y + the_list: + - "f" + - "d" + - id: z + the_list: + - "e" + - "f" + the: + sub: + key: diff --git a/test/integration/targets/lineinfile/files/test.txt b/test/integration/targets/lineinfile/files/test.txt new file mode 100644 index 0000000000..8187db9f02 --- /dev/null +++ b/test/integration/targets/lineinfile/files/test.txt @@ -0,0 +1,5 @@ +This is line 1 +This is line 2 +REF this is a line for backrefs REF +This is line 4 +This is line 5 diff --git a/test/integration/targets/lineinfile/files/testempty.txt b/test/integration/targets/lineinfile/files/testempty.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/lineinfile/files/testempty.txt diff --git a/test/integration/targets/lineinfile/files/testnoeof.txt b/test/integration/targets/lineinfile/files/testnoeof.txt new file mode 100644 index 0000000000..152780b9ff --- /dev/null +++ b/test/integration/targets/lineinfile/files/testnoeof.txt @@ -0,0 +1,2 @@ +This is line 1 +This is line 2
\ No newline at end of file diff --git a/test/integration/targets/lineinfile/meta/main.yml b/test/integration/targets/lineinfile/meta/main.yml new file mode 100644 index 0000000000..98e60f7806 --- /dev/null +++ b/test/integration/targets/lineinfile/meta/main.yml @@ -0,0 +1,20 @@ +# test code for the lineinfile module +# (c) 2014, James Cammarata <jcammarata@ansible.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/>. + +dependencies: + - prepare_tests diff --git a/test/integration/targets/lineinfile/tasks/main.yml b/test/integration/targets/lineinfile/tasks/main.yml new file mode 100644 index 0000000000..8cfb3430f6 --- /dev/null +++ b/test/integration/targets/lineinfile/tasks/main.yml @@ -0,0 +1,376 @@ +# test code for the lineinfile module +# (c) 2014, James Cammarata <jcammarata@ansible.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: deploy the test file for lineinfile + copy: src=test.txt dest={{output_dir}}/test.txt + register: result + +- name: assert that the test file was deployed + assert: + that: + - "result.changed == true" + - "result.checksum == '5feac65e442c91f557fc90069ce6efc4d346ab51'" + - "result.state == 'file'" + +- name: insert a line at the beginning of the file, and back it up + lineinfile: dest={{output_dir}}/test.txt state=present line="New line at the beginning" insertbefore="BOF" backup=yes + register: result + +- name: assert that the line was inserted at the head of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + - "result.backup != ''" + +- name: stat the backup file + stat: path={{result.backup}} + register: result + +- name: assert the backup file matches the previous hash + assert: + that: + - "result.stat.checksum == '5feac65e442c91f557fc90069ce6efc4d346ab51'" + +- name: stat the test after the insert at the head + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test hash is what we expect for the file with the insert at the head + assert: + that: + - "result.stat.checksum == '7eade4042b23b800958fe807b5bfc29f8541ec09'" + +- name: insert a line at the end of the file + lineinfile: dest={{output_dir}}/test.txt state=present line="New line at the end" insertafter="EOF" + register: result + +- name: assert that the line was inserted at the end of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the test after the insert at the end + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the insert at the end + assert: + that: + - "result.stat.checksum == 'fb57af7dc10a1006061b000f1f04c38e4bef50a9'" + +- name: insert a line after the first line + lineinfile: dest={{output_dir}}/test.txt state=present line="New line after line 1" insertafter="^This is line 1$" + register: result + +- name: assert that the line was inserted after the first line + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the test after insert after the first line + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the insert after the first line + assert: + that: + - "result.stat.checksum == '5348da605b1bc93dbadf3a16474cdf22ef975bec'" + +- name: insert a line before the last line + lineinfile: dest={{output_dir}}/test.txt state=present line="New line after line 5" insertbefore="^This is line 5$" + register: result + +- name: assert that the line was inserted before the last line + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: stat the test after the insert before the last line + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the insert before the last line + assert: + that: + - "result.stat.checksum == 'e1cae425403507feea4b55bb30a74decfdd4a23e'" + +- name: replace a line with backrefs + lineinfile: dest={{output_dir}}/test.txt state=present line="This is line 3" backrefs=yes regexp="^(REF) .* \\1$" + register: result + +- name: assert that the line with backrefs was changed + assert: + that: + - "result.changed == true" + - "result.msg == 'line replaced'" + +- name: stat the test after the backref line was replaced + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == '2ccdf45d20298f9eaece73b713648e5489a52444'" + +- name: remove the middle line + lineinfile: dest={{output_dir}}/test.txt state=absent regexp="^This is line 3$" + register: result + +- name: assert that the line was removed + assert: + that: + - "result.changed == true" + - "result.msg == '1 line(s) removed'" + +- name: stat the test after the middle line was removed + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the middle line was removed + assert: + that: + - "result.stat.checksum == 'a6ba6865547c19d4c203c38a35e728d6d1942c75'" + +- name: run a validation script that succeeds + lineinfile: dest={{output_dir}}/test.txt state=absent regexp="^This is line 5$" validate="true %s" + register: result + +- name: assert that the file validated after removing a line + assert: + that: + - "result.changed == true" + - "result.msg == '1 line(s) removed'" + +- name: stat the test after the validation succeeded + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after the validation succeeded + assert: + that: + - "result.stat.checksum == '76955a4516a00a38aad8427afc9ee3e361024ba5'" + +- name: run a validation script that fails + lineinfile: dest={{output_dir}}/test.txt state=absent regexp="^This is line 1$" validate="/bin/false %s" + register: result + ignore_errors: yes + +- name: assert that the validate failed + assert: + that: + - "result.failed == true" + +- name: stat the test after the validation failed + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches the previous after the validation failed + assert: + that: + - "result.stat.checksum == '76955a4516a00a38aad8427afc9ee3e361024ba5'" + +- name: use create=yes + lineinfile: dest={{output_dir}}/new_test.txt create=yes insertbefore=BOF state=present line="This is a new file" + register: result + +- name: assert that the new file was created + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: validate that the newly created file exists + stat: path={{output_dir}}/new_test.txt + register: result + ignore_errors: yes + +- name: assert the newly created test checksum matches + assert: + that: + - "result.stat.checksum == '038f10f9e31202451b093163e81e06fbac0c6f3a'" + +# Test EOF in cases where file has no newline at EOF +- name: testnoeof deploy the file for lineinfile + copy: src=testnoeof.txt dest={{output_dir}}/testnoeof.txt + register: result + +- name: testnoeof insert a line at the end of the file + lineinfile: dest={{output_dir}}/testnoeof.txt state=present line="New line at the end" insertafter="EOF" + register: result + +- name: testempty assert that the line was inserted at the end of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: insert a multiple lines at the end of the file + lineinfile: dest={{output_dir}}/test.txt state=present line="This is a line\nwith \\n character" insertafter="EOF" + register: result + +- name: assert that the multiple lines was inserted + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: testnoeof stat the no newline EOF test after the insert at the end + stat: path={{output_dir}}/testnoeof.txt + register: result + +- name: testnoeof assert test checksum matches after the insert at the end + assert: + that: + - "result.stat.checksum == 'f9af7008e3cb67575ce653d094c79cabebf6e523'" + +# Test EOF with empty file to make sure no unnecessary newline is added +- name: testempty deploy the testempty file for lineinfile + copy: src=testempty.txt dest={{output_dir}}/testempty.txt + register: result + +- name: testempty insert a line at the end of the file + lineinfile: dest={{output_dir}}/testempty.txt state=present line="New line at the end" insertafter="EOF" + register: result + +- name: testempty assert that the line was inserted at the end of the file + assert: + that: + - "result.changed == true" + - "result.msg == 'line added'" + +- name: testempty stat the test after the insert at the end + stat: path={{output_dir}}/testempty.txt + register: result + +- name: testempty assert test checksum matches after the insert at the end + assert: + that: + - "result.stat.checksum == 'f440dc65ea9cec3fd496c1479ddf937e1b949412'" + +- stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after inserting multiple lines + assert: + that: + - "result.stat.checksum == 'bf5b711f8f0509355aaeb9d0d61e3e82337c1365'" + +- name: replace a line with backrefs included in the line + lineinfile: dest={{output_dir}}/test.txt state=present line="New \\1 created with the backref" backrefs=yes regexp="^This is (line 4)$" + register: result + +- name: assert that the line with backrefs was changed + assert: + that: + - "result.changed == true" + - "result.msg == 'line replaced'" + +- name: stat the test after the backref line was replaced + stat: path={{output_dir}}/test.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == '04b7a54d0fb233a4e26c9e625325bb4874841b3c'" + +################################################################### +# issue 8535 + +- name: create a new file for testing quoting issues + file: dest={{output_dir}}/test_quoting.txt state=touch + register: result + +- name: assert the new file was created + assert: + that: + - result.changed + +- name: use with_items to add code-like strings to the quoting txt file + lineinfile: > + dest={{output_dir}}/test_quoting.txt + line="{{ item }}" + insertbefore=BOF + with_items: + - "'foo'" + - "dotenv.load();" + - "var dotenv = require('dotenv');" + register: result + +- name: assert the quote test file was modified correctly + assert: + that: + - result.results|length == 3 + - result.results[0].changed + - result.results[0].item == "'foo'" + - result.results[1].changed + - result.results[1].item == "dotenv.load();" + - result.results[2].changed + - result.results[2].item == "var dotenv = require('dotenv');" + +- name: stat the quote test file + stat: path={{output_dir}}/test_quoting.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == '7dc3cb033c3971e73af0eaed6623d4e71e5743f1'" + +- name: insert a line into the quoted file with a single quote + lineinfile: dest={{output_dir}}/test_quoting.txt line="import g'" + register: result + +- name: assert that the quoted file was changed + assert: + that: + - result.changed + +- name: stat the quote test file + stat: path={{output_dir}}/test_quoting.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == '73b271c2cc1cef5663713bc0f00444b4bf9f4543'" + +- name: insert a line into the quoted file with many double quotation strings + lineinfile: dest={{output_dir}}/test_quoting.txt line="\"quote\" and \"unquote\"" + register: result + +- name: assert that the quoted file was changed + assert: + that: + - result.changed + +- name: stat the quote test file + stat: path={{output_dir}}/test_quoting.txt + register: result + +- name: assert test checksum matches after backref line was replaced + assert: + that: + - "result.stat.checksum == 'b10ab2a3c3b6492680c8d0b1d6f35aa6b8f9e731'" + +################################################################### diff --git a/test/integration/targets/lookups/meta/main.yml b/test/integration/targets/lookups/meta/main.yml new file mode 100644 index 0000000000..ee75ae6e65 --- /dev/null +++ b/test/integration/targets/lookups/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - prepare_tests + - prepare_http_tests + diff --git a/test/integration/targets/lookups/tasks/main.yml b/test/integration/targets/lookups/tasks/main.yml new file mode 100644 index 0000000000..f861ceaede --- /dev/null +++ b/test/integration/targets/lookups/tasks/main.yml @@ -0,0 +1,270 @@ +# test code for lookup plugins +# (c) 2014, James Tanner <tanner.jc@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/>. + +# FILE LOOKUP + +- name: make a new file to read + copy: dest={{output_dir}}/foo.txt mode=0644 content="bar" + +- name: load the file as a fact + set_fact: + foo: "{{ lookup('file', output_dir + '/foo.txt' ) }}" + +- debug: var=foo + +- name: verify file lookup + assert: + that: + - "foo == 'bar'" + + +# PASSWORD LOOKUP + +- name: remove previous password files and directory + file: dest={{item}} state=absent + with_items: + - "{{output_dir}}/lookup/password" + - "{{output_dir}}/lookup/password_with_salt" + - "{{output_dir}}/lookup" + +- name: create a password file + set_fact: + newpass: "{{ lookup('password', output_dir + '/lookup/password length=8') }}" + +- name: stat the password file directory + stat: path="{{output_dir}}/lookup" + register: result + +- name: assert the directory's permissions + assert: + that: + - result.stat.mode == '0700' + +- name: stat the password file + stat: path="{{output_dir}}/lookup/password" + register: result + +- name: assert the directory's permissions + assert: + that: + - result.stat.mode == '0600' + +- name: get password length + shell: wc -c {{output_dir}}/lookup/password | awk '{print $1}' + register: wc_result + +- debug: var=wc_result.stdout + +- name: read password + shell: cat {{output_dir}}/lookup/password + register: cat_result + +- debug: var=cat_result.stdout + +- name: verify password + assert: + that: + - "wc_result.stdout == '9'" + - "cat_result.stdout == newpass" + - "' salt=' not in cat_result.stdout" + +- name: fetch password from an existing file + set_fact: + pass2: "{{ lookup('password', output_dir + '/lookup/password length=8') }}" + +- name: read password (again) + shell: cat {{output_dir}}/lookup/password + register: cat_result2 + +- debug: var=cat_result2.stdout + +- name: verify password (again) + assert: + that: + - "cat_result2.stdout == newpass" + - "' salt=' not in cat_result2.stdout" + + + +- name: create a password (with salt) file + debug: msg={{ lookup('password', output_dir + '/lookup/password_with_salt encrypt=sha256_crypt') }} + +- name: read password and salt + shell: cat {{output_dir}}/lookup/password_with_salt + register: cat_pass_salt + +- debug: var=cat_pass_salt.stdout + +- name: fetch unencrypted password + set_fact: + newpass: "{{ lookup('password', output_dir + '/lookup/password_with_salt') }}" + +- debug: var=newpass + +- name: verify password and salt + assert: + that: + - "cat_pass_salt.stdout != newpass" + - "cat_pass_salt.stdout.startswith(newpass)" + - "' salt=' in cat_pass_salt.stdout" + - "' salt=' not in newpass" + + +- name: fetch unencrypted password (using empty encrypt parameter) + set_fact: + newpass2: "{{ lookup('password', output_dir + '/lookup/password_with_salt encrypt=') }}" + +- name: verify lookup password behavior + assert: + that: + - "newpass == newpass2" + + +# ENV LOOKUP + +- name: get first environment var name + shell: env | fgrep -v '.' | head -n1 | cut -d\= -f1 + register: known_var_name + +- name: get first environment var value + shell: echo {{ '$' + known_var_name.stdout }} + register: known_var_value + +- name: use env lookup to get known var + set_fact: + test_val: "{{ lookup('env', known_var_name.stdout) }}" + +- debug: var=known_var_name.stdout +- debug: var=known_var_value.stdout +- debug: var=test_val + +- name: compare values + assert: + that: + - "test_val == known_var_value.stdout" + + +# PIPE LOOKUP + +# https://github.com/ansible/ansible/issues/6550 +- name: confirm pipe lookup works with a single positional arg + debug: msg="{{ lookup('pipe', 'ls') }}" + + +# LOOKUP TEMPLATING + +- name: use bare interpolation + debug: msg="got {{item}}" + with_items: "{{things1}}" + register: bare_var + +- name: verify that list was interpolated + assert: + that: + - "bare_var.results[0].item == 1" + - "bare_var.results[1].item == 2" + +- name: use list with bare strings in it + debug: msg={{item}} + with_items: + - things2 + - things1 + +- name: use list with undefined var in it + debug: msg={{item}} + with_items: "{{things2}}" + ignore_errors: True + + +# BUG #10073 nested template handling + +- name: set variable that clashes + set_fact: + LOGNAME: foobar + + +- name: get LOGNAME environment var value + shell: echo {{ '$LOGNAME' }} + register: known_var_value + +- name: do the lookup for env LOGNAME + set_fact: + test_val: "{{ lookup('env', 'LOGNAME') }}" + +- debug: var=test_val + +- name: compare values + assert: + that: + - "test_val == known_var_value.stdout" + + +- name: set with_dict + shell: echo "{{ item.key + '=' + item.value }}" + with_dict: "{{ mydict }}" + +# URL Lookups + +- name: Test that retrieving a url works + set_fact: + web_data: "{{ lookup('url', 'https://gist.githubusercontent.com/abadger/9858c22712f62a8effff/raw/43dd47ea691c90a5fa7827892c70241913351963/test') }}" + +- name: Assert that the url was retrieved + assert: + that: + - "'one' in web_data" + +- name: Test that retrieving a url with invalid cert fails + set_fact: + web_data: "{{ lookup('url', 'https://{{ badssl_host }}/') }}" + ignore_errors: True + register: url_invalid_cert + +- assert: + that: + - "url_invalid_cert.failed" + - "'Error validating the server' in url_invalid_cert.msg" + +- name: Test that retrieving a url with invalid cert with validate_certs=False works + set_fact: + web_data: "{{ lookup('url', 'https://{{ badssl_host }}/', validate_certs=False) }}" + register: url_no_validate_cert + +- assert: + that: + - "'{{ badssl_host_substring }}' in web_data" + +- name: Test cartesian lookup + debug: var={{item}} + with_cartesian: + - ["A", "B", "C"] + - ["1", "2", "3"] + register: product + +- name: Verify cartesian lookup + assert: + that: + - product.results[0]['item'] == ["A", "1"] + - product.results[1]['item'] == ["A", "2"] + - product.results[2]['item'] == ["A", "3"] + - product.results[3]['item'] == ["B", "1"] + - product.results[4]['item'] == ["B", "2"] + - product.results[5]['item'] == ["B", "3"] + - product.results[6]['item'] == ["C", "1"] + - product.results[7]['item'] == ["C", "2"] + - product.results[8]['item'] == ["C", "3"] diff --git a/test/integration/targets/lookups/vars/main.yml b/test/integration/targets/lookups/vars/main.yml new file mode 100644 index 0000000000..4c44b1cb86 --- /dev/null +++ b/test/integration/targets/lookups/vars/main.yml @@ -0,0 +1,9 @@ +mydict: + mykey1: myval1 + mykey2: myval2 +things1: + - 1 + - 2 +things2: + - "{{ foo }}" + - "{{ foob | default('') }}" diff --git a/test/integration/targets/loops/tasks/main.yml b/test/integration/targets/loops/tasks/main.yml new file mode 100644 index 0000000000..7fa6d0d03a --- /dev/null +++ b/test/integration/targets/loops/tasks/main.yml @@ -0,0 +1,20 @@ +- name: Measure time before + shell: date +%s + register: before + +- debug: + var: i + with_sequence: count=3 + loop_control: + loop_var: i + pause: 2 + +- name: Measure time after + shell: date +%s + register: after + +# since there is 3 rounds, and 2 seconds between, it should last 4 seconds +# we do not test the upper bound, since CI can lag significantly +- assert: + that: + - '(after.stdout |int) - (before.stdout|int) >= 4' diff --git a/test/integration/targets/mount/tasks/main.yml b/test/integration/targets/mount/tasks/main.yml new file mode 100644 index 0000000000..ab3e0c5f4a --- /dev/null +++ b/test/integration/targets/mount/tasks/main.yml @@ -0,0 +1,113 @@ +# (c) 2016, Toshio Kuratomi <tkuratomi@ansible.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 the mount point + file: + state: "directory" + path: "{{ outputdir }}/mount_dest" + +- name: Create a directory to bind mount + file: + state: "directory" + path: "{{ outputdir }}/mount_source" + +- name: Put something in the directory so we see that it worked + copy: + content: "Testing\n" + dest: "{{ outputdir }}/mount_source/test_file" + register: orig_info + +# The opts type of bind mount only works on Linux +- name: Bind mount a filesystem (Linux) + mount: + src: "{{ outputdir }}/mount_source" + name: "{{ outputdir }}/mount_dest" + state: "mounted" + fstype: "None" + opts: "bind" + when: ansible_system == 'Linux' + register: bind_result_linux + +# Nullfs is freebsd only +- name: Bind mount a filesystem (FreeBSD) + mount: + src: "{{ outputdir }}/mount_source" + name: "{{ outputdir }}/mount_dest" + state: "mounted" + fstype: "nullfs" + when: ansible_system == 'FreeBSD' + register: bind_result_freebsd + +- name: get checksum for bind mounted file + stat: + path: "{{ outputdir }}/mount_dest/test_file" + when: ansible_system in ('FreeBSD', 'Linux') + register: dest_stat + +- name: assert the bind mount was successful + assert: + that: + - "(ansible_system == 'Linux' and bind_result_linux['changed']) or (ansible_system == 'FreeBSD' and bind_result_freebsd['changed'])" + - "dest_stat['stat']['exists']" + - "orig_info['checksum'] == dest_stat['stat']['checksum']" + when: ansible_system in ('FreeBSD', 'Linux') + +# The opts type of bind mount only works on Linux +- name: Bind mount a filesystem (Linux) + mount: + src: "{{ outputdir }}/mount_source" + name: "{{ outputdir }}/mount_dest" + state: "mounted" + fstype: "None" + opts: "bind" + when: ansible_system == 'Linux' + register: bind_result_linux + +# Nullfs is freebsd only +- name: Bind mount a filesystem (FreeBSD) + mount: + src: "{{ outputdir }}/mount_source" + name: "{{ outputdir }}/mount_dest" + state: "mounted" + fstype: "nullfs" + when: ansible_system == 'FreeBSD' + register: bind_result_freebsd + +- name: Make sure we didn't mount a second time + assert: + that: + - "not bind_result_linux['changed'] and not bind_result_freebsd['changed']" + +- name: Unmount the bind mount + mount: + name: "{{ outputdir }}/mount_dest" + state: "absent" + when: ansible_system in ('Linux', 'FreeBSD') + register: unmount_result + +- name: Make sure the file no longer exists in dest + stat: + path: "{{ outputdir }}/mount_dest/test_file" + when: ansible_system in ('FreeBSD', 'Linux') + register: dest_stat + +- name: Check that we unmounted + assert: + that: + - "unmount_result['changed']" + - "not dest_stat['stat']['exists']" + when: ansible_system in ('FreeBSD', 'Linux') diff --git a/test/integration/targets/mysql_db/defaults/main.yml b/test/integration/targets/mysql_db/defaults/main.yml new file mode 100644 index 0000000000..456ba0eb4c --- /dev/null +++ b/test/integration/targets/mysql_db/defaults/main.yml @@ -0,0 +1,8 @@ +--- +# defaults file for test_mysql_db +db_name: 'data' +db_user1: 'datauser1' +db_user2: 'datauser2' + +tmp_dir: '/tmp' + diff --git a/test/integration/targets/mysql_db/meta/main.yml b/test/integration/targets/mysql_db/meta/main.yml new file mode 100644 index 0000000000..4aa170dc06 --- /dev/null +++ b/test/integration/targets/mysql_db/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_mysql_db diff --git a/test/integration/targets/mysql_db/tasks/main.yml b/test/integration/targets/mysql_db/tasks/main.yml new file mode 100644 index 0000000000..1c2adcce8e --- /dev/null +++ b/test/integration/targets/mysql_db/tasks/main.yml @@ -0,0 +1,198 @@ +# test code for the mysql_db module +# (c) 2014, Wayne Rosario <wrosario@ansible.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: make sure the test database is not there + command: mysql {{db_name}} + register: mysql_db_check + failed_when: "'1049' not in mysql_db_check.stderr" + +- name: test state=present for a database name (expect changed=true) + mysql_db: name={{ db_name }} state=present + register: result + +- name: assert output message that database exist + assert: + that: + - "result.changed == true" + - "result.db =='{{ db_name }}'" + +- name: run command to test state=present for a database name (expect db_name in stdout) + command: mysql "-e show databases like '{{ db_name }}';" + register: result + +- name: assert database exist + assert: { that: "'{{ db_name }}' in result.stdout" } + +# ============================================================ +- name: test state=absent for a database name (expect changed=true) + mysql_db: name={{ db_name }} state=absent + register: result + +- name: assert output message that database does not exist + assert: + that: + - "result.changed == true" + - "result.db =='{{ db_name }}'" + +- name: run command to test state=absent for a database name (expect db_name not in stdout) + command: mysql "-e show databases like '{{ db_name }}';" + register: result + +- name: assert database does not exist + assert: { that: "'{{ db_name }}' not in result.stdout" } + +# ============================================================ +- name: test mysql_db encoding param not valid - issue 8075 + mysql_db: name=datanotvalid state=present encoding=notvalid + register: result + ignore_errors: true + +- name: assert test mysql_db encoding param not valid - issue 8075 (failed=true) + assert: + that: + - "result.failed == true" + - "'Traceback' not in result.msg" + - "'Unknown character set' in result.msg" + +# ============================================================ +- name: test mysql_db using a valid encoding utf8 (expect changed=true) + mysql_db: name=en{{ db_name }} state=present encoding=utf8 + register: result + +- name: assert output message created a database + assert: { that: "result.changed == true" } + +- name: test database was created + command: mysql "-e SHOW CREATE DATABASE en{{ db_name }};" + register: result + +- name: assert created database is of encoding utf8 + assert: { that: "'utf8' in result.stdout" } + +- name: remove database + mysql_db: name=en{{ db_name }} state=absent + +# ============================================================ +- name: test mysql_db using valid encoding binary (expect changed=true) + mysql_db: name=en{{ db_name }} state=present encoding=binary + register: result + +- name: assert output message that database was created + assert: { that: "result.changed == true" } + +- name: run command to test database was created + command: mysql "-e SHOW CREATE DATABASE en{{ db_name }};" + register: result + +- name: assert created database is of encoding binary + assert: { that: "'binary' in result.stdout" } + +- name: remove database + mysql_db: name=en{{ db_name }} state=absent + +# ============================================================ +- name: create user1 to access database dbuser1 + mysql_user: name=user1 password=password1 priv=*.*:ALL state=present + +- name: create database dbuser1 using user1 + mysql_db: name={{ db_user1 }} state=present login_user=user1 login_password=password1 + register: result + +- name: assert output message that database was created + assert: { that: "result.changed == true" } + +- name: run command to test database was created using user1 + command: mysql "-e show databases like '{{ db_user1 }}';" + register: result + +- name: assert database exist + assert: { that: "'{{ db_user1 }}' in result.stdout" } + +# ============================================================ +- name: create user2 to access database with privilege select only + mysql_user: name=user2 password=password2 priv=*.*:SELECT state=present + +- name: create database dbuser2 using user2 with no privilege to create (expect failed=true) + mysql_db: name={{ db_user2 }} state=present login_user=user2 login_password=password2 + register: result + ignore_errors: true + +- name: assert output message that database was not created using dbuser2 + assert: + that: + - "result.failed == true" + - "'Access denied' in result.msg" + +- name: run command to test that database was not created + command: mysql "-e show databases like '{{ db_user2 }}';" + register: result + +- name: assert database does not exist + assert: { that: "'{{ db_user2 }}' not in result.stdout" } + +# ============================================================ +- name: delete database using user2 with no privilege to delete (expect failed=true) + mysql_db: name={{ db_user1 }} state=absent login_user=user2 login_password=password2 + register: result + ignore_errors: true + +- name: assert output message that database was not deleted using dbuser2 + assert: + that: + - "result.failed == true" + - "'Access denied' in result.msg" + +- name: run command to test database was not deleted + command: mysql "-e show databases like '{{ db_user1 }}';" + register: result + +- name: assert database still exist + assert: { that: "'{{ db_user1 }}' in result.stdout" } + +# ============================================================ +- name: delete database using user1 with all privilege to delete a database (expect changed=true) + mysql_db: name={{ db_user1 }} state=absent login_user=user1 login_password=password1 + register: result + ignore_errors: true + +- name: assert output message that database was deleted using user1 + assert: { that: "result.changed == true" } + +- name: run command to test database was deleted using user1 + command: mysql "-e show databases like '{{ db_name }}';" + register: result + +- name: assert database does not exist + assert: { that: "'{{ db_user1 }}' not in result.stdout" } + +# ============================================================ +- include: state_dump_import.yml format_type=sql file=dbdata.sql format_msg_type=ASCII + +- include: state_dump_import.yml format_type=gz file=dbdata.gz format_msg_type=gzip + +- include: state_dump_import.yml format_type=bz2 file=dbdata.bz2 format_msg_type=bzip2 + + + + + + + + diff --git a/test/integration/targets/mysql_db/tasks/state_dump_import.yml b/test/integration/targets/mysql_db/tasks/state_dump_import.yml new file mode 100644 index 0000000000..44267e1edb --- /dev/null +++ b/test/integration/targets/mysql_db/tasks/state_dump_import.yml @@ -0,0 +1,75 @@ +# test code for state dump and import for mysql_db module +# (c) 2014, Wayne Rosario <wrosario@ansible.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/>. + +# ============================================================ +- set_fact: db_file_name="{{tmp_dir}}/{{file}}" + +- name: state dump/import - create database + mysql_db: name={{ db_name }} state=present + +- name: state dump/import - create table employee + command: mysql {{ db_name }} '-e create table employee(id int, name varchar(100));' + +- name: state dump/import - insert data into table employee + command: mysql {{ db_name }} "-e insert into employee value(47,'Joe Smith');" + +- name: state dump/import - file name should not exist + file: name={{ db_file_name }} state=absent + +- name: test state=dump to backup the database of type {{ format_type }} (expect changed=true) + mysql_db: name={{ db_name }} state=dump target={{ db_file_name }} + register: result + +- name: assert output message backup the database + assert: + that: + - "result.changed == true" + - "result.db =='{{ db_name }}'" + +- name: assert database was backup successfully + command: file {{ db_file_name }} + register: result + +- name: assert file format type + assert: { that: "'{{format_msg_type}}' in result.stdout" } + +- name: update database table employee + command: mysql {{ db_name }} "-e update employee set name='John Doe' where id=47;" + +- name: test state=import to restore the database of type {{ format_type }} (expect changed=true) + mysql_db: name={{ db_name }} state=import target={{ db_file_name }} + register: result + +- name: assert output message restore the database + assert: { that: "result.changed == true" } + +- name: select data from table employee + command: mysql {{ db_name }} "-e select * from employee;" + register: result + +- name: assert data in database is from the restore database + assert: + that: + - "'47' in result.stdout" + - "'Joe Smith' in result.stdout" + +- name: remove database name + mysql_db: name={{ db_name }} state=absent + +- name: remove file name + file: name={{ db_file_name }} state=absent diff --git a/test/integration/targets/mysql_user/defaults/main.yml b/test/integration/targets/mysql_user/defaults/main.yml new file mode 100644 index 0000000000..ff7503f8f0 --- /dev/null +++ b/test/integration/targets/mysql_user/defaults/main.yml @@ -0,0 +1,16 @@ +--- +# defaults file for test_mysql_user +db_name: 'data' +user_name_1: 'db_user1' +user_name_2: 'db_user2' + +user_password_1: '12345' +user_password_2: '98765' + +db_names: + - clientdb + - employeedb + - providerdb + +tmp_dir: '/tmp' + diff --git a/test/integration/targets/mysql_user/meta/main.yml b/test/integration/targets/mysql_user/meta/main.yml new file mode 100644 index 0000000000..4aa170dc06 --- /dev/null +++ b/test/integration/targets/mysql_user/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_mysql_db diff --git a/test/integration/targets/mysql_user/tasks/assert_no_user.yml b/test/integration/targets/mysql_user/tasks/assert_no_user.yml new file mode 100644 index 0000000000..4d4a411fe2 --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/assert_no_user.yml @@ -0,0 +1,25 @@ +# test code to assert no mysql user +# (c) 2014, Wayne Rosario <wrosario@ansible.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: run command to query for mysql user + command: mysql "-e SELECT User FROM mysql.user where user='{{ user_name }}';" + register: result + +- name: assert mysql user is not present + assert: { that: "'{{ user_name }}' not in result.stdout" } diff --git a/test/integration/targets/mysql_user/tasks/assert_user.yml b/test/integration/targets/mysql_user/tasks/assert_user.yml new file mode 100644 index 0000000000..7b35a1f350 --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/assert_user.yml @@ -0,0 +1,34 @@ +# test code to assert mysql user +# (c) 2014, Wayne Rosario <wrosario@ansible.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: run command to query for mysql user + command: mysql "-e SELECT User FROM mysql.user where user='{{ user_name }}';" + register: result + +- name: assert mysql user is present + assert: { that: "'{{ user_name }}' in result.stdout" } + +- name: run command to show privileges for user (expect privileges in stdout) + command: mysql "-e SHOW GRANTS FOR '{{ user_name }}'@'localhost';" + register: result + when: priv is defined + +- name: assert user has giving privileges + assert: { that: "'GRANT {{priv}} ON *.*' in result.stdout" } + when: priv is defined diff --git a/test/integration/targets/mysql_user/tasks/create_user.yml b/test/integration/targets/mysql_user/tasks/create_user.yml new file mode 100644 index 0000000000..e7b0d7a33b --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/create_user.yml @@ -0,0 +1,25 @@ +# test code to create mysql user +# (c) 2014, Wayne Rosario <wrosario@ansible.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 mysql user {{user_name}} + mysql_user: name={{user_name}} password={{user_password}} state=present + register: result + +- name: assert output message mysql user was created + assert: { that: "result.changed == true" } diff --git a/test/integration/targets/mysql_user/tasks/main.yml b/test/integration/targets/mysql_user/tasks/main.yml new file mode 100644 index 0000000000..db6a74dc5c --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/main.yml @@ -0,0 +1,180 @@ +# test code for the mysql_user module +# (c) 2014, Wayne Rosario <wrosario@ansible.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 dof 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/>. + +# ============================================================ +# create mysql user and verify user is added to mysql database +# +- include: create_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +- include: assert_user.yml user_name={{user_name_1}} + +- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +- include: assert_no_user.yml user_name={{user_name_1}} + +# ============================================================ +# Create mysql user that already exist on mysql database +# +- include: create_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +- name: create mysql user that already exist (expect changed=false) + mysql_user: name={{user_name_1}} password={{user_password_1}} state=present + register: result + +- name: assert output message mysql user was not created + assert: { that: "result.changed == false" } + +# ============================================================ +# remove mysql user and verify user is removed from mysql database +# +- name: remove mysql user state=absent (expect changed=true) + mysql_user: name={{ user_name_1 }} password={{ user_password_1 }} state=absent + register: result + +- name: assert output message mysql user was removed + assert: { that: "result.changed == true" } + +- include: assert_no_user.yml user_name={{user_name_1}} + +# ============================================================ +# remove mysql user that does not exist on mysql database +# +- name: remove mysql user that does not exist state=absent (expect changed=false) + mysql_user: name={{ user_name_1 }} password={{ user_password_1 }} state=absent + register: result + +- name: assert output message mysql user that does not exist + assert: { that: "result.changed == false" } + +- include: assert_no_user.yml user_name={{user_name_1}} + +# ============================================================ +# Create user with no privileges and verify default privileges are assign +# +- name: create user with select privilege state=present (expect changed=true) + mysql_user: name={{ user_name_1 }} password={{ user_password_1 }} state=present + register: result + +- include: assert_user.yml user_name={{user_name_1}} priv=USAGE + +- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +- include: assert_no_user.yml user_name={{user_name_1}} + +# ============================================================ +# Create user with select privileges and verify select privileges are assign +# +- name: create user with select privilege state=present (expect changed=true) + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} state=present priv=*.*:SELECT + register: result + +- include: assert_user.yml user_name={{user_name_2}} priv=SELECT + +- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_2 }} + +- include: assert_no_user.yml user_name={{user_name_2}} + +# ============================================================ +# Assert user has access to multiple databases +# +- name: give users access to multiple databases + mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password={{ user_password_1 }} + with_nested: + - [ '{{ user_name_1 }}' , '{{ user_name_2 }}'] + - "{{db_names}}" + +- name: show grants access for user1 on multiple database + command: mysql "-e SHOW GRANTS FOR '{{ user_name_1 }}'@'localhost';" + register: result + +- name: assert grant access for user1 on multiple database + assert: { that: "'{{ item }}' in result.stdout" } + with_items: "{{db_names}}" + +- name: show grants access for user2 on multiple database + command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';" + register: result + +- name: assert grant access for user2 on multiple database + assert: { that: "'{{ item }}' in result.stdout" } + with_items: "{{db_names}}" + +- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }} + +- name: give user access to database via wildcard + mysql_user: name={{ user_name_1 }} priv=%db.*:SELECT append_privs=yes password={{ user_password_1 }} + +- name: show grants access for user1 on multiple database + command: mysql "-e SHOW GRANTS FOR '{{ user_name_1 }}'@'localhost';" + register: result + +- name: assert grant access for user1 on multiple database + assert: + that: + - "'%db' in result.stdout" + - "'SELECT' in result.stdout" + +- name: change user access to database via wildcard + mysql_user: name={{ user_name_1 }} priv=%db.*:INSERT append_privs=yes password={{ user_password_1 }} + +- name: show grants access for user1 on multiple database + command: mysql "-e SHOW GRANTS FOR '{{ user_name_1 }}'@'localhost';" + register: result + +- name: assert grant access for user1 on multiple database + assert: + that: + - "'%db' in result.stdout" + - "'INSERT' in result.stdout" + +- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +# ============================================================ +# Update user password for a user. +# Assert the user password is updated and old password can no longer be used. +# +- include: user_password_update_test.yml + +# ============================================================ +# Assert create user with SELECT privileges, attempt to create database and update privileges to create database +# +- include: test_privs.yml current_privilege=SELECT current_append_privs=no + +# ============================================================ +# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database +# +- include: test_privs.yml current_privilege=DROP current_append_privs=yes + +# ============================================================ +# Assert create user with SELECT privileges, attempt to create database and update privileges to create database +# +- include: test_privs.yml current_privilege='UPDATE,ALTER' current_append_privs=no + +# ============================================================ +# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database +# +- include: test_privs.yml current_privilege='INSERT,DELETE' current_append_privs=yes + + + + + + + diff --git a/test/integration/targets/mysql_user/tasks/remove_user.yml b/test/integration/targets/mysql_user/tasks/remove_user.yml new file mode 100644 index 0000000000..eed959300a --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/remove_user.yml @@ -0,0 +1,43 @@ +# test code to remove mysql user +# (c) 2014, Wayne Rosario <wrosario@ansible.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: remove mysql user {{user_name}} + mysql_user: name={{user_name}} password={{user_password}} state=absent + register: result + +- name: assert output message mysql user was removed + assert: { that: "result.changed == true" } + +# ============================================================ +- name: create blank mysql user to be removed later + mysql_user: name="" state=present + +- name: remove blank mysql user with hosts=all (expect changed) + mysql_user: user="" host_all=true state=absent + register: result + +- name: assert changed is true for removing all blank users + assert: { that: "result.changed == true" } + +- name: remove blank mysql user with hosts=all (expect ok) + mysql_user: user="" host_all=true state=absent + register: result + +- name: assert changed is true for removing all blank users + assert: { that: "result.changed == false" } diff --git a/test/integration/targets/mysql_user/tasks/test_privs.yml b/test/integration/targets/mysql_user/tasks/test_privs.yml new file mode 100644 index 0000000000..5787501350 --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/test_privs.yml @@ -0,0 +1,88 @@ +# test code for privileges for mysql_user module +# (c) 2014, Wayne Rosario <wrosario@ansible.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 user with basic select privileges + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:SELECT state=present + when: current_append_privs == "yes" + +- include: assert_user.yml user_name={{user_name_2}} priv='SELECT' + when: current_append_privs == "yes" + +- name: create user with current privileges (expect changed=true) + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:{{current_privilege}} append_privs={{current_append_privs}} state=present + register: result + +- name: assert output message for current privileges + assert: { that: "result.changed == true" } + +- name: run command to show privileges for user (expect privileges in stdout) + command: mysql "-e SHOW GRANTS FOR '{{user_name_2}}'@'localhost';" + register: result + +- name: assert user has correct privileges + assert: { that: "'GRANT {{current_privilege | replace(',', ', ')}} ON *.*' in result.stdout" } + when: current_append_privs == "no" + +- name: assert user has correct privileges + assert: { that: "'GRANT SELECT, {{current_privilege | replace(',', ', ')}} ON *.*' in result.stdout" } + when: current_append_privs == "yes" + +- name: create database using user current privileges + mysql_db: name={{ db_name }} state=present login_user={{ user_name_2 }} login_password={{ user_password_2 }} + ignore_errors: true + +- name: run command to test that database was not created + command: mysql "-e show databases like '{{ db_name }}';" + register: result + +- name: assert database was not created + assert: { that: "'{{ db_name }}' not in result.stdout" } + +# ============================================================ +- name: Add privs to a specific table (expect changed) + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=jmainguy.jmainguy:ALL state=present + register: result + +- name: Assert that priv changed + assert: { that: "result.changed == true" } + +- name: Add privs to a specific table (expect ok) + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=jmainguy.jmainguy:ALL state=present + register: result + +- name: Assert that priv did not change + assert: { that: "result.changed == false" } + +# ============================================================ +- name: update user with all privileges + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:ALL state=present + +- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES' + +- name: create database using user + mysql_db: name={{ db_name }} state=present login_user={{ user_name_2 }} login_password={{ user_password_2 }} + +- name: run command to test database was created using user new privileges + command: mysql "-e SHOW CREATE DATABASE {{ db_name }};" + +- name: drop database using user + mysql_db: name={{ db_name }} state=absent login_user={{ user_name_2 }} login_password={{ user_password_2 }} + +- name: remove username + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} state=absent diff --git a/test/integration/targets/mysql_user/tasks/user_password_update_test.yml b/test/integration/targets/mysql_user/tasks/user_password_update_test.yml new file mode 100644 index 0000000000..315066724e --- /dev/null +++ b/test/integration/targets/mysql_user/tasks/user_password_update_test.yml @@ -0,0 +1,117 @@ +# test code update password for the mysql_user module +# (c) 2014, Wayne Rosario <wrosario@ansible.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 dof 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/>. + +# ============================================================ +# Update user password for a user. +# Assert the user password is updated and old password can no longer be used. +# +- name: create user1 state=present with a password + mysql_user: name={{ user_name_1 }} password={{ user_password_1 }} priv=*.*:ALL state=present + +- name: create user2 state=present with a password + mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:ALL state=present + +- name: store user2 grants with old password (mysql 5.7.6 and newer) + command: mysql "-e SHOW CREATE USER '{{ user_name_2 }}'@'localhost';" + register: user_password_old_create + ignore_errors: yes + +- name: store user2 grants with old password (mysql 5.7.5 and older) + command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';" + register: user_password_old + when: user_password_old_create|failed + +# FIXME: not sure why this is failing, but it looks like it should expect changed=true +#- name: update user2 state=present with same password (expect changed=false) +# mysql_user: name={{ user_name_2 }} password={{ user_password_2 }} priv=*.*:ALL state=present +# register: result +# +#- name: assert output user2 was not updated +# assert: { that: "result.changed == false" } + +- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES' + +- name: update user2 state=present with a new password (expect changed=true) + mysql_user: name={{ user_name_2 }} password={{ user_password_1 }} state=present + register: result + +- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES' + +- name: store user2 grants with old password (mysql 5.7.6 and newer) + command: mysql "-e SHOW CREATE USER '{{ user_name_2 }}'@'localhost';" + register: user_password_new_create + ignore_errors: yes + +- name: store user2 grants with new password + command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';" + register: user_password_new + when: user_password_new_create|failed + +- name: assert output message password was update for user2 (mysql 5.7.6 and newer) + assert: { that: "user_password_old_create.stdout != user_password_new_create.stdout" } + when: not user_password_new_create|failed + +- name: assert output message password was update for user2 (mysql 5.7.5 and older) + assert: { that: "user_password_old.stdout != user_password_new.stdout" } + when: user_password_new_create|failed + +- name: create database using user2 and old password + mysql_db: name={{ db_name }} state=present login_user={{ user_name_2 }} login_password={{ user_password_2 }} + ignore_errors: true + register: result + +- debug: var=result.msg +- name: assert output message that database not create with old password + assert: + that: + - "result.failed == true" + +- name: create database using user2 and new password + mysql_db: name={{ db_name }} state=present login_user={{ user_name_2 }} login_password={{ user_password_1 }} + register: result + +- name: assert output message that database is created with new password + assert: { that: "result.changed == true" } + +- name: remove database + mysql_db: name={{ db_name }} state=absent + +- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }} + +- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }} + +- name: Create user with password1234 using hash. (expect changed=true) + mysql_user: name=jmainguy password='*D65798AAC0E5C6DF3F320F8A30E026E7EBD73A95' encrypted=yes + register: encrypt_result + +- name: Check that the module made a change + assert: + that: + - "encrypt_result.changed == True" + +- name: See if the password needs to be updated. (expect changed=false) + mysql_user: name=jmainguy password='password1234' + register: plain_result + +- name: Check that the module did not change the password + assert: + that: + - "plain_result.changed == False" + +- name: Remove user (cleanup) + mysql_user: name=jmainguy state=absent diff --git a/test/integration/targets/mysql_variables/defaults/main.yml b/test/integration/targets/mysql_variables/defaults/main.yml new file mode 100644 index 0000000000..4683ee0d78 --- /dev/null +++ b/test/integration/targets/mysql_variables/defaults/main.yml @@ -0,0 +1,5 @@ +--- +# defaults file for test_mysql_variables +user: 'user1' +password: 'password1' + diff --git a/test/integration/targets/mysql_variables/meta/main.yml b/test/integration/targets/mysql_variables/meta/main.yml new file mode 100644 index 0000000000..4aa170dc06 --- /dev/null +++ b/test/integration/targets/mysql_variables/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_mysql_db diff --git a/test/integration/targets/mysql_variables/tasks/assert_fail_msg.yml b/test/integration/targets/mysql_variables/tasks/assert_fail_msg.yml new file mode 100644 index 0000000000..ba51b9d67c --- /dev/null +++ b/test/integration/targets/mysql_variables/tasks/assert_fail_msg.yml @@ -0,0 +1,25 @@ +# test code to assert message in mysql_variables module +# (c) 2014, Wayne Rosario <wrosario@ansible.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/>. + +# ============================================================ +# Assert message failure and confirm failed=true +# +- name: assert message failure (expect failed=true) + assert: + that: + - "output.failed == true" diff --git a/test/integration/targets/mysql_variables/tasks/assert_var.yml b/test/integration/targets/mysql_variables/tasks/assert_var.yml new file mode 100644 index 0000000000..db7f3d188a --- /dev/null +++ b/test/integration/targets/mysql_variables/tasks/assert_var.yml @@ -0,0 +1,36 @@ +# test code to assert variables in mysql_variables module +# (c) 2014, Wayne Rosario <wrosario@ansible.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/>. + +# ============================================================ +# Assert mysql variable name and value from mysql database +# +- name: assert output message changed value + assert: { that: "output.changed == {{changed}}" } + +- name: run mysql command to show variable + command: mysql "-e show variables like '{{var_name}}';" + register: result + +- name: assert output mysql variable name and value + assert: + that: + - "result.changed == true" + - "'{{var_name}}' in result.stdout" + - "'{{var_value}}' in result.stdout" + + diff --git a/test/integration/targets/mysql_variables/tasks/assert_var_output.yml b/test/integration/targets/mysql_variables/tasks/assert_var_output.yml new file mode 100644 index 0000000000..6a321a6f83 --- /dev/null +++ b/test/integration/targets/mysql_variables/tasks/assert_var_output.yml @@ -0,0 +1,38 @@ +# test code to assert variables in mysql_variables module +# (c) 2014, Wayne Rosario <wrosario@ansible.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/>. + +# ============================================================ +# Assert output variable name/value match mysql variable name/value +# +- name: assert output message changed value + assert: { that: "output.changed == {{changed}}" } + +- set_fact: + key_name: "{{var_name}}" + key_value: "{{output.msg[0][0]}}" + +- name: run mysql command to show variable + command: mysql "-e show variables like '{{var_name}}';" + register: result + +- name: assert output variable info match mysql variable info + assert: + that: + - "result.changed == true" + - "key_name in result.stdout" + - "key_value in result.stdout" diff --git a/test/integration/targets/mysql_variables/tasks/main.yml b/test/integration/targets/mysql_variables/tasks/main.yml new file mode 100644 index 0000000000..26fc03c867 --- /dev/null +++ b/test/integration/targets/mysql_variables/tasks/main.yml @@ -0,0 +1,202 @@ +# test code for the mysql_variables module +# (c) 2014, Wayne Rosario <wrosario@ansible.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/>. + +# ============================================================ +# Verify mysql_variable successfully queries a variable +# +- set_fact: set_name='version' + +- name: read mysql variables (expect changed=false) + mysql_variables: variable={{set_name}} + register: result + +- include: assert_var_output.yml changed=false output={{result}} var_name={{set_name}} + +# ============================================================ +# Verify mysql_variable successfully updates a variable (issue:4568) +# +- set_fact: + set_name: 'delay_key_write' + set_value: 'ON' + +- name: set mysql variable + mysql_variables: variable={{set_name}} value={{set_value}} + +- name: update mysql variable to same value (expect changed=false) + mysql_variables: variable={{set_name}} value={{set_value}} + register: result + +- include: assert_var.yml changed=false output={{result}} var_name={{set_name}} var_value={{set_value}} + +# ============================================================ +# Verify mysql_variable successfully updates a variable using single quotes +# +- set_fact: + set_name: 'wait_timeout' + set_value: '300' + +- name: set mysql variable to a temp value + mysql_variables: variable={{set_name}} value='200' + +- name: update mysql variable value (expect changed=true) + mysql_variables: variable={{set_name}} value={{set_value}} + register: result + +- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}' + +# ============================================================ +# Verify mysql_variable successfully updates a variable using double quotes +# +- set_fact: + set_name: "wait_timeout" + set_value: "400" + +- name: set mysql variable to a temp value + mysql_variables: variable={{set_name}} value="200" + +- name: update mysql variable value (expect changed=true) + mysql_variables: variable={{set_name}} value={{set_value}} + register: result + +- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}' + +# ============================================================ +# Verify mysql_variable successfully updates a variable using no quotes +# +- set_fact: + set_name: wait_timeout + set_value: 500 + +- name: set mysql variable to a temp value + mysql_variables: variable={{set_name}} value=200 + +- name: update mysql variable value (expect changed=true) + mysql_variables: variable={{set_name}} value={{set_value}} + register: result + +- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}' + +# ============================================================ +# Verify mysql_variable successfully updates a variable using an expression (e.g. 1024*4) +# +- name: set mysql variable value to an expression + mysql_variables: variable=max_tmp_tables value="1024*4" + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='Incorrect argument type to variable' + +# ============================================================ +# Verify mysql_variable fails when setting an incorrect value (out of range) +# +- name: set mysql variable value to a number out of range + mysql_variables: variable=max_tmp_tables value=-1 + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='Truncated incorrect' + +# ============================================================ +# Verify mysql_variable fails when setting an incorrect value (incorrect type) +# +- name: set mysql variable value to a non-valid value number + mysql_variables: variable=max_tmp_tables value=TEST + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='Incorrect argument type to variable' + +# ============================================================ +# Verify mysql_variable fails when setting an unknown variable +# +- name: set a non mysql variable + mysql_variables: variable=my_sql_variable value=ON + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='Variable not available' + +# ============================================================ +# Verify mysql_variable fails when setting a read-only variable +# +- name: set value of a read only mysql variable + mysql_variables: variable=character_set_system value=utf16 + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='read only variable' + +#============================================================= +# Verify mysql_variable works with the login_user and login_password parameters +# +- name: create mysql user + mysql_user: name={{user}} password={{password}} state=present priv=*.*:ALL + +- set_fact: + set_name: wait_timeout + set_value: 77 + +- name: query mysql_variable using login_user and password_password + mysql_variables: variable={{set_name}} login_user={{user}} login_password={{password}} + register: result + +- include: assert_var_output.yml changed=false output={{result}} var_name={{set_name}} + +- name: set mysql variable to temp value using user login and password (expect changed=true) + mysql_variables: variable={{set_name}} value=20 login_user={{user}} login_password={{password}} + register: result + +- name: update mysql variable value using user login and password (expect changed=true) + mysql_variables: variable={{set_name}} value={{set_value}} login_user={{user}} login_password={{password}} + register: result + +- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}' + +#============================================================ +# Verify mysql_variable fails with an incorrect login_password parameter +# +- set_fact: + set_name: connect_timeout + set_value: 10 + +- name: query mysql_variable using incorrect login_password + mysql_variables: variable={{set_name}} login_user={{user}} login_password=wrongpassword + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='unable to connect to database' + +- name: update mysql variable value using incorrect login_password (expect failed=true) + mysql_variables: variable={{set_name}} value={{set_value}} login_user={{user}} login_password='this is an incorrect password' + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='unable to connect to database' + +- name: remove mysql_user {{user}} + mysql_user: name={{user}} state=absent + +#============================================================ +# Verify mysql_variable fails with an incorrect login_host parameter +# +- name: query mysql_variable using incorrect login_host + mysql_variables: variable=wait_timeout login_host=12.0.0.9 connect_timeout=5 + register: result + ignore_errors: true + +- include: assert_fail_msg.yml output={{result}} msg='unable to connect to database'
\ No newline at end of file diff --git a/test/integration/targets/ping/tasks/main.yml b/test/integration/targets/ping/tasks/main.yml new file mode 100644 index 0000000000..d7cbe64aff --- /dev/null +++ b/test/integration/targets/ping/tasks/main.yml @@ -0,0 +1,37 @@ +# test code for the ping module +# (c) 2014, James Cammarata <jcammarata@ansible.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: ping the test + ping: + register: result + +- name: assert the ping worked + assert: + that: + - "result.changed == false" + - "result.ping == 'pong'" + +- name: ping with data + ping: data="testing" + register: result + +- name: assert the ping worked with data + assert: + that: + - "result.changed == false" + - "result.ping == 'testing'" diff --git a/test/integration/targets/pip/meta/main.yml b/test/integration/targets/pip/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/pip/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/pip/tasks/main.yml b/test/integration/targets/pip/tasks/main.yml new file mode 100644 index 0000000000..fec7c2af51 --- /dev/null +++ b/test/integration/targets/pip/tasks/main.yml @@ -0,0 +1,5 @@ +# Current pip unconditionally uses md5. +# We can re-enable if pip switches to a different hash or allows us to not check md5. + +- include: 'pip.yml' + when: ansible_fips|bool != True diff --git a/test/integration/targets/pip/tasks/pip.yml b/test/integration/targets/pip/tasks/pip.yml new file mode 100644 index 0000000000..dc9bfb781e --- /dev/null +++ b/test/integration/targets/pip/tasks/pip.yml @@ -0,0 +1,164 @@ +# 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/>. + +# FIXME: replace the python test package + +# first some tests installed system-wide +# verify things were not installed to start with + +- name: ensure a package is not installed (precondition setup) + pip: name={{ pip_test_package }} state=absent + +# verify that a package that is uninstalled being set to absent +# results in an unchanged state and that the test package is not +# installed + +- name: ensure a package is not installed + pip: name={{ pip_test_package }} state=absent + register: uninstall_result + +- name: removing an unremoved package should return unchanged + assert: + that: + - "not uninstall_result.changed" + +- shell: "{{ ansible_python.executable }} -c 'import {{ pip_test_package }}'" + register: absent_result + ignore_errors: True + +- name: verify {{ pip_test_package }} is not present + assert: + that: + - "absent_result.rc != 0" + +# now we're going to install the test package knowing it is uninstalled +# and check that installation was ok + +- name: ensure a package is installed + pip: name={{ pip_test_package }} state=present + register: install_result + +- name: verify we recorded a change + assert: + that: + - "install_result.changed == True" + +- shell: "{{ ansible_python.executable }} -c 'import {{ pip_test_package }}'" + register: installed_result + +# now remove it to test uninstallation of a package we are sure is installed + +- name: now uninstall so we can see that a change occurred + pip: name={{ pip_test_package }} state=absent + register: absent2 + +- name: assert a change occurred on uninstallation + assert: + that: + - "absent2.changed" + +# put the test package back + +- name: now put it back in case someone wanted it (like us!) + pip: name={{ pip_test_package }} state=present + + +# Test virtualenv installations + +- name: make sure the test env doesn't exist + file: state=absent name={{ output_dir }}/pipenv + +- name: create a requirement file with an vcs url + copy: dest={{ output_dir }}/pipreq.txt + content="-e git+https://github.com/dvarrazzo/pyiso8601#egg=pyiso8601" + +- name: install the requirement file in a virtualenv + pip: requirements={{ output_dir}}/pipreq.txt + virtualenv={{ output_dir }}/pipenv + register: req_installed + +- name: check that a change occurred + assert: + that: + - "req_installed.changed" + +- name: repeat installation to check status didn't change + pip: requirements={{ output_dir}}/pipreq.txt + virtualenv={{ output_dir }}/pipenv + register: req_installed + +- name: check that a change didn't occurr this time (bug ansible#1705) + assert: + that: + - "not req_installed.changed" + +- name: install the same module from url + pip: name="git+https://github.com/dvarrazzo/pyiso8601#egg=pyiso8601" + virtualenv={{ output_dir }}/pipenv + register: url_installed + +- name: check that a change didn't occurr (bug ansible-modules-core#1645) + assert: + that: + - "not url_installed.changed" + +# Test pip package in check mode doesn't always report changed. + +# Special case for pip +- name: check for pip package + pip: name=pip virtualenv={{ output_dir }}/pipenv state=present + +- name: check for pip package in check_mode + pip: name=pip virtualenv={{ output_dir }}/pipenv state=present + check_mode: True + register: pip_check_mode + +- name: make sure pip in check_mode doesn't report changed + assert: + that: + - "not pip_check_mode.changed" + +# Special case for setuptools +- name: check for setuptools package + pip: name=setuptools virtualenv={{ output_dir }}/pipenv state=present + +- name: check for setuptools package in check_mode + pip: name=setuptools virtualenv={{ output_dir }}/pipenv state=present + check_mode: True + register: setuptools_check_mode + +- name: make sure setuptools in check_mode doesn't report changed + assert: + that: + - "not setuptools_check_mode.changed" + + +# Normal case +- name: check for q package + pip: name=q virtualenv={{ output_dir }}/pipenv state=present + +- name: check for q package in check_mode + pip: name=q virtualenv={{ output_dir }}/pipenv state=present + check_mode: True + register: q_check_mode + +- name: make sure q in check_mode doesn't report changed + assert: + that: + - "not q_check_mode.changed" + diff --git a/test/integration/targets/postgresql/defaults/main.yml b/test/integration/targets/postgresql/defaults/main.yml new file mode 100644 index 0000000000..cfc50737c6 --- /dev/null +++ b/test/integration/targets/postgresql/defaults/main.yml @@ -0,0 +1,8 @@ +--- +# defaults file for test_postgresql_db +db_name: 'ansible_db' +db_user1: 'ansible_db_user1' +db_user2: 'ansible_db_user2' + +tmp_dir: '/tmp' + diff --git a/test/integration/targets/postgresql/meta/main.yml b/test/integration/targets/postgresql/meta/main.yml new file mode 100644 index 0000000000..85b1dc7e4c --- /dev/null +++ b/test/integration/targets/postgresql/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - setup_postgresql_db diff --git a/test/integration/targets/postgresql/tasks/main.yml b/test/integration/targets/postgresql/tasks/main.yml new file mode 100644 index 0000000000..8949fe1efa --- /dev/null +++ b/test/integration/targets/postgresql/tasks/main.yml @@ -0,0 +1,882 @@ +# +# Create and destroy db +# +- name: Create DB + become_user: postgres + become: True + postgresql_db: + state: present + name: "{{ db_name }}" + register: result + +- name: assert that module reports the db was created + assert: + that: + - "result.changed == true" + - "result.db =='{{ db_name }}'" + +- name: Check that database created + become_user: postgres + become: True + shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + +- name: Run create on an already created db + become_user: postgres + become: True + postgresql_db: + state: present + name: "{{ db_name }}" + register: result + +- name: assert that module reports the db was unchanged + assert: + that: + - "result.changed == false" + +- name: Destroy DB + become_user: postgres + become: True + postgresql_db: + state: absent + name: "{{ db_name }}" + register: result + +- name: assert that module reports the db was changed + assert: + that: + - "result.changed == true" + +- name: Check that database was destroyed + become_user: postgres + become: True + shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +- name: Destroy DB + become_user: postgres + become: True + postgresql_db: + state: absent + name: "{{ db_name }}" + register: result + +- name: assert that removing an alreaady removed db makes no change + assert: + that: + - "result.changed == false" + + +# This corner case works to add but not to drop. This is sufficiently crazy +# that I'm not going to attempt to fix it unless someone lets me know that they +# need the functionality +# +# - postgresql_db: +# state: 'present' +# name: '"silly.""name"' +# - shell: echo "select datname from pg_database where datname = 'silly.""name';" | psql +# register: result +# +# - assert: +# that: "result.stdout_lines[-1] == '(1 row)'" +# - postgresql_db: +# state: absent +# name: '"silly.""name"' +# - shell: echo "select datname from pg_database where datname = 'silly.""name';" | psql +# register: result +# +# - assert: +# that: "result.stdout_lines[-1] == '(0 rows)'" + +# +# Test encoding, collate, ctype, template options +# +- name: Create a DB with encoding, collate, ctype, and template options + become_user: postgres + become: True + postgresql_db: + name: '{{ db_name }}' + state: 'present' + encoding: 'LATIN1' + lc_collate: 'pt_BR' + lc_ctype: 'es_MX' + template: 'template0' + +- name: Check that the DB has all of our options + become_user: postgres + become: True + shell: echo "select datname, pg_encoding_to_char(encoding), datcollate, datctype from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + - "'LATIN1' in result.stdout_lines[-2]" + - "'pt_BR' in result.stdout_lines[-2]" + - "'es_MX' in result.stdout_lines[-2]" + - "'UTF8' not in result.stdout_lines[-2]" + - "'en_US' not in result.stdout_lines[-2]" + +- name: Check that running db cration with options a second time does nothing + become_user: postgres + become: True + postgresql_db: + name: '{{ db_name }}' + state: 'present' + encoding: 'LATIN1' + lc_collate: 'pt_BR' + lc_ctype: 'es_MX' + template: 'template0' + register: result + +- assert: + that: + - 'result.changed == False' + + +- name: Check that attempting to change encoding returns an error + become_user: postgres + become: True + postgresql_db: + name: '{{ db_name }}' + state: 'present' + encoding: 'UTF8' + lc_collate: 'pt_BR' + lc_ctype: 'es_MX' + template: 'template0' + register: result + ignore_errors: True + +- assert: + that: + - 'result.failed == True' + +- name: Cleanup test DB + become_user: postgres + become: True + postgresql_db: + name: '{{ db_name }}' + state: 'absent' + +- shell: echo "select datname, pg_encoding_to_char(encoding), datcollate, datctype from pg_database where datname = '{{ db_name }}';" | psql + become_user: postgres + become: True + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +# +# Create and destroy user +# +- name: Create a user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + encrypted: 'yes' + password: "md55c8ccfd9d6711fc69a7eae647fc54f51" + register: result + +- name: Check that ansible reports they were created + assert: + that: + - "result.changed == True" + +- name: Check that they were created + become_user: postgres + become: True + shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + +- name: Check that creating user a second time does nothing + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + encrypted: 'yes' + password: "md55c8ccfd9d6711fc69a7eae647fc54f51" + register: result + +- name: Check that ansible reports no change + assert: + that: + - "result.changed == False" + +- name: Remove user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: 'absent' + register: result + +- name: Check that ansible reports they were removed + assert: + that: + - "result.changed == True" + +- name: Check that they were removed + become_user: postgres + become: True + shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +- name: Check that removing user a second time does nothing + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: 'absent' + register: result + +- name: Check that ansible reports no change + assert: + that: + - "result.changed == False" + +- name: Create a user with all role attributes + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: "present" + role_attr_flags: "SUPERUSER,CREATEROLE,CREATEDB,INHERIT,login" + +- name: Check that the user has the requested role attributes + become_user: postgres + become: True + shell: echo "select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + - "'super:t' in result.stdout_lines[-2]" + - "'createrole:t' in result.stdout_lines[-2]" + - "'create:t' in result.stdout_lines[-2]" + - "'inherit:t' in result.stdout_lines[-2]" + - "'login:t' in result.stdout_lines[-2]" + +- name: Modify a user to have no role attributes + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: "present" + role_attr_flags: "NOSUPERUSER,NOCREATEROLE,NOCREATEDB,noinherit,NOLOGIN" + register: result + +- name: Check that ansible reports it modified the role + assert: + that: + - "result.changed == True" + +- name: Check that the user has the requested role attributes + become_user: postgres + become: True + shell: echo "select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + - "'super:f' in result.stdout_lines[-2]" + - "'createrole:f' in result.stdout_lines[-2]" + - "'create:f' in result.stdout_lines[-2]" + - "'inherit:f' in result.stdout_lines[-2]" + - "'login:f' in result.stdout_lines[-2]" + +- name: Modify a single role attribute on a user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: "present" + role_attr_flags: "LOGIN" + register: result + +- name: Check that ansible reports it modified the role + assert: + that: + - "result.changed == True" + +- name: Check that the user has the requested role attributes + become_user: postgres + become: True + shell: echo "select 'super:'||rolsuper, 'createrole:'||rolcreaterole, 'create:'||rolcreatedb, 'inherit:'||rolinherit, 'login:'||rolcanlogin from pg_roles where rolname='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + - "'super:f' in result.stdout_lines[-2]" + - "'createrole:f' in result.stdout_lines[-2]" + - "'create:f' in result.stdout_lines[-2]" + - "'inherit:f' in result.stdout_lines[-2]" + - "'login:t' in result.stdout_lines[-2]" + +- name: Cleanup the user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: 'absent' + +- name: Check that they were removed + become_user: postgres + become: True + shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +### TODO: test expires, fail_on_user + +# +# Test db ownership +# +- name: Create an unprivileged user to own a DB + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + encrypted: 'yes' + password: "md55c8ccfd9d6711fc69a7eae647fc54f51" + +- name: Create db with user ownership + become_user: postgres + become: True + postgresql_db: + name: "{{ db_name }}" + state: "present" + owner: "{{ db_user1 }}" + +- name: Check that the user owns the newly created DB + become_user: postgres + become: True + shell: echo "select pg_catalog.pg_get_userbyid(datdba) from pg_catalog.pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + - "'{{ db_user1 }}' == '{{ result.stdout_lines[-2] | trim }}'" + +- name: Change the owner on an existing db + become_user: postgres + become: True + postgresql_db: + name: "{{ db_name }}" + state: "present" + owner: "postgres" + register: result + +- name: assert that ansible says it changed the db + assert: + that: + - "result.changed == True" + +- name: Check that the user owns the newly created DB + become_user: postgres + become: True + shell: echo "select pg_catalog.pg_get_userbyid(datdba) from pg_catalog.pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + - "'postgres' == '{{ result.stdout_lines[-2] | trim }}'" + +- name: Cleanup db + become_user: postgres + become: True + postgresql_db: + name: "{{ db_name }}" + state: "absent" + +- name: Check that database was destroyed + become_user: postgres + become: True + shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +- name: Cleanup test user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: 'absent' + +- name: Check that they were removed + become_user: postgres + become: True + shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +# +# Test settings privleges +# +- name: Create db + become_user: postgres + become: True + postgresql_db: + name: "{{ db_name }}" + state: "present" + +- name: Create some tables on the db + become_user: postgres + become: True + shell: echo "create table test_table1 (field text);" | psql {{ db_name }} + +- become_user: postgres + become: True + shell: echo "create table test_table2 (field text);" | psql {{ db_name }} + +- name: Create a user with some permissions on the db + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + encrypted: 'yes' + password: "md55c8ccfd9d6711fc69a7eae647fc54f51" + db: "{{ db_name }}" + priv: 'test_table1:INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER/test_table2:INSERT/CREATE,CONNECT,TEMP' + +- name: Check that the user has the requested permissions (table1) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }} + register: result_table1 + +- name: Check that the user has the requested permissions (table2) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }} + register: result_table2 + +- name: Check that the user has the requested permissions (database) + become_user: postgres + become: True + shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }} + register: result_database + +- assert: + that: + - "result_table1.stdout_lines[-1] == '(7 rows)'" + - "'INSERT' in result_table1.stdout" + - "'SELECT' in result_table1.stdout" + - "'UPDATE' in result_table1.stdout" + - "'DELETE' in result_table1.stdout" + - "'TRUNCATE' in result_table1.stdout" + - "'REFERENCES' in result_table1.stdout" + - "'TRIGGER' in result_table1.stdout" + - "result_table2.stdout_lines[-1] == '(1 row)'" + - "'INSERT' == '{{ result_table2.stdout_lines[-2] | trim }}'" + - "result_database.stdout_lines[-1] == '(1 row)'" + - "'{{ db_user1 }}=CTc/postgres' in result_database.stdout_lines[-2]" + +- name: Add another permission for the user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + encrypted: 'yes' + password: "md55c8ccfd9d6711fc69a7eae647fc54f51" + db: "{{ db_name }}" + priv: 'test_table2:select' + register: results + +- name: Check that ansible reports it changed the user + assert: + that: + - "results.changed == True" + +- name: Check that the user has the requested permissions (table2) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }} + register: result_table2 + +- assert: + that: + - "result_table2.stdout_lines[-1] == '(2 rows)'" + - "'INSERT' in result_table2.stdout" + - "'SELECT' in result_table2.stdout" + + +# +# Test priv setting via postgresql_privs module +# (Depends on state from previous _user privs tests) +# + +- name: Revoke a privilege + become_user: postgres + become: True + postgresql_privs: + type: "table" + state: "absent" + roles: "{{ db_user1 }}" + privs: "INSERT" + objs: "test_table2" + db: "{{ db_name }}" + register: results + +- name: Check that ansible reports it changed the user + assert: + that: + - "results.changed == True" + +- name: Check that the user has the requested permissions (table2) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }} + register: result_table2 + +- assert: + that: + - "result_table2.stdout_lines[-1] == '(1 row)'" + - "'SELECT' == '{{ result_table2.stdout_lines[-2] | trim }}'" + +- name: Revoke many privileges on multiple tables + become_user: postgres + become: True + postgresql_privs: + state: "absent" + roles: "{{ db_user1 }}" + privs: "INSERT,select,UPDATE,TRUNCATE,REFERENCES,TRIGGER,delete" + objs: "test_table2,test_table1" + db: "{{ db_name }}" + register: results + +- name: Check that ansible reports it changed the user + assert: + that: + - "results.changed == True" + +- name: Check that permissions were revoked (table1) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }} + register: result_table1 + +- name: Check that permissions were revoked (table2) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }} + register: result_table2 + +- assert: + that: + - "result_table1.stdout_lines[-1] == '(0 rows)'" + - "result_table2.stdout_lines[-1] == '(0 rows)'" + +- name: Revoke database privileges + become_user: postgres + become: True + postgresql_privs: + type: "database" + state: "absent" + roles: "{{ db_user1 }}" + privs: "Create,connect,TEMP" + objs: "{{ db_name }}" + db: "{{ db_name }}" + +- name: Check that the user has the requested permissions (database) + become_user: postgres + become: True + shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }} + register: result_database + +- assert: + that: + - "result_database.stdout_lines[-1] == '(1 row)'" + - "'{{ db_user1 }}' not in result_database.stdout" + +- name: Grant database privileges + become_user: postgres + become: True + postgresql_privs: + type: "database" + state: "present" + roles: "{{ db_user1 }}" + privs: "CREATE,connect" + objs: "{{ db_name }}" + db: "{{ db_name }}" + register: results + +- name: Check that ansible reports it changed the user + assert: + that: + - "results.changed == True" + +- name: Check that the user has the requested permissions (database) + become_user: postgres + become: True + shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }} + register: result_database + +- assert: + that: + - "result_database.stdout_lines[-1] == '(1 row)'" + - "'{{ db_user1 }}=Cc' in result_database.stdout" + +- name: Grant a single privilege on a table + become_user: postgres + become: True + postgresql_privs: + state: "present" + roles: "{{ db_user1 }}" + privs: "INSERT" + objs: "test_table1" + db: "{{ db_name }}" + +- name: Check that permissions were added (table1) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }} + register: result_table1 + +- assert: + that: + - "result_table1.stdout_lines[-1] == '(1 row)'" + - "'{{ result_table1.stdout_lines[-2] | trim }}' == 'INSERT'" + +- name: Grant many privileges on multiple tables + become_user: postgres + become: True + postgresql_privs: + state: "present" + roles: "{{ db_user1 }}" + privs: 'INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,trigger' + objs: "test_table2,test_table1" + db: "{{ db_name }}" + +- name: Check that permissions were added (table1) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table1';" | psql {{ db_name }} + register: result_table1 + +- name: Check that permissions were added (table2) + become_user: postgres + become: True + shell: echo "select privilege_type from information_schema.role_table_grants where grantee='{{ db_user1 }}' and table_name='test_table2';" | psql {{ db_name }} + register: result_table2 + +- assert: + that: + - "result_table1.stdout_lines[-1] == '(7 rows)'" + - "'INSERT' in result_table1.stdout" + - "'SELECT' in result_table1.stdout" + - "'UPDATE' in result_table1.stdout" + - "'DELETE' in result_table1.stdout" + - "'TRUNCATE' in result_table1.stdout" + - "'REFERENCES' in result_table1.stdout" + - "'TRIGGER' in result_table1.stdout" + - "result_table2.stdout_lines[-1] == '(7 rows)'" + - "'INSERT' in result_table2.stdout" + - "'SELECT' in result_table2.stdout" + - "'UPDATE' in result_table2.stdout" + - "'DELETE' in result_table2.stdout" + - "'TRUNCATE' in result_table2.stdout" + - "'REFERENCES' in result_table2.stdout" + - "'TRIGGER' in result_table2.stdout" + +# +# Cleanup +# +- name: Cleanup db + become_user: postgres + become: True + postgresql_db: + name: "{{ db_name }}" + state: "absent" + +- name: Check that database was destroyed + become_user: postgres + become: True + shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +- name: Cleanup test user + become_user: postgres + become: True + postgresql_user: + name: "{{ db_user1 }}" + state: 'absent' + +- name: Check that they were removed + become_user: postgres + become: True + shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +# +# Test login_user functionality +# +- name: Create a user to test login module parameters + become: True + become_user: postgres + postgresql_user: + name: "{{ db_user1 }}" + state: "present" + encrypted: 'no' + password: "password" + role_attr_flags: "CREATEDB,LOGIN,CREATEROLE" + +- name: Create db + postgresql_db: + name: "{{ db_name }}" + state: "present" + login_user: "{{ db_user1 }}" + login_password: "password" + login_host: "localhost" + +- name: Check that database created + become: True + become_user: postgres + shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + +- name: Create a user + postgresql_user: + name: "{{ db_user2 }}" + state: "present" + encrypted: 'yes' + password: "md55c8ccfd9d6711fc69a7eae647fc54f51" + db: "{{ db_name }}" + login_user: "{{ db_user1 }}" + login_password: "password" + login_host: "localhost" + +- name: Check that they were created + become: True + become_user: postgres + shell: echo "select * from pg_user where usename='{{ db_user2 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(1 row)'" + +- name: Grant database privileges + postgresql_privs: + type: "database" + state: "present" + roles: "{{ db_user2 }}" + privs: "CREATE,connect" + objs: "{{ db_name }}" + db: "{{ db_name }}" + login: "{{ db_user1 }}" + password: "password" + host: "localhost" + +- name: Check that the user has the requested permissions (database) + become: True + become_user: postgres + shell: echo "select datacl from pg_database where datname='{{ db_name }}';" | psql {{ db_name }} + register: result_database + +- assert: + that: + - "result_database.stdout_lines[-1] == '(1 row)'" + - "'{{ db_user2 }}=Cc' in result_database.stdout" + +- name: Remove user + postgresql_user: + name: "{{ db_user2 }}" + state: 'absent' + priv: "ALL" + db: "{{ db_name }}" + login_user: "{{ db_user1 }}" + login_password: "password" + login_host: "localhost" + +- name: Check that they were removed + become: True + become_user: postgres + shell: echo "select * from pg_user where usename='{{ db_user2 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +- name: Destroy DB + postgresql_db: + state: absent + name: "{{ db_name }}" + login_user: "{{ db_user1 }}" + login_password: "password" + login_host: "localhost" + +- name: Check that database was destroyed + become: True + become_user: postgres + shell: echo "select datname from pg_database where datname = '{{ db_name }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + +# +# Cleanup +# +- name: Cleanup test user + become: True + become_user: postgres + postgresql_user: + name: "{{ db_user1 }}" + state: 'absent' + +- name: Check that they were removed + become: True + become_user: postgres + shell: echo "select * from pg_user where usename='{{ db_user1 }}';" | psql + register: result + +- assert: + that: + - "result.stdout_lines[-1] == '(0 rows)'" + diff --git a/test/integration/targets/prepare_http_tests/defaults/main.yml b/test/integration/targets/prepare_http_tests/defaults/main.yml new file mode 100644 index 0000000000..1114729623 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/defaults/main.yml @@ -0,0 +1,4 @@ +badssl_host: wrong.host.badssl.com +httpbin_host: httpbin.org +sni_host: sni.velox.ch +badssl_host_substring: wrong.host.badssl.com diff --git a/test/integration/targets/prepare_http_tests/tasks/main.yml b/test/integration/targets/prepare_http_tests/tasks/main.yml new file mode 100644 index 0000000000..3e56320529 --- /dev/null +++ b/test/integration/targets/prepare_http_tests/tasks/main.yml @@ -0,0 +1,41 @@ +# The docker --link functionality gives us an ENV var we can key off of to see if we have access to +# the httptester container +- set_fact: + has_httptester: "{{ lookup('env', 'HTTPTESTER') != '' }}" + +# If we are running with access to a httptester container, grab it's cacert and install it +- block: + # Override hostname defaults with httptester linked names + - include_vars: httptester.yml + + - name: RedHat - Enable the dynamic CA configuration feature + command: update-ca-trust force-enable + when: ansible_os_family == 'RedHat' + + - name: RedHat - Retrieve test cacert + get_url: + url: "http://ansible.http.tests/cacert.pem" + dest: "/etc/pki/ca-trust/source/anchors/ansible.pem" + when: ansible_os_family == 'RedHat' + + - name: Suse - Retrieve test cacert + get_url: + url: "http://ansible.http.tests/cacert.pem" + dest: "/etc/pki/trust/anchors/ansible.pem" + when: ansible_os_family == 'Suse' + + - name: Debian - Retrieve test cacert + get_url: + url: "http://ansible.http.tests/cacert.pem" + dest: "/usr/local/share/ca-certificates/ansible.crt" + when: ansible_os_family == 'Debian' + + - name: Redhat - Update ca trust + command: update-ca-trust extract + when: ansible_os_family == 'RedHat' + + - name: Debian/Suse - Update ca certificates + command: update-ca-certificates + when: ansible_os_family == 'Debian' or ansible_os_family == 'Suse' + + when: has_httptester|bool diff --git a/test/integration/targets/prepare_http_tests/vars/httptester.yml b/test/integration/targets/prepare_http_tests/vars/httptester.yml new file mode 100644 index 0000000000..0e23ae936a --- /dev/null +++ b/test/integration/targets/prepare_http_tests/vars/httptester.yml @@ -0,0 +1,5 @@ +# these are fake hostnames provided by docker link for the httptester container +badssl_host: fail.ansible.http.tests +httpbin_host: ansible.http.tests +sni_host: sni1.ansible.http.tests +badssl_host_substring: HTTP Client Testing Service diff --git a/test/integration/targets/prepare_tests/tasks/main.yml b/test/integration/targets/prepare_tests/tasks/main.yml new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/prepare_tests/tasks/main.yml diff --git a/test/integration/targets/script/files/create_afile.sh b/test/integration/targets/script/files/create_afile.sh new file mode 100755 index 0000000000..e6fae448b2 --- /dev/null +++ b/test/integration/targets/script/files/create_afile.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "win" > "$1"
\ No newline at end of file diff --git a/test/integration/targets/script/files/remove_afile.sh b/test/integration/targets/script/files/remove_afile.sh new file mode 100755 index 0000000000..4a7fea6617 --- /dev/null +++ b/test/integration/targets/script/files/remove_afile.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +rm "$1"
\ No newline at end of file diff --git a/test/integration/targets/script/files/test.sh b/test/integration/targets/script/files/test.sh new file mode 100755 index 0000000000..ade17e9b8c --- /dev/null +++ b/test/integration/targets/script/files/test.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo -n "win"
\ No newline at end of file diff --git a/test/integration/targets/script/meta/main.yml b/test/integration/targets/script/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/script/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/script/tasks/main.yml b/test/integration/targets/script/tasks/main.yml new file mode 100644 index 0000000000..2c0f1f02ad --- /dev/null +++ b/test/integration/targets/script/tasks/main.yml @@ -0,0 +1,71 @@ +# Test code for the script module and action_plugin. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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/>. + +## +## prep +## + +- set_fact: output_dir_test={{output_dir}}/test_script + +- name: make sure our testing sub-directory does not exist + file: path="{{ output_dir_test }}" state=absent + +- name: create our testing sub-directory + file: path="{{ output_dir_test }}" state=directory + +## +## script +## + +- name: execute the test.sh script via command + script: test.sh + register: script_result0 + +- name: assert that the script executed correctly + assert: + that: + - "script_result0.rc == 0" + - "script_result0.stderr == ''" + - "script_result0.stdout == 'win'" + +# creates + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + +- name: create afile.txt with create_afile.sh via command + script: create_afile.sh {{output_dir_test | expanduser}}/afile.txt creates={{output_dir_test | expanduser}}/afile.txt + +- name: verify that afile.txt is present + file: path={{output_dir_test}}/afile.txt state=file + +# removes + +- name: remove afile.txt with remote_afile.sh via command + script: remove_afile.sh {{output_dir_test | expanduser}}/afile.txt removes={{output_dir_test | expanduser}}/afile.txt + register: script_result1 + +- name: verify that afile.txt is absent + file: path={{output_dir_test}}/afile.txt state=absent + register: script_result2 + +- name: assert that the file was removed by the script + assert: + that: + - "script_result1|changed" + - "script_result2.state == 'absent'" diff --git a/test/integration/targets/service/files/ansible-broken.upstart b/test/integration/targets/service/files/ansible-broken.upstart new file mode 100644 index 0000000000..4e9c6694a1 --- /dev/null +++ b/test/integration/targets/service/files/ansible-broken.upstart @@ -0,0 +1,10 @@ +description "ansible test daemon" + +start on runlevel [345] +stop on runlevel [!345] + +expect daemon + +exec ansible_test_service + +manual diff --git a/test/integration/targets/service/files/ansible.systemd b/test/integration/targets/service/files/ansible.systemd new file mode 100644 index 0000000000..c1a710a1b3 --- /dev/null +++ b/test/integration/targets/service/files/ansible.systemd @@ -0,0 +1,10 @@ +[Unit] +Description=Ansible Test Service + +[Service] +ExecStart=/usr/sbin/ansible_test_service "Test\nthat newlines in scripts\nwork" +ExecReload=/bin/true +Type=forking + +[Install] +WantedBy=multi-user.target diff --git a/test/integration/targets/service/files/ansible.sysv b/test/integration/targets/service/files/ansible.sysv new file mode 100755 index 0000000000..372ea6e5b9 --- /dev/null +++ b/test/integration/targets/service/files/ansible.sysv @@ -0,0 +1,134 @@ +#!/bin/sh +# + +# LSB header + +### BEGIN INIT INFO +# Provides: ansible-test +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Short-Description: test daemon for ansible +# Description: This is a test daemon used by ansible for testing only +### END INIT INFO + +# chkconfig header + +# chkconfig: 345 99 99 +# description: This is a test daemon used by ansible for testing only +# +# processname: /usr/sbin/ansible_test_service + +# Sanity checks. +[ -x /usr/sbin/ansible_test_service ] || exit 0 + +DEBIAN_VERSION=/etc/debian_version +SUSE_RELEASE=/etc/SuSE-release +# Source function library. +if [ -f $DEBIAN_VERSION ]; then + . /lib/lsb/init-functions +elif [ -f $SUSE_RELEASE -a -r /etc/rc.status ]; then + . /etc/rc.status +else + . /etc/rc.d/init.d/functions +fi + +SERVICE=ansible_test_service +PROCESS=ansible_test_service +CONFIG_ARGS=" " +if [ -f $DEBIAN_VERSION ]; then + LOCKFILE=/var/lock/$SERVICE +else + LOCKFILE=/var/lock/subsys/$SERVICE +fi + +RETVAL=0 + +start() { + echo -n "Starting ansible test daemon: " + if [ -f $SUSE_RELEASE ]; then + startproc -p /var/run/${SERVICE}.pid -f /usr/sbin/ansible_test_service + rc_status -v + elif [ -e $DEBIAN_VERSION ]; then + if [ -f $LOCKFILE ]; then + echo -n "already started, lock file found" + RETVAL=1 + elif /bin/python /usr/sbin/ansible_test_service; then + echo -n "OK" + RETVAL=0 + fi + else + daemon --check $SERVICE $PROCESS --daemonize $CONFIG_ARGS + fi + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch $LOCKFILE + return $RETVAL +} + +stop() { + echo -n "Stopping ansible test daemon: " + if [ -f $SUSE_RELEASE ]; then + killproc -TERM /usr/sbin/ansible_test_service + rc_status -v + elif [ -f $DEBIAN_VERSION ]; then + # Added this since Debian's start-stop-daemon doesn't support spawned processes + if ps -ef | grep "/bin/python /usr/sbin/ansible_test_service" | grep -v grep | awk '{print $2}' | xargs kill &> /dev/null; then + echo -n "OK" + RETVAL=0 + else + echo -n "Daemon is not started" + RETVAL=1 + fi + else + killproc -p /var/run/${SERVICE}.pid + fi + RETVAL=$? + echo + if [ $RETVAL -eq 0 ]; then + rm -f $LOCKFILE + rm -f /var/run/$SERVICE.pid + fi +} + +restart() { + stop + start +} + +# See how we were called. +case "$1" in + start|stop|restart) + $1 + ;; + status) + if [ -f $SUSE_RELEASE ]; then + echo -n "Checking for ansible test service " + checkproc /usr/sbin/ansible_test_service + rc_status -v + elif [ -f $DEBIAN_VERSION ]; then + if [ -f $LOCKFILE ]; then + RETVAL=0 + echo "ansible test is running." + else + RETVAL=1 + echo "ansible test is stopped." + fi + else + status $PROCESS + RETVAL=$? + fi + ;; + condrestart) + [ -f $LOCKFILE ] && restart || : + ;; + reload) + echo "ok" + RETVAL=0 + ;; + *) + echo "Usage: $0 {start|stop|status|restart|condrestart|reload}" + exit 1 + ;; +esac +exit $RETVAL + diff --git a/test/integration/targets/service/files/ansible.upstart b/test/integration/targets/service/files/ansible.upstart new file mode 100644 index 0000000000..369f61a83c --- /dev/null +++ b/test/integration/targets/service/files/ansible.upstart @@ -0,0 +1,9 @@ +description "ansible test daemon" + +start on runlevel [345] +stop on runlevel [!345] + +expect daemon + +exec ansible_test_service + diff --git a/test/integration/targets/service/files/ansible_test_service b/test/integration/targets/service/files/ansible_test_service new file mode 100755 index 0000000000..5e8691f2f1 --- /dev/null +++ b/test/integration/targets/service/files/ansible_test_service @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# this is mostly based off of the code found here: +# http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/ + +import os +import resource +import sys +import time + +UMASK = 0 +WORKDIR = "/" +MAXFD = 1024 + +if (hasattr(os, "devnull")): + REDIRECT_TO = os.devnull +else: + REDIRECT_TO = "/dev/null" + +def createDaemon(): + try: + pid = os.fork() + except OSError as e: + raise Exception, "%s [%d]" % (e.strerror, e.errno) + + if (pid == 0): + os.setsid() + + try: + pid = os.fork() + except OSError as e: + raise Exception, "%s [%d]" % (e.strerror, e.errno) + + if (pid == 0): + os.chdir(WORKDIR) + os.umask(UMASK) + else: + f = open('/var/run/ansible_test_service.pid', 'w') + f.write("%d\n" % pid) + f.close() + os._exit(0) + else: + os._exit(0) + + maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] + if (maxfd == resource.RLIM_INFINITY): + maxfd = MAXFD + + for fd in range(0, maxfd): + try: + os.close(fd) + except OSError: # ERROR, fd wasn't open to begin with (ignored) + pass + + os.open(REDIRECT_TO, os.O_RDWR) + os.dup2(0, 1) + os.dup2(0, 2) + + return(0) + +if __name__ == "__main__": + + retCode = createDaemon() + + while True: + time.sleep(1000) + diff --git a/test/integration/targets/service/meta/main.yml b/test/integration/targets/service/meta/main.yml new file mode 100644 index 0000000000..399f3fb6e7 --- /dev/null +++ b/test/integration/targets/service/meta/main.yml @@ -0,0 +1,20 @@ +# test code for the service module +# (c) 2014, James Cammarata <jcammarata@ansible.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/>. + +dependencies: + - prepare_tests diff --git a/test/integration/targets/service/tasks/main.yml b/test/integration/targets/service/tasks/main.yml new file mode 100644 index 0000000000..e11702c4df --- /dev/null +++ b/test/integration/targets/service/tasks/main.yml @@ -0,0 +1,155 @@ +- name: install the test daemon script + copy: src=ansible_test_service dest=/usr/sbin/ansible_test_service mode=755 + register: install_result + +- name: assert that the daemon script was installed + assert: + that: + - "install_result.dest == '/usr/sbin/ansible_test_service'" + - "install_result.checksum == '4e0164ceb9a5aeab76b38483ffd27fe791baa9f4'" + - "install_result.state == 'file'" + - "install_result.mode == '0755'" + +# determine init system is in use +- name: detect sysv init system + set_fact: service_type=sysv + when: ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] and (ansible_distribution_version|version_compare('6', '>=') and ansible_distribution_version|version_compare('7', '<')) +- name: detect systemd init system + set_fact: service_type=systemd + when: (ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux'] and (ansible_distribution_version|version_compare('7', '>=') and ansible_distribution_version|version_compare('8', '<'))) or ansible_distribution == 'Fedora' or (ansible_distribution == 'Ubuntu' and ansible_distribution_version|version_compare('15.04', '>=')) or (ansible_distribution == 'Debian' and ansible_distribution_version|version_compare('8', '>=')) or ansible_os_family == 'Suse' +- name: detect upstart init system + set_fact: service_type=upstart + when: ansible_distribution == 'Ubuntu' and ansible_distribution_version|version_compare('15.04', '<') + +# setup test service script +- include: 'sysv_setup.yml' + when: service_type == "sysv" +- include: 'systemd_setup.yml' + when: service_type == "systemd" +- include: 'upstart_setup.yml' + when: service_type == "upstart" + +- name: disable the ansible test service + service: name=ansible_test enabled=no + +- name: (check mode run) enable the ansible test service + service: name=ansible_test enabled=yes + register: enable_in_check_mode_result + check_mode: yes + +- name: assert that changes reported for check mode run + assert: + that: + - "enable_in_check_mode_result.changed == true" + +- name: enable the ansible test service + service: name=ansible_test enabled=yes + register: enable_result + +- name: assert that the service was enabled and changes reported + assert: + that: + - "enable_result.enabled == true" + - "enable_result.changed == true" + +- name: start the ansible test service + service: name=ansible_test state=started + register: start_result + +- name: assert that the service was started + assert: + that: + - "start_result.state == 'started'" + +- name: find the service with a pattern + service: name=ansible_test pattern="ansible_test_ser*" state=started + register: start2_result + # don't enable this check yet on systems with systemd because of https://github.com/ansible/ansible/issues/16694 + when: service_type != "systemd" + +- name: assert that the service was started via the pattern + assert: + that: + - "start2_result.name == 'ansible_test'" + - "start2_result.state == 'started'" + when: service_type != "systemd" + +- name: restart the ansible test service + service: name=ansible_test state=restarted + register: restart_result + +- name: assert that the service was restarted + assert: + that: + - "restart_result.state == 'started'" + +- name: restart the ansible test service with a sleep + service: name=ansible_test state=restarted sleep=2 + register: restart_sleep_result + # don't enable this check yet on systems with systemd because of https://github.com/ansible/ansible/issues/16694 + when: service_type != "systemd" + +- name: assert that the service was restarted with a sleep + assert: + that: + - "restart_sleep_result.state == 'started'" + when: service_type != "systemd" + +- name: reload the ansible test service + service: name=ansible_test state=reloaded + register: reload_result + # don't do this on systems with systemd because it triggers error: + # Unable to reload service ansible_test: ansible_test.service is not active, cannot reload. + when: service_type != "systemd" + +- name: assert that the service was reloaded + assert: + that: + - "reload_result.state == 'started'" + when: service_type != "systemd" + +- name: stop the ansible test service + service: name=ansible_test state=stopped + register: stop_result + +- name: assert that the service was stopped + assert: + that: + - "stop_result.state == 'stopped'" + +- name: disable the ansible test service + service: name=ansible_test enabled=no + register: disable_result + +- name: assert that the service was disabled + assert: + that: + - "disable_result.enabled == false" + +- name: try to enable a broken service + service: name=ansible_broken_test enabled=yes + register: broken_enable_result + ignore_errors: True + +- name: assert that the broken test failed + assert: + that: + - "broken_enable_result|failed" + +- name: remove the test daemon script + file: path=/usr/sbin/ansible_test_service state=absent + register: remove_result + +- name: assert that the test daemon script was removed + assert: + that: + - "remove_result.path == '/usr/sbin/ansible_test_service'" + - "remove_result.state == 'absent'" + +# cleaning up changes made by this playbook +- include: 'sysv_cleanup.yml' + when: service_type == "sysv" +- include: 'systemd_cleanup.yml' + when: service_type == "systemd" +- include: 'upstart_cleanup.yml' + when: service_type == "upstart" diff --git a/test/integration/targets/service/tasks/systemd_cleanup.yml b/test/integration/targets/service/tasks/systemd_cleanup.yml new file mode 100644 index 0000000000..10a60b216c --- /dev/null +++ b/test/integration/targets/service/tasks/systemd_cleanup.yml @@ -0,0 +1,26 @@ +- name: remove the systemd unit file + file: path=/usr/lib/systemd/system/ansible_test.service state=absent + register: remove_systemd_result + +- name: remove the systemd unit file + file: path=/usr/lib/systemd/system/ansible_test_broken.service state=absent + register: remove_systemd_broken_result + +- debug: var=remove_systemd_broken_result +- name: assert that the systemd unit file was removed + assert: + that: + - "remove_systemd_result.path == '/usr/lib/systemd/system/ansible_test.service'" + - "remove_systemd_result.state == 'absent'" + - "remove_systemd_broken_result.path == '/usr/lib/systemd/system/ansible_test_broken.service'" + - "remove_systemd_broken_result.state == 'absent'" + +- name: make sure systemd is reloaded + shell: systemctl daemon-reload + register: restart_systemd_result + +- name: assert that systemd was reloaded + assert: + that: + - "restart_systemd_result.rc == 0" + diff --git a/test/integration/targets/service/tasks/systemd_setup.yml b/test/integration/targets/service/tasks/systemd_setup.yml new file mode 100644 index 0000000000..bee5ce1f28 --- /dev/null +++ b/test/integration/targets/service/tasks/systemd_setup.yml @@ -0,0 +1,17 @@ +- name: install the systemd unit file + copy: src=ansible.systemd dest=/etc/systemd/system/ansible_test.service + register: install_systemd_result + +- name: install a broken systemd unit file + file: src=ansible_test.service path=/etc/systemd/system/ansible_test_broken.service state=link + register: install_broken_systemd_result + +- name: assert that the systemd unit file was installed + assert: + that: + - "install_systemd_result.dest == '/etc/systemd/system/ansible_test.service'" + - "install_systemd_result.state == 'file'" + - "install_systemd_result.mode == '0644'" + - "install_systemd_result.checksum == '6b5f2b9318524a387c77c550cef4dd411a471acf'" + - "install_broken_systemd_result.dest == '/etc/systemd/system/ansible_test_broken.service'" + - "install_broken_systemd_result.state == 'link'" diff --git a/test/integration/targets/service/tasks/sysv_cleanup.yml b/test/integration/targets/service/tasks/sysv_cleanup.yml new file mode 100644 index 0000000000..b42744391b --- /dev/null +++ b/test/integration/targets/service/tasks/sysv_cleanup.yml @@ -0,0 +1,10 @@ +- name: remove the sysV init file + file: path=/etc/init.d/ansible_test state=absent + register: remove_sysv_result + +- name: assert that the sysV init file was removed + assert: + that: + - "remove_sysv_result.path == '/etc/init.d/ansible_test'" + - "remove_sysv_result.state == 'absent'" + diff --git a/test/integration/targets/service/tasks/sysv_setup.yml b/test/integration/targets/service/tasks/sysv_setup.yml new file mode 100644 index 0000000000..796a2fe9a7 --- /dev/null +++ b/test/integration/targets/service/tasks/sysv_setup.yml @@ -0,0 +1,12 @@ +- name: install the sysV init file + copy: src=ansible.sysv dest=/etc/init.d/ansible_test mode=0755 + register: install_sysv_result + +- name: assert that the sysV init file was installed + assert: + that: + - "install_sysv_result.dest == '/etc/init.d/ansible_test'" + - "install_sysv_result.state == 'file'" + - "install_sysv_result.mode == '0755'" + - "install_sysv_result.checksum == '174fa255735064b420600e4c8637ea0eff28d0c1'" + diff --git a/test/integration/targets/service/tasks/upstart_cleanup.yml b/test/integration/targets/service/tasks/upstart_cleanup.yml new file mode 100644 index 0000000000..a589d5a986 --- /dev/null +++ b/test/integration/targets/service/tasks/upstart_cleanup.yml @@ -0,0 +1,15 @@ +- name: remove the upstart init file + file: path=/etc/init/ansible_test.conf state=absent + register: remove_upstart_result + +- name: remove the upstart init file + file: path=/etc/init/ansible_test_broken.conf state=absent + register: remove_upstart_broken_result + +- name: assert that the upstart init file was removed + assert: + that: + - "remove_upstart_result.path == '/etc/init/ansible_test.conf'" + - "remove_upstart_result.state == 'absent'" + - "remove_upstart_broken_result.path == '/etc/init/ansible_test_broken.conf'" + - "remove_upstart_broken_result.state == 'absent'" diff --git a/test/integration/targets/service/tasks/upstart_setup.yml b/test/integration/targets/service/tasks/upstart_setup.yml new file mode 100644 index 0000000000..e9607bb030 --- /dev/null +++ b/test/integration/targets/service/tasks/upstart_setup.yml @@ -0,0 +1,19 @@ +- name: install the upstart init file + copy: src=ansible.upstart dest=/etc/init/ansible_test.conf mode=0644 + register: install_upstart_result + +- name: install an upstart init file that will fail (manual in .conf) + copy: src=ansible-broken.upstart dest=/etc/init/ansible_broken_test.conf mode=0644 + register: install_upstart_broken_result + +- name: assert that the upstart init file was installed + assert: + that: + - "install_upstart_result.dest == '/etc/init/ansible_test.conf'" + - "install_upstart_result.state == 'file'" + - "install_upstart_result.mode == '0644'" + - "install_upstart_result.checksum == '5c314837b6c4dd6c68d1809653a2974e9078e02a'" + - "install_upstart_broken_result.dest == '/etc/init/ansible_broken_test.conf'" + - "install_upstart_broken_result.state == 'file'" + - "install_upstart_broken_result.mode == '0644'" + - "install_upstart_broken_result.checksum == 'e66497894f2b2bf71e1380a196cc26089cc24a10'" diff --git a/test/integration/targets/service/templates/main.yml b/test/integration/targets/service/templates/main.yml new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/service/templates/main.yml diff --git a/test/integration/targets/setup_mysql_db/defaults/main.yml b/test/integration/targets/setup_mysql_db/defaults/main.yml new file mode 100644 index 0000000000..744d0118d1 --- /dev/null +++ b/test/integration/targets/setup_mysql_db/defaults/main.yml @@ -0,0 +1,6 @@ +mysql_service: mysqld + +mysql_packages: + - mysql-server + - MySQL-python + - bzip2 diff --git a/test/integration/targets/setup_mysql_db/tasks/main.yml b/test/integration/targets/setup_mysql_db/tasks/main.yml new file mode 100644 index 0000000000..d30f6967cb --- /dev/null +++ b/test/integration/targets/setup_mysql_db/tasks/main.yml @@ -0,0 +1,45 @@ +# setup code for the mysql_db module +# (c) 2014, Wayne Rosario <wrosario@ansible.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/>. + +# ============================================================ +- include_vars: '{{ item }}' + with_first_found: + - files: + - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml' + - '{{ ansible_os_family }}-{{ ansible_distribution_major_version }}.yml' + - '{{ ansible_distribution }}.yml' + - '{{ ansible_os_family }}.yml' + paths: '../vars' + +- name: install mysqldb_test rpm dependencies + yum: name={{ item }} state=latest + with_items: "{{mysql_packages}}" + when: ansible_pkg_mgr == 'yum' + +- name: install mysqldb_test rpm dependencies + dnf: name={{ item }} state=latest + with_items: "{{mysql_packages}}" + when: ansible_pkg_mgr == 'dnf' + +- name: install mysqldb_test debian dependencies + apt: name={{ item }} state=latest + with_items: "{{mysql_packages}}" + when: ansible_pkg_mgr == 'apt' + +- name: start mysql_db service if not running + service: name={{ mysql_service }} state=started diff --git a/test/integration/targets/setup_mysql_db/vars/Debian.yml b/test/integration/targets/setup_mysql_db/vars/Debian.yml new file mode 100644 index 0000000000..21dfaad23a --- /dev/null +++ b/test/integration/targets/setup_mysql_db/vars/Debian.yml @@ -0,0 +1,6 @@ +mysql_service: 'mysql' + +mysql_packages: + - mysql-server + - python-mysqldb + - bzip2 diff --git a/test/integration/targets/setup_mysql_db/vars/Fedora.yml b/test/integration/targets/setup_mysql_db/vars/Fedora.yml new file mode 100644 index 0000000000..f8b29fd7a1 --- /dev/null +++ b/test/integration/targets/setup_mysql_db/vars/Fedora.yml @@ -0,0 +1,6 @@ +mysql_service: 'mariadb' + +mysql_packages: + - mariadb-server + - MySQL-python + - bzip2 diff --git a/test/integration/targets/setup_mysql_db/vars/RedHat-7.yml b/test/integration/targets/setup_mysql_db/vars/RedHat-7.yml new file mode 100644 index 0000000000..f8b29fd7a1 --- /dev/null +++ b/test/integration/targets/setup_mysql_db/vars/RedHat-7.yml @@ -0,0 +1,6 @@ +mysql_service: 'mariadb' + +mysql_packages: + - mariadb-server + - MySQL-python + - bzip2 diff --git a/test/integration/targets/setup_mysql_db/vars/RedHat.yml b/test/integration/targets/setup_mysql_db/vars/RedHat.yml new file mode 100644 index 0000000000..0c2e2b7d7c --- /dev/null +++ b/test/integration/targets/setup_mysql_db/vars/RedHat.yml @@ -0,0 +1,6 @@ +mysql_service: 'mysqld' + +mysql_packages: + - mysql-server + - MySQL-python + - bzip2
\ No newline at end of file diff --git a/test/integration/targets/setup_mysql_db/vars/Suse.yml b/test/integration/targets/setup_mysql_db/vars/Suse.yml new file mode 100644 index 0000000000..feeeccd053 --- /dev/null +++ b/test/integration/targets/setup_mysql_db/vars/Suse.yml @@ -0,0 +1,6 @@ +mysql_service: 'mysql' + +mysql_packages: + - mariadb + - python-MySQL-python + - bzip2 diff --git a/test/integration/targets/setup_postgresql_db/defaults/main.yml b/test/integration/targets/setup_postgresql_db/defaults/main.yml new file mode 100644 index 0000000000..08f3a91b46 --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/defaults/main.yml @@ -0,0 +1,5 @@ +postgresql_service: postgresql + +postgresql_packages: + - postgresql-server + - python-psycopg2 diff --git a/test/integration/targets/setup_postgresql_db/files/pg_hba.conf b/test/integration/targets/setup_postgresql_db/files/pg_hba.conf new file mode 100644 index 0000000000..a8defb8ee6 --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/files/pg_hba.conf @@ -0,0 +1,10 @@ +# !!! This file managed by Ansible. Any local changes may be overwritten. !!! + +# Database administrative login by UNIX sockets +# note: you may wish to restrict this further later +local all postgres trust + +# TYPE DATABASE USER CIDR-ADDRESS METHOD +local all all md5 +host all all 127.0.0.1/32 md5 +host all all ::1/128 md5 diff --git a/test/integration/targets/setup_postgresql_db/tasks/main.yml b/test/integration/targets/setup_postgresql_db/tasks/main.yml new file mode 100644 index 0000000000..351e5c8958 --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/tasks/main.yml @@ -0,0 +1,83 @@ +- include_vars: '{{ item }}' + with_first_found: + - files: + - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml' + - '{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml' + - '{{ ansible_os_family }}.yml' + - 'default.yml' + paths: '../vars' + +# Make sure we start fresh +- name: remove old db (RedHat or Suse) + command: rm -rf "{{ pg_dir }}" + ignore_errors: True + when: ansible_os_family == "RedHat" or ansible_os_family == "Suse" + +# Theoretically, pg_dropcluster should work but it doesn't so rm files +- name: remove old db config (debian) + command: rm -rf /etc/postgresql + ignore_errors: True + when: ansible_os_family == "Debian" + +- name: remove old db files (debian) + command: rm -rf /var/lib/postgresql + ignore_errors: True + when: ansible_os_family == "Debian" + +- name: install rpm dependencies for postgresql test + package: name={{ postgresql_package_item }} state=latest + with_items: "{{postgresql_packages}}" + loop_control: + loop_var: postgresql_package_item + when: ansible_os_family == "RedHat" or ansible_os_family == "Suse" + +- name: install dpkg dependencies for postgresql test + apt: name={{ postgresql_package_item }} state=latest + with_items: "{{postgresql_packages}}" + loop_control: + loop_var: postgresql_package_item + when: ansible_pkg_mgr == 'apt' + +- name: Initialize postgres (RedHat systemd) + command: postgresql-setup initdb + when: ansible_distribution == "Fedora" or (ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7) + +- name: Initialize postgres (RedHat sysv) + command: /sbin/service postgresql initdb + when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int <= 6 + +- name: Iniitalize postgres (Debian) + command: /usr/bin/pg_createcluster {{ pg_ver }} main + # Sometimes package install creates the db cluster, sometimes this step is needed + ignore_errors: True + when: ansible_os_family == 'Debian' + +- name: Initialize postgres (Suse) + service: name=postgresql state=restarted + when: ansible_os_family == 'Suse' + +- name: Copy pg_hba into place + copy: src=pg_hba.conf dest="{{ pg_hba_location }}" owner="postgres" group="root" mode="0644" + +- name: Generate pt_BR locale (Debian) + command: locale-gen pt_BR + when: ansible_os_family == 'Debian' + +- name: Generate es_MX locale (Debian) + command: locale-gen es_MX + when: ansible_os_family == 'Debian' + +- name: install i18ndata + zypper: name=glibc-i18ndata state=present + when: ansible_os_family == 'Suse' + +- name: Generate pt_BR locale (Red Hat) + command: localedef -f ISO-8859-1 -i pt_BR pt_BR + when: ansible_os_family == "RedHat" or ansible_os_family == "Suse" + +- name: Generate es_MX locale (Red Hat) + command: localedef -f ISO-8859-1 -i es_MX es_MX + when: ansible_os_family == "RedHat" or ansible_os_family == "Suse" + +- name: restart postgresql service + service: name={{ postgresql_service }} state=restarted diff --git a/test/integration/targets/setup_postgresql_db/vars/Debian-8.yml b/test/integration/targets/setup_postgresql_db/vars/Debian-8.yml new file mode 100644 index 0000000000..f829bd1996 --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/vars/Debian-8.yml @@ -0,0 +1,10 @@ +postgresql_service: "postgresql" + +postgresql_packages: + - "postgresql" + - "postgresql-common" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.4/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.4/main" +pg_ver: 9.4 diff --git a/test/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml b/test/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml new file mode 100644 index 0000000000..b2507c9849 --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml @@ -0,0 +1,11 @@ +postgresql_service: "postgresql" + +postgresql_packages: + - "postgresql" + - "postgresql-common" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.1/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.1/main" +pg_ver: 9.1 + diff --git a/test/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml b/test/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml new file mode 100644 index 0000000000..7d704264da --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml @@ -0,0 +1,10 @@ +postgresql_service: "postgresql" + +postgresql_packages: + - "postgresql" + - "postgresql-common" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.3/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.3/main" +pg_ver: 9.3 diff --git a/test/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml b/test/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml new file mode 100644 index 0000000000..7b6092a12c --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml @@ -0,0 +1,10 @@ +postgresql_service: "postgresql" + +postgresql_packages: + - "postgresql" + - "postgresql-common" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.5/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.5/main" +pg_ver: 9.5 diff --git a/test/integration/targets/setup_postgresql_db/vars/default.yml b/test/integration/targets/setup_postgresql_db/vars/default.yml new file mode 100644 index 0000000000..dc7db0fc98 --- /dev/null +++ b/test/integration/targets/setup_postgresql_db/vars/default.yml @@ -0,0 +1,8 @@ +postgresql_service: "postgresql" + +postgresql_packages: + - "postgresql-server" + - "python-psycopg2" + +pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" +pg_dir: "/var/lib/pgsql/data" diff --git a/test/integration/targets/special_vars/meta/main.yml b/test/integration/targets/special_vars/meta/main.yml new file mode 100644 index 0000000000..a8b63dfdf2 --- /dev/null +++ b/test/integration/targets/special_vars/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/special_vars/tasks/main.yml b/test/integration/targets/special_vars/tasks/main.yml new file mode 100644 index 0000000000..653bf7b905 --- /dev/null +++ b/test/integration/targets/special_vars/tasks/main.yml @@ -0,0 +1,37 @@ +# test code for the template module +# (c) 2015, Brian Coca <bcoca@ansible.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: veryfiy ansible_managed + template: src=foo.j2 dest={{output_dir}}/special_vars.yaml + +- name: read the file into facts + include_vars: "{{output_dir}}/special_vars.yaml" + + +- name: veriy all test vars are defined + assert: + that: + - 'item in hostvars[inventory_hostname].keys()' + with_items: + - test_template_host + - test_template_path + - test_template_mtime + - test_template_uid + - test_template_fullpath + - test_template_run_date + - test_ansible_managed diff --git a/test/integration/targets/special_vars/templates/foo.j2 b/test/integration/targets/special_vars/templates/foo.j2 new file mode 100644 index 0000000000..0f6db2a166 --- /dev/null +++ b/test/integration/targets/special_vars/templates/foo.j2 @@ -0,0 +1,7 @@ +test_template_host: "{{template_host}}" +test_template_path: "{{template_path}}" +test_template_mtime: "{{template_mtime}}" +test_template_uid: "{{template_uid}}" +test_template_fullpath: "{{template_fullpath}}" +test_template_run_date: "{{template_run_date}}" +test_ansible_managed: "{{ansible_managed}}" diff --git a/test/integration/targets/special_vars/vars/main.yml b/test/integration/targets/special_vars/vars/main.yml new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/integration/targets/special_vars/vars/main.yml diff --git a/test/integration/targets/stat/files/foo.txt b/test/integration/targets/stat/files/foo.txt new file mode 100644 index 0000000000..3e96db9b3e --- /dev/null +++ b/test/integration/targets/stat/files/foo.txt @@ -0,0 +1 @@ +templated_var_loaded diff --git a/test/integration/targets/stat/meta/main.yml b/test/integration/targets/stat/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/stat/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/stat/tasks/main.yml b/test/integration/targets/stat/tasks/main.yml new file mode 100644 index 0000000000..72bc1cac15 --- /dev/null +++ b/test/integration/targets/stat/tasks/main.yml @@ -0,0 +1,170 @@ +# test code for the stat module +# (c) 2014, James Tanner <tanner.jc@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: make a new file + copy: dest={{output_dir}}/foo.txt mode=0644 content="hello world" + +- name: check stat of file + stat: path={{output_dir}}/foo.txt + register: stat_result + +- debug: var=stat_result + +- assert: + that: + - "'changed' in stat_result" + - "stat_result.changed == false" + - "'stat' in stat_result" + - "'atime' in stat_result.stat" + - "'ctime' in stat_result.stat" + - "'dev' in stat_result.stat" + - "'exists' in stat_result.stat" + - "'gid' in stat_result.stat" + - "'inode' in stat_result.stat" + - "'isblk' in stat_result.stat" + - "'ischr' in stat_result.stat" + - "'isdir' in stat_result.stat" + - "'isfifo' in stat_result.stat" + - "'isgid' in stat_result.stat" + - "'isreg' in stat_result.stat" + - "'issock' in stat_result.stat" + - "'isuid' in stat_result.stat" + - "'md5' in stat_result.stat" + - "'checksum' in stat_result.stat" + - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" + - "'mode' in stat_result.stat" + - "'mtime' in stat_result.stat" + - "'nlink' in stat_result.stat" + - "'pw_name' in stat_result.stat" + - "'rgrp' in stat_result.stat" + - "'roth' in stat_result.stat" + - "'rusr' in stat_result.stat" + - "'size' in stat_result.stat" + - "'uid' in stat_result.stat" + - "'wgrp' in stat_result.stat" + - "'woth' in stat_result.stat" + - "'wusr' in stat_result.stat" + - "'xgrp' in stat_result.stat" + - "'xoth' in stat_result.stat" + - "'xusr' in stat_result.stat" + +- assert: + that: + - "stat_result.stat.md5 == '5eb63bbbe01eeed093cb22bb8f5acdc3'" + when: ansible_fips|bool != True + +- name: make a symlink + file: + src: "{{ output_dir }}/foo.txt" + path: "{{ output_dir }}/foo-link" + state: link + +- name: check stat of a symlink with follow off + stat: + path: "{{ output_dir }}/foo-link" + register: stat_result + +- debug: var=stat_result + +- assert: + that: + - "'changed' in stat_result" + - "stat_result.changed == false" + - "'stat' in stat_result" + - "'atime' in stat_result.stat" + - "'ctime' in stat_result.stat" + - "'dev' in stat_result.stat" + - "'exists' in stat_result.stat" + - "'gid' in stat_result.stat" + - "'inode' in stat_result.stat" + - "'isblk' in stat_result.stat" + - "'ischr' in stat_result.stat" + - "'isdir' in stat_result.stat" + - "'isfifo' in stat_result.stat" + - "'isgid' in stat_result.stat" + - "'isreg' in stat_result.stat" + - "'issock' in stat_result.stat" + - "'isuid' in stat_result.stat" + - "'islnk' in stat_result.stat" + - "'mode' in stat_result.stat" + - "'mtime' in stat_result.stat" + - "'nlink' in stat_result.stat" + - "'pw_name' in stat_result.stat" + - "'rgrp' in stat_result.stat" + - "'roth' in stat_result.stat" + - "'rusr' in stat_result.stat" + - "'size' in stat_result.stat" + - "'uid' in stat_result.stat" + - "'wgrp' in stat_result.stat" + - "'woth' in stat_result.stat" + - "'wusr' in stat_result.stat" + - "'xgrp' in stat_result.stat" + - "'xoth' in stat_result.stat" + - "'xusr' in stat_result.stat" + +- name: check stat of a symlink with follow on + stat: + path: "{{ output_dir }}/foo-link" + follow: True + register: stat_result + +- debug: var=stat_result + +- assert: + that: + - "'changed' in stat_result" + - "stat_result.changed == false" + - "'stat' in stat_result" + - "'atime' in stat_result.stat" + - "'ctime' in stat_result.stat" + - "'dev' in stat_result.stat" + - "'exists' in stat_result.stat" + - "'gid' in stat_result.stat" + - "'inode' in stat_result.stat" + - "'isblk' in stat_result.stat" + - "'ischr' in stat_result.stat" + - "'isdir' in stat_result.stat" + - "'isfifo' in stat_result.stat" + - "'isgid' in stat_result.stat" + - "'isreg' in stat_result.stat" + - "'issock' in stat_result.stat" + - "'isuid' in stat_result.stat" + - "'md5' in stat_result.stat" + - "'checksum' in stat_result.stat" + - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" + - "'mode' in stat_result.stat" + - "'mtime' in stat_result.stat" + - "'nlink' in stat_result.stat" + - "'pw_name' in stat_result.stat" + - "'rgrp' in stat_result.stat" + - "'roth' in stat_result.stat" + - "'rusr' in stat_result.stat" + - "'size' in stat_result.stat" + - "'uid' in stat_result.stat" + - "'wgrp' in stat_result.stat" + - "'woth' in stat_result.stat" + - "'wusr' in stat_result.stat" + - "'xgrp' in stat_result.stat" + - "'xoth' in stat_result.stat" + - "'xusr' in stat_result.stat" + +- assert: + that: + - "stat_result.stat.md5 == '5eb63bbbe01eeed093cb22bb8f5acdc3'" + when: ansible_fips|bool != True + diff --git a/test/integration/targets/subversion/meta/main.yml b/test/integration/targets/subversion/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/subversion/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/subversion/tasks/main.yml b/test/integration/targets/subversion/tasks/main.yml new file mode 100644 index 0000000000..d15d63ab02 --- /dev/null +++ b/test/integration/targets/subversion/tasks/main.yml @@ -0,0 +1,119 @@ +# test code for the svn 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: set where to extract the repo + set_fact: checkout_dir={{ output_dir }}/svn + +- name: set what repo to use + set_fact: repo=https://github.com/jimi-c/test_role + +- name: clean out the output_dir + shell: rm -rf {{ output_dir }}/* + +- name: verify that subversion is installed so this test can continue + shell: which svn + +# checks out every branch so using a small repo + +- name: initial checkout + subversion: repo={{ repo }} dest={{ checkout_dir }} + register: subverted + +- debug: var=subverted + +- shell: ls {{ checkout_dir }} + +# FIXME: the before/after logic here should be fixed to make them hashes, see GitHub 6078 +# looks like this: { +# "after": [ +# "Revision: 9", +# "URL: https://github.com/jimi-c/test_role" +# ], +# "before": null, +# "changed": true, +# "item": "" +# } + +- name: verify information about the initial clone + assert: + that: + - "'after' in subverted" + - "subverted.after.1 == 'URL: https://github.com/jimi-c/test_role'" + - "not subverted.before" + - "subverted.changed" + +- name: repeated checkout + subversion: repo={{ repo }} dest={{ checkout_dir }} + register: subverted2 + +- name: verify on a reclone things are marked unchanged + assert: + that: + - "not subverted2.changed" + +- name: check for tags + stat: path={{ checkout_dir }}/tags + register: tags + +- name: check for trunk + stat: path={{ checkout_dir }}/trunk + register: trunk + +- name: check for branches + stat: path={{ checkout_dir }}/branches + register: branches + +- name: assert presence of tags/trunk/branches + assert: + that: + - "tags.stat.isdir" + - "trunk.stat.isdir" + - "branches.stat.isdir" + +- name: checkout with quotes in username + subversion: repo={{ repo }} dest={{ checkout_dir }} username="quoteme'''" + register: subverted3 + +- debug: var=subverted3 + +- name: checkout with export + subversion: repo={{ repo }} dest={{ output_dir }}/svn-export export=True + register: subverted4 + +- name: check for tags + stat: path={{ output_dir }}/svn-export/tags + register: export_tags + +- name: check for trunk + stat: path={{ output_dir }}/svn-export/trunk + register: export_trunk + +- name: check for branches + stat: path={{ output_dir }}/svn-export/branches + register: export_branches + +- name: assert presence of tags/trunk/branches in export + assert: + that: + - "export_tags.stat.isdir" + - "export_trunk.stat.isdir" + - "export_branches.stat.isdir" + - "subverted4.changed" + +# TBA: test for additional options or URL variants welcome + diff --git a/test/integration/targets/synchronize/files/bar.txt b/test/integration/targets/synchronize/files/bar.txt new file mode 100644 index 0000000000..3e96db9b3e --- /dev/null +++ b/test/integration/targets/synchronize/files/bar.txt @@ -0,0 +1 @@ +templated_var_loaded diff --git a/test/integration/targets/synchronize/files/foo.txt b/test/integration/targets/synchronize/files/foo.txt new file mode 100644 index 0000000000..3e96db9b3e --- /dev/null +++ b/test/integration/targets/synchronize/files/foo.txt @@ -0,0 +1 @@ +templated_var_loaded diff --git a/test/integration/targets/synchronize/meta/main.yml b/test/integration/targets/synchronize/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/synchronize/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/synchronize/tasks/main.yml b/test/integration/targets/synchronize/tasks/main.yml new file mode 100644 index 0000000000..483367047d --- /dev/null +++ b/test/integration/targets/synchronize/tasks/main.yml @@ -0,0 +1,190 @@ +# test code for the synchronize module +# (c) 2014, James Tanner <tanner.jc@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: cleanup old files + shell: rm -rf {{output_dir}}/* + +- name: create test new files + copy: dest={{output_dir}}/{{item}} mode=0644 content="hello world" + with_items: + - foo.txt + - bar.txt + +- name: synchronize file to new filename + synchronize: src={{output_dir}}/foo.txt dest={{output_dir}}/foo.result + register: sync_result + +- assert: + that: + - "'changed' in sync_result" + - "sync_result.changed == true" + - "'cmd' in sync_result" + - "'rsync' in sync_result.cmd" + - "'msg' in sync_result" + - "sync_result.msg.startswith('>f+')" + - "sync_result.msg.endswith('+ foo.txt\n')" + +- name: test that the file was really copied over + stat: + path: "{{ output_dir }}/foo.result" + register: stat_result + +- assert: + that: + - "stat_result.stat.exists == True" + - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" + +- name: test that the file is not copied a second time + synchronize: src={{output_dir}}/foo.txt dest={{output_dir}}/foo.result + register: sync_result + +- assert: + that: + - "sync_result.changed == False" + +- name: Cleanup + file: + state: absent + path: "{{output_dir}}/{{item}}" + with_items: + - foo.result + - bar.result + +- name: Synchronize using the mode=push param + synchronize: + src: "{{output_dir}}/foo.txt" + dest: "{{output_dir}}/foo.result" + mode: push + register: sync_result + +- assert: + that: + - "'changed' in sync_result" + - "sync_result.changed == true" + - "'cmd' in sync_result" + - "'rsync' in sync_result.cmd" + - "'msg' in sync_result" + - "sync_result.msg.startswith('>f+')" + - "sync_result.msg.endswith('+ foo.txt\n')" + +- name: test that the file was really copied over + stat: + path: "{{ output_dir }}/foo.result" + register: stat_result + +- assert: + that: + - "stat_result.stat.exists == True" + - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" + +- name: test that the file is not copied a second time + synchronize: + src: "{{output_dir}}/foo.txt" + dest: "{{output_dir}}/foo.result" + mode: push + register: sync_result + +- assert: + that: + - "sync_result.changed == False" + +- name: Cleanup + file: + state: absent + path: "{{output_dir}}/{{item}}" + with_items: + - foo.result + - bar.result + +- name: Synchronize using the mode=pull param + synchronize: + src: "{{output_dir}}/foo.txt" + dest: "{{output_dir}}/foo.result" + mode: pull + register: sync_result + +- assert: + that: + - "'changed' in sync_result" + - "sync_result.changed == true" + - "'cmd' in sync_result" + - "'rsync' in sync_result.cmd" + - "'msg' in sync_result" + - "sync_result.msg.startswith('>f+')" + - "sync_result.msg.endswith('+ foo.txt\n')" + +- name: test that the file was really copied over + stat: + path: "{{ output_dir }}/foo.result" + register: stat_result + +- assert: + that: + - "stat_result.stat.exists == True" + - "stat_result.stat.checksum == '2aae6c35c94fcfb415dbe95f408b9ce91ee846ed'" + +- name: test that the file is not copied a second time + synchronize: + src: "{{output_dir}}/foo.txt" + dest: "{{output_dir}}/foo.result" + mode: pull + register: sync_result + +- assert: + that: + - "sync_result.changed == False" + +- name: Cleanup + file: + state: absent + path: "{{output_dir}}/{{item}}" + with_items: + - foo.result + - bar.result + +- name: synchronize files using with_items (issue#5965) + synchronize: src={{output_dir}}/{{item}} dest={{output_dir}}/{{item}}.result + with_items: + - foo.txt + - bar.txt + register: sync_result + +- assert: + that: + - "sync_result.changed" + - "sync_result.msg == 'All items completed'" + - "'results' in sync_result" + - "sync_result.results|length == 2" + - "sync_result.results[0].msg.endswith('+ foo.txt\n')" + - "sync_result.results[1].msg.endswith('+ bar.txt\n')" + +- name: synchronize files using rsync_path (issue#7182) + synchronize: src={{output_dir}}/foo.txt dest={{output_dir}}/foo.rsync_path rsync_path="sudo rsync" + register: sync_result + +- assert: + that: + - "'changed' in sync_result" + - "sync_result.changed == true" + - "'cmd' in sync_result" + - "'rsync' in sync_result.cmd" + - "'rsync_path' in sync_result.cmd" + - "'msg' in sync_result" + - "sync_result.msg.startswith('>f+')" + - "sync_result.msg.endswith('+ foo.txt\n')" + diff --git a/test/integration/targets/task_ordering/tasks/main.yml b/test/integration/targets/task_ordering/tasks/main.yml new file mode 100644 index 0000000000..edd1890a90 --- /dev/null +++ b/test/integration/targets/task_ordering/tasks/main.yml @@ -0,0 +1,13 @@ +- shell: mktemp + register: temppath +- include: taskorder-include.yml + with_items: + - 1 + - 2 + - 3 + +- assert: + that: lookup('file', temppath.stdout) == "one.1.two.1.three.1.four.1.one.2.two.2.three.2.four.2.one.3.two.3.three.3.four.3." + +- file: path="{{temppath.stdout}}" state=absent + diff --git a/test/integration/targets/task_ordering/tasks/taskorder-include.yml b/test/integration/targets/task_ordering/tasks/taskorder-include.yml new file mode 100644 index 0000000000..4e64b7e927 --- /dev/null +++ b/test/integration/targets/task_ordering/tasks/taskorder-include.yml @@ -0,0 +1,10 @@ +# This test ensures that included tasks are run in order. +# There have been regressions where included tasks and +# nested blocks ran out of order... + +- shell: printf one.{{ item }}. >> {{ temppath.stdout }} +- block: + - shell: printf two.{{ item }}. >> {{ temppath.stdout }} + - block: + - shell: printf three.{{ item }}. >> {{ temppath.stdout }} +- shell: printf four.{{ item }}. >> {{ temppath.stdout }} diff --git a/test/integration/targets/template/files/foo-py26.txt b/test/integration/targets/template/files/foo-py26.txt new file mode 100644 index 0000000000..76b0bb56f7 --- /dev/null +++ b/test/integration/targets/template/files/foo-py26.txt @@ -0,0 +1,9 @@ +templated_var_loaded + +{ + "bool": true, + "multi_part": "1Foo", + "null_type": null, + "number": 5, + "string_num": "5" +} diff --git a/test/integration/targets/template/files/foo.txt b/test/integration/targets/template/files/foo.txt new file mode 100644 index 0000000000..58af3be81b --- /dev/null +++ b/test/integration/targets/template/files/foo.txt @@ -0,0 +1,9 @@ +templated_var_loaded + +{ + "bool": true, + "multi_part": "1Foo", + "null_type": null, + "number": 5, + "string_num": "5" +} diff --git a/test/integration/targets/template/meta/main.yml b/test/integration/targets/template/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/template/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/template/tasks/main.yml b/test/integration/targets/template/tasks/main.yml new file mode 100644 index 0000000000..133690ff05 --- /dev/null +++ b/test/integration/targets/template/tasks/main.yml @@ -0,0 +1,227 @@ +# test code for the template 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: fill in a basic template + template: src=foo.j2 dest={{output_dir}}/foo.templated mode=0644 + register: template_result + +- assert: + that: + - "'changed' in template_result" + - "'dest' in template_result" + - "'group' in template_result" + - "'gid' in template_result" + - "'md5sum' in template_result" + - "'checksum' in template_result" + - "'owner' in template_result" + - "'size' in template_result" + - "'src' in template_result" + - "'state' in template_result" + - "'uid' in template_result" + +- name: verify that the file was marked as changed + assert: + that: + - "template_result.changed == true" + +# VERIFY CONTENTS + +- name: check what python version ansible is running on + command: "{{ ansible_python.executable }} -c 'import distutils.sysconfig ; print(distutils.sysconfig.get_python_version())'" + register: pyver + delegate_to: localhost + +- name: copy known good into place + copy: src=foo.txt dest={{output_dir}}/foo.txt + +- name: compare templated file to known good + shell: diff -w {{output_dir}}/foo.templated {{output_dir}}/foo.txt + register: diff_result + +- name: verify templated file matches known good + assert: + that: + - 'diff_result.stdout == ""' + - "diff_result.rc == 0" + +# VERIFY MODE + +- name: set file mode + file: path={{output_dir}}/foo.templated mode=0644 + register: file_result + +- name: ensure file mode did not change + assert: + that: + - "file_result.changed != True" + +# VERIFY dest as a directory does not break file attributes +# Note: expanduser is needed to go down the particular codepath that was broken before +- name: setup directory for test + file: state=directory dest={{output_dir | expanduser}}/template-dir mode=0755 owner=nobody group={{ ansible_env.GROUP | default('root') }} + +- name: set file mode when the destination is a directory + template: src=foo.j2 dest={{output_dir | expanduser}}/template-dir/ mode=0600 owner=root group={{ ansible_env.GROUP | default('root') }} + +- name: set file mode when the destination is a directory + template: src=foo.j2 dest={{output_dir | expanduser}}/template-dir/ mode=0600 owner=root group={{ ansible_env.GROUP | default('root') }} + register: file_result + +- name: check that the file has the correct attributes + stat: path={{output_dir | expanduser}}/template-dir/foo.j2 + register: file_attrs + +- assert: + that: + - "file_attrs.stat.uid == 0" + - "file_attrs.stat.pw_name == 'root'" + - "file_attrs.stat.mode == '0600'" + +- name: check that the containing directory did not change attributes + stat: path={{output_dir | expanduser}}/template-dir/ + register: dir_attrs + +- assert: + that: + - "dir_attrs.stat.uid != 0" + - "dir_attrs.stat.pw_name == 'nobody'" + - "dir_attrs.stat.mode == '0755'" + +- name: Check that template to a dirctory where the directory does not end with a / is allowed + template: src=foo.j2 dest={{output_dir | expanduser}}/template-dir mode=0600 owner=root group={{ ansible_env.GROUP | default('root') }} + +- name: make a symlink to the templated file + file: + path: '{{ output_dir }}/foo.symlink' + src: '{{ output_dir }}/foo.templated' + state: link + +- name: check that templating the symlink results in the file being templated + template: + src: foo.j2 + dest: '{{output_dir}}/foo.symlink' + mode: 0600 + follow: True + register: template_result + +- assert: + that: + - "template_result.changed == True" + +- name: check that the file has the correct attributes + stat: path={{output_dir | expanduser}}/template-dir/foo.j2 + register: file_attrs + +- assert: + that: + - "file_attrs.stat.mode == '0600'" + +- name: check that templating the symlink again makes no changes + template: + src: foo.j2 + dest: '{{output_dir}}/foo.symlink' + mode: 0600 + follow: True + register: template_result + +- assert: + that: + - "template_result.changed == False" + +# Test strange filenames + +- name: Create a temp dir for filename tests + file: + state: directory + dest: '{{ output_dir }}/filename-tests' + +- name: create a file with an unusual filename + template: + src: foo.j2 + dest: "{{ output_dir }}/filename-tests/foo t'e~m\\plated" + register: template_result + +- assert: + that: + - "template_result.changed == True" + +- name: check that the unusual filename was created + command: "ls {{ output_dir }}/filename-tests/" + register: unusual_results + +- assert: + that: + - "\"foo t'e~m\\plated\" in unusual_results.stdout_lines" + - "{{unusual_results.stdout_lines| length}} == 1" + +- name: check that the unusual filename can be checked for changes + template: + src: foo.j2 + dest: "{{ output_dir }}/filename-tests/foo t'e~m\\plated" + register: template_result + +- assert: + that: + - "template_result.changed == False" + + +# check_mode + +- name: fill in a basic template in check mode + template: src=short.j2 dest={{output_dir}}/short.templated + register: template_result + check_mode: True + +- name: check file exists + stat: path={{output_dir}}/short.templated + register: templated + +- name: verify that the file was marked as changed in check mode but was not created + assert: + that: + - "not templated.stat.exists" + - "template_result|changed" + +- name: fill in a basic template + template: src=short.j2 dest={{output_dir}}/short.templated + +- name: fill in a basic template in check mode + template: src=short.j2 dest={{output_dir}}/short.templated + register: template_result + check_mode: True + +- name: verify that the file was marked as not changes in check mode + assert: + that: + - "not template_result|changed" + - "'templated_var_loaded' in lookup('file', '{{output_dir | expanduser}}/short.templated')" + +- name: change var for the template + set_fact: + templated_var: "changed" + +- name: fill in a basic template with changed var in check mode + template: src=short.j2 dest={{output_dir}}/short.templated + register: template_result + check_mode: True + +- name: verify that the file was marked as changed in check mode but the content was not changed + assert: + that: + - "'templated_var_loaded' in lookup('file', '{{output_dir | expanduser }}/short.templated')" + - "template_result|changed" diff --git a/test/integration/targets/template/templates/foo.j2 b/test/integration/targets/template/templates/foo.j2 new file mode 100644 index 0000000000..22187f9130 --- /dev/null +++ b/test/integration/targets/template/templates/foo.j2 @@ -0,0 +1,3 @@ +{{ templated_var }} + +{{ templated_dict | to_nice_json }} diff --git a/test/integration/targets/template/templates/short.j2 b/test/integration/targets/template/templates/short.j2 new file mode 100644 index 0000000000..55aab8f1ea --- /dev/null +++ b/test/integration/targets/template/templates/short.j2 @@ -0,0 +1 @@ +{{ templated_var }} diff --git a/test/integration/targets/template/vars/main.yml b/test/integration/targets/template/vars/main.yml new file mode 100644 index 0000000000..16776cb7e8 --- /dev/null +++ b/test/integration/targets/template/vars/main.yml @@ -0,0 +1,16 @@ +templated_var: templated_var_loaded + +number_var: 5 +string_num: "5" +bool_var: true +part_1: 1 +part_2: "Foo" +null_type: !!null + +templated_dict: + number: "{{ number_var }}" + string_num: "{{ string_num }}" + null_type: "{{ null_type }}" + bool: "{{ bool_var }}" + multi_part: "{{ part_1 }}{{ part_2 }}" + diff --git a/test/integration/targets/unarchive/files/foo.txt b/test/integration/targets/unarchive/files/foo.txt new file mode 100644 index 0000000000..7c6ded14ec --- /dev/null +++ b/test/integration/targets/unarchive/files/foo.txt @@ -0,0 +1 @@ +foo.txt diff --git a/test/integration/targets/unarchive/files/test-unarchive-nonascii-くらとみ.tar.gz b/test/integration/targets/unarchive/files/test-unarchive-nonascii-くらとみ.tar.gz Binary files differnew file mode 100644 index 0000000000..4882b9207a --- /dev/null +++ b/test/integration/targets/unarchive/files/test-unarchive-nonascii-くらとみ.tar.gz diff --git a/test/integration/targets/unarchive/meta/main.yml b/test/integration/targets/unarchive/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/test/integration/targets/unarchive/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/test/integration/targets/unarchive/tasks/main.yml b/test/integration/targets/unarchive/tasks/main.yml new file mode 100644 index 0000000000..e17e5b2ccb --- /dev/null +++ b/test/integration/targets/unarchive/tasks/main.yml @@ -0,0 +1,427 @@ +# Test code for the unarchive module. +# (c) 2014, Richard Isaacson <richard.c.isaacson@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/>. +# Make sure we start fresh + +- name: Ensure zip is present to create test archive (yum) + yum: name=zip state=latest + when: ansible_pkg_mgr == 'yum' + + #- name: Ensure zip is present to create test archive (dnf) + # dnf: name=zip state=latest + # when: ansible_pkg_mgr == 'dnf' + +- name: Ensure zip is present to create test archive (apt) + apt: name=zip state=latest + when: ansible_pkg_mgr == 'apt' + +- name: prep our file + copy: src=foo.txt dest={{output_dir}}/foo-unarchive.txt + +- name: prep a tar file + shell: tar cvf test-unarchive.tar foo-unarchive.txt chdir={{output_dir}} + +- name: prep a tar.gz file + shell: tar czvf test-unarchive.tar.gz foo-unarchive.txt chdir={{output_dir}} + +- name: prep a zip file + shell: zip test-unarchive.zip foo-unarchive.txt chdir={{output_dir}} + +- name: prep a subdirectory + file: path={{output_dir}}/unarchive-dir state=directory + +- name: prep our file + copy: src=foo.txt dest={{output_dir}}/unarchive-dir/foo-unarchive.txt + +- name: prep a tar.gz file with directory + shell: tar czvf test-unarchive-dir.tar.gz unarchive-dir chdir={{output_dir}} + +- name: create our tar unarchive destination + file: path={{output_dir}}/test-unarchive-tar state=directory + +- name: unarchive a tar file + unarchive: src={{output_dir}}/test-unarchive.tar dest="{{output_dir | expanduser}}/test-unarchive-tar" remote_src=yes + register: unarchive01 + +- name: verify that the file was marked as changed + assert: + that: + - "unarchive01.changed == true" + +- name: verify that the file was unarchived + file: path={{output_dir}}/test-unarchive-tar/foo-unarchive.txt state=file + +- name: remove our tar unarchive destination + file: path={{output_dir}}/test-unarchive-tar state=absent + +- name: create our tar.gz unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=directory + +- name: unarchive a tar.gz file + unarchive: src={{output_dir}}/test-unarchive.tar.gz dest={{output_dir | expanduser}}/test-unarchive-tar-gz remote_src=yes + register: unarchive02 + +- name: verify that the file was marked as changed + assert: + that: + - "unarchive02.changed == true" + # Verify that no file list is generated + - "'files' not in unarchive02" + +- name: verify that the file was unarchived + file: path={{output_dir}}/test-unarchive-tar-gz/foo-unarchive.txt state=file + +- name: remove our tar.gz unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=absent + +- name: create our tar.gz unarchive destination for creates + file: path={{output_dir}}/test-unarchive-tar-gz state=directory + +- name: unarchive a tar.gz file with creates set + unarchive: src={{output_dir}}/test-unarchive.tar.gz dest={{output_dir | expanduser}}/test-unarchive-tar-gz remote_src=yes creates={{output_dir}}/test-unarchive-tar-gz/foo-unarchive.txt + register: unarchive02b + +- name: verify that the file was marked as changed + assert: + that: + - "unarchive02b.changed == true" + +- name: verify that the file was unarchived + file: path={{output_dir}}/test-unarchive-tar-gz/foo-unarchive.txt state=file + +- name: unarchive a tar.gz file with creates over an existing file + unarchive: src={{output_dir}}/test-unarchive.tar.gz dest={{output_dir | expanduser}}/test-unarchive-tar-gz remote_src=yes creates={{output_dir}}/test-unarchive-tar-gz/foo-unarchive.txt + register: unarchive02c + +- name: verify that the file was not marked as changed + assert: + that: + - "unarchive02c.changed == false" + +- name: unarchive a tar.gz file with creates over an existing file using complex_args + unarchive: + src: "{{output_dir}}/test-unarchive.tar.gz" + dest: "{{output_dir | expanduser}}/test-unarchive-tar-gz" + remote_src: yes + creates: "{{output_dir}}/test-unarchive-tar-gz/foo-unarchive.txt" + register: unarchive02d + +- name: verify that the file was not marked as changed + assert: + that: + - "unarchive02d.changed == false" + +- name: remove our tar.gz unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=absent + +- name: create our zip unarchive destination + file: path={{output_dir}}/test-unarchive-zip state=directory + +- name: unarchive a zip file + unarchive: src={{output_dir}}/test-unarchive.zip dest={{output_dir | expanduser}}/test-unarchive-zip remote_src=yes list_files=True + register: unarchive03 + +- name: verify that the file was marked as changed + assert: + that: + - "unarchive03.changed == true" + # Verify that file list is generated + - "'files' in unarchive03" + - "{{unarchive03['files']| length}} == 1" + - "'foo-unarchive.txt' in unarchive03['files']" + +- name: verify that the file was unarchived + file: path={{output_dir}}/test-unarchive-zip/foo-unarchive.txt state=file + +- name: remove our zip unarchive destination + file: path={{output_dir}}/test-unarchive-zip state=absent + +- name: remove our test file for the archive + file: path={{output_dir}}/foo-unarchive.txt state=absent + +- name: check if /tmp/foo-unarchive.text exists + stat: path=/tmp/foo-unarchive.txt + ignore_errors: True + register: unarchive04 + +- name: fail if the proposed destination file exists for safey + fail: msg="/tmp/foo-unarchive.txt already exists, aborting" + when: unarchive04.stat.exists + +- name: try unarchiving to /tmp + unarchive: src={{output_dir}}/test-unarchive.tar.gz dest=/tmp remote_src=yes + register: unarchive05 + +- name: verify that the file was marked as changed + assert: + that: + - "unarchive05.changed == true" + +- name: verify that the file was unarchived + file: path=/tmp/foo-unarchive.txt state=file + +- name: remove our unarchive destination + file: path=/tmp/foo-unarchive.txt state=absent + +- name: create our unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=directory + +- name: unarchive and set mode to 0600 + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/test-unarchive-tar-gz" + remote_src: yes + mode: "u+rwX,g-rwx,o-rwx" + list_files: True + register: unarchive06 + +- name: Test that the file modes were changed + stat: + path: "{{ output_dir | expanduser }}/test-unarchive-tar-gz/foo-unarchive.txt" + register: unarchive06_stat + +- name: Test that the file modes were changed + assert: + that: + - "unarchive06.changed == true" + - "unarchive06_stat.stat.mode == '0600'" + # Verify that file list is generated + - "'files' in unarchive06" + - "{{unarchive06['files']| length}} == 1" + - "'foo-unarchive.txt' in unarchive06['files']" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent + +- name: create our unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=directory + + +- name: unarchive over existing extraction and set mode to 0644 + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/test-unarchive-tar-gz" + remote_src: yes + mode: "u+rwX,g-wx,o-wx,g+r,o+r" + register: unarchive06_2 + +- name: Test that the file modes were changed + stat: + path: "{{ output_dir | expanduser }}/test-unarchive-tar-gz/foo-unarchive.txt" + register: unarchive06_2_stat + +- debug: var=unarchive06_2_stat.stat.mode +- name: Test that the files were changed + assert: + that: + - "unarchive06_2.changed == true" + - "unarchive06_2_stat.stat.mode == '0644'" + +- name: Repeat the last request to verify no changes + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/test-unarchive-tar-gz" + remote_src: yes + mode: "u+rwX,g-wx,o-wx,g+r,o+r" + list_files: True + register: unarchive07 + +- name: Test that the files were not changed + assert: + that: + - "unarchive07.changed == false" + # Verify that file list is generated + - "'files' in unarchive07" + - "{{unarchive07['files']| length}} == 1" + - "'foo-unarchive.txt' in unarchive07['files']" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent + + +- name: create our unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=directory + + +- name: create a directory with quotable chars + file: path="{{ output_dir }}/test-quotes~root" state=directory + +- name: unarchive into directory with quotable chars + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/test-quotes~root" + remote_src: yes + register: unarchive08 + +- name: Test that unarchive succeeded + assert: + that: + - "unarchive08.changed == true" + +- name: unarchive into directory with quotable chars a second time + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/test-quotes~root" + remote_src: yes + register: unarchive09 + +- name: Test that unarchive did nothing + assert: + that: + - "unarchive09.changed == false" + +- name: remove quotable chars test + file: path="{{ output_dir }}/test-quotes~root" state=absent + +- name: create our unarchive destination + file: + path: "{{ output_dir }}/test-unarchive-nonascii-くらとみ-tar-gz" + state: directory + +- name: test that unarchive works with an archive that contains non-ascii filenames + unarchive: + # Both the filename of the tarball and the filename inside the tarball have + # nonascii chars + src: "test-unarchive-nonascii-くらとみ.tar.gz" + dest: "{{ output_dir }}/test-unarchive-nonascii-くらとみ-tar-gz" + mode: "u+rwX,go+rX" + remote_src: no + register: nonascii_result0 + +- name: Check that file is really there + stat: + path: "{{ output_dir | expanduser }}/test-unarchive-nonascii-くらとみ-tar-gz/storage/àâæçéèïîôœ(copy)!@#$%^&-().jpg" + register: nonascii_stat0 + +- name: Assert that nonascii tests succeeded + assert: + that: + - "nonascii_result0.changed == true" + - "nonascii_stat0.stat.exists == true" + +- name: remove nonascii test + file: path="{{ output_dir }}/test-unarchive-nonascii-くらとみ-tar-gz" state=absent + +# Test that unarchiving is performed if files are missing +# https://github.com/ansible/ansible-modules-core/issues/1064 +- name: create our unarchive destination + file: path={{output_dir}}/test-unarchive-tar-gz state=directory + +- name: unarchive a tar that has directories + unarchive: + src: "{{ output_dir }}/test-unarchive-dir.tar.gz" + dest: "{{ output_dir }}/test-unarchive-tar-gz" + mode: "0700" + remote_src: yes + register: unarchive10 + +- name: Test that unarchive succeeded + assert: + that: + - "unarchive10.changed == true" + +- name: Change the mode of the toplevel dir + file: + path: "{{ output_dir }}/test-unarchive-tar-gz/unarchive-dir" + mode: 0701 + +- name: Remove a file from the extraction point + file: + path: "{{ output_dir }}/test-unarchive-tar-gz/unarchive-dir/foo-unarchive.txt" + state: absent + +- name: unarchive a tar that has directories + unarchive: + src: "{{ output_dir }}/test-unarchive-dir.tar.gz" + dest: "{{ output_dir }}/test-unarchive-tar-gz" + mode: "0700" + remote_src: yes + register: unarchive10_1 + +- name: Test that unarchive succeeded + assert: + that: + - "unarchive10_1.changed == true" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent + +# +# Symlink tests +# + +- name: Create a destination dir + file: + path: "{{ output_dir }}/test-unarchive-tar-gz" + state: directory + +- name: Create a symlink to the detination dir + file: + path: "{{ output_dir }}/link-to-unarchive-dir" + src: "{{ output_dir }}/test-unarchive-tar-gz" + state: "link" + +- name: test that unarchive works when dest is a symlink to a dir + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/link-to-unarchive-dir" + mode: "u+rwX,go+rX" + remote_src: yes + register: unarchive_11 + +- name: Check that file is really there + stat: + path: "{{ output_dir | expanduser }}/test-unarchive-tar-gz/foo-unarchive.txt" + register: unarchive11_stat0 + +- name: Assert that unarchive when dest is a symlink to a dir worked + assert: + that: + - "unarchive_11.changed == true" + - "unarchive11_stat0.stat.exists == true" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent + + +- name: Create a file + file: + path: "{{ output_dir }}/test-unarchive-tar-gz" + state: touch + +- name: Create a symlink to the file + file: + path: "{{ output_dir }}/link-to-unarchive-file" + src: "{{ output_dir }}/test-unarchive-tar-gz" + state: "link" + +- name: test that unarchive fails when dest is a link to a file + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/link-to-unarchive-file" + mode: "u+rwX,go+rX" + remote_src: yes + ignore_errors: True + register: unarchive_12 + +- name: Assert that unarchive when dest is a file failed + assert: + that: + - "unarchive_12.failed == true" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent diff --git a/test/integration/targets/until/tasks/main.yml b/test/integration/targets/until/tasks/main.yml new file mode 100644 index 0000000000..74a214756d --- /dev/null +++ b/test/integration/targets/until/tasks/main.yml @@ -0,0 +1,32 @@ +- shell: '{{ ansible_python.executable }} -c "import tempfile; print(tempfile.mkstemp()[1])"' + register: tempfilepath + +- set_fact: + until_tempfile_path: "{{ tempfilepath.stdout }}" + +- name: loop with default retries + shell: echo "run" >> {{ until_tempfile_path }} && wc -w < {{ until_tempfile_path }} | tr -d ' ' + register: runcount + until: runcount.stdout | int == 3 + delay: 0.01 + +- assert: + that: runcount.stdout | int == 3 + +- file: path="{{ until_tempfile_path }}" state=absent + +- name: loop with specified max retries + shell: echo "run" >> {{ until_tempfile_path }} + until: 1==0 + retries: 5 + delay: 0.01 + ignore_errors: true + +- name: validate output + shell: wc -l < {{ until_tempfile_path }} + register: runcount + +- assert: + that: runcount.stdout | int == 6 # initial + 5 retries + +- file: path="{{ until_tempfile_path }}" state=absent diff --git a/test/integration/targets/uri/files/README b/test/integration/targets/uri/files/README new file mode 100644 index 0000000000..ef7791262b --- /dev/null +++ b/test/integration/targets/uri/files/README @@ -0,0 +1,9 @@ +The files were taken from http://www.json.org/JSON_checker/ +> If the JSON_checker is working correctly, it must accept all of the pass*.json files and reject all of the fail*.json files. + +Difference with JSON_checker dataset: + - *${n}.json renamed to *${n-1}.json to be 0-based + - fail0.json renamed to pass3.json as python json module allows JSON payload to be string + - fail17.json renamed to pass4.json as python json module has no problems with deep structures + - fail32.json renamed to fail0.json to fill gap + - fail31.json renamed to fail17.json to fill gap diff --git a/test/integration/targets/uri/files/fail0.json b/test/integration/targets/uri/files/fail0.json new file mode 100644 index 0000000000..ca5eb19dc9 --- /dev/null +++ b/test/integration/targets/uri/files/fail0.json @@ -0,0 +1 @@ +["mismatch"}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail1.json b/test/integration/targets/uri/files/fail1.json new file mode 100644 index 0000000000..6b7c11e5a5 --- /dev/null +++ b/test/integration/targets/uri/files/fail1.json @@ -0,0 +1 @@ +["Unclosed array"
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail10.json b/test/integration/targets/uri/files/fail10.json new file mode 100644 index 0000000000..76eb95b458 --- /dev/null +++ b/test/integration/targets/uri/files/fail10.json @@ -0,0 +1 @@ +{"Illegal expression": 1 + 2}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail11.json b/test/integration/targets/uri/files/fail11.json new file mode 100644 index 0000000000..77580a4522 --- /dev/null +++ b/test/integration/targets/uri/files/fail11.json @@ -0,0 +1 @@ +{"Illegal invocation": alert()}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail12.json b/test/integration/targets/uri/files/fail12.json new file mode 100644 index 0000000000..379406b59b --- /dev/null +++ b/test/integration/targets/uri/files/fail12.json @@ -0,0 +1 @@ +{"Numbers cannot have leading zeroes": 013}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail13.json b/test/integration/targets/uri/files/fail13.json new file mode 100644 index 0000000000..0ed366b38a --- /dev/null +++ b/test/integration/targets/uri/files/fail13.json @@ -0,0 +1 @@ +{"Numbers cannot be hex": 0x14}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail14.json b/test/integration/targets/uri/files/fail14.json new file mode 100644 index 0000000000..fc8376b605 --- /dev/null +++ b/test/integration/targets/uri/files/fail14.json @@ -0,0 +1 @@ +["Illegal backslash escape: \x15"]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail15.json b/test/integration/targets/uri/files/fail15.json new file mode 100644 index 0000000000..3fe21d4b53 --- /dev/null +++ b/test/integration/targets/uri/files/fail15.json @@ -0,0 +1 @@ +[\naked]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail16.json b/test/integration/targets/uri/files/fail16.json new file mode 100644 index 0000000000..62b9214aed --- /dev/null +++ b/test/integration/targets/uri/files/fail16.json @@ -0,0 +1 @@ +["Illegal backslash escape: \017"]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail17.json b/test/integration/targets/uri/files/fail17.json new file mode 100644 index 0000000000..45cba7396f --- /dev/null +++ b/test/integration/targets/uri/files/fail17.json @@ -0,0 +1 @@ +{"Comma instead if closing brace": true,
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail18.json b/test/integration/targets/uri/files/fail18.json new file mode 100644 index 0000000000..3b9c46fa9a --- /dev/null +++ b/test/integration/targets/uri/files/fail18.json @@ -0,0 +1 @@ +{"Missing colon" null}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail19.json b/test/integration/targets/uri/files/fail19.json new file mode 100644 index 0000000000..27c1af3e72 --- /dev/null +++ b/test/integration/targets/uri/files/fail19.json @@ -0,0 +1 @@ +{"Double colon":: null}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail2.json b/test/integration/targets/uri/files/fail2.json new file mode 100644 index 0000000000..168c81eb78 --- /dev/null +++ b/test/integration/targets/uri/files/fail2.json @@ -0,0 +1 @@ +{unquoted_key: "keys must be quoted"}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail20.json b/test/integration/targets/uri/files/fail20.json new file mode 100644 index 0000000000..62474573b2 --- /dev/null +++ b/test/integration/targets/uri/files/fail20.json @@ -0,0 +1 @@ +{"Comma instead of colon", null}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail21.json b/test/integration/targets/uri/files/fail21.json new file mode 100644 index 0000000000..a7752581bc --- /dev/null +++ b/test/integration/targets/uri/files/fail21.json @@ -0,0 +1 @@ +["Colon instead of comma": false]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail22.json b/test/integration/targets/uri/files/fail22.json new file mode 100644 index 0000000000..494add1ca1 --- /dev/null +++ b/test/integration/targets/uri/files/fail22.json @@ -0,0 +1 @@ +["Bad value", truth]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail23.json b/test/integration/targets/uri/files/fail23.json new file mode 100644 index 0000000000..caff239bfc --- /dev/null +++ b/test/integration/targets/uri/files/fail23.json @@ -0,0 +1 @@ +['single quote']
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail24.json b/test/integration/targets/uri/files/fail24.json new file mode 100644 index 0000000000..8b7ad23e01 --- /dev/null +++ b/test/integration/targets/uri/files/fail24.json @@ -0,0 +1 @@ +[" tab character in string "]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail25.json b/test/integration/targets/uri/files/fail25.json new file mode 100644 index 0000000000..845d26a6a5 --- /dev/null +++ b/test/integration/targets/uri/files/fail25.json @@ -0,0 +1 @@ +["tab\ character\ in\ string\ "]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail26.json b/test/integration/targets/uri/files/fail26.json new file mode 100644 index 0000000000..6b01a2ca4a --- /dev/null +++ b/test/integration/targets/uri/files/fail26.json @@ -0,0 +1,2 @@ +["line +break"]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail27.json b/test/integration/targets/uri/files/fail27.json new file mode 100644 index 0000000000..621a0101c6 --- /dev/null +++ b/test/integration/targets/uri/files/fail27.json @@ -0,0 +1,2 @@ +["line\ +break"]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail28.json b/test/integration/targets/uri/files/fail28.json new file mode 100644 index 0000000000..47ec421bb6 --- /dev/null +++ b/test/integration/targets/uri/files/fail28.json @@ -0,0 +1 @@ +[0e]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail29.json b/test/integration/targets/uri/files/fail29.json new file mode 100644 index 0000000000..8ab0bc4b8b --- /dev/null +++ b/test/integration/targets/uri/files/fail29.json @@ -0,0 +1 @@ +[0e+]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail3.json b/test/integration/targets/uri/files/fail3.json new file mode 100644 index 0000000000..9de168bf34 --- /dev/null +++ b/test/integration/targets/uri/files/fail3.json @@ -0,0 +1 @@ +["extra comma",]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail30.json b/test/integration/targets/uri/files/fail30.json new file mode 100644 index 0000000000..1cce602b51 --- /dev/null +++ b/test/integration/targets/uri/files/fail30.json @@ -0,0 +1 @@ +[0e+-1]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail4.json b/test/integration/targets/uri/files/fail4.json new file mode 100644 index 0000000000..ddf3ce3d24 --- /dev/null +++ b/test/integration/targets/uri/files/fail4.json @@ -0,0 +1 @@ +["double extra comma",,]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail5.json b/test/integration/targets/uri/files/fail5.json new file mode 100644 index 0000000000..ed91580e1b --- /dev/null +++ b/test/integration/targets/uri/files/fail5.json @@ -0,0 +1 @@ +[ , "<-- missing value"]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail6.json b/test/integration/targets/uri/files/fail6.json new file mode 100644 index 0000000000..8a96af3e4e --- /dev/null +++ b/test/integration/targets/uri/files/fail6.json @@ -0,0 +1 @@ +["Comma after the close"],
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail7.json b/test/integration/targets/uri/files/fail7.json new file mode 100644 index 0000000000..b28479c6ec --- /dev/null +++ b/test/integration/targets/uri/files/fail7.json @@ -0,0 +1 @@ +["Extra close"]]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail8.json b/test/integration/targets/uri/files/fail8.json new file mode 100644 index 0000000000..5815574f36 --- /dev/null +++ b/test/integration/targets/uri/files/fail8.json @@ -0,0 +1 @@ +{"Extra comma": true,}
\ No newline at end of file diff --git a/test/integration/targets/uri/files/fail9.json b/test/integration/targets/uri/files/fail9.json new file mode 100644 index 0000000000..5d8c0047bd --- /dev/null +++ b/test/integration/targets/uri/files/fail9.json @@ -0,0 +1 @@ +{"Extra value after close": true} "misplaced quoted value"
\ No newline at end of file diff --git a/test/integration/targets/uri/files/pass0.json b/test/integration/targets/uri/files/pass0.json new file mode 100644 index 0000000000..70e2685436 --- /dev/null +++ b/test/integration/targets/uri/files/pass0.json @@ -0,0 +1,58 @@ +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* <!-- --", + "# -- --> */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00,2e+00,2e-00 +,"rosebud"]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/pass1.json b/test/integration/targets/uri/files/pass1.json new file mode 100644 index 0000000000..d3c63c7ad8 --- /dev/null +++ b/test/integration/targets/uri/files/pass1.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/pass2.json b/test/integration/targets/uri/files/pass2.json new file mode 100644 index 0000000000..4528d51f1a --- /dev/null +++ b/test/integration/targets/uri/files/pass2.json @@ -0,0 +1,6 @@ +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} diff --git a/test/integration/targets/uri/files/pass3.json b/test/integration/targets/uri/files/pass3.json new file mode 100644 index 0000000000..6216b865f1 --- /dev/null +++ b/test/integration/targets/uri/files/pass3.json @@ -0,0 +1 @@ +"A JSON payload should be an object or array, not a string."
\ No newline at end of file diff --git a/test/integration/targets/uri/files/pass4.json b/test/integration/targets/uri/files/pass4.json new file mode 100644 index 0000000000..edac92716f --- /dev/null +++ b/test/integration/targets/uri/files/pass4.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]
\ No newline at end of file diff --git a/test/integration/targets/uri/files/testserver.py b/test/integration/targets/uri/files/testserver.py new file mode 100644 index 0000000000..d0d24a0050 --- /dev/null +++ b/test/integration/targets/uri/files/testserver.py @@ -0,0 +1,7 @@ +import mimetypes + +if __name__ == '__main__': + mimetypes.init() + mimetypes.add_type('application/json', '.json') + import SimpleHTTPServer + SimpleHTTPServer.test() diff --git a/test/integration/targets/uri/meta/main.yml b/test/integration/targets/uri/meta/main.yml new file mode 100644 index 0000000000..a5f3f70736 --- /dev/null +++ b/test/integration/targets/uri/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - prepare_http_tests diff --git a/test/integration/targets/uri/tasks/main.yml b/test/integration/targets/uri/tasks/main.yml new file mode 100644 index 0000000000..bfb193a26b --- /dev/null +++ b/test/integration/targets/uri/tasks/main.yml @@ -0,0 +1,279 @@ +# test code for the uri module +# (c) 2014, Leonid Evdokimov <leon@darkk.net.ru> + +# 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: set role facts + set_fact: + http_port: 15260 + files_dir: '{{ output_dir|expanduser }}/files' + checkout_dir: '{{ output_dir }}/git' + +- name: create a directory to serve files from + file: + dest: "{{ files_dir }}" + state: directory + +- copy: + src: "{{ item }}" + dest: "{{files_dir}}/{{ item }}" + with_sequence: start=0 end=4 format=pass%d.json + +- copy: + src: "{{ item }}" + dest: "{{files_dir}}/{{ item }}" + with_sequence: start=0 end=30 format=fail%d.json + +- copy: + src: "testserver.py" + dest: "{{ output_dir }}/testserver.py" + +- name: start SimpleHTTPServer + shell: cd {{ files_dir }} && {{ ansible_python.executable }} {{ output_dir}}/testserver.py {{ http_port }} + async: 60 # this test set takes ~15 seconds to run + poll: 0 + +- wait_for: port={{ http_port }} + + +- name: checksum pass_json + stat: path={{ files_dir }}/{{ item }}.json get_checksum=yes + register: pass_checksum + with_sequence: start=0 end=4 format=pass%d + +- name: fetch pass_json + uri: return_content=yes url=http://localhost:{{ http_port }}/{{ item }}.json + register: pass + with_sequence: start=0 end=4 format=pass%d + +- name: check pass_json + assert: + that: + - '"json" in item.1' + - item.0.stat.checksum == item.1.content | checksum + with_together: + - "{{pass_checksum.results}}" + - "{{pass.results}}" + + +- name: checksum fail_json + stat: path={{ files_dir }}/{{ item }}.json get_checksum=yes + register: fail_checksum + with_sequence: start=0 end=30 format=fail%d + +- name: fetch fail_json + uri: return_content=yes url=http://localhost:{{ http_port }}/{{ item }}.json + register: fail + with_sequence: start=0 end=30 format=fail%d + +- name: check fail_json + assert: + that: + - item.0.stat.checksum == item.1.content | checksum + - '"json" not in item.1' + with_together: + - "{{fail_checksum.results}}" + - "{{fail.results}}" + +- name: test https fetch to a site with mismatched hostname and certificate + uri: + url: "https://{{ badssl_host }}/" + dest: "{{ output_dir }}/shouldnotexist.html" + ignore_errors: True + register: result + +- stat: + path: "{{ output_dir }}/shouldnotexist.html" + register: stat_result + +- name: Assert that the file was not downloaded + assert: + that: + - "result.failed == true" + - "'Failed to validate the SSL certificate' in result.msg" + - "stat_result.stat.exists == false" + +- name: Clean up any cruft from the results directory + file: + name: "{{ output_dir }}/kreitz.html" + state: absent + +- name: test https fetch to a site with mismatched hostname and certificate and validate_certs=no + uri: + url: "https://{{ badssl_host }}/" + dest: "{{ output_dir }}/kreitz.html" + validate_certs: no + register: result + +- stat: + path: "{{ output_dir }}/kreitz.html" + register: stat_result + +- name: Assert that the file was downloaded + assert: + that: + - "stat_result.stat.exists == true" + - "result.changed == true" + +- name: test redirect without follow_redirects + uri: + url: 'http://{{ httpbin_host }}/redirect/2' + follow_redirects: 'none' + status_code: 302 + register: result + +- name: Assert location header + assert: + that: + - 'result.location|default("") == "http://{{ httpbin_host }}/relative-redirect/1"' + +- name: Check SSL with redirect + uri: + url: 'https://{{ httpbin_host }}/redirect/2' + register: result + +- name: Assert SSL with redirect + assert: + that: + - 'result.url|default("") == "https://{{ httpbin_host }}/get"' + +- name: redirect to bad SSL site + uri: + url: 'http://{{ badssl_host }}' + register: result + ignore_errors: true + +- name: Ensure bad SSL site reidrect fails + assert: + that: + - result|failed + - 'badssl_host in result.msg' + +- name: test basic auth + uri: + url: 'http://{{ httpbin_host }}/basic-auth/user/passwd' + user: user + password: passwd + +- name: test basic forced auth + uri: + url: 'http://{{ httpbin_host }}/hidden-basic-auth/user/passwd' + force_basic_auth: true + user: user + password: passwd + +- name: test PUT + uri: + url: 'http://{{ httpbin_host }}/put' + method: PUT + body: 'foo=bar' + +- name: test OPTIONS + uri: + url: 'http://{{ httpbin_host }}/' + method: OPTIONS + register: result + +- name: Assert we got an allow header + assert: + that: + - 'result.allow|default("") == "HEAD, OPTIONS, GET"' + +# Ubuntu12.04 doesn't have python-urllib3, this makes handling required dependencies a pain across all variations +# We'll use this to just skip 12.04 on those tests. We should be sufficiently covered with other OSes and versions +- name: Set fact if running on Ubuntu 12.04 + set_fact: + is_ubuntu_precise: "{{ ansible_distribution == 'Ubuntu' and ansible_distribution_release == 'precise' }}" + +# These tests are just side effects of how the site is hosted. It's not +# specifically a test site. So the tests may break due to the hosting +# changing. Eventually we need to standup a webserver with SNI as part of the +# test run. +- name: Test that SNI succeeds on python versions that have SNI + uri: + url: 'https://{{ sni_host }}/' + return_content: true + when: ansible_python.has_sslcontext + register: result + +- name: Assert SNI verification succeeds on new python + assert: + that: + - result|success + - 'sni_host == result.content' + when: ansible_python.has_sslcontext + +- name: Verify SNI verification fails on old python without urllib3 contrib + uri: + url: 'https://{{ sni_host }}' + ignore_errors: true + when: not ansible_python.has_sslcontext + register: result + +- name: Assert SNI verification fails on old python + assert: + that: + - result|failed + when: not result|skipped + +- name: install OS packages that are needed for SNI on old python + package: + name: "{{ item }}" + with_items: "{{ uri_os_packages[ansible_os_family] | default([]) }}" + when: not ansible_python.has_sslcontext and not is_ubuntu_precise|bool + +- name: install python modules for Older Python SNI verification + pip: + name: "{{ item }}" + with_items: + - ndg-httpsclient + when: not ansible_python.has_sslcontext and not is_ubuntu_precise|bool + +- name: Verify SNI verification succeeds on old python with urllib3 contrib + uri: + url: 'https://{{ sni_host }}' + return_content: true + when: not ansible_python.has_sslcontext and not is_ubuntu_precise|bool + register: result + +- name: Assert SNI verification succeeds on old python + assert: + that: + - result|success + - 'sni_host == result.content' + when: not ansible_python.has_sslcontext and not is_ubuntu_precise|bool + +- name: Uninstall ndg-httpsclient and urllib3 + pip: + name: "{{ item }}" + state: absent + with_items: + - ndg-httpsclient + when: not ansible_python.has_sslcontext and not is_ubuntu_precise|bool + +- name: uninstall OS packages that are needed for SNI on old python + package: + name: "{{ item }}" + state: absent + with_items: "{{ uri_os_packages[ansible_os_family] | default([]) }}" + when: not ansible_python.has_sslcontext and not is_ubuntu_precise|bool + +- name: validate the status_codes are correct + uri: + url: "https://{{ httpbin_host }}/status/202" + status_code: 202 + method: POST + body: foo diff --git a/test/integration/targets/uri/vars/main.yml b/test/integration/targets/uri/vars/main.yml new file mode 100644 index 0000000000..b819276f65 --- /dev/null +++ b/test/integration/targets/uri/vars/main.yml @@ -0,0 +1,14 @@ +uri_os_packages: + RedHat: + - python-pyasn1 + - pyOpenSSL + - python-urllib3 + Debian: + - python-pyasn1 + - python-openssl + - python-urllib3 + +# Needs to be a url to a site that is hosted using SNI. +# Eventually we should make this a test server that we stand up as part of the test run. +#SNI_URI: 'https://sni.velox.ch' +SNI_URI: "https://www.mnot.net/blog/2014/05/09/if_you_can_read_this_youre_sniing" diff --git a/test/integration/targets/yum/meta/main.yml b/test/integration/targets/yum/meta/main.yml new file mode 100644 index 0000000000..07faa21776 --- /dev/null +++ b/test/integration/targets/yum/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/yum/tasks/main.yml b/test/integration/targets/yum/tasks/main.yml new file mode 100644 index 0000000000..41ce38f7c8 --- /dev/null +++ b/test/integration/targets/yum/tasks/main.yml @@ -0,0 +1,23 @@ +# test code for the yum module +# (c) 2014, James Tanner <tanner.jc@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/>. + +# Note: We install the yum package onto Fedora so that this will work on dnf systems +# We want to test that for people who don't want to upgrade their systems. +- include: 'yum.yml' + when: ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora'] and ansible_python.version.major == 2 + diff --git a/test/integration/targets/yum/tasks/yum.yml b/test/integration/targets/yum/tasks/yum.yml new file mode 100644 index 0000000000..f285326f5f --- /dev/null +++ b/test/integration/targets/yum/tasks/yum.yml @@ -0,0 +1,199 @@ +# UNINSTALL 'yum-utils' +# The `yum` module has the smarts to auto-install `yum-utils`. To test, we +# will first uninstall `yum-utils`. +- name: check yum-utils with rpm + shell: rpm -q yum-utils + register: rpm_result + ignore_errors: true + +# Don't uninstall yum-utils with the `yum` module, it would be bad. The `yum` +# module does some `repoquery` magic after removing a package. It fails when you +# remove `yum-utils. +- name: uninstall yum-utils with shell + shell: yum -y remove yum-utils + when: rpm_result|success + +# UNINSTALL +# With 'yum-utils' uninstalled, the first call to 'yum' should install +# yum-utils. +- name: uninstall sos + yum: name=sos state=removed + register: yum_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_result + +- debug: var=yum_result +- debug: var=rpm_result + +- name: verify uninstallation of sos + assert: + that: + - "yum_result.rc == 0" + - "rpm_result.rc == 1" + +# UNINSTALL AGAIN +- name: uninstall sos again + yum: name=sos state=removed + register: yum_result + +- name: verify no change on re-uninstall + assert: + that: + - "not yum_result.changed" + +# INSTALL +- name: install sos + yum: name=sos state=present + register: yum_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_result + +- debug: var=yum_result +- debug: var=rpm_result + +- name: verify installation of sos + assert: + that: + - "yum_result.rc == 0" + - "yum_result.changed" + - "rpm_result.rc == 0" + +- name: verify yum module outputs + assert: + that: + - "'changed' in yum_result" + - "'msg' in yum_result" + - "'rc' in yum_result" + - "'results' in yum_result" + +# INSTALL AGAIN +- name: install sos again + yum: name=sos state=present + register: yum_result + +- name: verify no change on second install + assert: + that: + - "not yum_result.changed" + +# Multiple packages +- name: uninstall sos and sharutils + yum: name=sos,sharutils state=removed + register: yum_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check sharutils with rpm + shell: rpm -q sharutils + failed_when: False + register: rpm_sharutils_result + +- name: verify packages installed + assert: + that: + - "rpm_sos_result.rc != 0" + - "rpm_sharutils_result.rc != 0" + +- name: install sos and sharutils as comma separated + yum: name=sos,sharutils state=present + register: yum_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check sharutils with rpm + shell: rpm -q sharutils + failed_when: False + register: rpm_sharutils_result + +- name: verify packages installed + assert: + that: + - "yum_result.rc == 0" + - "yum_result.changed" + - "rpm_sos_result.rc == 0" + - "rpm_sharutils_result.rc == 0" + +- name: uninstall sos and sharutils + yum: name=sos,sharutils state=removed + register: yum_result + +- name: install sos and sharutils as list + yum: + name: + - sos + - sharutils + state: present + register: yum_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check sharutils with rpm + shell: rpm -q sharutils + failed_when: False + register: rpm_sharutils_result + +- name: verify packages installed + assert: + that: + - "yum_result.rc == 0" + - "yum_result.changed" + - "rpm_sos_result.rc == 0" + - "rpm_sharutils_result.rc == 0" + +- name: uninstall sos and sharutils + yum: name=sos,sharutils state=removed + register: yum_result + +- name: install sos and sharutils as comma separated with spaces + yum: + name: "sos, sharutils" + state: present + register: yum_result + +- name: check sos with rpm + shell: rpm -q sos + failed_when: False + register: rpm_sos_result + +- name: check sos with rpm + shell: rpm -q sharutils + failed_when: False + register: rpm_sharutils_result + +- name: verify packages installed + assert: + that: + - "yum_result.rc == 0" + - "yum_result.changed" + - "rpm_sos_result.rc == 0" + - "rpm_sharutils_result.rc == 0" + +- name: uninstall sos and sharutils + yum: name=sos,sharutils state=removed + +- name: install non-existent rpm + yum: name="{{ item }}" + with_items: + - does-not-exist + register: non_existent_rpm + ignore_errors: True + +- name: check non-existent rpm install failed + assert: + that: + - non_existent_rpm|failed diff --git a/test/integration/targets/zypper/files/empty.spec b/test/integration/targets/zypper/files/empty.spec new file mode 100644 index 0000000000..044ea3a548 --- /dev/null +++ b/test/integration/targets/zypper/files/empty.spec @@ -0,0 +1,12 @@ +Summary: Empty RPM +Name: empty +Version: 1 +Release: 0 +License: GPLv3 +Group: Applications/System +BuildArch: noarch + +%description +Empty RPM + +%files diff --git a/test/integration/targets/zypper/meta/main.yml b/test/integration/targets/zypper/meta/main.yml new file mode 100644 index 0000000000..07faa21776 --- /dev/null +++ b/test/integration/targets/zypper/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/zypper/tasks/main.yml b/test/integration/targets/zypper/tasks/main.yml new file mode 100644 index 0000000000..f2ad3cdce0 --- /dev/null +++ b/test/integration/targets/zypper/tasks/main.yml @@ -0,0 +1,26 @@ +# test code for the zyppe module +# +# (c) 2015, Guido Günther <agx@sigxcpu.org> +# +# heavily based on the yum tests which are +# +# (c) 2014, James Tanner <tanner.jc@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/>. + +- include: 'zypper.yml' + when: ansible_os_family == 'Suse' + diff --git a/test/integration/targets/zypper/tasks/zypper.yml b/test/integration/targets/zypper/tasks/zypper.yml new file mode 100644 index 0000000000..4fe05f0125 --- /dev/null +++ b/test/integration/targets/zypper/tasks/zypper.yml @@ -0,0 +1,336 @@ +# UNINSTALL +- name: uninstall hello + zypper: name=hello state=removed + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: False + register: rpm_result + +- debug: var=zypper_result +- debug: var=rpm_result + +- name: verify uninstallation of hello + assert: + that: + - "zypper_result.rc == 0" + - "rpm_result.rc == 1" + +# UNINSTALL AGAIN +- name: uninstall hello again + zypper: name=hello state=removed + register: zypper_result + +- name: verify no change on re-uninstall + assert: + that: + - "not zypper_result.changed" + +# INSTALL +- name: install hello + zypper: name=hello state=present + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: False + register: rpm_result + +- debug: var=zypper_result +- debug: var=rpm_result + +- name: verify installation of hello + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_result.rc == 0" + +# INSTALL AGAIN +- name: install hello again + zypper: name=hello state=present + register: zypper_result + +- name: verify no change on second install + assert: + that: + - "not zypper_result.changed" + +# Multiple packages +- name: uninstall hello and metamail + zypper: + name: + - hello + - metamail + state: removed + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: False + register: rpm_hello_result + +- name: check metamail with rpm + shell: rpm -q metamail + failed_when: False + register: rpm_metamail_result + +- name: verify packages uninstalled + assert: + that: + - "rpm_hello_result.rc != 0" + - "rpm_metamail_result.rc != 0" + +- name: install hello and metamail + zypper: + name: + - hello + - metamail + state: present + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: False + register: rpm_hello_result + +- name: check metamail with rpm + shell: rpm -q metamail + failed_when: False + register: rpm_metamail_result + +- name: verify packages installed + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_hello_result.rc == 0" + - "rpm_metamail_result.rc == 0" + +- name: uninstall hello and metamail + zypper: + name: + - hello + - metamail + state: removed + +# INSTALL nonexistent package +- name: install hello from url + zypper: name=doesnotexist state=present + register: zypper_result + ignore_errors: yes + +- name: verify package installation failed + assert: + that: + - "zypper_result.rc == 104" + - "zypper_result.msg.startswith('No provider of')" + +# INSTALL broken local package +- name: create directory + file: + path: "{{output_dir | expanduser}}/zypper1" + state: directory + +- name: fake rpm package + file: + path: "{{output_dir | expanduser}}/zypper1/broken.rpm" + state: touch + +- name: install broken rpm + zypper: + name="{{output_dir | expanduser}}/zypper1/broken.rpm" + state=present + register: zypper_result + ignore_errors: yes + +- debug: var=zypper_result + +- name: verify we failed installation of broken rpm + assert: + that: + - "zypper_result.rc == 3" + - "'Problem reading the RPM header' in zypper_result.stdout" + +# Build and install an empty rpm +- name: uninstall empty + zypper: + name: empty + state: removed + +- name: clean zypper RPM cache + file: + name: /var/cache/zypper/RPMS + state: absent + +- name: create directory + file: + path: "{{output_dir | expanduser}}/zypper2" + state: directory + +- name: copy spec file + copy: + src: empty.spec + dest: "{{ output_dir | expanduser }}/zypper2/empty.spec" + +- name: build rpm + command: | + rpmbuild -bb \ + --define "_topdir {{output_dir | expanduser }}/zypper2/rpm-build" + --define "_builddir %{_topdir}" \ + --define "_rpmdir %{_topdir}" \ + --define "_srcrpmdir %{_topdir}" \ + --define "_specdir {{output_dir | expanduser}}/zypper2" \ + --define "_sourcedir %{_topdir}" \ + {{ output_dir }}/zypper2/empty.spec + register: rpm_build_result + +- name: install empty rpm + zypper: + name: "{{ output_dir | expanduser }}/zypper2/rpm-build/noarch/empty-1-0.noarch.rpm" + register: zypper_result + +- name: check empty with rpm + shell: rpm -q empty + failed_when: False + register: rpm_result + +- name: verify installation of empty + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_result.rc == 0" + +- name: uninstall empty + zypper: + name: empty + state: removed + +# test simultaneous remove and install using +- prefixes + +- name: install hello to prep next task + zypper: name=hello, state=present + +- name: remove metamail to prep next task + zypper: name=hello, state=absent + +- name: install and remove in the same run, with +- prefix + zypper: + name: + - -hello + - +metamail + state: present + register: zypper_res1 + +- name: install and remove again, leave out plus + zypper: name={{item}} state=present + with_items: + - metamail + - -hello + register: zypper_res1a + +- name: in and rm swapped + zypper: name={{item}} state=present + with_items: + - -metamail + - hello + register: zypper_res1b + +- name: install metamail + zypper: name=metamail state=absent + register: zypper_res2 + +- name: remove hello + zypper: name=hello state=present + register: zypper_res3 + +- name: verify simultaneous install/remove worked + assert: + that: + - zypper_res1|success + - zypper_res1|changed + - not zypper_res1a|changed + - zypper_res1b|changed + - not zypper_res2|changed + - not zypper_res3|changed + + +- name: install and remove with state=absent + zypper: name={{item}} state=absent + with_items: + - metamail + - +hello + register: zypper_res + ignore_errors: yes + +- name: verify simultaneous install/remove failed with absent + assert: + that: + - zypper_res|failed + - zypper_res.results[0].msg == "Can not combine '+' prefix with state=remove/absent." + +- name: try rm patch + zypper: name=openSUSE-2016-128 type=patch state=absent + ignore_errors: yes + register: zypper_patch +- assert: + that: + - zypper_patch|failed + - zypper_patch.msg.startswith('Can not remove patches.') + +- name: try rm URL + zypper: name=http://download.opensuse.org/repositories/openSUSE:/Leap:/42.1/standard/x86_64/hello-2.9-6.2.x86_64.rpm state=absent + ignore_errors: yes + register: zypper_rm +- assert: + that: + - zypper_rm|failed + - zypper_rm.msg.startswith('Can not remove via URL.') + +# use of version specific (42.1) data in the following +- block: + # test for #1627 + - name: in existing patch + zypper: name=openSUSE-2016-128 type=patch state=present + - name: in existing patch again + zypper: name=openSUSE-2016-128 type=patch state=present + register: zypper_patch + - assert: + that: not zypper_patch.changed + + - name: in non-existing patch + zypper: name=openSUSE-1800-1 type=patch state=present + ignore_errors: yes + register: zypper_patch + - assert: + that: zypper_patch|failed + + - name: remove pattern update_test + zypper: name=update_test type=pattern state=absent + - name: install pattern update_test + zypper: name=update_test type=pattern state=present + register: zypper_install_pattern1 + - name: install pattern update_test again + zypper: name=update_test type=pattern state=present + register: zypper_install_pattern2 + - assert: + that: + - zypper_install_pattern1|changed + - not zypper_install_pattern2|changed + + - name: remove hello + zypper: name=hello state=absent + - name: install via URL + zypper: state=present name=http://download.opensuse.org/repositories/openSUSE:/Leap:/42.1/standard/x86_64/hello-2.9-6.2.x86_64.rpm + register: zypperin1 + - name: test install + zypper: name=hello state=present + register: zypperin2 + - assert: + that: + - zypperin1|success + - zypperin1|changed + - not zypperin2|changed + when: ansible_distribution == 'openSUSE Leap' and ansible_distribution_version == '42.1' diff --git a/test/integration/targets/zypper_repository/meta/main.yml b/test/integration/targets/zypper_repository/meta/main.yml new file mode 100644 index 0000000000..07faa21776 --- /dev/null +++ b/test/integration/targets/zypper_repository/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_tests diff --git a/test/integration/targets/zypper_repository/tasks/main.yml b/test/integration/targets/zypper_repository/tasks/main.yml new file mode 100644 index 0000000000..b2c4f754b9 --- /dev/null +++ b/test/integration/targets/zypper_repository/tasks/main.yml @@ -0,0 +1,22 @@ +# test code for the zypper repository module +# +# (c) 2016, Guido Günther <agx@sigxcpu.org> + +# 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/>. + +- include: 'zypper_repository.yml' + when: ansible_os_family == 'Suse' + diff --git a/test/integration/targets/zypper_repository/tasks/zypper_repository.yml b/test/integration/targets/zypper_repository/tasks/zypper_repository.yml new file mode 100644 index 0000000000..027a11ce55 --- /dev/null +++ b/test/integration/targets/zypper_repository/tasks/zypper_repository.yml @@ -0,0 +1,143 @@ +--- + +- name: ensure zypper ref works + command: zypper -n ref + +- name: Delete + zypper_repository: + name: test + state: absent + register: zypper_result + +- name: Add repo + zypper_repository: + name: test + state: present + repo: http://dl.google.com/linux/chrome/rpm/stable/x86_64 + register: zypper_result + +- debug: var=zypper_result + +- name: verify repo addition + assert: + that: + - "zypper_result.changed" + +- name: Add repo again + zypper_repository: + name: test + state: present + repo: http://dl.google.com/linux/chrome/rpm/stable/x86_64 + register: zypper_result + +- name: verify no change on second install + assert: + that: + - "not zypper_result.changed" + +- name: Change repo URL + zypper_repository: + name: test + state: present + repo: http://download.videolan.org/pub/vlc/SuSE/Leap_42.1/ + register: zypper_result + +- name: Verify change on URL only change + assert: + that: + - "zypper_result.changed" + +- name: Remove repo by name (also to not mess up later tasks) + zypper_repository: + name: test + state: absent + +- name: use refresh option + zypper_repository: + name: testrefresh + refresh: no + state: present + repo: http://download.opensuse.org/distribution/leap/42.1/repo/oss/ + +- name: check refreshoption + command: zypper -x lr testrefresh + register: zypper_result + +- assert: + that: + - '"autorefresh=\"0\"" in zypper_result.stdout' + +- name: set repo priority + zypper_repository: + name: testprio + priority: 55 + state: present + repo: http://download.opensuse.org/distribution/leap/42.1/repo/oss/ + +- name: check refreshoption + command: zypper -x lr testprio + register: zypper_result + +- assert: + that: + - '"priority=\"55\"" in zypper_result.stdout' + +- name: add two repos with same url + zypper_repository: + name: "{{item}}" + state: present + repo: http://download.opensuse.org/distribution/leap/42.1/repo/oss/ + with_items: + - oss1 + - oss2 + +- name: check repo is updated by url + command: zypper lr oss1 + register: zypper_result1 + ignore_errors: yes + +- name: check repo is updated by url + command: zypper lr oss2 + register: zypper_result2 + +- assert: + that: + - "zypper_result1.rc == 6" + - "'not found' in zypper_result1.stderr" + - "zypper_result2.rc == 0" + - "'http://download.opensuse.org/distribution/leap/42.1/repo/oss/' in zypper_result2.stdout" + + +- name: reset oss repo (to not break zypper later) + zypper_repository: + name: OSS + state: present + repo: http://download.opensuse.org/distribution/leap/42.1/repo/oss/ + priority: 99 + refresh: yes + +- name: add two repos with same name + zypper_repository: + name: samename + state: present + repo: "{{ item }}" + with_items: + - http://download.opensuse.org/repositories/science/openSUSE_Leap_42.1/ + - http://download.opensuse.org/repositories/devel:/languages:/python/openSUSE_Leap_42.1/ + +- name: check repo is updated by name + command: zypper lr samename + register: zypper_result + +- assert: + that: + - "'/science/' not in zypper_result.stdout" + - "'/devel:/languages:/python/' in zypper_result.stdout" + +- name: remove last added repos (by URL to test that) + zypper_repository: + repo: http://download.opensuse.org/repositories/devel:/languages:/python/openSUSE_Leap_42.1/ + state: absent + +- name: ensure zypper ref still works + command: zypper -n ref |