From 45abe3c16b6e5139875bc78bbe2b6a0c73d99bec Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sun, 18 Mar 2012 18:29:11 -0400 Subject: [PATCH] Add unit tests for playbooks, and fix an error caught by one --- lib/ansible/playbook.py | 3 +- test/TestPlayBook.py | 122 ++++++++++++++++++++++++++++++++++++++++ test/TestRunner.py | 2 +- test/playbook1.yml | 34 +++++++++++ 4 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 test/TestPlayBook.py create mode 100644 test/playbook1.yml diff --git a/lib/ansible/playbook.py b/lib/ansible/playbook.py index 486e72c817b..549d1842f5e 100755 --- a/lib/ansible/playbook.py +++ b/lib/ansible/playbook.py @@ -83,6 +83,7 @@ class PlayBook(object): # playbook file can be passed in as a path or # as file contents (to support API usage) + print "DEBUG: playbook=%s" % playbook self.basedir = os.path.dirname(playbook) self.playbook = self._parse_playbook(playbook) @@ -471,7 +472,7 @@ class PlayBook(object): for (host, host_result) in contacted_hosts.iteritems(): if 'failed' in host_result: self.callbacks.on_failed(host, host_result) - self.failed[host] = 1 + self.failures[host] = 1 # now for each result, load into the setup cache so we can # let runner template out future commands diff --git a/test/TestPlayBook.py b/test/TestPlayBook.py new file mode 100644 index 00000000000..9b9bf29b735 --- /dev/null +++ b/test/TestPlayBook.py @@ -0,0 +1,122 @@ + +# tests are fairly 'live' (but safe to run) +# setup authorized_keys for logged in user such +# that the user can log in as themselves before running tests + +import unittest +import getpass +import ansible.playbook +import os +import shutil +import time +try: + import json +except: + import simplejson as json + +class TestCallbacks(object): + + def __init__(self): + self.tasks_started = [] + self.plays_started = [] + self.unreachable = {} + self.failed = {} + self.ok_counts = {} + self.poll_events = [] + self.dark = [] + + def results(self): + return dict( + tasks_started = self.tasks_started, + plays_started = self.plays_started, + unreachable = self.unreachable, + failed = self.failed, + ok_counts = self.ok_counts, + poll_events = self.poll_events, + dark = self.dark + ) + + def set_playbook(self, playbook): + self.playbook = playbook + + def on_start(self): + pass + + def on_task_start(self, name, is_conditional): + self.tasks_started.append(name) + + def on_unreachable(self, host, msg): + self.unreachable[host] = msg + + def on_failed(self, host, results): + self.failed[host] = results + + def on_ok(self, host): + ok = self.ok_counts.get(host, 0) + self.ok_counts[host] = ok + 1 + + def on_play_start(self, pattern): + self.plays_started.append(pattern) + + def on_async_confused(self, msg): + raise Exception("confused: %s" % msg) + + def on_async_poll(self, jid, host, clock, host_result): + self.poll_events.append([jid,host,clock.host_result]) + + def on_dark_host(self, host, msg): + self.dark.append([host,msg]) + + +class TestRunner(unittest.TestCase): + + def setUp(self): + self.user = getpass.getuser() + self.cwd = os.getcwd() + self.test_dir = os.path.join(self.cwd, 'test') + self.stage_dir = self._prepare_stage_dir() + + def _prepare_stage_dir(self): + stage_path = os.path.join(self.test_dir, 'test_data') + if os.path.exists(stage_path): + shutil.rmtree(stage_path, ignore_errors=False) + assert not os.path.exists(stage_path) + os.makedirs(stage_path) + assert os.path.exists(stage_path) + return stage_path + + def _get_test_file(self, filename): + # get a file inside the test input directory + filename = os.path.join(self.test_dir, filename) + assert os.path.exists(filename) + return filename + + def _get_stage_file(self, filename): + # get a file inside the test output directory + filename = os.path.join(self.stage_dir, filename) + return filename + + def _run(self, test_playbook): + ''' run a module and get the localhost results ''' + self.test_callbacks = TestCallbacks() + self.playbook = ansible.playbook.PlayBook( + playbook = test_playbook, + host_list = 'test/ansible_hosts', + module_path = 'library/', + forks = 1, + timeout = 5, + remote_user = self.user, + remote_pass = None, + verbose = False, + callbacks = self.test_callbacks + ) + results = self.playbook.run() + return dict( + results = results, + callbacks = self.test_callbacks.results(), + ) + + def test_one(self): + pb = os.path.join(self.test_dir, 'playbook1.yml') + print self._run(pb) + diff --git a/test/TestRunner.py b/test/TestRunner.py index 299943e0872..ca77e24f239 100644 --- a/test/TestRunner.py +++ b/test/TestRunner.py @@ -22,7 +22,7 @@ class TestRunner(unittest.TestCase): module_name='ping', module_path='library/', module_args=[], - remote_user='root', + remote_user=self.user, remote_pass=None, host_list='test/ansible_hosts', timeout=5, diff --git a/test/playbook1.yml b/test/playbook1.yml new file mode 100644 index 00000000000..0e8bc4ee45a --- /dev/null +++ b/test/playbook1.yml @@ -0,0 +1,34 @@ +# extremely simple test of the most basic of playbook engine/functions +--- +- hosts: all + vars: + answer: "I think so, Brain, but if they called them sad meals, kids wouldn't buy them." + port: 5150 + + tasks: + + - name: test basic success command + action: command /bin/true + + - name: test basic success command 2 + action: command /bin/true + + - name: test basic shell + action: echo $HOME + + - name: test copy + action: copy src=sample.j2 dest=test_data/copy.out + + - name: test template + action: template src=sample.j2 dest=test_data/template.out + + handlers: + + - name: on change 1 + action: command /bin/true + - name: on change 2 + action: command /bin/true + - action: on change 3 + action: command /bin/true + +