Merge pull request #224 from chrismeyersfsu/feature-schedule_jobs_no_credentials_fail_fast

restrict the running of jobs
This commit is contained in:
Chris Meyers 2015-05-21 13:23:44 -04:00
commit c46c58fe7a
5 changed files with 116 additions and 63 deletions

View File

@ -1249,6 +1249,19 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions):
def task_impact(self):
return 50
# InventoryUpdate credential required
# Custom InventoryUpdate credential not required
@property
def can_start(self):
if not super(InventoryUpdate, self).can_start:
return False
if (self.source != 'custom'
and not (self.credential and self.credential.active)):
return False
return True
class CustomInventoryScript(CommonModelNameNotUnique):
class Meta:

View File

@ -491,6 +491,17 @@ class Job(UnifiedJob, JobOptions):
presets[kw] = getattr(self, kw)
return self.job_template.create_unified_job(**presets)
# Job Credential required
@property
def can_start(self):
if not super(Job, self).can_start:
return False
if not (self.credential and self.credential.active):
return False
return True
class JobHostSummary(CreatedModifiedModel):
'''
Per-host statistics for each job.

View File

@ -16,6 +16,7 @@ from django.core.urlresolvers import reverse
from crum import impersonate
# AWX
from awx.main.utils import * # noqa
from awx.main.models import * # noqa
from awx.main.tests.base import BaseJobExecutionTest
from awx.main.tests.tasks import TEST_SSH_KEY_DATA, TEST_SSH_KEY_DATA_LOCKED, TEST_SSH_KEY_DATA_UNLOCK
@ -28,23 +29,24 @@ class BaseAdHocCommandTest(BaseJobExecutionTest):
'''
def setUp(self):
super(BaseAdHocCommandTest, self).setUp()
self.setup_instances()
self.setup_users()
self.organization = self.make_organizations(self.super_django_user, 1)[0]
self.organization.admins.add(self.normal_django_user)
self.inventory = self.organization.inventories.create(name='test-inventory', description='description for test-inventory')
self.host = self.inventory.hosts.create(name='host.example.com')
self.host2 = self.inventory.hosts.create(name='host2.example.com')
self.group = self.inventory.groups.create(name='test-group')
self.group2 = self.inventory.groups.create(name='test-group2')
self.group.hosts.add(self.host)
self.group2.hosts.add(self.host, self.host2)
self.inventory2 = self.organization.inventories.create(name='test-inventory2')
self.host3 = self.inventory2.hosts.create(name='host3.example.com')
self.credential = None
settings.INTERNAL_API_URL = self.live_server_url
settings.CALLBACK_CONSUMER_PORT = ''
with ignore_inventory_computed_fields():
super(BaseAdHocCommandTest, self).setUp()
self.setup_instances()
self.setup_users()
self.organization = self.make_organizations(self.super_django_user, 1)[0]
self.organization.admins.add(self.normal_django_user)
self.inventory = self.organization.inventories.create(name='test-inventory', description='description for test-inventory')
self.host = self.inventory.hosts.create(name='host.example.com')
self.host2 = self.inventory.hosts.create(name='host2.example.com')
self.group = self.inventory.groups.create(name='test-group')
self.group2 = self.inventory.groups.create(name='test-group2')
self.group.hosts.add(self.host)
self.group2.hosts.add(self.host, self.host2)
self.inventory2 = self.organization.inventories.create(name='test-inventory2')
self.host3 = self.inventory2.hosts.create(name='host3.example.com')
self.credential = None
settings.INTERNAL_API_URL = self.live_server_url
settings.CALLBACK_CONSUMER_PORT = ''
def create_test_credential(self, **kwargs):
self.credential = self.make_credential(**kwargs)
@ -124,7 +126,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.check_job_result(ad_hoc_command, 'failed')
self.check_ad_hoc_command_events(ad_hoc_command, 'unreachable')
def test_cancel_ad_hoc_command(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('canceled', 0))
def test_cancel_ad_hoc_command(self, ignore):
ad_hoc_command = self.create_test_ad_hoc_command()
self.assertEqual(ad_hoc_command.status, 'new')
self.assertFalse(ad_hoc_command.cancel_flag)
@ -145,7 +148,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
# Unable to start ad hoc command again.
self.assertFalse(ad_hoc_command.signal_start())
def test_ad_hoc_command_options(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('successful', 0))
def test_ad_hoc_command_options(self, ignore):
ad_hoc_command = self.create_test_ad_hoc_command(forks=2, verbosity=2)
self.assertEqual(ad_hoc_command.status, 'new')
self.assertFalse(ad_hoc_command.passwords_needed_to_start)
@ -191,7 +195,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.check_ad_hoc_command_events(ad_hoc_command3, 'ok', hosts=[])
self.assertEqual(ad_hoc_command3.ad_hoc_command_events.count(), 0)
def test_ssh_username_and_password(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('successful', 0))
def test_ssh_username_and_password(self, ignore):
self.create_test_credential(username='sshuser', password='sshpass')
ad_hoc_command = self.create_test_ad_hoc_command()
self.assertEqual(ad_hoc_command.status, 'new')
@ -199,10 +204,11 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertTrue(ad_hoc_command.signal_start())
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
self.check_job_result(ad_hoc_command, 'successful')
self.assertTrue('"-u"' in ad_hoc_command.job_args)
self.assertTrue('"--ask-pass"' in ad_hoc_command.job_args)
self.assertIn('"-u"', ad_hoc_command.job_args)
self.assertIn('"--ask-pass"', ad_hoc_command.job_args)
def test_ssh_ask_password(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('successful', 0))
def test_ssh_ask_password(self, ignore):
self.create_test_credential(password='ASK')
ad_hoc_command = self.create_test_ad_hoc_command()
self.assertEqual(ad_hoc_command.status, 'new')
@ -212,9 +218,10 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertTrue(ad_hoc_command.signal_start(ssh_password='sshpass'))
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
self.check_job_result(ad_hoc_command, 'successful')
self.assertTrue('"--ask-pass"' in ad_hoc_command.job_args)
self.assertIn('"--ask-pass"', ad_hoc_command.job_args)
def test_sudo_username_and_password(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('successful', 0))
def test_sudo_username_and_password(self, ignore):
self.create_test_credential(become_method="sudo",
become_username='sudouser',
become_password='sudopass')
@ -223,15 +230,14 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertFalse(ad_hoc_command.passwords_needed_to_start)
self.assertTrue(ad_hoc_command.signal_start())
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
# Job may fail if current user doesn't have password-less sudo
# privileges, but we're mainly checking the command line arguments.
self.check_job_result(ad_hoc_command, ('successful', 'failed'))
self.assertTrue('"--become-method"' in ad_hoc_command.job_args)
self.assertTrue('"--become-user"' in ad_hoc_command.job_args)
self.assertTrue('"--ask-become-pass"' in ad_hoc_command.job_args)
self.assertFalse('"--become"' in ad_hoc_command.job_args)
self.assertIn('"--become-method"', ad_hoc_command.job_args)
self.assertIn('"--become-user"', ad_hoc_command.job_args)
self.assertIn('"--ask-become-pass"', ad_hoc_command.job_args)
self.assertNotIn('"--become"', ad_hoc_command.job_args)
def test_sudo_ask_password(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('successful', 0))
def test_sudo_ask_password(self, ignore):
self.create_test_credential(become_password='ASK')
ad_hoc_command = self.create_test_ad_hoc_command()
self.assertEqual(ad_hoc_command.status, 'new')
@ -240,13 +246,13 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertFalse(ad_hoc_command.signal_start())
self.assertTrue(ad_hoc_command.signal_start(become_password='sudopass'))
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
# Job may fail, but we're mainly checking the command line arguments.
self.check_job_result(ad_hoc_command, ('successful', 'failed'))
self.assertTrue('"--ask-become-pass"' in ad_hoc_command.job_args)
self.assertFalse('"--become-user"' in ad_hoc_command.job_args)
self.assertFalse('"--become"' in ad_hoc_command.job_args)
self.assertIn('"--ask-become-pass"', ad_hoc_command.job_args)
self.assertNotIn('"--become-user"', ad_hoc_command.job_args)
self.assertNotIn('"--become"', ad_hoc_command.job_args)
def test_unlocked_ssh_key(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('successful', 0))
def test_unlocked_ssh_key(self, ignore):
self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA)
ad_hoc_command = self.create_test_ad_hoc_command()
self.assertEqual(ad_hoc_command.status, 'new')
@ -254,8 +260,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertTrue(ad_hoc_command.signal_start())
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
self.check_job_result(ad_hoc_command, 'successful')
self.assertFalse('"--private-key=' in ad_hoc_command.job_args)
self.assertTrue('ssh-agent' in ad_hoc_command.job_args)
self.assertNotIn('"--private-key=', ad_hoc_command.job_args)
self.assertIn('ssh-agent', ad_hoc_command.job_args)
def test_locked_ssh_key_with_password(self):
self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
@ -266,8 +272,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertTrue(ad_hoc_command.signal_start())
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
self.check_job_result(ad_hoc_command, 'successful')
self.assertTrue('ssh-agent' in ad_hoc_command.job_args)
self.assertTrue('Bad passphrase' not in ad_hoc_command.result_stdout)
self.assertIn('ssh-agent', ad_hoc_command.job_args)
self.assertNotIn('Bad passphrase', ad_hoc_command.result_stdout)
def test_locked_ssh_key_with_bad_password(self):
self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
@ -278,8 +284,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertTrue(ad_hoc_command.signal_start())
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
self.check_job_result(ad_hoc_command, 'failed')
self.assertTrue('ssh-agent' in ad_hoc_command.job_args)
self.assertTrue('Bad passphrase' in ad_hoc_command.result_stdout)
self.assertIn('ssh-agent', ad_hoc_command.job_args)
self.assertIn('Bad passphrase', ad_hoc_command.result_stdout)
def test_locked_ssh_key_ask_password(self):
self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA_LOCKED,
@ -303,8 +309,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.assertTrue(ad_hoc_command.signal_start(ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK))
ad_hoc_command = AdHocCommand.objects.get(pk=ad_hoc_command.pk)
self.check_job_result(ad_hoc_command, 'successful')
self.assertTrue('ssh-agent' in ad_hoc_command.job_args)
self.assertTrue('Bad passphrase' not in ad_hoc_command.result_stdout)
self.assertIn('ssh-agent', ad_hoc_command.job_args)
self.assertNotIn('Bad passphrase', ad_hoc_command.result_stdout)
def test_run_with_proot(self):
# Only run test if proot is installed
@ -348,7 +354,8 @@ class RunAdHocCommandTest(BaseAdHocCommandTest):
self.check_job_result(ad_hoc_command, 'successful')
self.check_ad_hoc_command_events(ad_hoc_command, 'ok')
def test_run_with_proot_not_installed(self):
@mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('failed', 0))
def test_run_with_proot_not_installed(self, ignore):
# Enable proot for this test, specify invalid proot cmd.
settings.AWX_PROOT_ENABLED = True
settings.AWX_PROOT_CMD = 'PR00T'

