diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index e0a906c735..bee15004a7 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -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: diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 714ac794de..53dcdad818 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -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. diff --git a/awx/main/tests/ad_hoc.py b/awx/main/tests/ad_hoc.py index 76c89c79f6..3445d9225e 100644 --- a/awx/main/tests/ad_hoc.py +++ b/awx/main/tests/ad_hoc.py @@ -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' diff --git a/awx/main/tests/commands/commands_monolithic.py b/awx/main/tests/commands/commands_monolithic.py index 972ca8ac9b..1ee6c33bb1 100644 --- a/awx/main/tests/commands/commands_monolithic.py +++ b/awx/main/tests/commands/commands_monolithic.py @@ -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) diff --git a/awx/main/tests/tasks.py b/awx/main/tests/tasks.py index cfbb0bd401..0ef05567b3 100644 --- a/awx/main/tests/tasks.py +++ b/awx/main/tests/tasks.py @@ -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)