mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 23:51:09 +03:00
fixup unit tests for tasks
This commit is contained in:
parent
8a04c22b2b
commit
5135b8a969
@ -18,7 +18,6 @@ import yaml
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
from awx.main.models import (
|
from awx.main.models import (
|
||||||
AdHocCommand,
|
AdHocCommand,
|
||||||
Credential,
|
Credential,
|
||||||
@ -41,6 +40,7 @@ from awx.main import tasks
|
|||||||
from awx.main.queue import CallbackQueueDispatcher
|
from awx.main.queue import CallbackQueueDispatcher
|
||||||
from awx.main.utils import encrypt_field, encrypt_value, OutputEventFilter
|
from awx.main.utils import encrypt_field, encrypt_value, OutputEventFilter
|
||||||
from awx.main.utils.safe_yaml import SafeLoader
|
from awx.main.utils.safe_yaml import SafeLoader
|
||||||
|
from awx.main.exceptions import AwxTaskError
|
||||||
|
|
||||||
|
|
||||||
class TestJobExecution(object):
|
class TestJobExecution(object):
|
||||||
@ -65,7 +65,12 @@ def patch_Job():
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def job():
|
def job():
|
||||||
return Job(pk=1, id=1, project=Project(), inventory=Inventory())
|
return Job(pk=1, id=1, project=Project(), inventory=Inventory(), job_template=JobTemplate(id=1, name='foo'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def adhoc_job():
|
||||||
|
return AdHocCommand(pk=1, id=1, inventory=Inventory())
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -77,6 +82,15 @@ def update_model_wrapper(job):
|
|||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def adhoc_update_model_wrapper(adhoc_job):
|
||||||
|
def fn(pk, **kwargs):
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
setattr(adhoc_job, k, v)
|
||||||
|
return adhoc_job
|
||||||
|
return fn
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def patch_CallbackQueueDispatcher():
|
def patch_CallbackQueueDispatcher():
|
||||||
with mock.patch('awx.main.tasks.CallbackQueueDispatcher') as m:
|
with mock.patch('awx.main.tasks.CallbackQueueDispatcher') as m:
|
||||||
@ -103,16 +117,14 @@ def test_work_success_callback_missing_job():
|
|||||||
assert tasks.handle_work_success(task_data) is None
|
assert tasks.handle_work_success(task_data) is None
|
||||||
|
|
||||||
|
|
||||||
def test_send_notifications_list(mocker):
|
@mock.patch('awx.main.models.UnifiedJob.objects.get')
|
||||||
patches = list()
|
@mock.patch('awx.main.models.Notification.objects.filter')
|
||||||
|
def test_send_notifications_list(mock_notifications_filter, mock_job_get, mocker):
|
||||||
mock_job = mocker.MagicMock(spec=UnifiedJob)
|
mock_job = mocker.MagicMock(spec=UnifiedJob)
|
||||||
patches.append(mocker.patch('awx.main.models.UnifiedJob.objects.get', return_value=mock_job))
|
mock_job_get.return_value = mock_job
|
||||||
|
|
||||||
mock_notifications = [mocker.MagicMock(spec=Notification, subject="test", body={'hello': 'world'})]
|
mock_notifications = [mocker.MagicMock(spec=Notification, subject="test", body={'hello': 'world'})]
|
||||||
patches.append(mocker.patch('awx.main.models.Notification.objects.filter', return_value=mock_notifications))
|
mock_notifications_filter.return_value = mock_notifications
|
||||||
|
|
||||||
with apply_patches(patches):
|
|
||||||
tasks.send_notifications([1,2], job_id=1)
|
tasks.send_notifications([1,2], job_id=1)
|
||||||
assert Notification.objects.filter.call_count == 1
|
assert Notification.objects.filter.call_count == 1
|
||||||
assert mock_notifications[0].status == "successful"
|
assert mock_notifications[0].status == "successful"
|
||||||
@ -142,7 +154,7 @@ def test_safe_env_returns_new_copy():
|
|||||||
@pytest.mark.parametrize("source,expected", [
|
@pytest.mark.parametrize("source,expected", [
|
||||||
(None, True), (False, False), (True, True)
|
(None, True), (False, False), (True, True)
|
||||||
])
|
])
|
||||||
def test_openstack_client_config_generation(mocker, source, expected):
|
def test_openstack_client_config_generation(mocker, source, expected, private_data_dir):
|
||||||
update = tasks.RunInventoryUpdate()
|
update = tasks.RunInventoryUpdate()
|
||||||
credential_type = CredentialType.defaults['openstack']()
|
credential_type = CredentialType.defaults['openstack']()
|
||||||
inputs = {
|
inputs = {
|
||||||
@ -162,7 +174,7 @@ def test_openstack_client_config_generation(mocker, source, expected):
|
|||||||
'source_vars_dict': {},
|
'source_vars_dict': {},
|
||||||
'get_cloud_credential': cred_method
|
'get_cloud_credential': cred_method
|
||||||
})
|
})
|
||||||
cloud_config = update.build_private_data(inventory_update)
|
cloud_config = update.build_private_data(inventory_update, private_data_dir)
|
||||||
cloud_credential = yaml.load(
|
cloud_credential = yaml.load(
|
||||||
cloud_config.get('credentials')[credential]
|
cloud_config.get('credentials')[credential]
|
||||||
)
|
)
|
||||||
@ -184,7 +196,7 @@ def test_openstack_client_config_generation(mocker, source, expected):
|
|||||||
@pytest.mark.parametrize("source,expected", [
|
@pytest.mark.parametrize("source,expected", [
|
||||||
(False, False), (True, True)
|
(False, False), (True, True)
|
||||||
])
|
])
|
||||||
def test_openstack_client_config_generation_with_private_source_vars(mocker, source, expected):
|
def test_openstack_client_config_generation_with_private_source_vars(mocker, source, expected, private_data_dir):
|
||||||
update = tasks.RunInventoryUpdate()
|
update = tasks.RunInventoryUpdate()
|
||||||
credential_type = CredentialType.defaults['openstack']()
|
credential_type = CredentialType.defaults['openstack']()
|
||||||
inputs = {
|
inputs = {
|
||||||
@ -203,7 +215,7 @@ def test_openstack_client_config_generation_with_private_source_vars(mocker, sou
|
|||||||
'source_vars_dict': {'private': source},
|
'source_vars_dict': {'private': source},
|
||||||
'get_cloud_credential': cred_method
|
'get_cloud_credential': cred_method
|
||||||
})
|
})
|
||||||
cloud_config = update.build_private_data(inventory_update)
|
cloud_config = update.build_private_data(inventory_update, private_data_dir)
|
||||||
cloud_credential = yaml.load(
|
cloud_credential = yaml.load(
|
||||||
cloud_config.get('credentials')[credential]
|
cloud_config.get('credentials')[credential]
|
||||||
)
|
)
|
||||||
@ -251,12 +263,14 @@ class TestExtraVarSanitation(TestJobExecution):
|
|||||||
|
|
||||||
UNSAFE = '{{ lookup(''pipe'',''ls -la'') }}'
|
UNSAFE = '{{ lookup(''pipe'',''ls -la'') }}'
|
||||||
|
|
||||||
def test_vars_unsafe_by_default(self):
|
def test_vars_unsafe_by_default(self, job, private_data_dir):
|
||||||
self.instance.created_by = User(pk=123, username='angry-spud')
|
job.created_by = User(pk=123, username='angry-spud')
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
task = tasks.RunJob()
|
||||||
args, cwd, env, stdout = args
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
extra_vars = parse_extra_vars(args)
|
|
||||||
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
|
|
||||||
# ensure that strings are marked as unsafe
|
# ensure that strings are marked as unsafe
|
||||||
for unsafe in ['awx_job_template_name', 'tower_job_template_name',
|
for unsafe in ['awx_job_template_name', 'tower_job_template_name',
|
||||||
@ -271,104 +285,87 @@ class TestExtraVarSanitation(TestJobExecution):
|
|||||||
'tower_user_id', 'tower_job_template_id',
|
'tower_user_id', 'tower_job_template_id',
|
||||||
'tower_job_id']:
|
'tower_job_id']:
|
||||||
assert not hasattr(extra_vars[safe], '__UNSAFE__')
|
assert not hasattr(extra_vars[safe], '__UNSAFE__')
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
|
||||||
self.task.run(self.pk)
|
|
||||||
|
|
||||||
def test_launchtime_vars_unsafe(self):
|
def test_launchtime_vars_unsafe(self, job, private_data_dir):
|
||||||
self.instance.extra_vars = json.dumps({'msg': self.UNSAFE})
|
job.extra_vars = json.dumps({'msg': self.UNSAFE})
|
||||||
|
task = tasks.RunJob()
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
args, cwd, env, stdout = args
|
|
||||||
extra_vars = parse_extra_vars(args)
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
assert extra_vars['msg'] == self.UNSAFE
|
assert extra_vars['msg'] == self.UNSAFE
|
||||||
assert hasattr(extra_vars['msg'], '__UNSAFE__')
|
assert hasattr(extra_vars['msg'], '__UNSAFE__')
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
def test_nested_launchtime_vars_unsafe(self, job, private_data_dir):
|
||||||
self.task.run(self.pk)
|
job.extra_vars = json.dumps({'msg': {'a': [self.UNSAFE]}})
|
||||||
|
task = tasks.RunJob()
|
||||||
|
|
||||||
def test_nested_launchtime_vars_unsafe(self):
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
self.instance.extra_vars = json.dumps({'msg': {'a': [self.UNSAFE]}})
|
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
args, cwd, env, stdout = args
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
extra_vars = parse_extra_vars(args)
|
|
||||||
assert extra_vars['msg'] == {'a': [self.UNSAFE]}
|
assert extra_vars['msg'] == {'a': [self.UNSAFE]}
|
||||||
assert hasattr(extra_vars['msg']['a'][0], '__UNSAFE__')
|
assert hasattr(extra_vars['msg']['a'][0], '__UNSAFE__')
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
def test_whitelisted_jt_extra_vars(self, job, private_data_dir):
|
||||||
self.task.run(self.pk)
|
job.job_template.extra_vars = job.extra_vars = json.dumps({'msg': self.UNSAFE})
|
||||||
|
task = tasks.RunJob()
|
||||||
|
|
||||||
def test_whitelisted_jt_extra_vars(self):
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
self.instance.job_template.extra_vars = self.instance.extra_vars = json.dumps({'msg': self.UNSAFE})
|
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
args, cwd, env, stdout = args
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
extra_vars = parse_extra_vars(args)
|
|
||||||
assert extra_vars['msg'] == self.UNSAFE
|
assert extra_vars['msg'] == self.UNSAFE
|
||||||
assert not hasattr(extra_vars['msg'], '__UNSAFE__')
|
assert not hasattr(extra_vars['msg'], '__UNSAFE__')
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
def test_nested_whitelisted_vars(self, job, private_data_dir):
|
||||||
self.task.run(self.pk)
|
job.extra_vars = json.dumps({'msg': {'a': {'b': [self.UNSAFE]}}})
|
||||||
|
job.job_template.extra_vars = job.extra_vars
|
||||||
|
task = tasks.RunJob()
|
||||||
|
|
||||||
def test_nested_whitelisted_vars(self):
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
self.instance.extra_vars = json.dumps({'msg': {'a': {'b': [self.UNSAFE]}}})
|
|
||||||
self.instance.job_template.extra_vars = self.instance.extra_vars
|
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
args, cwd, env, stdout = args
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
extra_vars = parse_extra_vars(args)
|
|
||||||
assert extra_vars['msg'] == {'a': {'b': [self.UNSAFE]}}
|
assert extra_vars['msg'] == {'a': {'b': [self.UNSAFE]}}
|
||||||
assert not hasattr(extra_vars['msg']['a']['b'][0], '__UNSAFE__')
|
assert not hasattr(extra_vars['msg']['a']['b'][0], '__UNSAFE__')
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
def test_sensitive_values_dont_leak(self, job, private_data_dir):
|
||||||
self.task.run(self.pk)
|
|
||||||
|
|
||||||
def test_sensitive_values_dont_leak(self):
|
|
||||||
# JT defines `msg=SENSITIVE`, the job *should not* be able to do
|
# JT defines `msg=SENSITIVE`, the job *should not* be able to do
|
||||||
# `other_var=SENSITIVE`
|
# `other_var=SENSITIVE`
|
||||||
self.instance.job_template.extra_vars = json.dumps({'msg': self.UNSAFE})
|
job.job_template.extra_vars = json.dumps({'msg': self.UNSAFE})
|
||||||
self.instance.extra_vars = json.dumps({
|
job.extra_vars = json.dumps({
|
||||||
'msg': 'other-value',
|
'msg': 'other-value',
|
||||||
'other_var': self.UNSAFE
|
'other_var': self.UNSAFE
|
||||||
})
|
})
|
||||||
|
task = tasks.RunJob()
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
args, cwd, env, stdout = args
|
|
||||||
extra_vars = parse_extra_vars(args)
|
|
||||||
|
|
||||||
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
assert extra_vars['msg'] == 'other-value'
|
assert extra_vars['msg'] == 'other-value'
|
||||||
assert hasattr(extra_vars['msg'], '__UNSAFE__')
|
assert hasattr(extra_vars['msg'], '__UNSAFE__')
|
||||||
|
|
||||||
assert extra_vars['other_var'] == self.UNSAFE
|
assert extra_vars['other_var'] == self.UNSAFE
|
||||||
assert hasattr(extra_vars['other_var'], '__UNSAFE__')
|
assert hasattr(extra_vars['other_var'], '__UNSAFE__')
|
||||||
|
|
||||||
return ['successful', 0]
|
def test_overwritten_jt_extra_vars(self, job, private_data_dir):
|
||||||
|
job.job_template.extra_vars = json.dumps({'msg': 'SAFE'})
|
||||||
|
job.extra_vars = json.dumps({'msg': self.UNSAFE})
|
||||||
|
task = tasks.RunJob()
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
task.build_extra_vars_file(job, private_data_dir, {})
|
||||||
self.task.run(self.pk)
|
|
||||||
|
|
||||||
def test_overwritten_jt_extra_vars(self):
|
fd = open(os.path.join(private_data_dir, 'env', 'extravars'))
|
||||||
self.instance.job_template.extra_vars = json.dumps({'msg': 'SAFE'})
|
extra_vars = yaml.load(fd, SafeLoader)
|
||||||
self.instance.extra_vars = json.dumps({'msg': self.UNSAFE})
|
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
|
||||||
args, cwd, env, stdout = args
|
|
||||||
extra_vars = parse_extra_vars(args)
|
|
||||||
assert extra_vars['msg'] == self.UNSAFE
|
assert extra_vars['msg'] == self.UNSAFE
|
||||||
assert hasattr(extra_vars['msg'], '__UNSAFE__')
|
assert hasattr(extra_vars['msg'], '__UNSAFE__')
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
|
||||||
self.task.run(self.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class TestGenericRun(TestJobExecution):
|
class TestGenericRun():
|
||||||
|
|
||||||
def test_generic_failure(self, patch_Job):
|
def test_generic_failure(self, patch_Job):
|
||||||
job = Job(status='running', inventory=Inventory())
|
job = Job(status='running', inventory=Inventory())
|
||||||
@ -554,40 +551,59 @@ class TestGenericRun(TestJobExecution):
|
|||||||
|
|
||||||
class TestAdhocRun(TestJobExecution):
|
class TestAdhocRun(TestJobExecution):
|
||||||
|
|
||||||
TASK_CLS = tasks.RunAdHocCommand
|
def test_options_jinja_usage(self, adhoc_job, adhoc_update_model_wrapper):
|
||||||
|
adhoc_job.module_args = '{{ ansible_ssh_pass }}'
|
||||||
|
adhoc_job.websocket_emit_status = mock.Mock()
|
||||||
|
|
||||||
def get_instance(self):
|
task = tasks.RunAdHocCommand()
|
||||||
return AdHocCommand(
|
task.update_model = mock.Mock(wraps=adhoc_update_model_wrapper)
|
||||||
pk=1,
|
task.build_inventory = mock.Mock()
|
||||||
created=datetime.utcnow(),
|
|
||||||
status='new',
|
|
||||||
cancel_flag=False,
|
|
||||||
verbosity=3,
|
|
||||||
extra_vars={'awx_foo': 'awx-bar'}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_options_jinja_usage(self):
|
|
||||||
self.instance.module_args = '{{ ansible_ssh_pass }}'
|
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
self.task.run(self.pk)
|
task.run(adhoc_job.pk)
|
||||||
update_model_call = self.task.update_model.call_args[1]
|
|
||||||
|
call_args, _ = task.update_model.call_args_list[0]
|
||||||
|
update_model_call = task.update_model.call_args[1]
|
||||||
assert 'Jinja variables are not allowed' in update_model_call['result_traceback']
|
assert 'Jinja variables are not allowed' in update_model_call['result_traceback']
|
||||||
|
|
||||||
def test_created_by_extra_vars(self):
|
'''
|
||||||
self.instance.created_by = User(pk=123, username='angry-spud')
|
TODO: The jinja action is in _write_extra_vars_file. The extra vars should
|
||||||
|
be wrapped in unsafe
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
def test_extra_vars_jinja_usage(self, adhoc_job, adhoc_update_model_wrapper):
|
||||||
|
adhoc_job.module_args = 'ls'
|
||||||
|
adhoc_job.extra_vars = json.dumps({
|
||||||
|
'foo': '{{ bar }}'
|
||||||
|
})
|
||||||
|
#adhoc_job.websocket_emit_status = mock.Mock()
|
||||||
|
|
||||||
def run_pexpect_side_effect(*args, **kwargs):
|
task = tasks.RunAdHocCommand()
|
||||||
args, cwd, env, stdout = args
|
#task.update_model = mock.Mock(wraps=adhoc_update_model_wrapper)
|
||||||
extra_vars = parse_extra_vars(args)
|
#task.build_inventory = mock.Mock(return_value='/tmp/something.inventory')
|
||||||
|
task._write_extra_vars_file = mock.Mock()
|
||||||
|
|
||||||
|
task.build_extra_vars_file(adhoc_job, 'ignore')
|
||||||
|
|
||||||
|
call_args, _ = task._write_extra_vars_file.call_args_list[0]
|
||||||
|
private_data_dir, extra_vars = call_args
|
||||||
|
assert extra_vars['foo'] == '{{ bar }}'
|
||||||
|
'''
|
||||||
|
|
||||||
|
def test_created_by_extra_vars(self):
|
||||||
|
adhoc_job = AdHocCommand(created_by=User(pk=123, username='angry-spud'))
|
||||||
|
|
||||||
|
task = tasks.RunAdHocCommand()
|
||||||
|
task._write_extra_vars_file = mock.Mock()
|
||||||
|
task.build_extra_vars_file(adhoc_job, None, dict())
|
||||||
|
|
||||||
|
call_args, _ = task._write_extra_vars_file.call_args_list[0]
|
||||||
|
|
||||||
|
private_data_dir, extra_vars = call_args
|
||||||
assert extra_vars['tower_user_id'] == 123
|
assert extra_vars['tower_user_id'] == 123
|
||||||
assert extra_vars['tower_user_name'] == "angry-spud"
|
assert extra_vars['tower_user_name'] == "angry-spud"
|
||||||
assert extra_vars['awx_user_id'] == 123
|
assert extra_vars['awx_user_id'] == 123
|
||||||
assert extra_vars['awx_user_name'] == "angry-spud"
|
assert extra_vars['awx_user_name'] == "angry-spud"
|
||||||
assert extra_vars['awx_foo'] == "awx-bar"
|
|
||||||
return ['successful', 0]
|
|
||||||
|
|
||||||
self.run_pexpect.side_effect = run_pexpect_side_effect
|
|
||||||
self.task.run(self.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class TestIsolatedExecution(TestJobExecution):
|
class TestIsolatedExecution(TestJobExecution):
|
||||||
|
Loading…
Reference in New Issue
Block a user