View File

@ -341,7 +341,7 @@ class CleanupJobsTest(BaseCommandMixin, BaseLiveServerTest):
shutil.rmtree(self.test_project_path, True)
def create_test_credential(self, **kwargs):
self.credential = self.make_credential(kwargs)
self.credential = self.make_credential(**kwargs)
return self.credential
def create_test_project(self, playbook_content):
@ -409,6 +409,7 @@ class CleanupJobsTest(BaseCommandMixin, BaseLiveServerTest):
self.assertEqual(ad_hoc_commands_before, ad_hoc_commands_after)
# Create and run job.
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)

View File

@ -18,6 +18,7 @@ from django.utils.timezone import now
from crum import impersonate
# AWX
from awx.main.utils import * # noqa
from awx.main.models import * # noqa
from awx.main.tests.base import BaseJobExecutionTest
@ -345,22 +346,23 @@ class RunJobTest(BaseJobExecutionTest):
'''
def setUp(self):
super(RunJobTest, self).setUp()
self.test_project_path = None
self.setup_instances()
self.setup_users()
self.organization = self.make_organizations(self.super_django_user, 1)[0]
self.inventory = self.organization.inventories.create(name='test-inventory',
description='description for test-inventory')
self.host = self.inventory.hosts.create(name='host.example.com')
self.group = self.inventory.groups.create(name='test-group')
self.group2 = self.inventory.groups.create(name='test-group2')
self.group.hosts.add(self.host)
self.group2.hosts.add(self.host)
self.project = None
self.credential = None
self.cloud_credential = None
settings.INTERNAL_API_URL = self.live_server_url
with ignore_inventory_computed_fields():
super(RunJobTest, self).setUp()
self.test_project_path = None
self.setup_instances()
self.setup_users()
self.organization = self.make_organizations(self.super_django_user, 1)[0]
self.inventory = self.organization.inventories.create(name='test-inventory',
description='description for test-inventory')
self.host = self.inventory.hosts.create(name='host.example.com')
self.group = self.inventory.groups.create(name='test-group')
self.group2 = self.inventory.groups.create(name='test-group2')
self.group.hosts.add(self.host)
self.group2.hosts.add(self.host)
self.project = None
self.credential = None
self.cloud_credential = None
settings.INTERNAL_API_URL = self.live_server_url
def tearDown(self):
super(RunJobTest, self).tearDown()
@ -562,6 +564,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertEqual(qs.count(), 0)
def test_run_job(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -590,6 +593,7 @@ class RunJobTest(BaseJobExecutionTest):
return job
def test_check_job(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template, job_type='check')
@ -617,6 +621,7 @@ class RunJobTest(BaseJobExecutionTest):
return job
def test_run_job_that_fails(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK2)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -644,6 +649,7 @@ class RunJobTest(BaseJobExecutionTest):
return job
def test_run_job_with_ignore_errors(self):
self.create_test_credential()
self.create_test_project(TEST_IGNORE_ERRORS_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -766,6 +772,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertEqual(self.host.last_job_host_summary, None)
def test_check_job_where_task_would_fail(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK2)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template, job_type='check')
@ -799,6 +806,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertTrue(job.cancel()) # No change from calling again.
def test_cancel_job(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template()
# Pass save=False just for the sake of test coverage.
@ -824,6 +832,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertFalse(job.signal_start())
def test_extra_job_options(self):
self.create_test_credential()
self.create_test_project(TEST_EXTRA_VARS_PLAYBOOK)
# Test with extra_vars containing misc whitespace.
job_template = self.create_test_job_template(force_handlers=True,
@ -856,6 +865,7 @@ class RunJobTest(BaseJobExecutionTest):
self.check_job_result(job3, 'successful')
def test_lots_of_extra_vars(self):
self.create_test_credential()
self.create_test_project(TEST_EXTRA_VARS_PLAYBOOK)
extra_vars = json.dumps(dict(('var_%d' % x, x) for x in xrange(200)))
job_template = self.create_test_job_template(extra_vars=extra_vars)
@ -869,6 +879,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertTrue('"-e"' in job.job_args)
def test_limit_option(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template(limit='bad.example.com')
job = self.create_test_job(job_template=job_template)
@ -893,6 +904,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertTrue('ssh-agent' in job.job_args)
def test_tag_and_task_options(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK_WITH_TAGS)
job_template = self.create_test_job_template(job_tags='runme',
skip_tags='skipme',
@ -972,6 +984,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertFalse('"--become-method"' in job.job_args)
def test_job_template_become_enabled(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template(become_enabled=True)
job = self.create_test_job(job_template=job_template)
@ -1127,6 +1140,7 @@ class RunJobTest(BaseJobExecutionTest):
ssh_key_data=TEST_SSH_CERT_KEY)
playbook = TEST_ENV_PLAYBOOK % {'env_var1': env_var1,
'env_var2': env_var2}
self.create_test_credential()
self.create_test_project(playbook)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -1154,6 +1168,7 @@ class RunJobTest(BaseJobExecutionTest):
self._test_cloud_credential_environment_variables('vmware')
def test_run_async_job(self):
self.create_test_credential()
self.create_test_project(TEST_ASYNC_OK_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -1184,6 +1199,7 @@ class RunJobTest(BaseJobExecutionTest):
# FIXME: We are not sure why proot needs to be disabled on this test
# Maybe they are simply susceptable to timing and proot adds time
settings.AWX_PROOT_ENABLED = False
self.create_test_credential()
self.create_test_project(TEST_ASYNC_FAIL_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -1214,6 +1230,7 @@ class RunJobTest(BaseJobExecutionTest):
# FIXME: We are not sure why proot needs to be disabled on this test
# Maybe they are simply susceptable to timing and proot adds time
settings.AWX_PROOT_ENABLED = False
self.create_test_credential()
self.create_test_project(TEST_ASYNC_TIMEOUT_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -1242,6 +1259,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertEqual(job.processed_hosts.count(), 1)
def test_run_async_job_fire_and_forget(self):
self.create_test_credential()
self.create_test_project(TEST_ASYNC_NOWAIT_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -1269,6 +1287,7 @@ class RunJobTest(BaseJobExecutionTest):
self.assertEqual(job.processed_hosts.count(), 1)
def test_run_job_with_roles(self):
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK_WITH_ROLES, TEST_ROLE_PLAYBOOKS)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)
@ -1299,6 +1318,7 @@ class RunJobTest(BaseJobExecutionTest):
settings.AWX_PROOT_HIDE_PATHS = [os.path.join(settings.BASE_DIR, 'settings')]
# Create another project alongside the one we're using to verify it
# is hidden.
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
other_project_path = self.project.local_path
# Create a temp directory that should not be visible to the playbook.
@ -1334,6 +1354,7 @@ class RunJobTest(BaseJobExecutionTest):
# Enable proot for this test, specify invalid proot cmd.
settings.AWX_PROOT_ENABLED = True
settings.AWX_PROOT_CMD = 'PR00T'
self.create_test_credential()
self.create_test_project(TEST_PLAYBOOK)
job_template = self.create_test_job_template()
job = self.create_test_job(job_template=job_template)