summaryrefslogtreecommitdiff
path: root/test/integration/targets
diff options
context:
space:
mode:
Diffstat (limited to 'test/integration/targets')
-rw-r--r--test/integration/targets/add_host/tasks/main.yml39
-rw-r--r--test/integration/targets/apache2_module/meta/main.yml3
-rw-r--r--test/integration/targets/apache2_module/tasks/actualtest.yml61
-rw-r--r--test/integration/targets/apache2_module/tasks/main.yml6
-rw-r--r--test/integration/targets/apt/meta/main.yml2
-rw-r--r--test/integration/targets/apt/tasks/apt-builddep.yml55
-rw-r--r--test/integration/targets/apt/tasks/apt.yml150
-rw-r--r--test/integration/targets/apt/tasks/main.yml22
-rw-r--r--test/integration/targets/apt_repository/meta/main.yml2
-rw-r--r--test/integration/targets/apt_repository/tasks/apt.yml205
-rw-r--r--test/integration/targets/apt_repository/tasks/cleanup.yml18
-rw-r--r--test/integration/targets/apt_repository/tasks/main.yml21
-rw-r--r--test/integration/targets/assemble/files/fragment11
-rw-r--r--test/integration/targets/assemble/files/fragment21
-rw-r--r--test/integration/targets/assemble/files/fragment31
-rw-r--r--test/integration/targets/assemble/files/fragment41
-rw-r--r--test/integration/targets/assemble/files/fragment51
-rw-r--r--test/integration/targets/assemble/meta/main.yml20
-rw-r--r--test/integration/targets/assemble/tasks/main.yml93
-rw-r--r--test/integration/targets/async/library/async_test.py39
-rw-r--r--test/integration/targets/async/meta/main.yml3
-rw-r--r--test/integration/targets/async/tasks/main.yml154
-rw-r--r--test/integration/targets/authorized_key/defaults/main.yml15
-rw-r--r--test/integration/targets/authorized_key/meta/main.yml2
-rw-r--r--test/integration/targets/authorized_key/tasks/main.yml272
-rw-r--r--test/integration/targets/become/files/baz.txt1
-rw-r--r--test/integration/targets/become/tasks/main.yml83
-rw-r--r--test/integration/targets/become/templates/bar.j21
-rw-r--r--test/integration/targets/become/vars/default.yml1
-rw-r--r--test/integration/targets/binary/files/b64_latin11
-rw-r--r--test/integration/targets/binary/files/b64_utf81
-rw-r--r--test/integration/targets/binary/files/from_playbook1
-rw-r--r--test/integration/targets/binary/meta/main.yml3
-rw-r--r--test/integration/targets/binary/tasks/main.yml131
-rw-r--r--test/integration/targets/binary/templates/b64_latin1_template.j21
-rw-r--r--test/integration/targets/binary/templates/b64_utf8_template.j21
-rw-r--r--test/integration/targets/binary/templates/from_playbook_template.j21
-rw-r--r--test/integration/targets/binary/vars/main.yml3
-rw-r--r--test/integration/targets/changed_when/meta/main.yml3
-rw-r--r--test/integration/targets/changed_when/tasks/main.yml42
-rwxr-xr-xtest/integration/targets/command_shell/files/create_afile.sh3
-rwxr-xr-xtest/integration/targets/command_shell/files/remove_afile.sh3
-rwxr-xr-xtest/integration/targets/command_shell/files/test.sh3
-rw-r--r--test/integration/targets/command_shell/meta/main.yml3
-rw-r--r--test/integration/targets/command_shell/tasks/main.yml219
-rw-r--r--test/integration/targets/conditionals/tasks/main.yml309
-rw-r--r--test/integration/targets/conditionals/vars/main.yml17
-rw-r--r--test/integration/targets/copy/files/foo.txt1
-rw-r--r--test/integration/targets/copy/files/subdir/bar.txt1
-rw-r--r--test/integration/targets/copy/files/subdir/subdir2/baz.txt1
-rw-r--r--test/integration/targets/copy/files/subdir/subdir2/subdir3/subdir4/qux.txt1
-rw-r--r--test/integration/targets/copy/meta/main.yml3
-rw-r--r--test/integration/targets/copy/tasks/main.yml260
-rw-r--r--test/integration/targets/docker/files/devdockerCA.crt23
-rw-r--r--test/integration/targets/docker/files/devdockerCA.key27
-rw-r--r--test/integration/targets/docker/files/devdockerCA.srl1
-rw-r--r--test/integration/targets/docker/files/docker-registry.htpasswd1
-rw-r--r--test/integration/targets/docker/files/dockertest.ansible.com.crt21
-rw-r--r--test/integration/targets/docker/files/dockertest.ansible.com.csr17
-rw-r--r--test/integration/targets/docker/files/dockertest.ansible.com.key27
-rw-r--r--test/integration/targets/docker/files/nginx-docker-registry.conf40
-rw-r--r--test/integration/targets/docker/meta/main.yml20
-rw-r--r--test/integration/targets/docker/tasks/docker-setup-debian.yml6
-rw-r--r--test/integration/targets/docker/tasks/docker-setup-rht.yml17
-rw-r--r--test/integration/targets/docker/tasks/docker-tests.yml58
-rw-r--r--test/integration/targets/docker/tasks/main.yml23
-rw-r--r--test/integration/targets/docker/tasks/registry-tests.yml189
-rw-r--r--test/integration/targets/embedded_module/library/test_integration_module3
-rw-r--r--test/integration/targets/embedded_module/tasks/main.yml9
-rw-r--r--test/integration/targets/facts_d/meta/main.yml3
-rw-r--r--test/integration/targets/facts_d/tasks/main.yml39
-rw-r--r--test/integration/targets/failed_when/tasks/main.yml69
-rw-r--r--test/integration/targets/fetch/meta/main.yml3
-rw-r--r--test/integration/targets/fetch/tasks/main.yml97
-rw-r--r--test/integration/targets/file/files/foo.txt1
-rw-r--r--test/integration/targets/file/files/foobar/directory/fileC0
-rw-r--r--test/integration/targets/file/files/foobar/directory/fileD0
-rw-r--r--test/integration/targets/file/files/foobar/fileA0
-rw-r--r--test/integration/targets/file/files/foobar/fileB0
-rw-r--r--test/integration/targets/file/meta/main.yml3
-rw-r--r--test/integration/targets/file/tasks/main.yml564
-rw-r--r--test/integration/targets/file/tasks/selinux_tests.yml30
-rw-r--r--test/integration/targets/filters/files/9851.txt3
-rw-r--r--test/integration/targets/filters/files/foo.txt61
-rw-r--r--test/integration/targets/filters/meta/main.yml3
-rw-r--r--test/integration/targets/filters/tasks/main.yml112
-rw-r--r--test/integration/targets/filters/templates/foo.j255
-rw-r--r--test/integration/targets/filters/templates/py26json.j22
-rw-r--r--test/integration/targets/filters/vars/main.yml18
-rw-r--r--test/integration/targets/gem/meta/main.yml3
-rw-r--r--test/integration/targets/gem/tasks/main.yml40
-rw-r--r--test/integration/targets/get_url/meta/main.yml4
-rw-r--r--test/integration/targets/get_url/tasks/main.yml213
-rw-r--r--test/integration/targets/git/meta/main.yml3
-rw-r--r--test/integration/targets/git/tasks/main.yml661
-rw-r--r--test/integration/targets/hg/meta/main.yml3
-rw-r--r--test/integration/targets/hg/tasks/main.yml78
-rw-r--r--test/integration/targets/ignore_errors/meta/main.yml3
-rw-r--r--test/integration/targets/ignore_errors/tasks/main.yml23
-rw-r--r--test/integration/targets/include_vars/defaults/main.yml3
-rw-r--r--test/integration/targets/include_vars/tasks/main.yml86
-rw-r--r--test/integration/targets/include_vars/vars/all/all.yml3
-rw-r--r--test/integration/targets/include_vars/vars/environments/development/all.yml3
-rw-r--r--test/integration/targets/include_vars/vars/environments/development/services/webapp.yml4
-rw-r--r--test/integration/targets/include_vars/vars/services/webapp.yml4
-rw-r--r--test/integration/targets/iterators/meta/main.yml3
-rw-r--r--test/integration/targets/iterators/tasks/main.yml257
-rw-r--r--test/integration/targets/iterators/vars/main.yml43
-rw-r--r--test/integration/targets/lineinfile/files/test.txt5
-rw-r--r--test/integration/targets/lineinfile/files/testempty.txt0
-rw-r--r--test/integration/targets/lineinfile/files/testnoeof.txt2
-rw-r--r--test/integration/targets/lineinfile/meta/main.yml20
-rw-r--r--test/integration/targets/lineinfile/tasks/main.yml376
-rw-r--r--test/integration/targets/lookups/meta/main.yml4
-rw-r--r--test/integration/targets/lookups/tasks/main.yml270
-rw-r--r--test/integration/targets/lookups/vars/main.yml9
-rw-r--r--test/integration/targets/loops/tasks/main.yml20
-rw-r--r--test/integration/targets/mount/tasks/main.yml113
-rw-r--r--test/integration/targets/mysql_db/defaults/main.yml8
-rw-r--r--test/integration/targets/mysql_db/meta/main.yml2
-rw-r--r--test/integration/targets/mysql_db/tasks/main.yml198
-rw-r--r--test/integration/targets/mysql_db/tasks/state_dump_import.yml75
-rw-r--r--test/integration/targets/mysql_user/defaults/main.yml16
-rw-r--r--test/integration/targets/mysql_user/meta/main.yml2
-rw-r--r--test/integration/targets/mysql_user/tasks/assert_no_user.yml25
-rw-r--r--test/integration/targets/mysql_user/tasks/assert_user.yml34
-rw-r--r--test/integration/targets/mysql_user/tasks/create_user.yml25
-rw-r--r--test/integration/targets/mysql_user/tasks/main.yml180
-rw-r--r--test/integration/targets/mysql_user/tasks/remove_user.yml43
-rw-r--r--test/integration/targets/mysql_user/tasks/test_privs.yml88
-rw-r--r--test/integration/targets/mysql_user/tasks/user_password_update_test.yml117
-rw-r--r--test/integration/targets/mysql_variables/defaults/main.yml5
-rw-r--r--test/integration/targets/mysql_variables/meta/main.yml2
-rw-r--r--test/integration/targets/mysql_variables/tasks/assert_fail_msg.yml25
-rw-r--r--test/integration/targets/mysql_variables/tasks/assert_var.yml36
-rw-r--r--test/integration/targets/mysql_variables/tasks/assert_var_output.yml38
-rw-r--r--test/integration/targets/mysql_variables/tasks/main.yml202
-rw-r--r--test/integration/targets/ping/tasks/main.yml37
-rw-r--r--test/integration/targets/pip/meta/main.yml3
-rw-r--r--test/integration/targets/pip/tasks/main.yml5
-rw-r--r--test/integration/targets/pip/tasks/pip.yml164
-rw-r--r--test/integration/targets/postgresql/defaults/main.yml8
-rw-r--r--test/integration/targets/postgresql/meta/main.yml3
-rw-r--r--test/integration/targets/postgresql/tasks/main.yml882
-rw-r--r--test/integration/targets/prepare_http_tests/defaults/main.yml4
-rw-r--r--test/integration/targets/prepare_http_tests/tasks/main.yml41
-rw-r--r--test/integration/targets/prepare_http_tests/vars/httptester.yml5
-rw-r--r--test/integration/targets/prepare_tests/tasks/main.yml0
-rwxr-xr-xtest/integration/targets/script/files/create_afile.sh3
-rwxr-xr-xtest/integration/targets/script/files/remove_afile.sh3
-rwxr-xr-xtest/integration/targets/script/files/test.sh3
-rw-r--r--test/integration/targets/script/meta/main.yml3
-rw-r--r--test/integration/targets/script/tasks/main.yml71
-rw-r--r--test/integration/targets/service/files/ansible-broken.upstart10
-rw-r--r--test/integration/targets/service/files/ansible.systemd10
-rwxr-xr-xtest/integration/targets/service/files/ansible.sysv134
-rw-r--r--test/integration/targets/service/files/ansible.upstart9
-rwxr-xr-xtest/integration/targets/service/files/ansible_test_service67
-rw-r--r--test/integration/targets/service/meta/main.yml20
-rw-r--r--test/integration/targets/service/tasks/main.yml155
-rw-r--r--test/integration/targets/service/tasks/systemd_cleanup.yml26
-rw-r--r--test/integration/targets/service/tasks/systemd_setup.yml17
-rw-r--r--test/integration/targets/service/tasks/sysv_cleanup.yml10
-rw-r--r--test/integration/targets/service/tasks/sysv_setup.yml12
-rw-r--r--test/integration/targets/service/tasks/upstart_cleanup.yml15
-rw-r--r--test/integration/targets/service/tasks/upstart_setup.yml19
-rw-r--r--test/integration/targets/service/templates/main.yml0
-rw-r--r--test/integration/targets/setup_mysql_db/defaults/main.yml6
-rw-r--r--test/integration/targets/setup_mysql_db/tasks/main.yml45
-rw-r--r--test/integration/targets/setup_mysql_db/vars/Debian.yml6
-rw-r--r--test/integration/targets/setup_mysql_db/vars/Fedora.yml6
-rw-r--r--test/integration/targets/setup_mysql_db/vars/RedHat-7.yml6
-rw-r--r--test/integration/targets/setup_mysql_db/vars/RedHat.yml6
-rw-r--r--test/integration/targets/setup_mysql_db/vars/Suse.yml6
-rw-r--r--test/integration/targets/setup_postgresql_db/defaults/main.yml5
-rw-r--r--test/integration/targets/setup_postgresql_db/files/pg_hba.conf10
-rw-r--r--test/integration/targets/setup_postgresql_db/tasks/main.yml83
-rw-r--r--test/integration/targets/setup_postgresql_db/vars/Debian-8.yml10
-rw-r--r--test/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml11
-rw-r--r--test/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml10
-rw-r--r--test/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml10
-rw-r--r--test/integration/targets/setup_postgresql_db/vars/default.yml8
-rw-r--r--test/integration/targets/special_vars/meta/main.yml3
-rw-r--r--test/integration/targets/special_vars/tasks/main.yml37
-rw-r--r--test/integration/targets/special_vars/templates/foo.j27
-rw-r--r--test/integration/targets/special_vars/vars/main.yml0
-rw-r--r--test/integration/targets/stat/files/foo.txt1
-rw-r--r--test/integration/targets/stat/meta/main.yml3
-rw-r--r--test/integration/targets/stat/tasks/main.yml170
-rw-r--r--test/integration/targets/subversion/meta/main.yml3
-rw-r--r--test/integration/targets/subversion/tasks/main.yml119
-rw-r--r--test/integration/targets/synchronize/files/bar.txt1
-rw-r--r--test/integration/targets/synchronize/files/foo.txt1
-rw-r--r--test/integration/targets/synchronize/meta/main.yml3
-rw-r--r--test/integration/targets/synchronize/tasks/main.yml190
-rw-r--r--test/integration/targets/task_ordering/tasks/main.yml13
-rw-r--r--test/integration/targets/task_ordering/tasks/taskorder-include.yml10
-rw-r--r--test/integration/targets/template/files/foo-py26.txt9
-rw-r--r--test/integration/targets/template/files/foo.txt9
-rw-r--r--test/integration/targets/template/meta/main.yml3
-rw-r--r--test/integration/targets/template/tasks/main.yml227
-rw-r--r--test/integration/targets/template/templates/foo.j23
-rw-r--r--test/integration/targets/template/templates/short.j21
-rw-r--r--test/integration/targets/template/vars/main.yml16
-rw-r--r--test/integration/targets/unarchive/files/foo.txt1
-rw-r--r--test/integration/targets/unarchive/files/test-unarchive-nonascii-くらとみ.tar.gzbin0 -> 4947 bytes
-rw-r--r--test/integration/targets/unarchive/meta/main.yml3
-rw-r--r--test/integration/targets/unarchive/tasks/main.yml427
-rw-r--r--test/integration/targets/until/tasks/main.yml32
-rw-r--r--test/integration/targets/uri/files/README9
-rw-r--r--test/integration/targets/uri/files/fail0.json1
-rw-r--r--test/integration/targets/uri/files/fail1.json1
-rw-r--r--test/integration/targets/uri/files/fail10.json1
-rw-r--r--test/integration/targets/uri/files/fail11.json1
-rw-r--r--test/integration/targets/uri/files/fail12.json1
-rw-r--r--test/integration/targets/uri/files/fail13.json1
-rw-r--r--test/integration/targets/uri/files/fail14.json1
-rw-r--r--test/integration/targets/uri/files/fail15.json1
-rw-r--r--test/integration/targets/uri/files/fail16.json1
-rw-r--r--test/integration/targets/uri/files/fail17.json1
-rw-r--r--test/integration/targets/uri/files/fail18.json1
-rw-r--r--test/integration/targets/uri/files/fail19.json1
-rw-r--r--test/integration/targets/uri/files/fail2.json1
-rw-r--r--test/integration/targets/uri/files/fail20.json1
-rw-r--r--test/integration/targets/uri/files/fail21.json1
-rw-r--r--test/integration/targets/uri/files/fail22.json1
-rw-r--r--test/integration/targets/uri/files/fail23.json1
-rw-r--r--test/integration/targets/uri/files/fail24.json1
-rw-r--r--test/integration/targets/uri/files/fail25.json1
-rw-r--r--test/integration/targets/uri/files/fail26.json2
-rw-r--r--test/integration/targets/uri/files/fail27.json2
-rw-r--r--test/integration/targets/uri/files/fail28.json1
-rw-r--r--test/integration/targets/uri/files/fail29.json1
-rw-r--r--test/integration/targets/uri/files/fail3.json1
-rw-r--r--test/integration/targets/uri/files/fail30.json1
-rw-r--r--test/integration/targets/uri/files/fail4.json1
-rw-r--r--test/integration/targets/uri/files/fail5.json1
-rw-r--r--test/integration/targets/uri/files/fail6.json1
-rw-r--r--test/integration/targets/uri/files/fail7.json1
-rw-r--r--test/integration/targets/uri/files/fail8.json1
-rw-r--r--test/integration/targets/uri/files/fail9.json1
-rw-r--r--test/integration/targets/uri/files/pass0.json58
-rw-r--r--test/integration/targets/uri/files/pass1.json1
-rw-r--r--test/integration/targets/uri/files/pass2.json6
-rw-r--r--test/integration/targets/uri/files/pass3.json1
-rw-r--r--test/integration/targets/uri/files/pass4.json1
-rw-r--r--test/integration/targets/uri/files/testserver.py7
-rw-r--r--test/integration/targets/uri/meta/main.yml3
-rw-r--r--test/integration/targets/uri/tasks/main.yml279
-rw-r--r--test/integration/targets/uri/vars/main.yml14
-rw-r--r--test/integration/targets/yum/meta/main.yml2
-rw-r--r--test/integration/targets/yum/tasks/main.yml23
-rw-r--r--test/integration/targets/yum/tasks/yum.yml199
-rw-r--r--test/integration/targets/zypper/files/empty.spec12
-rw-r--r--test/integration/targets/zypper/meta/main.yml2
-rw-r--r--test/integration/targets/zypper/tasks/main.yml26
-rw-r--r--test/integration/targets/zypper/tasks/zypper.yml336
-rw-r--r--test/integration/targets/zypper_repository/meta/main.yml2
-rw-r--r--test/integration/targets/zypper_repository/tasks/main.yml22
-rw-r--r--test/integration/targets/zypper_repository/tasks/zypper_repository.yml143
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
new file mode 100644
index 0000000000..4882b9207a
--- /dev/null
+++ b/test/integration/targets/unarchive/files/test-unarchive-nonascii-くらとみ.tar.gz
Binary files differ
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": "&#34; \u0022 %22 0x22 034 &#x22;",
+ "\/\\\"\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