| |
@@ -11,6 +11,7 @@
|
| |
import os
|
| |
import json
|
| |
import subprocess
|
| |
+ import yaml
|
| |
|
| |
from libtaskotron import executor
|
| |
import libtaskotron.exceptions as exc
|
| |
@@ -66,6 +67,8 @@
|
| |
self.playbook.write(PLAYBOOK)
|
| |
self.ipaddr = '127.0.0.1'
|
| |
self.executor = executor.Executor(self.arg_data)
|
| |
+ self.playbook_vars = self.executor._create_playbook_vars(
|
| |
+ self.playbook_name)
|
| |
|
| |
monkeypatch.setattr(config, '_config', None)
|
| |
self.conf = config.get_config()
|
| |
@@ -118,32 +121,26 @@
|
| |
|
| |
def test_run_playbook(self, monkeypatch):
|
| |
'''A standard invocation of ansible-playbook'''
|
| |
- mock_check_syntax = mock.Mock()
|
| |
- monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
- mock_check_syntax)
|
| |
mock_signal = mock.Mock()
|
| |
monkeypatch.setattr(signal, 'signal', mock_signal)
|
| |
mock_popen = mock.Mock(return_value=('fake output', None))
|
| |
monkeypatch.setattr(os_utils, 'popen_rt', mock_popen)
|
| |
|
| |
- output, _ = self.executor._run_playbook(self.playbook_name,
|
| |
- self.ipaddr)
|
| |
+ output = self.executor._run_playbook(self.playbook_name,
|
| |
+ self.ipaddr, self.playbook_vars)
|
| |
|
| |
# must return playbook output
|
| |
assert output == 'fake output'
|
| |
|
| |
- # must check syntax
|
| |
- mock_check_syntax.assert_called_once()
|
| |
-
|
| |
# must mask signals
|
| |
assert mock_signal.call_count == 4 # 2 signals masked, then reset
|
| |
|
| |
# must export ansible vars
|
| |
varsfile = os.path.join(self.artifactsdir.strpath, self.playbook_name,
|
| |
- 'taskotron', 'ansible_vars.json')
|
| |
- varsfile_internal = os.path.join(self.artifactsdir.strpath,
|
| |
- self.playbook_name, 'taskotron', 'ansible_vars_internal.json')
|
| |
- for vf in [varsfile, varsfile_internal]:
|
| |
+ 'taskotron', 'task_vars.json')
|
| |
+ allvarsfile = os.path.join(self.artifactsdir.strpath,
|
| |
+ self.playbook_name, 'taskotron', 'internal_vars.json')
|
| |
+ for vf in [varsfile, allvarsfile]:
|
| |
assert os.path.isfile(vf)
|
| |
with open(vf, 'r') as f:
|
| |
json.load(f)
|
| |
@@ -153,16 +150,12 @@
|
| |
cmd_args = mock_popen.call_args[0][0]
|
| |
assert cmd_args[0] == 'ansible-playbook'
|
| |
assert '--inventory={},'.format(self.ipaddr) in cmd_args
|
| |
- assert '--extra-vars=@{}'.format(varsfile) in cmd_args
|
| |
- assert '--extra-vars=@{}'.format(varsfile_internal) in cmd_args
|
| |
+ assert '--extra-vars=@{}'.format(allvarsfile) in cmd_args
|
| |
assert '--become' in cmd_args
|
| |
assert '--connection=local' in cmd_args
|
| |
|
| |
def test_run_playbook_failed(self, monkeypatch):
|
| |
'''Should raise when ansible-playbook fails'''
|
| |
- mock_check_syntax = mock.Mock()
|
| |
- monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
- mock_check_syntax)
|
| |
mock_signal = mock.Mock()
|
| |
monkeypatch.setattr(signal, 'signal', mock_signal)
|
| |
cpe = subprocess.CalledProcessError(returncode=99, cmd='fake')
|
| |
@@ -170,7 +163,8 @@
|
| |
monkeypatch.setattr(os_utils, 'popen_rt', mock_popen)
|
| |
|
| |
with pytest.raises(exc.TaskotronError):
|
| |
- self.executor._run_playbook(self.playbook_name, self.ipaddr)
|
| |
+ self.executor._run_playbook(self.playbook_name, self.ipaddr,
|
| |
+ self.playbook_vars)
|
| |
|
| |
# must unmask signals even when playbook failed
|
| |
assert mock_signal.call_count == 4 # 2 signals masked, then reset
|
| |
@@ -180,9 +174,6 @@
|
| |
|
| |
def test_run_playbook_interrupted(self, monkeypatch):
|
| |
'''Should try failsafe stop when interrupted'''
|
| |
- mock_check_syntax = mock.Mock()
|
| |
- monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
- mock_check_syntax)
|
| |
mock_signal = mock.Mock()
|
| |
monkeypatch.setattr(signal, 'signal', mock_signal)
|
| |
error = exc.TaskotronInterruptError(15, 'SIGTERM')
|
| |
@@ -190,7 +181,8 @@
|
| |
monkeypatch.setattr(os_utils, 'popen_rt', mock_popen)
|
| |
|
| |
with pytest.raises(exc.TaskotronInterruptError):
|
| |
- self.executor._run_playbook(self.playbook_name, self.ipaddr)
|
| |
+ self.executor._run_playbook(self.playbook_name, self.ipaddr,
|
| |
+ self.playbook_vars)
|
| |
|
| |
# must unmask signals even when playbook failed
|
| |
assert mock_signal.call_count == 4 # 2 signals masked, then reset
|
| |
@@ -202,9 +194,7 @@
|
| |
|
| |
def test_run_playbook_failsafe_error(self, monkeypatch):
|
| |
'''When failsafe stop gives an error, it should be ignored'''
|
| |
- mock_check_syntax = mock.Mock()
|
| |
- monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
- mock_check_syntax)
|
| |
+ # don't override Ctrl+C during testing
|
| |
mock_signal = mock.Mock()
|
| |
monkeypatch.setattr(signal, 'signal', mock_signal)
|
| |
error = exc.TaskotronInterruptError(15, 'SIGTERM')
|
| |
@@ -213,10 +203,72 @@
|
| |
monkeypatch.setattr(os_utils, 'popen_rt', mock_popen)
|
| |
|
| |
with pytest.raises(exc.TaskotronInterruptError):
|
| |
- self.executor._run_playbook(self.playbook_name, self.ipaddr)
|
| |
+ self.executor._run_playbook(self.playbook_name, self.ipaddr,
|
| |
+ self.playbook_vars)
|
| |
+
|
| |
+ def test_run_playbook_forwarded_vars(self, monkeypatch):
|
| |
+ '''Only certain vars should get forwarded to task playbook, no other'''
|
| |
+ # don't override Ctrl+C during testing
|
| |
+ mock_signal = mock.Mock()
|
| |
+ monkeypatch.setattr(signal, 'signal', mock_signal)
|
| |
+ mock_popen = mock.Mock(return_value=('fake output', None))
|
| |
+ monkeypatch.setattr(os_utils, 'popen_rt', mock_popen)
|
| |
+
|
| |
+ self.executor._run_playbook(self.playbook_name,
|
| |
+ self.ipaddr, self.playbook_vars)
|
| |
+ varsfile = os.path.join(self.artifactsdir.strpath, self.playbook_name,
|
| |
+ 'taskotron', 'task_vars.json')
|
| |
+
|
| |
+ assert os.path.isfile(varsfile)
|
| |
+ with open(varsfile, 'r') as vf:
|
| |
+ vars_ = json.load(vf)
|
| |
+
|
| |
+ for var in executor.Executor.FORWARDED_VARS:
|
| |
+ assert var in vars_
|
| |
+ assert len(vars_) == len(executor.Executor.FORWARDED_VARS)
|
| |
+
|
| |
+ def test_get_client_ipaddr_local(self):
|
| |
+ '''Local execution'''
|
| |
+ self.arg_data['local'] = True
|
| |
+ self.arg_data['libvirt'] = False
|
| |
+ self.arg_data['ssh'] = False
|
| |
+ self.executor = executor.Executor(self.arg_data)
|
| |
+
|
| |
+ ipaddr = self.executor._get_client_ipaddr()
|
| |
+
|
| |
+ assert ipaddr == '127.0.0.1'
|
| |
+ assert self.executor.run_remotely == False
|
| |
+
|
| |
+ def test_get_client_ipaddr_libvirt(self):
|
| |
+ '''Libvirt execution'''
|
| |
+ self.arg_data['local'] = False
|
| |
+ self.arg_data['libvirt'] = True
|
| |
+ self.arg_data['ssh'] = False
|
| |
+ self.executor = executor.Executor(self.arg_data)
|
| |
+
|
| |
+ ipaddr = self.executor._get_client_ipaddr()
|
| |
+
|
| |
+ assert ipaddr == None
|
| |
+ assert self.executor.run_remotely == True
|
| |
+
|
| |
+ def test_get_client_ipaddr_ssh(self):
|
| |
+ '''Ssh execution'''
|
| |
+ self.arg_data['local'] = False
|
| |
+ self.arg_data['libvirt'] = False
|
| |
+ self.arg_data['ssh'] = True
|
| |
+ self.arg_data['machine'] = '127.0.0.2'
|
| |
+ self.executor = executor.Executor(self.arg_data)
|
| |
+
|
| |
+ ipaddr = self.executor._get_client_ipaddr()
|
| |
+
|
| |
+ assert ipaddr == '127.0.0.2'
|
| |
+ assert self.executor.run_remotely == True
|
| |
|
| |
def test_execute_local(self, monkeypatch):
|
| |
'''Execution using local mode'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_spawn_vm = mock.Mock()
|
| |
monkeypatch.setattr(executor.Executor, '_spawn_vm', mock_spawn_vm)
|
| |
mock_run_playbook = mock.Mock(return_value=(None, True))
|
| |
@@ -225,11 +277,13 @@
|
| |
mock_report_results = mock.Mock()
|
| |
monkeypatch.setattr(executor.Executor, '_report_results',
|
| |
mock_report_results)
|
| |
- assert self.executor.arg_data['local'] == True
|
| |
+ self.executor.ipaddr = '127.0.0.1'
|
| |
+ self.executor.run_remotely = False
|
| |
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == True
|
| |
+ mock_check_syntax.assert_called_once()
|
| |
mock_spawn_vm.assert_not_called()
|
| |
mock_run_playbook.assert_called_once()
|
| |
assert mock_run_playbook.call_args[0][1] == '127.0.0.1'
|
| |
@@ -237,6 +291,9 @@
|
| |
|
| |
def test_execute_ssh(self, monkeypatch):
|
| |
'''Execution using ssh mode'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_spawn_vm = mock.Mock()
|
| |
monkeypatch.setattr(executor.Executor, '_spawn_vm', mock_spawn_vm)
|
| |
mock_run_playbook = mock.Mock(return_value=(None, True))
|
| |
@@ -245,13 +302,13 @@
|
| |
mock_report_results = mock.Mock()
|
| |
monkeypatch.setattr(executor.Executor, '_report_results',
|
| |
mock_report_results)
|
| |
- self.executor.arg_data['local'] = False
|
| |
- self.executor.arg_data['ssh'] = True
|
| |
- self.executor.arg_data['machine'] = '127.0.0.2'
|
| |
+ self.executor.ipaddr = '127.0.0.2'
|
| |
+ self.executor.run_remotely = True
|
| |
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == True
|
| |
+ mock_check_syntax.assert_called_once()
|
| |
mock_spawn_vm.assert_not_called()
|
| |
mock_run_playbook.assert_called_once()
|
| |
assert mock_run_playbook.call_args[0][1] == '127.0.0.2'
|
| |
@@ -259,6 +316,9 @@
|
| |
|
| |
def test_execute_libvirt(self, monkeypatch):
|
| |
'''Execution using libvirt mode'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_spawn_vm = mock.Mock(return_value='127.0.0.3')
|
| |
monkeypatch.setattr(executor.Executor, '_spawn_vm', mock_spawn_vm)
|
| |
mock_run_playbook = mock.Mock(return_value=(None, True))
|
| |
@@ -269,12 +329,13 @@
|
| |
mock_report_results)
|
| |
mock_task_vm = mock.Mock()
|
| |
self.executor.task_vm = mock_task_vm
|
| |
- self.executor.arg_data['local'] = False
|
| |
- self.executor.arg_data['libvirt'] = True
|
| |
+ self.executor.ipaddr = None
|
| |
+ self.executor.run_remotely = True
|
| |
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == True
|
| |
+ mock_check_syntax.assert_called_once()
|
| |
mock_spawn_vm.assert_called_once()
|
| |
mock_run_playbook.assert_called_once()
|
| |
assert mock_run_playbook.call_args[0][1] == '127.0.0.3'
|
| |
@@ -283,6 +344,9 @@
|
| |
|
| |
def test_execute_no_playbooks(self, monkeypatch):
|
| |
'''Should raise when there are no playbooks'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_run_playbook = mock.Mock()
|
| |
monkeypatch.setattr(executor.Executor, '_run_playbook',
|
| |
mock_run_playbook)
|
| |
@@ -294,11 +358,15 @@
|
| |
with pytest.raises(exc.TaskotronError):
|
| |
self.executor.execute()
|
| |
|
| |
+ mock_check_syntax.assert_not_called()
|
| |
mock_run_playbook.assert_not_called()
|
| |
mock_report_results.assert_not_called()
|
| |
|
| |
def test_execute_more_playbooks(self, monkeypatch):
|
| |
'''Should execute all found playbooks'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_run_playbook = mock.Mock(return_value=(None, True))
|
| |
monkeypatch.setattr(executor.Executor, '_run_playbook',
|
| |
mock_run_playbook)
|
| |
@@ -310,6 +378,7 @@
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == True
|
| |
+ assert mock_check_syntax.call_count == 2
|
| |
assert mock_run_playbook.call_count == 2
|
| |
playbooks = [
|
| |
mock_run_playbook.call_args_list[0][0][0],
|
| |
@@ -327,6 +396,9 @@
|
| |
|
| |
def test_execute_error(self, monkeypatch):
|
| |
'''Should raise on playbook errors'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_run_playbook = mock.Mock(side_effect=exc.TaskotronError)
|
| |
monkeypatch.setattr(executor.Executor, '_run_playbook',
|
| |
mock_run_playbook)
|
| |
@@ -337,11 +409,15 @@
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == False
|
| |
+ mock_check_syntax.assert_called_once()
|
| |
mock_run_playbook.assert_called_once()
|
| |
mock_report_results.assert_not_called()
|
| |
|
| |
def test_execute_more_playbooks_error(self, monkeypatch):
|
| |
'''Should execute all found playbooks even if some has error'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_run_playbook = mock.Mock(side_effect=[
|
| |
exc.TaskotronError,
|
| |
(None, True)])
|
| |
@@ -355,6 +431,7 @@
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == False
|
| |
+ assert mock_check_syntax.call_count == 2
|
| |
assert mock_run_playbook.call_count == 2
|
| |
playbooks = [
|
| |
mock_run_playbook.call_args_list[0][0][0],
|
| |
@@ -371,6 +448,9 @@
|
| |
def test_execute_sti_no_report(self, monkeypatch):
|
| |
'''Shouldn't try to report results for STI tasks'''
|
| |
self.playbook.write(PLAYBOOK_STI)
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
mock_spawn_vm = mock.Mock()
|
| |
monkeypatch.setattr(executor.Executor, '_spawn_vm', mock_spawn_vm)
|
| |
mock_run_playbook = mock.Mock(return_value=(None, False))
|
| |
@@ -383,6 +463,7 @@
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == True
|
| |
+ mock_check_syntax.assert_called_once()
|
| |
mock_spawn_vm.assert_not_called()
|
| |
mock_run_playbook.assert_called_once()
|
| |
assert mock_run_playbook.call_args[0][1] == '127.0.0.1'
|
| |
@@ -390,6 +471,9 @@
|
| |
|
| |
def test_execute_interrupted(self, monkeypatch):
|
| |
'''Should halt execution and return when interrupted'''
|
| |
+ mock_check_syntax = mock.Mock()
|
| |
+ monkeypatch.setattr(executor.Executor, '_check_playbook_syntax',
|
| |
+ mock_check_syntax)
|
| |
error = exc.TaskotronInterruptError(15, 'SIGTERM')
|
| |
mock_run_playbook = mock.Mock(side_effect=error)
|
| |
monkeypatch.setattr(executor.Executor, '_run_playbook',
|
| |
@@ -402,6 +486,7 @@
|
| |
success = self.executor.execute()
|
| |
|
| |
assert success == False
|
| |
+ mock_check_syntax.assert_called_once()
|
| |
mock_run_playbook.assert_called_once()
|
| |
mock_report_results.assert_not_called()
|
| |
|
| |
@@ -416,7 +501,7 @@
|
| |
vm_instance.ipaddr = '10.11.12.13'
|
| |
monkeypatch.setattr(vm, 'TestCloudMachine', mock_vm)
|
| |
|
| |
- ipaddr = self.executor._spawn_vm(None)
|
| |
+ ipaddr = self.executor._spawn_vm(None, self.playbook_vars)
|
| |
assert ipaddr == '10.11.12.13'
|
| |
assert mock_vm_prepare.call_count == 2
|
| |
|
| |
@@ -428,6 +513,28 @@
|
| |
monkeypatch.setattr(vm, 'TestCloudMachine', mock_vm)
|
| |
|
| |
with pytest.raises(exc.TaskotronMinionError):
|
| |
- self.executor._spawn_vm(None)
|
| |
+ self.executor._spawn_vm(None, self.playbook_vars)
|
| |
|
| |
assert mock_vm_prepare.call_count == self.conf.spawn_vm_retries
|
| |
+
|
| |
+ def test_load_playbook_accepted_vars(self):
|
| |
+ '''All accepted vars must be loaded and none other'''
|
| |
+ playbook = yaml.safe_load(PLAYBOOK)[0]
|
| |
+ playbook['vars'].clear()
|
| |
+ # add all accepted
|
| |
+ for var in executor.Executor.ACCEPTED_VARS:
|
| |
+ playbook['vars'][var] = 'This is ' + var
|
| |
+ # add unaccepted starting with taskotron_
|
| |
+ playbook['vars']['taskotron_unaccepted'] = 'ignore me'
|
| |
+ # add unaccepted generic
|
| |
+ playbook['vars']['unaccepted'] = 'ignore me'
|
| |
+ self.playbook.remove()
|
| |
+ self.playbook.write(yaml.safe_dump([playbook]))
|
| |
+
|
| |
+ vars_ = self.executor._load_playbook_vars(self.playbook_name)
|
| |
+
|
| |
+ for var in executor.Executor.ACCEPTED_VARS:
|
| |
+ assert var in vars_
|
| |
+ assert 'taskotron_unaccepted' not in vars_
|
| |
+ assert 'unaccepted' not in vars_
|
| |
+ assert len(vars_) == len(executor.Executor.ACCEPTED_VARS)
|
| |
Playbooks can now contain these variables for devise_environment function:
- taskotron_match_host_distro
- taskotron_match_host_release
- taskotron_match_host_arch
If not present, values default to False and default env/{distro,release,arch} is used instead.