Add the start of an integration test structure using Ansible playbooks, also added an assert action plugin to make writing those easier.

This commit is contained in:
Michael DeHaan 2014-02-13 18:28:29 -05:00
parent 6c3b4570d7
commit c91ceddfac
20 changed files with 290 additions and 2 deletions

View file

@ -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:

View file

@ -0,0 +1,56 @@
# Copyright 2012, Dag Wieers <dag@wieers.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/>.
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)

39
library/utilities/assert Normal file
View file

@ -0,0 +1,39 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2012 Dag Wieers <dag@wieers.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/>.
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"
'''

7
test/README.md Normal file
View file

@ -0,0 +1,7 @@
Legacy tests
============
These tests are active but are being refactored.
See 'new_tests' directory for what will soon replace them.

22
tests_new/README.md Normal file
View file

@ -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.

View file

@ -0,0 +1,13 @@
Integration tests
=================
The ansible integration system.
Tests for playbooks, by playbooks.
Some tests may require cloud credentials.
Instructions
============
Pending

View file

@ -0,0 +1,6 @@
- include: test_setup.yml
- include: non_destructive.yml
- include: destructive.yml
- include: rackspace.yml
- include: amazon.yml

View file

@ -0,0 +1,4 @@
- hosts: testhost
gather_facts: True
roles: []

View file

@ -0,0 +1,4 @@
- hosts: testhost
gather_facts: True
roles: []

View file

@ -0,0 +1,2 @@
[local]
testhost ansible_ssh_host=127.0.0.1 ansible_connection=local

View file

@ -0,0 +1,5 @@
- hosts: testhost
gather_facts: True
roles:
- { role: test_copy, tags: test_copy }

View file

@ -0,0 +1,4 @@
- hosts: testhost
gather_facts: True
roles: []

View file

@ -0,0 +1,26 @@
# 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: clean out the test directory
file: name={{output_dir|mandatory}} state=absent
- name: create the test directory
file: name={{output_dir}} state=directory

View file

@ -0,0 +1 @@
foo.txt

View file

@ -0,0 +1 @@
foo.txt

View file

@ -0,0 +1,3 @@
dependencies:
- prepare_tests

View file

@ -0,0 +1,84 @@
# 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/>.
# 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: ...

View file

@ -0,0 +1 @@
ansible-playbook non_destructive.yml -i inventory -e output_dir=~/ansible_testing -v

View file

@ -0,0 +1,5 @@
- hosts: testhost
gather_facts: False
roles:
- test_setup

View file

@ -0,0 +1,5 @@
Unit tests
==========
Tests at code level. Should be concise and to the point, and organized by subject.