Browse code

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

Michael DeHaan authored on 2014/02/14 08:28:29
Showing 20 changed files
... ...
@@ -17,7 +17,7 @@ Major features/changes:
17 17
 * play_hosts is a new magic variable to provide a list of hosts in scope for the current play.
18 18
 * ec2 module now accepts 'exact_count' and 'count_tag' as a way to enforce a running number of nodes by tags.
19 19
 * 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.
20
-
20
+* Start of new integration test infrastructure (WIP, more details TBD)
21 21
 
22 22
 New modules:
23 23
 
... ...
@@ -31,7 +31,7 @@ New modules:
31 31
 * cloud: rax_queue
32 32
 * messaging: rabbitmq_policy
33 33
 * system: at
34
-
34
+* utilities: assert
35 35
 
36 36
 Misc:
37 37
 
38 38
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+# Copyright 2012, Dag Wieers <dag@wieers.com>
1
+#
2
+# This file is part of Ansible
3
+#
4
+# Ansible is free software: you can redistribute it and/or modify
5
+# it under the terms of the GNU General Public License as published by
6
+# the Free Software Foundation, either version 3 of the License, or
7
+# (at your option) any later version.
8
+#
9
+# Ansible is distributed in the hope that it will be useful,
10
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+# GNU General Public License for more details.
13
+#
14
+# You should have received a copy of the GNU General Public License
15
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
16
+
17
+import ansible
18
+
19
+from ansible import utils
20
+from ansible.runner.return_data import ReturnData
21
+
22
+class ActionModule(object):
23
+    ''' Fail with custom message '''
24
+
25
+    TRANSFERS_FILES = False
26
+
27
+    def __init__(self, runner):
28
+        self.runner = runner
29
+
30
+    def run(self, conn, tmp, module_name, module_args, inject, complex_args=None, **kwargs):
31
+
32
+        # note: the fail module does not need to pay attention to check mode
33
+        # it always runs.
34
+
35
+        args = {}
36
+        if complex_args:
37
+            args.update(complex_args)
38
+        args.update(utils.parse_kv(module_args))
39
+
40
+        msg = ''
41
+
42
+        if 'msg' in args:
43
+            msg = args['msg']
44
+
45
+        if not 'that' in args:
46
+            raise errors.AnsibleError('conditional required in "that" string')
47
+
48
+        result = utils.check_conditional(args['that'], self.runner.basedir, inject, fail_on_undefined=True)
49
+
50
+        if not result:
51
+            result = dict(failed=True, assertion=args['that'], evaluated_to=result)
52
+        else:
53
+            result = dict(msg='ok', assertion=args['that'], evaluated_to=result)
54
+
55
+        return ReturnData(conn=conn, result=result)
0 56
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+#!/usr/bin/python
1
+# -*- coding: utf-8 -*-
2
+
3
+# Copyright 2012 Dag Wieers <dag@wieers.com>
4
+#
5
+# This file is part of Ansible
6
+#
7
+# Ansible is free software: you can redistribute it and/or modify
8
+# it under the terms of the GNU General Public License as published by
9
+# the Free Software Foundation, either version 3 of the License, or
10
+# (at your option) any later version.
11
+#
12
+# Ansible is distributed in the hope that it will be useful,
13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
+# GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+DOCUMENTATION = '''
21
+---
22
+module: assert
23
+short_description: Fail with custom message
24
+description:
25
+     - This module asserts that a given expression is true and can be a simpler alternative to the 'fail' module in some cases.
26
+version_added: "1.5"
27
+options:
28
+  that:
29
+    description:
30
+      - "A string expression of the same form that can be passed to the 'when' statement"
31
+    required: true
32
+author: Michael DeHaan
33
+'''
34
+
35
+EXAMPLES = '''
36
+- assert: ansible_os_family != "RedHat"
37
+- assert: "'foo' in some_command_result.stdout"
38
+'''
0 39
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+Legacy tests
1
+============
2
+
3
+These tests are active but are being refactored.
4
+
5
+See 'new_tests' directory for what will soon replace them.
6
+
0 7
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+Ansible Test System
1
+===================
2
+
3
+Folders
4
+
5
+unit
6
+----
7
+
8
+New and approved unit tests, that test small pieces of code not suited for the integration test layer
9
+
10
+Not yet ready for pull requests.  Opening this up soon.
11
+
12
+integration
13
+-----------
14
+
15
+New integration test layer, constructed using playbooks.
16
+
17
+Not yet ready for pull requests.  Opening this up soon.
18
+
19
+Some tests may require cloud credentials, others will not, and destructive tests will be seperated from non-destructive so a subset
20
+can be run on development machines.
21
+
0 22
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+Integration tests
1
+=================
2
+
3
+The ansible integration system.
4
+
5
+Tests for playbooks, by playbooks.
6
+
7
+Some tests may require cloud credentials.
8
+
9
+Instructions
10
+============
11
+
12
+Pending
0 13
new file mode 100644
... ...
@@ -0,0 +1,6 @@
0
+- include: test_setup.yml
1
+- include: non_destructive.yml
2
+- include: destructive.yml
3
+- include: rackspace.yml
4
+- include: amazon.yml
5
+
0 6
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+- hosts: testhost
1
+  gather_facts: True
2
+  roles: []
3
+
0 4
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+- hosts: testhost
1
+  gather_facts: True
2
+  roles: []
3
+
0 4
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+[local]
1
+testhost ansible_ssh_host=127.0.0.1 ansible_connection=local
0 2
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+- hosts: testhost
1
+  gather_facts: True
2
+  roles: 
3
+    - { role: test_copy, tags: test_copy }
4
+
0 5
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+- hosts: testhost
1
+  gather_facts: True
2
+  roles: []
3
+
0 4
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+# test code for the copy module and action plugin
1
+# (c) 2014, Michael DeHaan <michael.dehaan@gmail.com>
2
+
3
+# This file is part of Ansible
4
+#
5
+# Ansible is free software: you can redistribute it and/or modify
6
+# it under the terms of the GNU General Public License as published by
7
+# the Free Software Foundation, either version 3 of the License, or
8
+# (at your option) any later version.
9
+#
10
+# Ansible is distributed in the hope that it will be useful,
11
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+# GNU General Public License for more details.
14
+#
15
+# You should have received a copy of the GNU General Public License
16
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17
+
18
+
19
+- name: clean out the test directory
20
+  file: name={{output_dir|mandatory}} state=absent
21
+
22
+- name: create the test directory
23
+  file: name={{output_dir}} state=directory
24
+
25
+
0 26
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+foo.txt
0 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+foo.txt
0 1
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+dependencies: 
1
+  - prepare_tests
2
+
0 3
new file mode 100644
... ...
@@ -0,0 +1,84 @@
0
+# test code for the copy module and action plugin
1
+# (c) 2014, Michael DeHaan <michael.dehaan@gmail.com>
2
+
3
+# This file is part of Ansible
4
+#
5
+# Ansible is free software: you can redistribute it and/or modify
6
+# it under the terms of the GNU General Public License as published by
7
+# the Free Software Foundation, either version 3 of the License, or
8
+# (at your option) any later version.
9
+#
10
+# Ansible is distributed in the hope that it will be useful,
11
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+# GNU General Public License for more details.
14
+#
15
+# You should have received a copy of the GNU General Public License
16
+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17
+
18
+# FIXME: replace all fail_when's with asserts
19
+
20
+- set_fact: output_file={{output_dir}}/foo.txt
21
+
22
+- name: initiate a basic copy
23
+  copy: src=foo.txt dest={{output_file}}
24
+  register: copy_result
25
+
26
+- assert: { that: "'changed' in copy_result" }
27
+- assert: { that: "'dest' in copy_result" }
28
+- assert: { that: "'group' in copy_result" }
29
+- assert: { that: "'gid' in copy_result" }
30
+- assert: { that: "'md5sum' in copy_result" }
31
+- assert: { that: "'owner' in copy_result" }
32
+- assert: { that: "'size' in copy_result" }
33
+- assert: { that: "'src' in copy_result" }
34
+- assert: { that: "'state' in copy_result" }
35
+- assert: { that: "'uid' in copy_result" }
36
+
37
+
38
+- name: verify that the file was marked as changed
39
+  assert: { that: "copy_result.changed == true" }
40
+
41
+- name: verify that the file md5sum is correct
42
+  assert: { that: "copy_result.md5sum == 'c47397529fe81ab62ba3f85e9f4c71f2'" }
43
+
44
+- name: check the stat results of the file
45
+  stat: path={{output_file}}
46
+  register: stat_results
47
+
48
+- debug: var=stat_results
49
+
50
+- assert: { that: "stat_results.stat.exists == true" }
51
+- assert: { that: "stat_results.stat.isblk == false" }
52
+- assert: { that: "stat_results.stat.isfifo == false" }
53
+- assert: { that: "stat_results.stat.isreg == true" }
54
+- assert: { that: "stat_results.stat.issock == false" }
55
+- assert: { that: "stat_results.stat.md5 == 'c47397529fe81ab62ba3f85e9f4c71f2'" }
56
+
57
+- name: overwrite the file via same means
58
+  copy: src=foo.txt dest={{output_file}}
59
+  register: copy_result2
60
+
61
+- name: verify the copy was marked unchanged
62
+  assert: { that: "not copy_result2|changed" }
63
+
64
+- name: overwrite the file using the content system
65
+  copy: content="modified" dest={{output_file}}
66
+  register: copy_result3
67
+
68
+- name: verify the copy result was marked changed
69
+  assert: { that: "copy_result3|changed" }
70
+
71
+- name: overwrite the file again using the content system
72
+  copy: content="modified" dest={{output_file}}
73
+  register: copy_result4
74
+  
75
+- name: verify the copy result was marked unchanged
76
+  assert: { that: "not copy_result4|changed" }
77
+
78
+# TODO: test recursive copy
79
+# TODO: test copy where destination is a directory like {{output_dir}}/ 
80
+# TODO: test that copy fails if the path does not exist
81
+# TODO: ...
82
+
83
+
0 84
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+ansible-playbook non_destructive.yml -i inventory -e output_dir=~/ansible_testing -v
0 1
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+- hosts: testhost
1
+  gather_facts: False
2
+  roles:
3
+    - test_setup
4
+
0 5
new file mode 100644
... ...
@@ -0,0 +1,5 @@
0
+Unit tests
1
+==========
2
+
3
+Tests at code level.   Should be concise and to the point, and organized by subject.
4
+