From c91ceddfac82bcef4b2edc0ee6bfca2271ef892c Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 13 Feb 2014 18:28:29 -0500 Subject: [PATCH] Add the start of an integration test structure using Ansible playbooks, also added an assert action plugin to make writing those easier. --- CHANGELOG.md | 4 +- lib/ansible/runner/action_plugins/assert.py | 56 +++++++++++++ library/utilities/assert | 39 +++++++++ test/README.md | 7 ++ tests_new/README.md | 22 +++++ tests_new/integration/README.md | 13 +++ tests_new/integration/all.yml | 6 ++ tests_new/integration/amazon.yml | 4 + tests_new/integration/destructive.yml | 4 + tests_new/integration/inventory | 2 + tests_new/integration/non_destructive.yml | 5 ++ tests_new/integration/rackspace.yml | 4 + .../roles/prepare_tests/tasks/main.yml | 26 ++++++ .../integration/roles/test_copy/files/foo.txt | 1 + tests_new/integration/roles/test_copy/foo.txt | 1 + .../integration/roles/test_copy/meta/main.yml | 3 + .../roles/test_copy/tasks/main.yml | 84 +++++++++++++++++++ tests_new/integration/test.sh | 1 + tests_new/integration/test_setup.yml | 5 ++ tests_new/units/README.md | 5 ++ 20 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 lib/ansible/runner/action_plugins/assert.py create mode 100644 library/utilities/assert create mode 100644 test/README.md create mode 100644 tests_new/README.md create mode 100644 tests_new/integration/README.md create mode 100644 tests_new/integration/all.yml create mode 100644 tests_new/integration/amazon.yml create mode 100644 tests_new/integration/destructive.yml create mode 100644 tests_new/integration/inventory create mode 100644 tests_new/integration/non_destructive.yml create mode 100644 tests_new/integration/rackspace.yml create mode 100644 tests_new/integration/roles/prepare_tests/tasks/main.yml create mode 100644 tests_new/integration/roles/test_copy/files/foo.txt create mode 100644 tests_new/integration/roles/test_copy/foo.txt create mode 100644 tests_new/integration/roles/test_copy/meta/main.yml create mode 100644 tests_new/integration/roles/test_copy/tasks/main.yml create mode 100644 tests_new/integration/test.sh create mode 100644 tests_new/integration/test_setup.yml create mode 100644 tests_new/units/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 332224d742..a118497ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Major features/changes: * play_hosts is a new magic variable to provide a list of hosts in scope for the current play. * ec2 module now accepts 'exact_count' and 'count_tag' as a way to enforce a running number of nodes by tags. * all ec2 modules that work with Eucalyptus also now support a 'validate_certs' option, which can be set to 'off' for installations using self-signed certs. - +* Start of new integration test infrastructure (WIP, more details TBD) New modules: @@ -31,7 +31,7 @@ New modules: * cloud: rax_queue * messaging: rabbitmq_policy * system: at - +* utilities: assert Misc: diff --git a/lib/ansible/runner/action_plugins/assert.py b/lib/ansible/runner/action_plugins/assert.py new file mode 100644 index 0000000000..8091326046 --- /dev/null +++ b/lib/ansible/runner/action_plugins/assert.py @@ -0,0 +1,56 @@ +# Copyright 2012, Dag Wieers +# +# 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 . + +import ansible + +from ansible import utils +from ansible.runner.return_data import ReturnData + +class ActionModule(object): + ''' Fail with custom message ''' + + TRANSFERS_FILES = False + + def __init__(self, runner): + self.runner = runner + + def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs): + + # note: the fail module does not need to pay attention to check mode + # it always runs. + + args = {} + if complex_args: + args.update(complex_args) + args.update(utils.parse_kv(module_args)) + + msg = '' + + if 'msg' in args: + msg = args['msg'] + + if not 'that' in args: + raise errors.AnsibleError('conditional required in "that" string') + + result = utils.check_conditional(args['that'], self.runner.basedir, inject, fail_on_undefined=True) + + if not result: + result = dict(failed=True, assertion=args['that'], evaluated_to=result) + else: + result = dict(msg='ok', assertion=args['that'], evaluated_to=result) + + return ReturnData(conn=conn, result=result) diff --git a/library/utilities/assert b/library/utilities/assert new file mode 100644 index 0000000000..2b25298945 --- /dev/null +++ b/library/utilities/assert @@ -0,0 +1,39 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright 2012 Dag Wieers +# +# 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 . + +DOCUMENTATION = ''' +--- +module: assert +short_description: Fail with custom message +description: + - This module asserts that a given expression is true and can be a simpler alternative to the 'fail' module in some cases. +version_added: "1.5" +options: + that: + description: + - "A string expression of the same form that can be passed to the 'when' statement" + required: true +author: Michael DeHaan +''' + +EXAMPLES = ''' +- assert: ansible_os_family != "RedHat" +- assert: "'foo' in some_command_result.stdout" +''' diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000000..8da8aeb727 --- /dev/null +++ b/test/README.md @@ -0,0 +1,7 @@ +Legacy tests +============ + +These tests are active but are being refactored. + +See 'new_tests' directory for what will soon replace them. + diff --git a/tests_new/README.md b/tests_new/README.md new file mode 100644 index 0000000000..0972689483 --- /dev/null +++ b/tests_new/README.md @@ -0,0 +1,22 @@ +Ansible Test System +=================== + +Folders + +unit +---- + +New and approved unit tests, that test small pieces of code not suited for the integration test layer + +Not yet ready for pull requests. Opening this up soon. + +integration +----------- + +New integration test layer, constructed using playbooks. + +Not yet ready for pull requests. Opening this up soon. + +Some tests may require cloud credentials, others will not, and destructive tests will be seperated from non-destructive so a subset +can be run on development machines. + diff --git a/tests_new/integration/README.md b/tests_new/integration/README.md new file mode 100644 index 0000000000..8ea3fd6262 --- /dev/null +++ b/tests_new/integration/README.md @@ -0,0 +1,13 @@ +Integration tests +================= + +The ansible integration system. + +Tests for playbooks, by playbooks. + +Some tests may require cloud credentials. + +Instructions +============ + +Pending diff --git a/tests_new/integration/all.yml b/tests_new/integration/all.yml new file mode 100644 index 0000000000..7195dba161 --- /dev/null +++ b/tests_new/integration/all.yml @@ -0,0 +1,6 @@ +- include: test_setup.yml +- include: non_destructive.yml +- include: destructive.yml +- include: rackspace.yml +- include: amazon.yml + diff --git a/tests_new/integration/amazon.yml b/tests_new/integration/amazon.yml new file mode 100644 index 0000000000..a6ba60c13e --- /dev/null +++ b/tests_new/integration/amazon.yml @@ -0,0 +1,4 @@ +- hosts: testhost + gather_facts: True + roles: [] + diff --git a/tests_new/integration/destructive.yml b/tests_new/integration/destructive.yml new file mode 100644 index 0000000000..a6ba60c13e --- /dev/null +++ b/tests_new/integration/destructive.yml @@ -0,0 +1,4 @@ +- hosts: testhost + gather_facts: True + roles: [] + diff --git a/tests_new/integration/inventory b/tests_new/integration/inventory new file mode 100644 index 0000000000..0cadcbeb92 --- /dev/null +++ b/tests_new/integration/inventory @@ -0,0 +1,2 @@ +[local] +testhost ansible_ssh_host=127.0.0.1 ansible_connection=local diff --git a/tests_new/integration/non_destructive.yml b/tests_new/integration/non_destructive.yml new file mode 100644 index 0000000000..bd7c5ceafd --- /dev/null +++ b/tests_new/integration/non_destructive.yml @@ -0,0 +1,5 @@ +- hosts: testhost + gather_facts: True + roles: + - { role: test_copy, tags: test_copy } + diff --git a/tests_new/integration/rackspace.yml b/tests_new/integration/rackspace.yml new file mode 100644 index 0000000000..a6ba60c13e --- /dev/null +++ b/tests_new/integration/rackspace.yml @@ -0,0 +1,4 @@ +- hosts: testhost + gather_facts: True + roles: [] + diff --git a/tests_new/integration/roles/prepare_tests/tasks/main.yml b/tests_new/integration/roles/prepare_tests/tasks/main.yml new file mode 100644 index 0000000000..7847a7ce6c --- /dev/null +++ b/tests_new/integration/roles/prepare_tests/tasks/main.yml @@ -0,0 +1,26 @@ +# test code for the copy module and action plugin +# (c) 2014, Michael DeHaan + +# 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 . + + +- name: clean out the test directory + file: name={{output_dir|mandatory}} state=absent + +- name: create the test directory + file: name={{output_dir}} state=directory + + diff --git a/tests_new/integration/roles/test_copy/files/foo.txt b/tests_new/integration/roles/test_copy/files/foo.txt new file mode 100644 index 0000000000..7c6ded14ec --- /dev/null +++ b/tests_new/integration/roles/test_copy/files/foo.txt @@ -0,0 +1 @@ +foo.txt diff --git a/tests_new/integration/roles/test_copy/foo.txt b/tests_new/integration/roles/test_copy/foo.txt new file mode 100644 index 0000000000..7c6ded14ec --- /dev/null +++ b/tests_new/integration/roles/test_copy/foo.txt @@ -0,0 +1 @@ +foo.txt diff --git a/tests_new/integration/roles/test_copy/meta/main.yml b/tests_new/integration/roles/test_copy/meta/main.yml new file mode 100644 index 0000000000..1050c23ce3 --- /dev/null +++ b/tests_new/integration/roles/test_copy/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + diff --git a/tests_new/integration/roles/test_copy/tasks/main.yml b/tests_new/integration/roles/test_copy/tasks/main.yml new file mode 100644 index 0000000000..756e048812 --- /dev/null +++ b/tests_new/integration/roles/test_copy/tasks/main.yml @@ -0,0 +1,84 @@ +# test code for the copy module and action plugin +# (c) 2014, Michael DeHaan + +# 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 . + +# FIXME: replace all fail_when's with asserts + +- set_fact: output_file={{output_dir}}/foo.txt + +- name: initiate a basic copy + copy: src=foo.txt dest={{output_file}} + register: copy_result + +- assert: { that: "'changed' in copy_result" } +- assert: { that: "'dest' in copy_result" } +- assert: { that: "'group' in copy_result" } +- assert: { that: "'gid' in copy_result" } +- assert: { that: "'md5sum' in copy_result" } +- assert: { that: "'owner' in copy_result" } +- assert: { that: "'size' in copy_result" } +- assert: { that: "'src' in copy_result" } +- assert: { that: "'state' in copy_result" } +- assert: { that: "'uid' in copy_result" } + + +- name: verify that the file was marked as changed + assert: { that: "copy_result.changed == true" } + +- name: verify that the file md5sum is correct + assert: { that: "copy_result.md5sum == 'c47397529fe81ab62ba3f85e9f4c71f2'" } + +- name: check the stat results of the file + stat: path={{output_file}} + register: stat_results + +- debug: var=stat_results + +- assert: { that: "stat_results.stat.exists == true" } +- assert: { that: "stat_results.stat.isblk == false" } +- assert: { that: "stat_results.stat.isfifo == false" } +- assert: { that: "stat_results.stat.isreg == true" } +- assert: { that: "stat_results.stat.issock == false" } +- assert: { that: "stat_results.stat.md5 == 'c47397529fe81ab62ba3f85e9f4c71f2'" } + +- name: overwrite the file via same means + copy: src=foo.txt dest={{output_file}} + register: copy_result2 + +- name: verify the copy was marked unchanged + assert: { that: "not copy_result2|changed" } + +- name: overwrite the file using the content system + copy: content="modified" dest={{output_file}} + register: copy_result3 + +- name: verify the copy result was marked changed + assert: { that: "copy_result3|changed" } + +- name: overwrite the file again using the content system + copy: content="modified" dest={{output_file}} + register: copy_result4 + +- name: verify the copy result was marked unchanged + assert: { that: "not copy_result4|changed" } + +# TODO: test recursive copy +# TODO: test copy where destination is a directory like {{output_dir}}/ +# TODO: test that copy fails if the path does not exist +# TODO: ... + + diff --git a/tests_new/integration/test.sh b/tests_new/integration/test.sh new file mode 100644 index 0000000000..dae0cdeae2 --- /dev/null +++ b/tests_new/integration/test.sh @@ -0,0 +1 @@ +ansible-playbook non_destructive.yml -i inventory -e output_dir=~/ansible_testing -v diff --git a/tests_new/integration/test_setup.yml b/tests_new/integration/test_setup.yml new file mode 100644 index 0000000000..a96abed99a --- /dev/null +++ b/tests_new/integration/test_setup.yml @@ -0,0 +1,5 @@ +- hosts: testhost + gather_facts: False + roles: + - test_setup + diff --git a/tests_new/units/README.md b/tests_new/units/README.md new file mode 100644 index 0000000000..d0b3dd5abd --- /dev/null +++ b/tests_new/units/README.md @@ -0,0 +1,5 @@ +Unit tests +========== + +Tests at code level. Should be concise and to the point, and organized by subject. +