From 478bcc0b07be5764dfe0c7f8bdad11628d8c687c Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Thu, 14 Dec 2017 11:34:43 -0500 Subject: [PATCH] Removing old unused tests --- awx/main/tests/old/README.md | 7 - awx/main/tests/old/ad_hoc.py | 961 ---------- awx/main/tests/old/api/job_tasks.py | 48 - awx/main/tests/old/commands/command_base.py | 87 - .../tests/old/commands/commands_monolithic.py | 376 ---- awx/main/tests/old/inventory.py | 1448 -------------- awx/main/tests/old/jobs/job_launch.py | 232 --- awx/main/tests/old/jobs/job_relaunch.py | 79 - awx/main/tests/old/jobs/jobs_monolithic.py | 1209 ------------ awx/main/tests/old/jobs/start_cancel.py | 248 --- awx/main/tests/old/jobs/survey_password.py | 240 --- awx/main/tests/old/projects.py | 1703 ----------------- awx/main/tests/old/schedules.py | 210 -- awx/main/tests/old/scripts.py | 402 ---- awx/main/tests/old/settings.py | 128 -- awx/main/tests/old/tasks.py | 1244 ------------ awx/main/tests/old/users.py | 1073 ----------- 17 files changed, 9695 deletions(-) delete mode 100644 awx/main/tests/old/README.md delete mode 100644 awx/main/tests/old/ad_hoc.py delete mode 100644 awx/main/tests/old/api/job_tasks.py delete mode 100644 awx/main/tests/old/commands/command_base.py delete mode 100644 awx/main/tests/old/commands/commands_monolithic.py delete mode 100644 awx/main/tests/old/inventory.py delete mode 100644 awx/main/tests/old/jobs/job_launch.py delete mode 100644 awx/main/tests/old/jobs/job_relaunch.py delete mode 100644 awx/main/tests/old/jobs/jobs_monolithic.py delete mode 100644 awx/main/tests/old/jobs/start_cancel.py delete mode 100644 awx/main/tests/old/jobs/survey_password.py delete mode 100644 awx/main/tests/old/projects.py delete mode 100644 awx/main/tests/old/schedules.py delete mode 100644 awx/main/tests/old/scripts.py delete mode 100644 awx/main/tests/old/settings.py delete mode 100644 awx/main/tests/old/tasks.py delete mode 100644 awx/main/tests/old/users.py diff --git a/awx/main/tests/old/README.md b/awx/main/tests/old/README.md deleted file mode 100644 index 92582d890d..0000000000 --- a/awx/main/tests/old/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Old Tests -========= -This are the old django.TestCase / unittest.TestCase tests for Tower. - -No new tests should be added to this folder. Overtime, we will be refactoring -tests out of this folder as py.test tests and into their respective functional -and unit folders. diff --git a/awx/main/tests/old/ad_hoc.py b/awx/main/tests/old/ad_hoc.py deleted file mode 100644 index 8da0e33d24..0000000000 --- a/awx/main/tests/old/ad_hoc.py +++ /dev/null @@ -1,961 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import glob -import os -import subprocess -import tempfile -import mock -import unittest2 as unittest - - -# Django -from django.conf import settings -from django.core.urlresolvers import reverse - -# Django-CRUM -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.data.ssh import ( - TEST_SSH_KEY_DATA, - TEST_SSH_KEY_DATA_LOCKED, - TEST_SSH_KEY_DATA_UNLOCK, -) - -__all__ = ['RunAdHocCommandTest', 'AdHocCommandApiTest'] - - -class BaseAdHocCommandTest(BaseJobExecutionTest): - ''' - Common initialization for testing ad hoc commands. - ''' - - def setUp(self): - 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.admin_role.members.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) - return self.credential - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class RunAdHocCommandTest(BaseAdHocCommandTest): - ''' - Test cases for RunAdHocCommand celery task. - ''' - - def create_test_ad_hoc_command(self, **kwargs): - with impersonate(self.super_django_user): - opts = { - 'inventory': self.inventory, - 'credential': self.credential, - 'job_type': 'run', - 'module_name': 'command', - 'module_args': 'uptime', - } - opts.update(kwargs) - self.ad_hoc_command = AdHocCommand.objects.create(**opts) - return self.ad_hoc_command - - def check_ad_hoc_command_events(self, ad_hoc_command, runner_status='ok', - hosts=None): - ad_hoc_command_events = ad_hoc_command.ad_hoc_command_events.all() - for ad_hoc_command_event in ad_hoc_command_events: - unicode(ad_hoc_command_event) # For test coverage. - should_be_failed = bool(runner_status not in ('ok', 'skipped')) - should_be_changed = bool(runner_status in ('ok', 'failed') and ad_hoc_command.job_type == 'run') - if hosts is not None: - host_pks = set([x.pk for x in hosts]) - else: - host_pks = set(ad_hoc_command.inventory.hosts.values_list('pk', flat=True)) - qs = ad_hoc_command_events.filter(event=('runner_on_%s' % runner_status)) - self.assertEqual(qs.count(), len(host_pks)) - for evt in qs: - self.assertTrue(evt.host_id in host_pks) - self.assertTrue(evt.host_name) - self.assertEqual(evt.failed, should_be_failed) - self.assertEqual(evt.changed, should_be_changed) - - def test_run_ad_hoc_command(self): - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command, 'ok') - - def test_check_mode_ad_hoc_command(self): - ad_hoc_command = self.create_test_ad_hoc_command(module_name='ping', module_args='', job_type='check') - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command, 'ok') - - def test_run_ad_hoc_command_that_fails(self): - ad_hoc_command = self.create_test_ad_hoc_command(module_args='false') - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'failed') - self.check_ad_hoc_command_events(ad_hoc_command, 'failed') - - def test_check_mode_where_command_would_fail(self): - ad_hoc_command = self.create_test_ad_hoc_command(job_type='check', module_args='false') - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command, 'skipped') - - @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) - self.assertFalse(ad_hoc_command.passwords_needed_to_start) - ad_hoc_command.cancel_flag = True - ad_hoc_command.save(update_fields=['cancel_flag']) - 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, 'canceled') - self.assertTrue(ad_hoc_command.cancel_flag) - # Calling cancel afterwards just returns the cancel flag. - self.assertTrue(ad_hoc_command.cancel()) - # Read attribute for test coverage. - ad_hoc_command.celery_task - ad_hoc_command.celery_task_id = '' - ad_hoc_command.save(update_fields=['celery_task_id']) - self.assertEqual(ad_hoc_command.celery_task, None) - # Unable to start ad hoc command again. - self.assertFalse(ad_hoc_command.signal_start()) - - @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) - 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('"--forks=2"' in ad_hoc_command.job_args) - self.assertTrue('"-vv"' in ad_hoc_command.job_args) - # Test with basic become privilege escalation - ad_hoc_command2 = self.create_test_ad_hoc_command(become_enabled=True) - self.assertEqual(ad_hoc_command2.status, 'new') - self.assertFalse(ad_hoc_command2.passwords_needed_to_start) - self.assertTrue(ad_hoc_command2.signal_start()) - ad_hoc_command2 = AdHocCommand.objects.get(pk=ad_hoc_command2.pk) - self.check_job_result(ad_hoc_command2, ('successful', 'failed')) - self.assertTrue('"--become"' in ad_hoc_command2.job_args) - - def test_limit_option(self): - # Test limit by hostname. - ad_hoc_command = self.create_test_ad_hoc_command(limit='host.example.com') - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command, 'ok', hosts=[self.host]) - self.assertTrue('"host.example.com"' in ad_hoc_command.job_args) - # Test limit by group name. - ad_hoc_command2 = self.create_test_ad_hoc_command(limit='test-group') - self.assertEqual(ad_hoc_command2.status, 'new') - self.assertFalse(ad_hoc_command2.passwords_needed_to_start) - self.assertTrue(ad_hoc_command2.signal_start()) - ad_hoc_command2 = AdHocCommand.objects.get(pk=ad_hoc_command.pk) - self.check_job_result(ad_hoc_command2, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command2, 'ok', hosts=[self.host]) - # Test limit by host not in inventory. - ad_hoc_command3 = self.create_test_ad_hoc_command(limit='bad-host') - self.assertEqual(ad_hoc_command3.status, 'new') - self.assertFalse(ad_hoc_command3.passwords_needed_to_start) - self.assertTrue(ad_hoc_command3.signal_start()) - ad_hoc_command3 = AdHocCommand.objects.get(pk=ad_hoc_command3.pk) - self.check_job_result(ad_hoc_command3, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command3, 'ok', hosts=[]) - self.assertEqual(ad_hoc_command3.ad_hoc_command_events.count(), 0) - - @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') - 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) - self.check_job_result(ad_hoc_command, 'successful') - self.assertIn('"-u"', ad_hoc_command.job_args) - self.assertIn('"--ask-pass"', ad_hoc_command.job_args) - - @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') - self.assertTrue(ad_hoc_command.passwords_needed_to_start) - self.assertTrue('ssh_password' in ad_hoc_command.passwords_needed_to_start) - self.assertFalse(ad_hoc_command.signal_start()) - 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.assertIn('"--ask-pass"', ad_hoc_command.job_args) - - @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') - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, ('successful', 'failed')) - 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) - - @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') - self.assertTrue(ad_hoc_command.passwords_needed_to_start) - self.assertTrue('become_password' in ad_hoc_command.passwords_needed_to_start) - 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) - self.check_job_result(ad_hoc_command, ('successful', 'failed')) - 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) - - @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') - 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) - self.check_job_result(ad_hoc_command, 'successful') - 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, - ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK) - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'successful') - 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, - ssh_key_unlock='not the passphrase') - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'failed') - 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, - ssh_key_unlock='ASK') - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - self.assertTrue(ad_hoc_command.passwords_needed_to_start) - self.assertTrue('ssh_key_unlock' in ad_hoc_command.passwords_needed_to_start) - self.assertFalse(ad_hoc_command.signal_start()) - self.assertTrue(ad_hoc_command.signal_start(ssh_key_unlock='not it')) - 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) - # Try again and pass correct password. - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - self.assertTrue(ad_hoc_command.passwords_needed_to_start) - self.assertTrue('ssh_key_unlock' in ad_hoc_command.passwords_needed_to_start) - self.assertFalse(ad_hoc_command.signal_start()) - 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.assertIn('ssh-agent', ad_hoc_command.job_args) - self.assertNotIn('Bad passphrase', ad_hoc_command.result_stdout) - - def test_run_with_bubblewrap(self): - # Only run test if bubblewrap is installed - cmd = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--version'] - try: - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - proc.communicate() - has_bubblewrap = bool(proc.returncode == 0) - except (OSError, ValueError): - has_bubblewrap = False - if not has_bubblewrap: - self.skipTest('bubblewrap is not installed') - # Enable bubblewrap for this test. - settings.AWX_PROOT_ENABLED = True - # Hide local settings path. - settings.AWX_PROOT_HIDE_PATHS = [os.path.join(settings.BASE_DIR, 'settings')] - # Create list of paths that should not be visible to the command. - hidden_paths = [ - os.path.join(settings.PROJECTS_ROOT, '*'), - os.path.join(settings.JOBOUTPUT_ROOT, '*'), - ] - # Create a temp directory that should not be visible to the command. - temp_path = tempfile.mkdtemp() - self._temp_paths.append(temp_path) - hidden_paths.append(temp_path) - # Find a file in supervisor logs that should not be visible. - try: - supervisor_log_path = glob.glob('/var/log/supervisor/*')[0] - except IndexError: - supervisor_log_path = None - if supervisor_log_path: - hidden_paths.append(supervisor_log_path) - # Create and run ad hoc command. - module_args = ' && '.join(['echo %s && test ! -e %s' % (x, x) for x in hidden_paths]) - ad_hoc_command = self.create_test_ad_hoc_command(module_name='shell', module_args=module_args, verbosity=2) - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'successful') - self.check_ad_hoc_command_events(ad_hoc_command, 'ok') - - @mock.patch('awx.main.tasks.BaseTask.run_pexpect', return_value=('failed', 0)) - def test_run_with_bubblewrap_not_installed(self, ignore): - # Enable bubblewrap for this test, specify invalid bubblewrap cmd. - settings.AWX_PROOT_ENABLED = True - settings.AWX_PROOT_CMD = 'PR00T' - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.check_job_result(ad_hoc_command, 'error', expect_traceback=True) - - -def run_pexpect_mock(self, *args, **kwargs): - return 'successful', 0 - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class AdHocCommandApiTest(BaseAdHocCommandTest): - ''' - Test API list/detail views for ad hoc commands. - ''' - - def setUp(self): - super(AdHocCommandApiTest, self).setUp() - self.create_test_credential(user=self.normal_django_user) - - def run_test_ad_hoc_command(self, **kwargs): - # Post to list to start a new ad hoc command. - expect = kwargs.pop('expect', 201) - url = kwargs.pop('url', reverse('api:ad_hoc_command_list')) - data = { - 'inventory': self.inventory.pk, - 'credential': self.credential.pk, - 'module_name': 'command', - 'module_args': 'uptime', - } - data.update(kwargs) - for k,v in data.items(): - if v is None: - del data[k] - return self.post(url, data, expect=expect) - - - @mock.patch('awx.main.tasks.BaseTask.run_pexpect', side_effect=run_pexpect_mock) - def test_ad_hoc_command_detail(self, ignore): - with self.current_user('admin'): - response1 = self.run_test_ad_hoc_command() - response2 = self.run_test_ad_hoc_command() - response3 = self.run_test_ad_hoc_command() - - # Retrieve detail for ad hoc command. Only GET is supported. - with self.current_user('admin'): - url = reverse('api:ad_hoc_command_detail', args=(response1['id'],)) - self.assertEqual(url, response1['url']) - response = self.get(url, expect=200) - self.assertEqual(response['credential'], self.credential.pk) - self.assertEqual(response['related']['credential'], - reverse('api:credential_detail', args=(self.credential.pk,))) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['related']['inventory'], - reverse('api:inventory_detail', args=(self.inventory.pk,))) - self.assertTrue(response['related']['stdout']) - self.assertTrue(response['related']['cancel']) - self.assertTrue(response['related']['relaunch']) - self.assertTrue(response['related']['events']) - self.assertTrue(response['related']['activity_stream']) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=204) - self.delete(url, expect=404) - with self.current_user('normal'): - url = reverse('api:ad_hoc_command_detail', args=(response2['id'],)) - self.assertEqual(url, response2['url']) - response = self.get(url, expect=200) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=204) - self.delete(url, expect=404) - url = reverse('api:ad_hoc_command_detail', args=(response3['id'],)) - self.assertEqual(url, response3['url']) - with self.current_user('other'): - response = self.get(url, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=403) - with self.current_user('nobody'): - response = self.get(url, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=403) - with self.current_user(None): - response = self.get(url, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - # Verify that the credential and inventory are null when they have - # been deleted, can delete an ad hoc command without inventory or - # credential. - self.credential.delete() - self.inventory.delete() - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['credential'], None) - self.assertEqual(response['inventory'], None) - self.delete(url, expect=204) - self.delete(url, expect=404) - - @mock.patch('awx.main.tasks.BaseTask.run_pexpect', side_effect=run_pexpect_mock) - def test_ad_hoc_command_cancel(self, ignore): - # Override setting so that ad hoc command isn't actually started. - with self.settings(CELERY_UNIT_TEST=False): - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - - # Retrieve the cancel URL, should indicate it can be canceled. - url = reverse('api:ad_hoc_command_cancel', args=(response['id'],)) - self.assertEqual(url, response['related']['cancel']) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['can_cancel'], True) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['can_cancel'], True) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - # Cancel ad hoc command (before it starts) and verify the can_cancel - # flag is False and attempts to cancel again fail. - with self.current_user('normal'): - self.post(url, {}, expect=202) - response = self.get(url, expect=200) - self.assertEqual(response['can_cancel'], False) - self.post(url, {}, expect=403) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['can_cancel'], False) - self.post(url, {}, expect=405) - - @mock.patch('awx.main.tasks.BaseTask.run_pexpect', side_effect=run_pexpect_mock) - def test_ad_hoc_command_relaunch(self, ignore): - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - - # Retrieve the relaunch URL, should indicate no passwords are needed - # and it can be relaunched. Relaunch and fetch the new command. - url = reverse('api:ad_hoc_command_relaunch', args=(response['id'],)) - self.assertEqual(url, response['related']['relaunch']) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['passwords_needed_to_start'], []) - response = self.post(url, {}, expect=201) - self.assertTrue(response['ad_hoc_command']) - self.get(response['url'], expect=200) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['passwords_needed_to_start'], []) - response = self.post(url, {}, expect=201) - self.assertTrue(response['ad_hoc_command']) - self.get(response['url'], expect=200) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - # Try to relaunch ad hoc command when module has been removed from - # allowed list of modules. - try: - ad_hoc_commands = settings.AD_HOC_COMMANDS - settings.AD_HOC_COMMANDS = [] - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['passwords_needed_to_start'], []) - response = self.post(url, {}, expect=400) - finally: - settings.AD_HOC_COMMANDS = ad_hoc_commands - - # Try to relaunch after the inventory has been marked inactive. - self.inventory.delete() - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['passwords_needed_to_start'], []) - response = self.post(url, {}, expect=400) - - # Try to relaunch with expired license. - with self.current_user('admin'): - response = self.run_test_ad_hoc_command(inventory=self.inventory2.pk) - self.create_expired_license_file() - with self.current_user('admin'): - self.post(response['related']['relaunch'], {}, expect=403) - - def test_ad_hoc_command_events_list(self): - # TODO: Create test events instead of relying on playbooks execution - - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - response = self.run_test_ad_hoc_command() - - # Check list of ad hoc command events for a specific ad hoc command. - ad_hoc_command_id = response['id'] - url = reverse('api:ad_hoc_command_ad_hoc_command_events_list', args=(ad_hoc_command_id,)) - self.assertEqual(url, response['related']['events']) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], self.inventory.hosts.count()) - for result in response['results']: - self.assertEqual(result['ad_hoc_command'], ad_hoc_command_id) - self.assertTrue(result['id']) - self.assertTrue(result['url']) - self.assertEqual(result['event'], 'runner_on_ok') - self.assertFalse(result['failed']) - self.assertTrue(result['changed']) - self.assertTrue(result['host'] in set(self.inventory.hosts.values_list('pk', flat=True))) - self.assertTrue(result['host_name'] in set(self.inventory.hosts.values_list('name', flat=True))) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], self.inventory.hosts.count()) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - # Test top level ad hoc command events list. - url = reverse('api:ad_hoc_command_event_list') - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 2 * self.inventory.hosts.count()) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 2 * self.inventory.hosts.count()) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 0) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 0) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - def test_ad_hoc_command_event_detail(self): - # TODO: Mock pexpect. Create test events instead of relying on playbooks execution - - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - - # Check ad hoc command event detail view. - ad_hoc_command_event_ids = AdHocCommandEvent.objects.values_list('pk', flat=True) - with self.current_user('admin'): - for ahce_id in ad_hoc_command_event_ids: - url = reverse('api:ad_hoc_command_event_detail', args=(ahce_id,)) - response = self.get(url, expect=200) - self.assertTrue(response['ad_hoc_command']) - self.assertEqual(response['id'], ahce_id) - self.assertEqual(response['url'], url) - self.assertEqual(response['event'], 'runner_on_ok') - self.assertFalse(response['failed']) - self.assertTrue(response['changed']) - self.assertTrue(response['host'] in set(self.inventory.hosts.values_list('pk', flat=True))) - self.assertTrue(response['host_name'] in set(self.inventory.hosts.values_list('name', flat=True))) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - for ahce_id in ad_hoc_command_event_ids: - url = reverse('api:ad_hoc_command_event_detail', args=(ahce_id,)) - self.get(url, expect=200) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - for ahce_id in ad_hoc_command_event_ids: - url = reverse('api:ad_hoc_command_event_detail', args=(ahce_id,)) - self.get(url, expect=403) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - for ahce_id in ad_hoc_command_event_ids: - url = reverse('api:ad_hoc_command_event_detail', args=(ahce_id,)) - self.get(url, expect=403) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - for ahce_id in ad_hoc_command_event_ids: - url = reverse('api:ad_hoc_command_event_detail', args=(ahce_id,)) - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - @mock.patch('awx.main.tasks.BaseTask.run_pexpect', side_effect=run_pexpect_mock) - def test_ad_hoc_command_activity_stream(self, ignore): - # TODO: Test non-enterprise license - self.create_test_license_file() - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - - # Check activity stream for ad hoc command. There should only be one - # entry when it was created; other changes made while running should - # not show up. - url = reverse('api:ad_hoc_command_activity_stream_list', args=(response['id'],)) - self.assertEqual(url, response['related']['activity_stream']) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 1) - result = response['results'][0] - self.assertTrue(result['id']) - self.assertTrue(result['url']) - self.assertEqual(result['operation'], 'create') - self.assertTrue(result['changes']) - self.assertTrue(result['timestamp']) - self.assertEqual(result['object1'], 'ad_hoc_command') - self.assertEqual(result['object2'], '') - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 1) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - - def test_host_ad_hoc_commands_list(self): - # TODO: Figure out why this test needs pexpect - - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - response = self.run_test_ad_hoc_command(limit=self.host2.name) - - # Test the ad hoc commands list for a host. Should only return the ad - # hoc command(s) run against that host. Posting should start a new ad - # hoc command and always set the inventory and limit based on URL. - url = reverse('api:host_ad_hoc_commands_list', args=(self.host.pk,)) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 1) - response = self.run_test_ad_hoc_command(url=url, inventory=None, expect=201) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['limit'], self.host.name) - response = self.run_test_ad_hoc_command(url=url, inventory=self.inventory2.pk, limit=self.host2.name, expect=201) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['limit'], self.host.name) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 3) - response = self.run_test_ad_hoc_command(url=url, inventory=None, expect=201) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['limit'], self.host.name) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - # Try to run with expired license. - self.create_expired_license_file() - with self.current_user('admin'): - self.run_test_ad_hoc_command(url=url, expect=403) - with self.current_user('normal'): - self.run_test_ad_hoc_command(url=url, expect=403) - - def test_group_ad_hoc_commands_list(self): - # TODO: Figure out why this test needs pexpect - - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() # self.host + self.host2 - response = self.run_test_ad_hoc_command(limit=self.group.name) # self.host - response = self.run_test_ad_hoc_command(limit=self.host2.name) # self.host2 - - # Test the ad hoc commands list for a group. Should return the ad - # hoc command(s) run against any hosts in that group. Posting should - # start a new ad hoc command and always set the inventory and limit - # based on URL. - url = reverse('api:group_ad_hoc_commands_list', args=(self.group.pk,)) # only self.host - url2 = reverse('api:group_ad_hoc_commands_list', args=(self.group2.pk,)) # self.host + self.host2 - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 2) - response = self.get(url2, expect=200) - self.assertEqual(response['count'], 3) - response = self.run_test_ad_hoc_command(url=url, inventory=None, expect=201) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['limit'], self.group.name) - response = self.run_test_ad_hoc_command(url=url, inventory=self.inventory2.pk, limit=self.group2.name, expect=201) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['limit'], self.group.name) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 4) - response = self.run_test_ad_hoc_command(url=url, inventory=None, expect=201) - self.assertEqual(response['inventory'], self.inventory.pk) - self.assertEqual(response['limit'], self.group.name) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=403) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) - - # Try to run with expired license. - self.create_expired_license_file() - with self.current_user('admin'): - self.run_test_ad_hoc_command(url=url, expect=403) - with self.current_user('normal'): - self.run_test_ad_hoc_command(url=url, expect=403) - - def test_host_ad_hoc_command_events_list(self): - # TODO: Mock run_pexpect. Create test events instead of relying on playbooks execution - - with self.current_user('admin'): - response = self.run_test_ad_hoc_command() - - # Test the ad hoc command events list for a host. Should return the - # events only for that particular host. - url = reverse('api:host_ad_hoc_command_events_list', args=(self.host.pk,)) - with self.current_user('admin'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 1) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('normal'): - response = self.get(url, expect=200) - self.assertEqual(response['count'], 1) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('other'): - self.get(url, expect=403) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user('nobody'): - self.get(url, expect=403) - self.post(url, {}, expect=405) - self.put(url, {}, expect=405) - self.patch(url, {}, expect=405) - self.delete(url, expect=405) - with self.current_user(None): - self.get(url, expect=401) - self.post(url, {}, expect=401) - self.put(url, {}, expect=401) - self.patch(url, {}, expect=401) - self.delete(url, expect=401) diff --git a/awx/main/tests/old/api/job_tasks.py b/awx/main/tests/old/api/job_tasks.py deleted file mode 100644 index 2471f9e614..0000000000 --- a/awx/main/tests/old/api/job_tasks.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -import os -import unittest2 as unittest - -from django.conf import settings -from django.test import LiveServerTestCase -from django.test.utils import override_settings - -from awx.main.tests.job_base import BaseJobTestMixin - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local') -class JobTasksTests(BaseJobTestMixin, LiveServerTestCase): - """A set of tests to ensure that the job_tasks endpoint, available at - `/api/v1/jobs/{id}/job_tasks/`, works as expected. - """ - def setUp(self): - super(JobTasksTests, self).setUp() - settings.INTERNAL_API_URL = self.live_server_url - - def test_tasks_endpoint(self): - """Establish that the `job_tasks` endpoint shows what we expect, - which is a rollup of information about each of the corresponding - job events. - """ - # Create a job - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new') - job.signal_start() - - # Get the initial job event. - event = job.job_events.get(event='playbook_on_play_start') - - # Actually make the request for the job tasks. - with self.current_user(self.user_sue): - url = '/api/v1/jobs/%d/job_tasks/?event_id=%d' % (job.id, event.id) - response = self.get(url) - - # Test to make sure we got back what we expected. - result = response['results'][0] - self.assertEqual(result['host_count'], 7) - self.assertEqual(result['changed_count'], 7) - self.assertFalse(result['failed']) - self.assertTrue(result['changed']) diff --git a/awx/main/tests/old/commands/command_base.py b/awx/main/tests/old/commands/command_base.py deleted file mode 100644 index f5e762cbee..0000000000 --- a/awx/main/tests/old/commands/command_base.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved - -# Python -import StringIO -import sys -import json - -# Django -from django.core.management import call_command - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTestMixin - - -class BaseCommandMixin(BaseTestMixin): - def create_test_inventories(self): - self.setup_users() - self.organizations = self.make_organizations(self.super_django_user, 2) - self.projects = self.make_projects(self.normal_django_user, 2) - self.organizations[0].projects.add(self.projects[1]) - self.organizations[1].projects.add(self.projects[0]) - self.inventories = [] - self.hosts = [] - self.groups = [] - for n, organization in enumerate(self.organizations): - inventory = Inventory.objects.create(name='inventory-%d' % n, - description='description for inventory %d' % n, - organization=organization, - variables=json.dumps({'n': n}) if n else '') - self.inventories.append(inventory) - hosts = [] - for x in xrange(10): - if n > 0: - variables = json.dumps({'ho': 'hum-%d' % x}) - else: - variables = '' - host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x), - inventory=inventory, - variables=variables) - hosts.append(host) - self.hosts.extend(hosts) - groups = [] - for x in xrange(5): - if n > 0: - variables = json.dumps({'gee': 'whiz-%d' % x}) - else: - variables = '' - group = inventory.groups.create(name='group-%d' % x, - inventory=inventory, - variables=variables) - groups.append(group) - group.hosts.add(hosts[x]) - group.hosts.add(hosts[x + 5]) - if n > 0 and x == 4: - group.parents.add(groups[3]) - self.groups.extend(groups) - - def run_command(self, name, *args, **options): - ''' - Run a management command and capture its stdout/stderr along with any - exceptions. - ''' - command_runner = options.pop('command_runner', call_command) - stdin_fileobj = options.pop('stdin_fileobj', None) - options.setdefault('verbosity', 1) - options.setdefault('interactive', False) - original_stdin = sys.stdin - original_stdout = sys.stdout - original_stderr = sys.stderr - if stdin_fileobj: - sys.stdin = stdin_fileobj - sys.stdout = StringIO.StringIO() - sys.stderr = StringIO.StringIO() - result = None - try: - result = command_runner(name, *args, **options) - except Exception as e: - result = e - finally: - captured_stdout = sys.stdout.getvalue() - captured_stderr = sys.stderr.getvalue() - sys.stdin = original_stdin - sys.stdout = original_stdout - sys.stderr = original_stderr - return result, captured_stdout, captured_stderr diff --git a/awx/main/tests/old/commands/commands_monolithic.py b/awx/main/tests/old/commands/commands_monolithic.py deleted file mode 100644 index 216a617eaa..0000000000 --- a/awx/main/tests/old/commands/commands_monolithic.py +++ /dev/null @@ -1,376 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import json -import os -import shutil -import StringIO -import sys -import time -import unittest2 as unittest - -# Django -from django.core.management import call_command -from django.utils.timezone import now -from django.test.utils import override_settings - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTest, BaseLiveServerTest - -__all__ = ['CreateDefaultOrgTest', 'DumpDataTest', 'CleanupDeletedTest', - 'CleanupJobsTest', 'CleanupActivityStreamTest', - 'InventoryImportTest'] - -TEST_PLAYBOOK = '''- hosts: test-group - gather_facts: False - tasks: - - name: should pass - command: test 1 = 1 - - name: should also pass - command: test 2 = 2 -''' - - -class BaseCommandMixin(object): - ''' - Base class for tests that run management commands. - ''' - - def create_test_inventories(self): - self.setup_users() - self.organizations = self.make_organizations(self.super_django_user, 2) - self.projects = self.make_projects(self.normal_django_user, 2) - self.organizations[0].projects.add(self.projects[1]) - self.organizations[1].projects.add(self.projects[0]) - self.inventories = [] - self.hosts = [] - self.groups = [] - for n, organization in enumerate(self.organizations): - inventory = Inventory.objects.create(name='inventory-%d' % n, - description='description for inventory %d' % n, - organization=organization, - variables=json.dumps({'n': n}) if n else '') - self.inventories.append(inventory) - hosts = [] - for x in xrange(10): - if n > 0: - variables = json.dumps({'ho': 'hum-%d' % x}) - else: - variables = '' - host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x), - inventory=inventory, - variables=variables) - hosts.append(host) - self.hosts.extend(hosts) - groups = [] - for x in xrange(5): - if n > 0: - variables = json.dumps({'gee': 'whiz-%d' % x}) - else: - variables = '' - group = inventory.groups.create(name='group-%d' % x, - inventory=inventory, - variables=variables) - groups.append(group) - group.hosts.add(hosts[x]) - group.hosts.add(hosts[x + 5]) - if n > 0 and x == 4: - group.parents.add(groups[3]) - self.groups.extend(groups) - - def run_command(self, name, *args, **options): - ''' - Run a management command and capture its stdout/stderr along with any - exceptions. - ''' - command_runner = options.pop('command_runner', call_command) - stdin_fileobj = options.pop('stdin_fileobj', None) - options.setdefault('verbosity', 1) - options.setdefault('interactive', False) - original_stdin = sys.stdin - original_stdout = sys.stdout - original_stderr = sys.stderr - if stdin_fileobj: - sys.stdin = stdin_fileobj - sys.stdout = StringIO.StringIO() - sys.stderr = StringIO.StringIO() - result = None - try: - result = command_runner(name, *args, **options) - except Exception as e: - result = e - finally: - captured_stdout = sys.stdout.getvalue() - captured_stderr = sys.stderr.getvalue() - sys.stdin = original_stdin - sys.stdout = original_stdout - sys.stderr = original_stderr - return result, captured_stdout, captured_stderr - - -class CreateDefaultOrgTest(BaseCommandMixin, BaseTest): - ''' - Test cases for create_default_org management command. - ''' - - def setUp(self): - super(CreateDefaultOrgTest, self).setUp() - self.setup_instances() - - def test_create_default_org(self): - self.setup_users() - self.assertEqual(Organization.objects.count(), 0) - result, stdout, stderr = self.run_command('create_preload_data') - self.assertEqual(result, None) - self.assertTrue('Default organization added' in stdout) - self.assertEqual(Organization.objects.count(), 1) - org = Organization.objects.all()[0] - self.assertEqual(org.created_by, self.super_django_user) - self.assertEqual(org.modified_by, self.super_django_user) - result, stdout, stderr = self.run_command('create_preload_data') - self.assertEqual(result, None) - self.assertFalse('Default organization added' in stdout) - self.assertEqual(Organization.objects.count(), 1) - - -class DumpDataTest(BaseCommandMixin, BaseTest): - ''' - Test cases for dumpdata management command. - ''' - - def setUp(self): - super(DumpDataTest, self).setUp() - self.create_test_inventories() - - def test_dumpdata(self): - result, stdout, stderr = self.run_command('dumpdata') - self.assertEqual(result, None) - json.loads(stdout) - - -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local') -class CleanupJobsTest(BaseCommandMixin, BaseLiveServerTest): - ''' - Test cases for cleanup_jobs management command. - ''' - - def setUp(self): - super(CleanupJobsTest, 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 = Inventory.objects.create(name='test-inventory', - description='description for test-inventory', - organization=self.organization) - self.host = self.inventory.hosts.create(name='host.example.com', - inventory=self.inventory) - self.group = self.inventory.groups.create(name='test-group', - inventory=self.inventory) - self.group.hosts.add(self.host) - self.project = None - self.credential = None - self.start_queue() - - def tearDown(self): - super(CleanupJobsTest, self).tearDown() - self.terminate_queue() - if self.test_project_path: - shutil.rmtree(self.test_project_path, True) - - def create_test_credential(self, **kwargs): - self.credential = self.make_credential(**kwargs) - return self.credential - - def create_test_project(self, playbook_content): - self.project = self.make_projects(self.normal_django_user, 1, playbook_content)[0] - self.organization.projects.add(self.project) - - def create_test_job_template(self, **kwargs): - opts = { - 'name': 'test-job-template %s' % str(now()), - 'inventory': self.inventory, - 'project': self.project, - 'credential': self.credential, - 'job_type': 'run', - } - try: - opts['playbook'] = self.project.playbooks[0] - except (AttributeError, IndexError): - pass - opts.update(kwargs) - self.job_template = JobTemplate.objects.create(**opts) - return self.job_template - - def create_test_job(self, **kwargs): - job_template = kwargs.pop('job_template', None) - if job_template: - self.job = job_template.create_job(**kwargs) - else: - opts = { - 'name': 'test-job %s' % str(now()), - 'inventory': self.inventory, - 'project': self.project, - 'credential': self.credential, - 'job_type': 'run', - } - try: - opts['playbook'] = self.project.playbooks[0] - except (AttributeError, IndexError): - pass - opts.update(kwargs) - self.job = Job.objects.create(**opts) - return self.job - - def create_test_ad_hoc_command(self, **kwargs): - opts = { - 'inventory': self.inventory, - 'credential': self.credential, - 'module_name': 'command', - 'module_args': 'uptime', - } - opts.update(kwargs) - self.ad_hoc_command = AdHocCommand.objects.create(**opts) - return self.ad_hoc_command - - def test_cleanup_jobs(self): - # Test with no jobs to be cleaned up. - jobs_before = Job.objects.all().count() - self.assertFalse(jobs_before) - ad_hoc_commands_before = AdHocCommand.objects.all().count() - self.assertFalse(ad_hoc_commands_before) - result, stdout, stderr = self.run_command('cleanup_jobs') - self.assertEqual(result, None) - jobs_after = Job.objects.all().count() - self.assertEqual(jobs_before, jobs_after) - ad_hoc_commands_after = AdHocCommand.objects.all().count() - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.assertEqual(job.status, 'successful') - - # Create and run ad hoc command. - ad_hoc_command = self.create_test_ad_hoc_command() - self.assertEqual(ad_hoc_command.status, 'new') - 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) - self.assertEqual(ad_hoc_command.status, 'successful') - - # With days=1, no jobs will be deleted. - jobs_before = Job.objects.all().count() - self.assertTrue(jobs_before) - ad_hoc_commands_before = AdHocCommand.objects.all().count() - self.assertTrue(ad_hoc_commands_before) - result, stdout, stderr = self.run_command('cleanup_jobs', days=1) - self.assertEqual(result, None) - jobs_after = Job.objects.all().count() - self.assertEqual(jobs_before, jobs_after) - ad_hoc_commands_after = AdHocCommand.objects.all().count() - self.assertEqual(ad_hoc_commands_before, ad_hoc_commands_after) - - # With days=0 and dry_run=True, no jobs will be deleted. - jobs_before = Job.objects.all().count() - self.assertTrue(jobs_before) - ad_hoc_commands_before = AdHocCommand.objects.all().count() - self.assertTrue(ad_hoc_commands_before) - result, stdout, stderr = self.run_command('cleanup_jobs', days=0, - dry_run=True) - self.assertEqual(result, None) - jobs_after = Job.objects.all().count() - self.assertEqual(jobs_before, jobs_after) - ad_hoc_commands_after = AdHocCommand.objects.all().count() - self.assertEqual(ad_hoc_commands_before, ad_hoc_commands_after) - - # With days=0, our job and ad hoc command will be deleted. - jobs_before = Job.objects.all().count() - self.assertTrue(jobs_before) - ad_hoc_commands_before = AdHocCommand.objects.all().count() - self.assertTrue(ad_hoc_commands_before) - result, stdout, stderr = self.run_command('cleanup_jobs', days=0) - self.assertEqual(result, None) - jobs_after = Job.objects.all().count() - self.assertNotEqual(jobs_before, jobs_after) - self.assertFalse(jobs_after) - ad_hoc_commands_after = AdHocCommand.objects.all().count() - self.assertNotEqual(ad_hoc_commands_before, ad_hoc_commands_after) - self.assertFalse(ad_hoc_commands_after) - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class CleanupActivityStreamTest(BaseCommandMixin, BaseTest): - ''' - Test cases for cleanup_activitystream management command. - ''' - - def setUp(self): - super(CleanupActivityStreamTest, self).setUp() - self.start_rabbit() - self.create_test_inventories() - - def tearDown(self): - self.stop_rabbit() - super(CleanupActivityStreamTest, self).tearDown() - - def test_cleanup(self): - # Should already have entries due to test case setup. With no - # parameters, "days" defaults to 30, which won't cleanup anything. - count_before = ActivityStream.objects.count() - self.assertTrue(count_before) - result, stdout, stderr = self.run_command('cleanup_activitystream') - self.assertEqual(result, None) - count_after = ActivityStream.objects.count() - self.assertEqual(count_before, count_after) - - # With days=1, nothing should be changed. - result, stdout, stderr = self.run_command('cleanup_activitystream', days=1) - self.assertEqual(result, None) - count_after = ActivityStream.objects.count() - self.assertEqual(count_before, count_after) - - # With days=0 and dry_run=True, nothing should be changed. - result, stdout, stderr = self.run_command('cleanup_activitystream', days=0, dry_run=True) - self.assertEqual(result, None) - count_after = ActivityStream.objects.count() - self.assertEqual(count_before, count_after) - - # With days=0, everything should be cleaned up. - result, stdout, stderr = self.run_command('cleanup_activitystream', days=0) - self.assertEqual(result, None) - count_after = ActivityStream.objects.count() - self.assertNotEqual(count_before, count_after) - self.assertFalse(count_after) - - # Modify hosts to create 1000 activity stream entries. - t = time.time() - for x in xrange(0, 1000 / Host.objects.count()): - for host in Host.objects.all(): - host.name = u'%s-update-%d' % (host.name, x) - host.save() - create_elapsed = time.time() - t - - # Time how long it takes to cleanup activity stream, should be no more - # than 1/4 the time taken to create the entries. - count_before = ActivityStream.objects.count() - self.assertTrue(count_before) - t = time.time() - result, stdout, stderr = self.run_command('cleanup_activitystream', days=0) - cleanup_elapsed = time.time() - t - self.assertEqual(result, None) - count_after = ActivityStream.objects.count() - self.assertNotEqual(count_before, count_after) - self.assertFalse(count_after) - self.assertTrue(cleanup_elapsed < (create_elapsed / 4), - 'create took %0.3fs, cleanup took %0.3fs, expected < %0.3fs' % (create_elapsed, cleanup_elapsed, create_elapsed / 4)) diff --git a/awx/main/tests/old/inventory.py b/awx/main/tests/old/inventory.py deleted file mode 100644 index 528d9c2343..0000000000 --- a/awx/main/tests/old/inventory.py +++ /dev/null @@ -1,1448 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import glob -import json -import os -import re -import tempfile -import time -import unittest2 as unittest - - -# Django -from django.conf import settings -from django.core.urlresolvers import reverse -from django.test.utils import override_settings - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTest, BaseTransactionTest - -__all__ = ['InventoryTest', 'InventoryUpdatesTest', 'InventoryCredentialTest'] - -TEST_SIMPLE_INVENTORY_SCRIPT = "#!/usr/bin/env python\nimport json\nprint json.dumps({'hosts': ['ahost-01', 'ahost-02', 'ahost-03', 'ahost-04']})" -TEST_SIMPLE_INVENTORY_SCRIPT_WITHOUT_HASHBANG = "import json\nprint json.dumps({'hosts': ['ahost-01', 'ahost-02', 'ahost-03', 'ahost-04']})" - -TEST_UNICODE_INVENTORY_SCRIPT = u"""#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import json -inventory = dict() -inventory['group-\u037c\u03b4\u0138\u0137\u03cd\u03a1\u0121\u0137\u0138\u01a1'] = list() -inventory['group-\u037c\u03b4\u0138\u0137\u03cd\u03a1\u0121\u0137\u0138\u01a1'].append('host-\xb3\u01a0\u0157\u0157\u0157\u0157:\u02fe\u032d\u015f') -inventory['group-\u037c\u03b4\u0138\u0137\u03cd\u03a1\u0121\u0137\u0138\u01a1'].append('host-\u0124\u01bd\u03d1j\xffFK\u0145\u024c\u024c') -inventory['group-\u037c\u03b4\u0138\u0137\u03cd\u03a1\u0121\u0137\u0138\u01a1'].append('host-@|\u022e|\u022e\xbf\u03db\u0148\u02b9\xbf') -inventory['group-\u037c\u03b4\u0138\u0137\u03cd\u03a1\u0121\u0137\u0138\u01a1'].append('host-;\u023a\u023a\u0181\u017f\u0242\u0242\u029c\u0250') -inventory['group-\u037c\u03b4\u0138\u0137\u03cd\u03a1\u0121\u0137\u0138\u01a1'].append('host-B\u0338\u0338\u0330\u0365\u01b2\u02fa\xdd\u013b\u01b2') -print json.dumps(inventory) -""" - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class InventoryTest(BaseTest): - - def setUp(self): - self.start_rabbit() - super(InventoryTest, self).setUp() - self.setup_instances() - self.setup_users() - self.organizations = self.make_organizations(self.super_django_user, 3) - self.organizations[0].admin_role.members.add(self.normal_django_user) - self.organizations[0].member_role.members.add(self.other_django_user) - self.organizations[0].member_role.members.add(self.normal_django_user) - - self.inventory_a = Inventory.objects.create(name='inventory-a', description='foo', organization=self.organizations[0]) - self.inventory_b = Inventory.objects.create(name='inventory-b', description='bar', organization=self.organizations[1]) - - # the normal user is an org admin of org 0 - - # create a permission here on the 'other' user so they have edit access on the org - # we may add another permission type later. - self.inventory_b.read_role.members.add(self.other_django_user) - - def tearDown(self): - super(InventoryTest, self).tearDown() - self.stop_rabbit() - - def test_get_inventory_list(self): - url = reverse('api:inventory_list') - qs = Inventory.objects.distinct() - - # Check list view with invalid authentication. - self.check_invalid_auth(url) - - # a super user can list all inventories - self.check_get_list(url, self.super_django_user, qs) - - # an org admin can list inventories but is filtered to what he adminsters - normal_qs = qs.filter(organization__admin_role__members=self.normal_django_user) - self.check_get_list(url, self.normal_django_user, normal_qs) - - # a user who is on a team who has a read permissions on an inventory can see filtered inventories - other_qs = Inventory.accessible_objects(self.other_django_user, 'read_role').distinct() - self.check_get_list(url, self.other_django_user, other_qs) - - # a regular user not part of anything cannot see any inventories - nobody_qs = qs.none() - self.check_get_list(url, self.nobody_django_user, nobody_qs) - - def test_post_inventory_list(self): - url = reverse('api:inventory_list') - - # Check post to list view with invalid authentication. - new_inv_0 = dict(name='inventory-c', description='baz', organization=self.organizations[0].pk) - self.check_invalid_auth(url, new_inv_0, methods=('post',)) - - # a super user can create inventory - new_inv_1 = dict(name='inventory-c', description='baz', organization=self.organizations[0].pk) - new_id = max(Inventory.objects.values_list('pk', flat=True)) + 1 - with self.current_user(self.super_django_user): - data = self.post(url, data=new_inv_1, expect=201) - self.assertEquals(data['id'], new_id) - - # an org admin of any org can create inventory, if it is one of his organizations - # the organization parameter is required! - new_inv_incomplete = dict(name='inventory-d', description='baz') - new_inv_not_my_org = dict(name='inventory-d', description='baz', organization=self.organizations[2].pk) - new_inv_my_org = dict(name='inventory-d', description='baz', organization=self.organizations[0].pk) - with self.current_user(self.normal_django_user): - data = self.post(url, data=new_inv_incomplete, expect=400) - data = self.post(url, data=new_inv_not_my_org, expect=403) - data = self.post(url, data=new_inv_my_org, expect=201) - - # a regular user cannot create inventory - new_inv_denied = dict(name='inventory-e', description='glorp', organization=self.organizations[0].pk) - with self.current_user(self.other_django_user): - data = self.post(url, data=new_inv_denied, expect=403) - - def test_get_inventory_detail(self): - url_a = reverse('api:inventory_detail', args=(self.inventory_a.pk,)) - url_b = reverse('api:inventory_detail', args=(self.inventory_b.pk,)) - - # Check detail view with invalid authentication. - self.check_invalid_auth(url_a) - self.check_invalid_auth(url_b) - - # a super user can get inventory records - with self.current_user(self.super_django_user): - data = self.get(url_a, expect=200) - self.assertEquals(data['name'], 'inventory-a') - - # an org admin can get inventory records for his orgs only - with self.current_user(self.normal_django_user): - data = self.get(url_a, expect=200) - self.assertEquals(data['name'], 'inventory-a') - data = self.get(url_b, expect=403) - - # a user who is on a team who has read permissions on an inventory can see inventory records - with self.current_user(self.other_django_user): - data = self.get(url_a, expect=403) - data = self.get(url_b, expect=200) - self.assertEquals(data['name'], 'inventory-b') - - # a regular user cannot read any inventory records - with self.current_user(self.nobody_django_user): - data = self.get(url_a, expect=403) - data = self.get(url_b, expect=403) - - def test_put_inventory_detail(self): - url_a = reverse('api:inventory_detail', args=(self.inventory_a.pk,)) - url_b = reverse('api:inventory_detail', args=(self.inventory_b.pk,)) - - # Check put to detail view with invalid authentication. - self.check_invalid_auth(url_a, methods=('put',)) - self.check_invalid_auth(url_b, methods=('put',)) - - # a super user can update inventory records - with self.current_user(self.super_django_user): - data = self.get(url_a, expect=200) - data['name'] = 'inventory-a-update1' - self.put(url_a, data, expect=200) - data = self.get(url_b, expect=200) - data['name'] = 'inventory-b-update1' - self.put(url_b, data, expect=200) - - # an org admin can update inventory records for his orgs only. - with self.current_user(self.normal_django_user): - data = self.get(url_a, expect=200) - data['name'] = 'inventory-a-update2' - self.put(url_a, data, expect=200) - self.put(url_b, data, expect=403) - - # a user who is on a team who has read permissions on an inventory can - # see inventory records, but not update. - with self.current_user(self.other_django_user): - data = self.get(url_b, expect=200) - data['name'] = 'inventory-b-update3' - self.put(url_b, data, expect=403) - - # a regular user cannot update any inventory records - with self.current_user(self.nobody_django_user): - self.put(url_a, {}, expect=403) - self.put(url_b, {}, expect=403) - - # a superuser can reassign an inventory to another organization. - with self.current_user(self.super_django_user): - data = self.get(url_b, expect=200) - self.assertEqual(data['organization'], self.organizations[1].pk) - data['organization'] = self.organizations[0].pk - self.put(url_b, data, expect=200) - - # a normal user can't reassign an inventory to an organization where - # he isn't an admin. - with self.current_user(self.normal_django_user): - data = self.get(url_a, expect=200) - self.assertEqual(data['organization'], self.organizations[0].pk) - data['organization'] = self.organizations[1].pk - self.put(url_a, data, expect=403) - - # Via AC-376: - # Create an inventory. Leave the description empty. - # Edit the new inventory, change the Name, click Save. - list_url = reverse('api:inventory_list') - new_data = dict(name='inventory-c', description='', - organization=self.organizations[0].pk) - new_id = max(Inventory.objects.values_list('pk', flat=True)) + 1 - with self.current_user(self.super_django_user): - data = self.post(list_url, data=new_data, expect=201) - self.assertEqual(data['id'], new_id) - self.assertEqual(data['description'], '') - url_c = reverse('api:inventory_detail', args=(new_id,)) - data = self.get(url_c, expect=200) - self.assertEqual(data['description'], '') - data['description'] = None - #data['name'] = 'inventory-a-update2' - self.put(url_c, data, expect=200) - - def test_delete_inventory_detail(self): - url_a = reverse('api:inventory_detail', args=(self.inventory_a.pk,)) - url_b = reverse('api:inventory_detail', args=(self.inventory_b.pk,)) - - # Create test hosts and groups within each inventory. - self.inventory_a.hosts.create(name='host-a') - self.inventory_a.groups.create(name='group-a') - self.inventory_b.hosts.create(name='host-b') - self.inventory_b.groups.create(name='group-b') - a_pk = self.inventory_a.pk - b_pk = self.inventory_b.pk - - # Check put to detail view with invalid authentication. - self.check_invalid_auth(url_a, methods=('delete',)) - self.check_invalid_auth(url_b, methods=('delete',)) - - # a regular user cannot delete any inventory records - with self.current_user(self.nobody_django_user): - self.delete(url_a, expect=403) - self.delete(url_b, expect=403) - - # a user who is on a team who has read permissions on an inventory can - # see inventory records, but not delete. - with self.current_user(self.other_django_user): - self.get(url_b, expect=200) - self.delete(url_b, expect=403) - - # an org admin can delete inventory records for his orgs only. - with self.current_user(self.normal_django_user): - self.get(url_a, expect=200) - self.delete(url_a, expect=204) - self.delete(url_b, expect=403) - - # Verify that the inventory was deleted - assert Inventory.objects.filter(pk=a_pk).count() == 0 - - # a super user can delete inventory records - with self.current_user(self.super_django_user): - self.delete(url_a, expect=404) - self.delete(url_b, expect=204) - - # Verify that the inventory was deleted - assert Inventory.objects.filter(pk=b_pk).count() == 0 - - def test_inventory_access_deleted_permissions(self): - temp_org = self.make_organizations(self.super_django_user, 1)[0] - temp_org.admin_role.members.add(self.normal_django_user) - temp_org.member_role.members.add(self.other_django_user) - temp_org.member_role.members.add(self.normal_django_user) - temp_inv = temp_org.inventories.create(name='Delete Org Inventory') - temp_inv.groups.create(name='Delete Org Inventory Group') - - temp_inv.read_role.members.add(self.other_django_user) - - reverse('api:organization_detail', args=(temp_org.pk,)) - inventory_detail = reverse('api:inventory_detail', args=(temp_inv.pk,)) - read_role_users_list = reverse('api:role_users_list', args=(temp_inv.read_role.pk,)) - - self.get(inventory_detail, expect=200, auth=self.get_other_credentials()) - self.post(read_role_users_list, data={'disassociate': True, "id": self.other_django_user.id}, expect=204, auth=self.get_super_credentials()) - self.get(inventory_detail, expect=403, auth=self.get_other_credentials()) - - def test_create_inventory_script(self): - inventory_scripts = reverse('api:inventory_script_list') - new_script = dict(name="Test", description="Test Script", script=TEST_SIMPLE_INVENTORY_SCRIPT, organization=self.organizations[0].id) - self.post(inventory_scripts, data=new_script, expect=201, auth=self.get_super_credentials()) - - got = self.get(inventory_scripts, expect=200, auth=self.get_super_credentials()) - self.assertEquals(got['count'], 1) - - new_failed_script = dict( - name="Should not fail", description="This test should not fail", - script=TEST_SIMPLE_INVENTORY_SCRIPT, organization=self.organizations[0].id - ) - self.post(inventory_scripts, data=new_failed_script, expect=201, auth=self.get_normal_credentials()) - - failed_no_shebang = dict(name="ShouldFail", descript="This test should fail", script=TEST_SIMPLE_INVENTORY_SCRIPT_WITHOUT_HASHBANG, - organization=self.organizations[0].id) - self.post(inventory_scripts, data=failed_no_shebang, expect=400, auth=self.get_super_credentials()) - - def test_get_inventory_script_view(self): - i_a = self.inventory_a - i_a.variables = json.dumps({'i-vars': 123}) - i_a.save() - # Group A is parent of B, B is parent of C, C is parent of D. - g_a = i_a.groups.create(name='A', variables=json.dumps({'A-vars': 'AAA'})) - g_b = i_a.groups.create(name='B', variables=json.dumps({'B-vars': 'BBB'})) - g_b.parents.add(g_a) - g_c = i_a.groups.create(name='C', variables=json.dumps({'C-vars': 'CCC'})) - g_c.parents.add(g_b) - g_d = i_a.groups.create(name='D', variables=json.dumps({'D-vars': 'DDD'})) - g_d.parents.add(g_c) - # Each group "X" contains one host "x". - h_a = i_a.hosts.create(name='a', variables=json.dumps({'a-vars': 'aaa'})) - h_a.groups.add(g_a) - h_b = i_a.hosts.create(name='b', variables=json.dumps({'b-vars': 'bbb'})) - h_b.groups.add(g_b) - h_c = i_a.hosts.create(name='c', variables=json.dumps({'c-vars': 'ccc'})) - h_c.groups.add(g_c) - h_d = i_a.hosts.create(name='d', variables=json.dumps({'d-vars': 'ddd'})) - h_d.groups.add(g_d) - # Add another host not in any groups. - i_a.hosts.create(name='z', variables=json.dumps({'z-vars': 'zzz'})) - - # Old, slow 1.2 way. - url = reverse('api:inventory_script_view', args=(i_a.pk,)) - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertTrue('all' in response) - self.assertEqual(response['all']['vars'], i_a.variables_dict) - self.assertEqual(response['all']['hosts'], ['z']) - for g in i_a.groups.all(): - self.assertTrue(g.name in response) - self.assertEqual(response[g.name]['vars'], g.variables_dict) - self.assertEqual(set(response[g.name]['children']), - set(g.children.values_list('name', flat=True))) - self.assertEqual(set(response[g.name]['hosts']), - set(g.hosts.values_list('name', flat=True))) - self.assertFalse('_meta' in response) - for h in i_a.hosts.all(): - h_url = '%s?host=%s' % (url, h.name) - with self.current_user(self.super_django_user): - response = self.get(h_url, expect=200) - self.assertEqual(response, h.variables_dict) - - # Now add localhost to the inventory. - i_a.hosts.create(name='localhost', variables=json.dumps({'ansible_connection': 'local'})) - - # New 1.3 way. - url = reverse('api:inventory_script_view', args=(i_a.pk,)) - url = '%s?hostvars=1' % url - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertTrue('all' in response) - self.assertEqual(response['all']['vars'], i_a.variables_dict) - self.assertEqual(response['all']['hosts'], ['localhost', 'z']) - self.assertTrue('_meta' in response) - self.assertTrue('hostvars' in response['_meta']) - for h in i_a.hosts.all(): - self.assertEqual(response['_meta']['hostvars'][h.name], - h.variables_dict) - - def test_get_inventory_tree_view(self): - # Group A is parent of B, B is parent of C, C is parent of D. - g_a = self.inventory_a.groups.create(name='A') - g_a.inventory_source - g_b = self.inventory_a.groups.create(name='B') - g_b.inventory_source - g_b.parents.add(g_a) - g_c = self.inventory_a.groups.create(name='C') - g_c.inventory_source - g_c.parents.add(g_b) - g_d = self.inventory_a.groups.create(name='D') - g_d.inventory_source - g_d.parents.add(g_c) - - url = reverse('api:inventory_tree_view', args=(self.inventory_a.pk,)) - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - - self.assertTrue(isinstance(response, list)) - self.assertEqual(len(response), 1) - self.assertEqual(response[0]['id'], g_a.pk) - self.assertEqual(len(response[0]['children']), 1) - self.assertEqual(response[0]['children'][0]['id'], g_b.pk) - self.assertEqual(len(response[0]['children'][0]['children']), 1) - self.assertEqual(response[0]['children'][0]['children'][0]['id'], g_c.pk) - self.assertEqual(len(response[0]['children'][0]['children'][0]['children']), 1) - self.assertEqual(response[0]['children'][0]['children'][0]['children'][0]['id'], g_d.pk) - self.assertEqual(len(response[0]['children'][0]['children'][0]['children'][0]['children']), 0) - - def test_migrate_children_when_group_removed(self): - # Group A is parent of B, B is parent of C, C is parent of D. - g_a = self.inventory_a.groups.create(name='A') - g_b = self.inventory_a.groups.create(name='B') - g_b.parents.add(g_a) - g_c = self.inventory_a.groups.create(name='C') - g_c.parents.add(g_b) - g_d = self.inventory_a.groups.create(name='D') - g_d.parents.add(g_c) - # Each group "X" contains one host "x". - h_a = self.inventory_a.hosts.create(name='a') - h_a.groups.add(g_a) - h_b = self.inventory_a.hosts.create(name='b') - h_b.groups.add(g_b) - h_c = self.inventory_a.hosts.create(name='c') - h_c.groups.add(g_c) - h_d = self.inventory_a.hosts.create(name='d') - h_d.groups.add(g_d) - - # Verify that grand-child groups/hosts are not direct children of the - # parent groups. - self.assertFalse(g_c in g_a.children.all()) - self.assertFalse(g_d in g_a.children.all()) - self.assertFalse(g_d in g_b.children.all()) - self.assertFalse(h_b in g_a.hosts.all()) - self.assertFalse(h_c in g_a.hosts.all()) - self.assertFalse(h_c in g_b.hosts.all()) - self.assertFalse(h_d in g_a.hosts.all()) - self.assertFalse(h_d in g_b.hosts.all()) - self.assertFalse(h_d in g_c.hosts.all()) - - # Delete group B. Its child groups and hosts should now be attached to - # group A. Group C and D hosts and child groups should be unchanged. - g_b.delete() - self.assertTrue(g_c in g_a.children.all()) - self.assertTrue(h_b in g_a.hosts.all()) - self.assertFalse(g_d in g_a.children.all()) - self.assertFalse(h_c in g_a.hosts.all()) - self.assertFalse(h_d in g_a.hosts.all()) - self.assertFalse(h_d in g_c.hosts.all()) - - # Mark group C inactive. Its child groups and hosts should now also be - # attached to group A. Group D hosts should be unchanged. Group C - # should also no longer have any group or host relationships. - g_c.delete() - self.assertTrue(g_d in g_a.children.all()) - self.assertTrue(h_c in g_a.hosts.all()) - self.assertFalse(h_d in g_a.hosts.all()) - - def test_safe_delete_recursion(self): - # First hierarchy - top_group = self.inventory_a.groups.create(name='Top1') - sub_group = self.inventory_a.groups.create(name='Sub1') - low_group = self.inventory_a.groups.create(name='Low1') - - # Second hierarchy - other_top_group = self.inventory_a.groups.create(name='Top2') - other_sub_group = self.inventory_a.groups.create(name='Sub2') - other_low_group = self.inventory_a.groups.create(name='Low2') - - sub_group.parents.add(top_group) - low_group.parents.add(sub_group) - - other_sub_group.parents.add(other_top_group) - other_low_group.parents.add(other_sub_group) - - t1 = self.inventory_a.hosts.create(name='t1') - t1.groups.add(top_group) - s1 = self.inventory_a.hosts.create(name='s1') - s1.groups.add(sub_group) - l1 = self.inventory_a.hosts.create(name='l1') - l1.groups.add(low_group) - - t2 = self.inventory_a.hosts.create(name='t2') - t2.groups.add(other_top_group) - s2 = self.inventory_a.hosts.create(name='s2') - s2.groups.add(other_sub_group) - l2 = self.inventory_a.hosts.create(name='l2') - l2.groups.add(other_low_group) - - # Copy second hierarchy subgroup under the first hierarchy subgroup - other_sub_group.parents.add(sub_group) - self.assertTrue(s2 in sub_group.all_hosts.all()) - self.assertTrue(other_sub_group in sub_group.children.all()) - - # Now recursively remove its parent and the reference from subgroup should remain - other_top_group.delete_recursive() - self.assertTrue(s2 in sub_group.all_hosts.all()) - self.assertTrue(other_sub_group in sub_group.children.all()) - - def test_group_parents_and_children(self): - # Test for various levels of group parent/child relations, with hosts, - # to verify that helper properties return the correct querysets. - - # Group A is parent of B, B is parent of C, C is parent of D. Group E - # is part of the inventory, but outside of the ABCD tree. - g_a = self.inventory_a.groups.create(name='A') - g_b = self.inventory_a.groups.create(name='B') - g_b.parents.add(g_a) - g_c = self.inventory_a.groups.create(name='C') - g_c.parents.add(g_b) - g_d = self.inventory_a.groups.create(name='D') - g_d.parents.add(g_c) - g_e = self.inventory_a.groups.create(name='E') - # Each group "X" contains one host "x". - h_a = self.inventory_a.hosts.create(name='a') - h_a.groups.add(g_a) - h_b = self.inventory_a.hosts.create(name='b') - h_b.groups.add(g_b) - h_c = self.inventory_a.hosts.create(name='c') - h_c.groups.add(g_c) - h_d = self.inventory_a.hosts.create(name='d') - h_d.groups.add(g_d) - h_e = self.inventory_a.hosts.create(name='e') - h_e.groups.add(g_e) - # Test all_children property on groups. - self.assertEqual(set(g_a.all_children.values_list('pk', flat=True)), - set([g_b.pk, g_c.pk, g_d.pk])) - self.assertEqual(set(g_b.all_children.values_list('pk', flat=True)), - set([g_c.pk, g_d.pk])) - self.assertEqual(set(g_c.all_children.values_list('pk', flat=True)), - set([g_d.pk])) - self.assertEqual(set(g_d.all_children.values_list('pk', flat=True)), - set([])) - self.assertEqual(set(g_e.all_children.values_list('pk', flat=True)), - set([])) - # Test all_parents property on groups. - self.assertEqual(set(g_a.all_parents.values_list('pk', flat=True)), - set([])) - self.assertEqual(set(g_b.all_parents.values_list('pk', flat=True)), - set([g_a.pk])) - self.assertEqual(set(g_c.all_parents.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk])) - self.assertEqual(set(g_d.all_parents.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk, g_c.pk])) - self.assertEqual(set(g_e.all_parents.values_list('pk', flat=True)), - set([])) - # Test all_hosts property on groups. - self.assertEqual(set(g_a.all_hosts.values_list('pk', flat=True)), - set([h_a.pk, h_b.pk, h_c.pk, h_d.pk])) - self.assertEqual(set(g_b.all_hosts.values_list('pk', flat=True)), - set([h_b.pk, h_c.pk, h_d.pk])) - self.assertEqual(set(g_c.all_hosts.values_list('pk', flat=True)), - set([h_c.pk, h_d.pk])) - self.assertEqual(set(g_d.all_hosts.values_list('pk', flat=True)), - set([h_d.pk])) - self.assertEqual(set(g_e.all_hosts.values_list('pk', flat=True)), - set([h_e.pk])) - # Test all_groups property on hosts. - self.assertEqual(set(h_a.all_groups.values_list('pk', flat=True)), - set([g_a.pk])) - self.assertEqual(set(h_b.all_groups.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk])) - self.assertEqual(set(h_c.all_groups.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk, g_c.pk])) - self.assertEqual(set(h_d.all_groups.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk, g_c.pk, g_d.pk])) - self.assertEqual(set(h_e.all_groups.values_list('pk', flat=True)), - set([g_e.pk])) - # Now create a circular relationship from D back to A. - g_a.parents.add(g_d) - # All groups "ABCD" should be parents of each other, and children of - # each other, and contain all hosts "abcd". - for g in [g_a, g_b, g_c, g_d]: - self.assertEqual(set(g.all_children.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk, g_c.pk, g_d.pk])) - self.assertEqual(set(g.all_parents.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk, g_c.pk, g_d.pk])) - self.assertEqual(set(g.all_hosts.values_list('pk', flat=True)), - set([h_a.pk, h_b.pk, h_c.pk, h_d.pk])) - # All hosts "abcd" should be members of all groups "ABCD". - for h in [h_a, h_b, h_c, h_d]: - self.assertEqual(set(h.all_groups.values_list('pk', flat=True)), - set([g_a.pk, g_b.pk, g_c.pk, g_d.pk])) - # Group E and host e should not be affected. - self.assertEqual(set(g_e.all_children.values_list('pk', flat=True)), - set([])) - self.assertEqual(set(g_e.all_parents.values_list('pk', flat=True)), - set([])) - self.assertEqual(set(g_e.all_hosts.values_list('pk', flat=True)), - set([h_e.pk])) - self.assertEqual(set(h_e.all_groups.values_list('pk', flat=True)), - set([g_e.pk])) - - def test_dashboard_hosts_count(self): - url = reverse('api:dashboard_view') - - # Test with zero hosts. - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertEqual(response['hosts']['total'], 0) - self.assertEqual(response['hosts']['failed'], 0) - - # Create hosts with the same name in different inventories. This host - # count should include total hosts, not unique names. - for x in xrange(4): - hostname = 'host-%d' % x - self.inventory_a.hosts.create(name=hostname) - self.inventory_b.hosts.create(name=hostname) - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertEqual(response['hosts']['total'], 8) - self.assertEqual(response['hosts']['failed'], 0) - - # Mark all hosts in one inventory as failed. Failed count should - # reflect all hosts, not unique hostnames. - for host in self.inventory_a.hosts.all(): - host.has_active_failures = True - host.save() - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertEqual(response['hosts']['total'], 8) - self.assertEqual(response['hosts']['failed'], 4) - - # Mark all hosts in the other inventory as failed. Failed count - # should reflect all hosts and never be greater than total. - for host in self.inventory_b.hosts.all(): - host.has_active_failures = True - host.save() - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertEqual(response['hosts']['total'], 8) - self.assertEqual(response['hosts']['failed'], 8) - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - IGNORE_CELERY_INSPECTOR=True, - UNIT_TEST_IGNORE_TASK_WAIT=True, - PEXPECT_TIMEOUT=60) -class InventoryUpdatesTest(BaseTransactionTest): - - def setUp(self): - super(InventoryUpdatesTest, self).setUp() - self.setup_instances() - self.setup_users() - self.organization = self.make_organizations(self.super_django_user, 1)[0] - self.organization.admin_role.members.add(self.normal_django_user) - self.organization.member_role.members.add(self.other_django_user) - self.organization.member_role.members.add(self.normal_django_user) - self.inventory = self.organization.inventories.create(name='Cloud Inventory') - self.group = self.inventory.groups.create(name='Cloud Group') - self.inventory2 = self.organization.inventories.create(name='Cloud Inventory 2') - self.group2 = self.inventory2.groups.create(name='Cloud Group 2') - self.start_queue() - - def tearDown(self): - super(InventoryUpdatesTest, self).tearDown() - self.terminate_queue() - - def update_inventory_source(self, group, **kwargs): - inventory_source = group.inventory_source - update_fields = [] - for field, value in kwargs.items(): - if getattr(inventory_source, field) != value: - setattr(inventory_source, field, value) - update_fields.append(field) - if update_fields: - inventory_source.save(update_fields=update_fields) - return inventory_source - - def check_inventory_update(self, inventory_source, should_fail=False, - **kwargs): - inventory_update = kwargs.pop('inventory_update', None) - should_error = kwargs.pop('should_error', False) - if not inventory_update: - inventory_update = inventory_source.update(**kwargs) - if not should_fail and not should_error: - self.assertTrue(inventory_update) - elif not inventory_update: - return None - inventory_update = InventoryUpdate.objects.get(pk=inventory_update.pk) - #print inventory_update.result_stdout - if should_error: - self.assertEqual(inventory_update.status, 'error', - inventory_update.result_stdout + - inventory_update.result_traceback) - elif should_fail: - self.assertEqual(inventory_update.status, 'failed', - inventory_update.result_stdout + - inventory_update.result_traceback) - elif should_fail is False: - self.assertEqual(inventory_update.status, 'successful', - inventory_update.result_stdout + - inventory_update.result_traceback) - else: - pass # If should_fail is None, we don't care. - return inventory_update - - def check_inventory_source(self, inventory_source, initial=True, enabled_host_pks=None, instance_id_group_ok=False): - enabled_host_pks = enabled_host_pks or set() - inventory_source = InventorySource.objects.get(pk=inventory_source.pk) - inventory = inventory_source.group.inventory - self.assertTrue(inventory_source.can_update) - if initial: - self.assertEqual(inventory.groups.count(), 1) - self.assertEqual(inventory.hosts.count(), 0) - self.assertEqual(inventory_source.groups.count(), 0) - self.assertEqual(inventory_source.hosts.count(), 0) - inventory_update = self.check_inventory_update(inventory_source) - inventory_source = InventorySource.objects.get(pk=inventory_source.pk) - self.assertNotEqual(inventory.groups.count(), 1) - self.assertNotEqual(inventory.hosts.count(), 0) - self.assertNotEqual(inventory_source.groups.count(), 0) - self.assertNotEqual(inventory_source.hosts.count(), 0) - with self.current_user(self.super_django_user): - url = reverse('api:inventory_source_groups_list', args=(inventory_source.pk,)) - response = self.get(url, expect=200) - self.assertNotEqual(response['count'], 0) - url = reverse('api:inventory_source_hosts_list', args=(inventory_source.pk,)) - response = self.get(url, expect=200) - self.assertNotEqual(response['count'], 0) - for host in inventory.hosts.all(): - source_pks = host.inventory_sources.values_list('pk', flat=True) - self.assertTrue(inventory_source.pk in source_pks) - self.assertTrue(host.has_inventory_sources) - if host.pk in enabled_host_pks: - self.assertTrue(host.enabled) - # Make sure EC2 RDS hosts are excluded. - if inventory_source.source == 'ec2': - self.assertFalse(re.match(r'^.+\.rds\.amazonaws\.com$', host.name, re.I), - host.name) - with self.current_user(self.super_django_user): - url = reverse('api:host_inventory_sources_list', args=(host.pk,)) - response = self.get(url, expect=200) - self.assertNotEqual(response['count'], 0) - for group in inventory.groups.all(): - source_pks = group.inventory_sources.values_list('pk', flat=True) - self.assertTrue(inventory_source.pk in source_pks) - self.assertTrue(group.has_inventory_sources) - self.assertTrue(group.children.exists() or - group.hosts.exists()) - # Make sure EC2 instance ID groups and RDS groups are excluded. - if inventory_source.source == 'ec2' and not instance_id_group_ok: - self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I), - group.name) - if inventory_source.source == 'ec2': - self.assertFalse(re.match(r'^rds|rds_.+|type_db_.+$', group.name, re.I), - group.name) - # Make sure Rackspace instance ID groups are excluded. - if inventory_source.source == 'rax' and not instance_id_group_ok: - self.assertFalse(re.match(r'^instance-.+$', group.name, re.I), - group.name) - with self.current_user(self.super_django_user): - url = reverse('api:group_inventory_sources_list', args=(group.pk,)) - response = self.get(url, expect=200) - self.assertNotEqual(response['count'], 0) - # Try to set a source on a child group that was imported. Should not - # be allowed. - for group in inventory_source.group.children.all(): - inv_src_2 = group.inventory_source - inv_src_url2 = reverse('api:inventory_source_detail', args=(inv_src_2.pk,)) - with self.current_user(self.super_django_user): - data = self.get(inv_src_url2, expect=200) - if inventory_source.credential is not None: - data.update({ - 'source': inventory_source.source, - 'credential': inventory_source.credential.pk, - }) - else: - data.update({'source': inventory_source.source}) - if inventory_source.source == 'custom': - data['source_script'] = inventory_source.source_script.pk - response = self.put(inv_src_url2, data, expect=400) - self.assertTrue('source' in response, response) - # Make sure we can delete the inventory update. - inv_up_url = reverse('api:inventory_update_detail', args=(inventory_update.pk,)) - with self.current_user(self.super_django_user): - self.get(inv_up_url, expect=200) - self.delete(inv_up_url, expect=204) - self.get(inv_up_url, expect=404) - - def print_group_tree(self, group, depth=0): - print (' ' * depth) + '+ ' + group.name - for host in group.hosts.order_by('name'): - print (' ' * depth) + ' - ' + host.name - for child in group.children.order_by('name'): - self.print_group_tree(child, depth + 1) - - def print_inventory_tree(self, inventory): - # Print out group/host tree for debugging. - for group in inventory.root_groups.order_by('name'): - self.print_group_tree(group) - - def test_put_inventory_source_detail_with_regions(self): - creds_url = reverse('api:credential_list') - inv_src_url1 = reverse('api:inventory_source_detail', - args=(self.group.inventory_source.pk,)) - inv_src_url2 = reverse('api:inventory_source_detail', - args=(self.group2.inventory_source.pk,)) - # Create an AWS credential to use for first inventory source. - aws_cred_data = { - 'name': 'AWS key that does not need to have valid info because ' - 'we do not care if the update actually succeeds', - 'kind': 'aws', - 'user': self.super_django_user.pk, - 'username': 'aws access key id goes here', - 'password': 'aws secret access key goes here', - } - with self.current_user(self.super_django_user): - aws_cred_response = self.post(creds_url, aws_cred_data, expect=201) - aws_cred_id = aws_cred_response['id'] - # Create a RAX credential to use for second inventory source. - rax_cred_data = { - 'name': 'RAX cred that does not need to have valid info because ' - 'we do not care if the update actually succeeds', - 'kind': 'rax', - 'user': self.super_django_user.pk, - 'username': 'rax username', - 'password': 'rax api key', - } - with self.current_user(self.super_django_user): - rax_cred_response = self.post(creds_url, rax_cred_data, expect=201) - rax_cred_id = rax_cred_response['id'] - # Verify the options request gives ec2 and rax region choices. - with self.current_user(self.super_django_user): - response = self.options(inv_src_url1, expect=200) - self.assertTrue('ec2_region_choices' in response['actions']['GET']['source_regions']) - self.assertTrue('rax_region_choices' in response['actions']['GET']['source_regions']) - # Updaate the first inventory source to use EC2 with empty regions. - inv_src_data = { - 'source': 'ec2', - 'credential': aws_cred_id, - 'source_regions': '', - 'instance_filters': '', - 'group_by': '', - } - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], '') - # EC2 sources should allow an empty credential (to support IAM roles). - inv_src_data['credential'] = None - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['credential'], None) - inv_src_data['credential'] = aws_cred_id - # Null for instance filters and group_by should be converted to empty - # string. - inv_src_data['instance_filters'] = None - inv_src_data['group_by'] = None - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['instance_filters'], '') - self.assertEqual(response['group_by'], '') - # Invalid string for instance filters. - inv_src_data['instance_filters'] = 'tag-key_123=Name,' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # Invalid field name for instance filters. - inv_src_data['instance_filters'] = 'foo=bar,' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # Invalid tag expression for instance filters. - inv_src_data['instance_filters'] = 'tag:=,' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # Another invalid tag expression for instance filters. - inv_src_data['instance_filters'] = 'tag:Name,' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # Valid string for instance filters. - inv_src_data['instance_filters'] = 'tag-key=Name' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - # Another valid value for instance filters. - inv_src_data['instance_filters'] = 'tag:Name=test*' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - # Another valid instance filter with nothing after =. - inv_src_data['instance_filters'] = 'tag:Name=' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - # Invalid string for group_by. - inv_src_data['group_by'] = 'ec2_region,' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # Valid string for group_by. - inv_src_data['group_by'] = 'region,key_pair,instance_type' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - # All region. - inv_src_data['source_regions'] = 'ALL' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'all') - # Invalid region. - inv_src_data['source_regions'] = 'us-north-99' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # All takes precedence over any other regions. - inv_src_data['source_regions'] = 'us-north-99,,all' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'all') - # Valid region. - inv_src_data['source_regions'] = 'us-west-1' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'us-west-1') - # Invalid region (along with valid one). - inv_src_data['source_regions'] = 'us-west-1, us-north-99' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=400) - # Valid regions. - inv_src_data['source_regions'] = 'us-west-1, us-east-1, ' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url1, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'us-west-1,us-east-1') - # Updaate the second inventory source to use RAX with empty regions. - inv_src_data = { - 'source': 'rax', - 'credential': rax_cred_id, - 'source_regions': '', - 'instance_filters': None, - 'group_by': None, - } - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], '') - # All region. - inv_src_data['source_regions'] = 'all' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'ALL') - # Invalid region. - inv_src_data['source_regions'] = 'RDU' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=400) - # All takes precedence over any other regions. - inv_src_data['source_regions'] = 'RDU,,all' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'ALL') - # Valid region. - inv_src_data['source_regions'] = 'dfw' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'DFW') - # Invalid region (along with valid one). - inv_src_data['source_regions'] = 'dfw, rdu' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=400) - # Valid regions. - inv_src_data['source_regions'] = 'ORD, iad, ' - with self.current_user(self.super_django_user): - response = self.put(inv_src_url2, inv_src_data, expect=200) - self.assertEqual(response['source_regions'], 'ORD,IAD') - - def test_update_from_ec2(self): - source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '') - source_password = getattr(settings, 'TEST_AWS_SECRET_ACCESS_KEY', '') - source_regions = getattr(settings, 'TEST_AWS_REGIONS', 'all') - if not all([source_username, source_password]): - self.skipTest('no test ec2 credentials defined!') - self.create_test_license_file() - credential = Credential.objects.create(kind='aws', - username=source_username, - password=source_password) - credential.admin_role.members.add(self.super_django_user) - # Set parent group name to one that might be created by the sync. - group = self.group - group.name = 'ec2' - group.save() - self.group = group - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - inventory_source = self.update_inventory_source(self.group, - source='ec2', credential=credential, source_regions=source_regions, - source_vars='---\n\nnested_groups: false\ncache_path: %s\n' % cache_path) - # Check first without instance_id set (to import by name only). - with self.settings(EC2_INSTANCE_ID_VAR=''): - self.check_inventory_source(inventory_source) - # Rename hosts and verify the import picks up the instance_id present - # in host variables. - for host in self.inventory.hosts.all(): - self.assertFalse(host.instance_id, host.instance_id) - host.name = 'updated-%s' % host.name - host.save() - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertEqual(old_host_pks, new_host_pks) - # Manually disable all hosts, verify a new update re-enables them. - # Also change the host name, and verify it is not deleted, but instead - # updated because the instance ID matches. - enabled_host_pks = set(self.inventory.hosts.filter(enabled=True).values_list('pk', flat=True)) - instance_types = {} - key_names = {} - for host in self.inventory.hosts.all(): - host.enabled = False - host.name = 'changed-%s' % host.name - host.save() - # Get instance types for later use with instance_filters. - instance_type = host.variables_dict.get('ec2_instance_type', '') - if instance_type: - instance_types.setdefault(instance_type, []).append(host.pk) - # Get key names for later use with instance_filters. - key_name = host.variables_dict.get('ec2_key_name', '') - if key_name: - key_names.setdefault(key_name, []).append(host.pk) - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False, enabled_host_pks=enabled_host_pks) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertEqual(old_host_pks, new_host_pks) - # Verify that main group is in top level groups (hasn't been added as - # its own child). - self.assertTrue(self.group in self.inventory.root_groups) - - # Now add instance filters and verify that only matching hosts are - # synced, specify new cache path to force refresh. - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - instance_type = max(instance_types.items(), key=lambda x: len(x[1]))[0] - inventory_source.instance_filters = 'instance-type=%s' % instance_type - inventory_source.source_vars = '---\n\nnested_groups: false\ncache_path: %s\n' % cache_path - inventory_source.overwrite = True - inventory_source.save() - self.check_inventory_source(inventory_source, initial=False) - for host in self.inventory.hosts.all(): - self.assertEqual(host.variables_dict['ec2_instance_type'], instance_type) - - # Try invalid instance filters that should be ignored: - # empty filter, only "=", more than one "=", whitespace, invalid value - # for given filter name. - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - key_name = max(key_names.items(), key=lambda x: len(x[1]))[0] - inventory_source.instance_filters = ',=,image-id=ami=12345678,instance-type=%s, key-name=%s, architecture=ppc' % (instance_type, key_name) - inventory_source.source_vars = '---\n\nnested_groups: false\ncache_path: %s\n' % cache_path - inventory_source.save() - self.check_inventory_source(inventory_source, initial=False) - - - def test_update_from_ec2_sts_iam(self): - source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '') - source_password = getattr(settings, 'TEST_AWS_SECRET_ACCESS_KEY', '') - source_regions = getattr(settings, 'TEST_AWS_REGIONS', 'all') - source_token = getattr(settings, 'TEST_AWS_SECURITY_TOKEN', '') - if not all([source_username, source_password, source_token]): - self.skipTest('no test ec2 sts credentials defined!') - self.create_test_license_file() - credential = Credential.objects.create(kind='aws', - username=source_username, - password=source_password, - security_token=source_token) - credential.admin_role.members.add(self.super_django_user) - # Set parent group name to one that might be created by the sync. - group = self.group - group.name = 'ec2' - group.save() - self.group = group - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - inventory_source = self.update_inventory_source(self.group, - source='ec2', credential=credential, source_regions=source_regions, - source_vars='---\n\nnested_groups: false\ncache_path: %s\n' % cache_path) - self.check_inventory_source(inventory_source) - - def test_update_from_ec2_sts_iam_bad_token(self): - source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '') - source_password = getattr(settings, 'TEST_AWS_SECRET_ACCESS_KEY', '') - source_regions = getattr(settings, 'TEST_AWS_REGIONS', 'all') - self.create_test_license_file() - credential = Credential.objects.create(kind='aws', - username=source_username, - password=source_password, - security_token="BADTOKEN") - credential.admin_role.members.add(self.super_django_user) - - # Set parent group name to one that might be created by the sync. - group = self.group - group.name = 'ec2' - group.save() - self.group = group - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - inventory_source = self.update_inventory_source(self.group, - source='ec2', credential=credential, source_regions=source_regions, - source_vars='---\n\nnested_groups: false\ncache_path: %s\n' % cache_path) - self.check_inventory_update(inventory_source, should_fail=True) - - def test_update_from_ec2_without_credential(self): - self.create_test_license_file() - group = self.group - group.name = 'ec2' - group.save() - self.group = group - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - inventory_source = self.update_inventory_source(self.group, source='ec2') - self.check_inventory_update(inventory_source, should_fail=True) - - def test_update_from_ec2_with_nested_groups(self): - source_username = getattr(settings, 'TEST_AWS_ACCESS_KEY_ID', '') - source_password = getattr(settings, 'TEST_AWS_SECRET_ACCESS_KEY', '') - source_regions = getattr(settings, 'TEST_AWS_REGIONS', 'all') - if not all([source_username, source_password]): - self.skipTest('no test ec2 credentials defined!') - self.create_test_license_file() - credential = Credential.objects.create(kind='aws', - username=source_username, - password=source_password) - credential.admin_role.members.add(self.super_django_user) - group = self.group - group.name = 'AWS Inventory' - group.save() - self.group = group - cache_path_pattern = os.path.join(tempfile.gettempdir(), 'awx_ec2_*') - old_cache_paths = set(glob.glob(cache_path_pattern)) - inventory_source = self.update_inventory_source(self.group, - source='ec2', credential=credential, source_regions=source_regions, - source_vars='---') # nested_groups is true by default. - self.check_inventory_source(inventory_source) - # Verify that main group is in top level groups (hasn't been added as - # its own child). - self.assertTrue(self.group in self.inventory.root_groups) - # Verify that returned groups are nested: - #self.print_inventory_tree(self.inventory) - child_names = self.group.children.values_list('name', flat=True) - for name in child_names: - self.assertFalse(name.startswith('us-')) - self.assertFalse(name.startswith('type_')) - self.assertFalse(name.startswith('key_')) - self.assertFalse(name.startswith('security_group_')) - self.assertFalse(re.search(r'tag_.*?_', name)) - self.assertFalse(name.startswith('ami-')) - self.assertFalse(name.startswith('vpc-')) - self.assertTrue('ec2' in child_names) - self.assertTrue('regions' in child_names) - self.assertTrue('types' in child_names) - self.assertTrue('keys' in child_names) - self.assertTrue('security_groups' in child_names) - self.assertTrue('tags' in child_names) - self.assertTrue('images' in child_names) - self.assertFalse('tag_none' in child_names) - # Only check for tag_none as a child of tags if there is a tag_none group; - # the test inventory *may* have tags set for all hosts. - if self.inventory.groups.filter(name='tag_none').exists(): - self.assertTrue('tag_none' in self.group.children.get(name='tags').children.values_list('name', flat=True)) - self.assertFalse('instances' in child_names) - # Make sure we clean up the cache path when finished (when one is not - # provided explicitly via source_vars). - new_cache_paths = set(glob.glob(cache_path_pattern)) - self.assertEqual(old_cache_paths, new_cache_paths) - # Sync again with group_by set to a non-empty value. - cache_path = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path) - inventory_source.group_by = 'region,instance_type' - inventory_source.source_vars = '---\n\ncache_path: %s\n' % cache_path - inventory_source.overwrite = True - inventory_source.save() - self.check_inventory_source(inventory_source, initial=False) - # Verify that only the desired groups are returned. - child_names = self.group.children.values_list('name', flat=True) - self.assertTrue('ec2' in child_names) - self.assertTrue('regions' in child_names) - self.assertTrue(self.group.children.get(name='regions').children.count()) - self.assertTrue('types' in child_names) - self.assertTrue(self.group.children.get(name='types').children.count()) - self.assertFalse('keys' in child_names) - self.assertFalse('security_groups' in child_names) - self.assertFalse('tags' in child_names) - self.assertFalse('images' in child_names) - self.assertFalse('vpcs' in child_names) - self.assertFalse('instances' in child_names) - # Sync again with group_by set to include all possible groups. - cache_path2 = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path2) - inventory_source.group_by = 'instance_id, region, availability_zone, ami_id, instance_type, key_pair, vpc_id, security_group, tag_keys, tag_none' - inventory_source.source_vars = '---\n\ncache_path: %s\n' % cache_path2 - inventory_source.overwrite = True - inventory_source.save() - self.check_inventory_source(inventory_source, initial=False, instance_id_group_ok=True) - # Verify that only the desired groups are returned. - # Skip vpcs as selected inventory may or may not have any. - child_names = self.group.children.values_list('name', flat=True) - self.assertTrue('ec2' in child_names) - self.assertFalse('tag_none' in child_names) - self.assertTrue('regions' in child_names) - self.assertTrue(self.group.children.get(name='regions').children.count()) - self.assertTrue('types' in child_names) - self.assertTrue(self.group.children.get(name='types').children.count()) - self.assertTrue('keys' in child_names) - self.assertTrue(self.group.children.get(name='keys').children.count()) - self.assertTrue('security_groups' in child_names) - self.assertTrue(self.group.children.get(name='security_groups').children.count()) - self.assertTrue('tags' in child_names) - self.assertTrue(self.group.children.get(name='tags').children.count()) - # Only check for tag_none as a child of tags if there is a tag_none group; - # the test inventory *may* have tags set for all hosts. - if self.inventory.groups.filter(name='tag_none').exists(): - self.assertTrue('tag_none' in self.group.children.get(name='tags').children.values_list('name', flat=True)) - self.assertTrue('images' in child_names) - self.assertTrue(self.group.children.get(name='images').children.count()) - self.assertTrue('instances' in child_names) - self.assertTrue(self.group.children.get(name='instances').children.count()) - # Sync again with overwrite set to False after renaming a group that - # was created by the sync. With overwrite false, the renamed group and - # the original group (created again by the sync) will both exist. - region_group = self.group.children.get(name='regions').children.all()[0] - region_group_original_name = region_group.name - region_group.name = region_group.name + '-renamed' - region_group.save(update_fields=['name']) - cache_path3 = tempfile.mkdtemp(prefix='awx_ec2_') - self._temp_paths.append(cache_path3) - inventory_source.source_vars = '---\n\ncache_path: %s\n' % cache_path3 - inventory_source.overwrite = False - inventory_source.save() - self.check_inventory_source(inventory_source, initial=False, instance_id_group_ok=True) - child_names = self.group.children.values_list('name', flat=True) - self.assertTrue(region_group_original_name in self.group.children.get(name='regions').children.values_list('name', flat=True)) - self.assertTrue(region_group.name in self.group.children.get(name='regions').children.values_list('name', flat=True)) - # Replacement text should not be left in inventory source name. - self.assertFalse(InventorySource.objects.filter(name__icontains='__replace_').exists()) - # Inventory update name should be based on inventory/group names and need not have the inventory source pk. - #print InventoryUpdate.objects.values_list('name', 'inventory_source__name') - for inventory_update in InventoryUpdate.objects.all(): - self.assertFalse(inventory_update.name.endswith(inventory_update.inventory_source.name), inventory_update.name) - - def test_update_from_rax(self): - self.skipTest('Skipping until we can resolve the CERTIFICATE_VERIFY_FAILED issue: #1706') - source_username = getattr(settings, 'TEST_RACKSPACE_USERNAME', '') - source_password = getattr(settings, 'TEST_RACKSPACE_API_KEY', '') - source_regions = getattr(settings, 'TEST_RACKSPACE_REGIONS', '') - if not all([source_username, source_password]): - self.skipTest('no test rackspace credentials defined!') - self.create_test_license_file() - credential = Credential.objects.create(kind='rax', - username=source_username, - password=source_password) - credential.admin_role.members.add(self.super_django_user) - # Set parent group name to one that might be created by the sync. - group = self.group - group.name = 'DFW' - group.save() - self.group = group - inventory_source = self.update_inventory_source(self.group, - source='rax', credential=credential, source_regions=source_regions) - # Check first without instance_id set (to import by name only). - with self.settings(RAX_INSTANCE_ID_VAR=''): - self.check_inventory_source(inventory_source) - # Rename hosts and verify the import picks up the instance_id present - # in host variables. - for host in self.inventory.hosts.all(): - self.assertFalse(host.instance_id, host.instance_id) - host.name = 'updated-%s' % host.name - host.save() - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertEqual(old_host_pks, new_host_pks) - # Manually disable all hosts, verify a new update re-enables them. - # Also change the host name, and verify it is not deleted, but instead - # updated because the instance ID matches. - enabled_host_pks = set(self.inventory.hosts.filter(enabled=True).values_list('pk', flat=True)) - for host in self.inventory.hosts.all(): - host.enabled = False - host.name = 'changed-%s' % host.name - host.save() - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False, enabled_host_pks=enabled_host_pks) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertEqual(old_host_pks, new_host_pks) - # If test source regions is given, test again with empty string. - if source_regions: - inventory_source2 = self.update_inventory_source(self.group2, - source='rax', credential=credential, source_regions='') - self.check_inventory_source(inventory_source2) - # Verify that main group is in top level groups (hasn't been added as - # its own child). - self.assertTrue(self.group in self.inventory.root_groups) - - def test_update_from_vmware(self): - source_host = getattr(settings, 'TEST_VMWARE_HOST', '') - source_username = getattr(settings, 'TEST_VMWARE_USER', '') - source_password = getattr(settings, 'TEST_VMWARE_PASSWORD', '') - if not all([source_host, source_username, source_password]): - self.skipTest('no test vmware credentials defined!') - self.create_test_license_file() - credential = Credential.objects.create(kind='vmware', - username=source_username, - password=source_password, - host=source_host) - credential.admin_role.members.add(self.super_django_user) - inventory_source = self.update_inventory_source(self.group, - source='vmware', credential=credential) - # Check first without instance_id set (to import by name only). - with self.settings(VMWARE_INSTANCE_ID_VAR=''): - self.check_inventory_source(inventory_source) - # Rename hosts and verify the import picks up the instance_id present - # in host variables. - for host in self.inventory.hosts.all(): - self.assertFalse(host.instance_id, host.instance_id) - if host.enabled and host.variables_dict.get('vmware_ipAddress', ''): - self.assertTrue(host.variables_dict.get('ansible_ssh_host', '')) - # Test a field that should be present for host systems, not VMs. - self.assertFalse(host.variables_dict.get('vmware_product_name', '')) - host.name = 'updated-%s' % host.name - host.save() - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertEqual(old_host_pks, new_host_pks) - # Manually disable all hosts, verify a new update re-enables them. - # Also change the host name, and verify it is not deleted, but instead - # updated because the instance ID matches. - enabled_host_pks = set(self.inventory.hosts.filter(enabled=True).values_list('pk', flat=True)) - for host in self.inventory.hosts.all(): - host.enabled = False - host.name = 'changed-%s' % host.name - host.save() - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False, enabled_host_pks=enabled_host_pks) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertEqual(old_host_pks, new_host_pks) - # Update again and include host systems in addition to guests. - inventory_source.source_vars = '---\n\nguests_only: false\n' - inventory_source.save() - old_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.check_inventory_source(inventory_source, initial=False) - new_host_pks = set(self.inventory.hosts.values_list('pk', flat=True)) - self.assertTrue(new_host_pks > old_host_pks) - for host in self.inventory.hosts.filter(pk__in=(new_host_pks - old_host_pks)): - if host.enabled: - self.assertTrue(host.variables_dict.get('ansible_ssh_host', '')) - # Test a field only present for host systems. - self.assertTrue(host.variables_dict.get('vmware_product_name', '')) - - def test_update_from_custom_script(self): - # Create the inventory script - self.create_test_license_file() - inventory_scripts = reverse('api:inventory_script_list') - new_script = dict(name="Test", description="Test Script", script=TEST_SIMPLE_INVENTORY_SCRIPT, organization=self.organization.id) - script_data = self.post(inventory_scripts, data=new_script, expect=201, auth=self.get_super_credentials()) - - custom_inv = self.organization.inventories.create(name='Custom Script Inventory') - custom_group = custom_inv.groups.create(name="Custom Script Group") - custom_inv_src = reverse('api:inventory_source_detail', - args=(custom_group.inventory_source.pk,)) - reverse('api:inventory_source_update_view', - args=(custom_group.inventory_source.pk,)) - inv_src_opts = {'source': 'custom', - 'source_script': script_data["id"], - 'source_vars': json.dumps({'HOME': 'no-place-like', 'USER': 'notme', '_': 'nope', 'INVENTORY_SOURCE_ID': -1}) - } - with self.current_user(self.super_django_user): - self.put(custom_inv_src, inv_src_opts, expect=200) - self.check_inventory_source(custom_group.inventory_source) - - # Delete script, verify that update fails. - inventory_source = InventorySource.objects.get(pk=custom_group.inventory_source.pk) - self.assertTrue(inventory_source.can_update) - self.delete(script_data['url'], expect=204, auth=self.get_super_credentials()) - inventory_source = InventorySource.objects.get(pk=inventory_source.pk) - self.assertFalse(inventory_source.can_update) - self.check_inventory_update(inventory_source, should_fail=True) - - # Test again using a script containing some funky unicode gibberish. - unicode_script = dict(name="Unicodes", description="", script=TEST_UNICODE_INVENTORY_SCRIPT, organization=self.organization.id) - script_data = self.post(inventory_scripts, data=unicode_script, expect=201, auth=self.get_super_credentials()) - - custom_inv = self.organization.inventories.create(name='Unicode Script Inventory') - custom_group = custom_inv.groups.create(name="Unicode Script Group") - custom_inv_src = reverse('api:inventory_source_detail', - args=(custom_group.inventory_source.pk,)) - reverse('api:inventory_source_update_view', - args=(custom_group.inventory_source.pk,)) - inv_src_opts = {'source': 'custom', 'source_script': script_data["id"]} - with self.current_user(self.super_django_user): - self.put(custom_inv_src, inv_src_opts, expect=200) - self.check_inventory_source(custom_group.inventory_source) - - # This shouldn't work because we are trying to use a custom script from one organization with - # an inventory that belong to a different organization - other_org = self.make_organizations(self.super_django_user, 1)[0] - other_inv = other_org.inventories.create(name="A Different Org") - other_group = other_inv.groups.create(name='A Different Org Group') - other_inv_src = reverse('api:inventory_source_detail', - args=(other_group.inventory_source.pk,)) - reverse('api:inventory_source_update_view', - args=(other_group.inventory_source.pk,)) - other_inv_src_opts = {'source': 'custom', 'source_script': script_data['id']} - with self.current_user(self.super_django_user): - self.put(other_inv_src, other_inv_src_opts, expect=400) - - def test_update_expired_license(self): - self.create_test_license_file(license_date=int(time.time() - 3600)) - inventory_scripts = reverse('api:inventory_script_list') - new_script = dict(name="Test", description="Test Script", script=TEST_SIMPLE_INVENTORY_SCRIPT, organization=self.organization.id) - script_data = self.post(inventory_scripts, data=new_script, expect=201, auth=self.get_super_credentials()) - - custom_inv = self.organization.inventories.create(name='Custom Script Inventory') - custom_group = custom_inv.groups.create(name="Custom Script Group") - custom_inv_src = reverse('api:inventory_source_detail', - args=(custom_group.inventory_source.pk,)) - reverse('api:inventory_source_update_view', - args=(custom_group.inventory_source.pk,)) - inv_src_opts = {'source': 'custom', - 'source_script': script_data["id"], - 'source_vars': json.dumps({'HOME': 'no-place-like', 'USER': 'notme', '_': 'nope', 'INVENTORY_SOURCE_ID': -1}) - } - with self.current_user(self.super_django_user): - response = self.put(custom_inv_src, inv_src_opts, expect=200) - - inventory_source = InventorySource.objects.get(pk=response['id']) - inventory_update = inventory_source.update(inventory_source=inventory_source) - self.assertFalse(inventory_update.license_error) - - def test_update_from_openstack(self): - api_url = getattr(settings, 'TEST_OPENSTACK_HOST', '') - api_user = getattr(settings, 'TEST_OPENSTACK_USER', '') - api_password = getattr(settings, 'TEST_OPENSTACK_PASSWORD', '') - api_project = getattr(settings, 'TEST_OPENSTACK_PROJECT', '') - if not all([api_url, api_user, api_password, api_project]): - self.skipTest("No test openstack credentials defined") - self.create_test_license_file() - credential = Credential.objects.create(kind='openstack', - host=api_url, - username=api_user, - password=api_password, - project=api_project) - inventory_source = self.update_inventory_source(self.group, source='openstack', credential=credential) - self.check_inventory_source(inventory_source) - self.assertFalse(self.group.all_hosts.filter(instance_id='').exists()) - - def test_update_from_openstack_with_domain(self): - # Check that update works with Keystone v3 identity service - api_url = getattr(settings, 'TEST_OPENSTACK_HOST_V3', '') - api_user = getattr(settings, 'TEST_OPENSTACK_USER', '') - api_password = getattr(settings, 'TEST_OPENSTACK_PASSWORD', '') - api_project = getattr(settings, 'TEST_OPENSTACK_PROJECT', '') - api_domain = getattr(settings, 'TEST_OPENSTACK_DOMAIN', '') - if not all([api_url, api_user, api_password, api_project, api_domain]): - self.skipTest("No test openstack credentials defined with a domain") - self.create_test_license_file() - credential = Credential.objects.create(kind='openstack', - host=api_url, - username=api_user, - password=api_password, - project=api_project, - domain=api_domain) - inventory_source = self.update_inventory_source(self.group, source='openstack', credential=credential) - self.check_inventory_source(inventory_source) - self.assertFalse(self.group.all_hosts.filter(instance_id='').exists()) - - def test_update_from_azure(self): - source_username = getattr(settings, 'TEST_AZURE_USERNAME', '') - source_key_data = getattr(settings, 'TEST_AZURE_KEY_DATA', '') - if not all([source_username, source_key_data]): - self.skipTest("No test azure credentials defined") - self.create_test_license_file() - credential = Credential.objects.create(kind='azure', - username=source_username, - ssh_key_data=source_key_data) - inventory_source = self.update_inventory_source(self.group, source='azure', credential=credential) - self.check_inventory_source(inventory_source) - self.assertFalse(self.group.all_hosts.filter(instance_id='').exists()) - diff --git a/awx/main/tests/old/jobs/job_launch.py b/awx/main/tests/old/jobs/job_launch.py deleted file mode 100644 index 5ffd8bdaf6..0000000000 --- a/awx/main/tests/old/jobs/job_launch.py +++ /dev/null @@ -1,232 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved - -# Python -from __future__ import absolute_import -import os -import unittest2 as unittest - -# Django -import django -from django.core.urlresolvers import reverse - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.job_base import BaseJobTestMixin -import yaml - -__all__ = ['JobTemplateLaunchTest', 'JobTemplateLaunchPasswordsTest'] - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase): - def setUp(self): - super(JobTemplateLaunchTest, self).setUp() - - self.url = reverse('api:job_template_list') - self.data = dict( - name = 'launched job template', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_eng.pk, - project = self.proj_dev.pk, - credential = self.cred_sue.pk, - playbook = self.proj_dev.playbooks[0], - ask_variables_on_launch = True, - ask_credential_on_launch = True, - ) - self.data_no_cred = dict( - name = 'launched job template no credential', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_eng.pk, - project = self.proj_dev.pk, - playbook = self.proj_dev.playbooks[0], - ask_credential_on_launch = True, - ask_variables_on_launch = True, - ) - self.data_cred_ask = dict(self.data) - self.data_cred_ask['name'] = 'launched job templated with ask passwords' - self.data_cred_ask['credential'] = self.cred_sue_ask.pk - - with self.current_user(self.user_sue): - response = self.post(self.url, self.data, expect=201) - self.launch_url = reverse('api:job_template_launch', - args=(response['id'],)) - - def test_launch_job_template(self): - with self.current_user(self.user_sue): - self.data['name'] = 'something different' - response = self.post(self.url, self.data, expect=201) - detail_url = reverse('api:job_template_detail', - args=(response['id'],)) - self.assertEquals(response['url'], detail_url) - - def test_no_cred_update_template(self): - # You can still post the job template without a credential, just can't launch it without one - with self.current_user(self.user_sue): - response = self.post(self.url, self.data_no_cred, expect=201) - detail_url = reverse('api:job_template_detail', - args=(response['id'],)) - self.assertEquals(response['url'], detail_url) - - def test_invalid_auth_unauthorized(self): - # Invalid auth can't trigger the launch endpoint - self.check_invalid_auth(self.launch_url, {}, methods=('post',)) - - def test_credential_implicit(self): - # Implicit, attached credentials - with self.current_user(self.user_sue): - response = self.post(self.launch_url, {}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertTrue(j.status == 'new') - - def test_launch_extra_vars_json(self): - # Sending extra_vars as a JSON string, implicit credentials - with self.current_user(self.user_sue): - data = dict(extra_vars = '{\"a\":3}') - response = self.post(self.launch_url, data, expect=201) - j = Job.objects.get(pk=response['job']) - ev_dict = yaml.load(j.extra_vars) - self.assertIn('a', ev_dict) - if 'a' in ev_dict: - self.assertEqual(ev_dict['a'], 3) - - def test_launch_extra_vars_yaml(self): - # Sending extra_vars as a JSON string, implicit credentials - with self.current_user(self.user_sue): - data = dict(extra_vars = 'a: 3') - response = self.post(self.launch_url, data, expect=201) - j = Job.objects.get(pk=response['job']) - ev_dict = yaml.load(j.extra_vars) - self.assertIn('a', ev_dict) - if 'a' in ev_dict: - self.assertEqual(ev_dict['a'], 3) - - def test_credential_explicit(self): - # Explicit, credential - with self.current_user(self.user_sue): - self.cred_sue.delete() - response = self.post(self.launch_url, {'credential': self.cred_doug.pk}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertEqual(j.status, 'new') - self.assertEqual(j.credential.pk, self.cred_doug.pk) - - def test_credential_explicit_via_credential_id(self): - # Explicit, credential - with self.current_user(self.user_sue): - self.cred_sue.delete() - response = self.post(self.launch_url, {'credential_id': self.cred_doug.pk}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertEqual(j.status, 'new') - self.assertEqual(j.credential.pk, self.cred_doug.pk) - - def test_credential_override(self): - # Explicit, credential - with self.current_user(self.user_sue): - response = self.post(self.launch_url, {'credential': self.cred_doug.pk}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertEqual(j.status, 'new') - self.assertEqual(j.credential.pk, self.cred_doug.pk) - - def test_credential_override_via_credential_id(self): - # Explicit, credential - with self.current_user(self.user_sue): - response = self.post(self.launch_url, {'credential_id': self.cred_doug.pk}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertEqual(j.status, 'new') - self.assertEqual(j.credential.pk, self.cred_doug.pk) - - def test_bad_credential_launch_fail(self): - # Can't launch a job template without a credential defined (or if we - # pass an invalid/inactive credential value). - with self.current_user(self.user_sue): - self.cred_sue.delete() - self.post(self.launch_url, {}, expect=400) - self.post(self.launch_url, {'credential': 0}, expect=400) - self.post(self.launch_url, {'credential_id': 0}, expect=400) - self.post(self.launch_url, {'credential': 'one'}, expect=400) - self.post(self.launch_url, {'credential_id': 'one'}, expect=400) - cred_doug_pk = self.cred_doug.pk - self.cred_doug.delete() - self.post(self.launch_url, {'credential': cred_doug_pk}, expect=400) - self.post(self.launch_url, {'credential_id': cred_doug_pk}, expect=400) - - def test_explicit_unowned_cred(self): - # Explicitly specify a credential that we don't have access to - with self.current_user(self.user_juan): - launch_url = reverse('api:job_template_launch', - args=(self.jt_eng_run.pk,)) - self.post(launch_url, {'credential_id': self.cred_sue.pk}, expect=403) - - def test_no_project_fail(self): - # Job Templates without projects cannot be launched - with self.current_user(self.user_sue): - self.data['name'] = "missing proj" - response = self.post(self.url, self.data, expect=201) - jt = JobTemplate.objects.get(pk=response['id']) - jt.project = None - jt.save() - launch_url2 = reverse('api:job_template_launch', - args=(response['id'],)) - self.post(launch_url2, {}, expect=400) - - def test_no_inventory_fail(self): - # Job Templates without inventory cannot be launched - with self.current_user(self.user_sue): - self.data['name'] = "missing inv" - response = self.post(self.url, self.data, expect=201) - jt = JobTemplate.objects.get(pk=response['id']) - jt.inventory = None - jt.save() - launch_url3 = reverse('api:job_template_launch', - args=(response['id'],)) - self.post(launch_url3, {}, expect=400) - - def test_deleted_credential_fail(self): - # Job Templates with deleted credentials cannot be launched. - self.cred_sue.delete() - with self.current_user(self.user_sue): - self.post(self.launch_url, {}, expect=400) - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobTemplateLaunchPasswordsTest(BaseJobTestMixin, django.test.TransactionTestCase): - def setUp(self): - super(JobTemplateLaunchPasswordsTest, self).setUp() - - self.url = reverse('api:job_template_list') - self.data = dict( - name = 'launched job template', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_eng.pk, - project = self.proj_dev.pk, - credential = self.cred_sue_ask.pk, - playbook = self.proj_dev.playbooks[0], - ask_credential_on_launch = True, - ) - - with self.current_user(self.user_sue): - response = self.post(self.url, self.data, expect=201) - self.launch_url = reverse('api:job_template_launch', - args=(response['id'],)) - - # should return explicit credentials required passwords - def test_explicit_cred_with_ask_passwords_fail(self): - passwords_required = ['ssh_password', 'become_password', 'ssh_key_unlock'] - # Job Templates with deleted credentials cannot be launched. - with self.current_user(self.user_sue): - self.cred_sue_ask.delete() - response = self.post(self.launch_url, {'credential_id': self.cred_sue_ask_many.pk}, expect=400) - for p in passwords_required: - self.assertIn(p, response['passwords_needed_to_start']) - self.assertEqual(len(passwords_required), len(response['passwords_needed_to_start'])) - - def test_explicit_cred_with_ask_password(self): - with self.current_user(self.user_sue): - response = self.post(self.launch_url, {'ssh_password': 'whatever'}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertEqual(j.status, 'new') - - def test_explicit_cred_with_ask_password_empty_string_fail(self): - with self.current_user(self.user_sue): - response = self.post(self.launch_url, {'ssh_password': ''}, expect=400) - self.assertIn('ssh_password', response['passwords_needed_to_start']) diff --git a/awx/main/tests/old/jobs/job_relaunch.py b/awx/main/tests/old/jobs/job_relaunch.py deleted file mode 100644 index 5cb8f30cac..0000000000 --- a/awx/main/tests/old/jobs/job_relaunch.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved - -# Python -from __future__ import absolute_import -import json -import os -import unittest2 as unittest - -# Django -from django.core.urlresolvers import reverse - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseLiveServerTest -from awx.main.tests.job_base import BaseJobTestMixin - -__all__ = ['JobRelaunchTest',] - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobRelaunchTest(BaseJobTestMixin, BaseLiveServerTest): - - def test_job_relaunch(self): - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success') - url = reverse('api:job_relaunch', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.post(url, {}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertTrue(j.status == 'successful') - self.assertEqual(j.launch_type, 'relaunch') - # Test with a job that prompts for SSH and sudo passwords. - job = self.make_job(self.jt_sup_run, self.user_sue, 'success') - url = reverse('api:job_start', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - self.assertEqual(set(response['passwords_needed_to_start']), - set(['ssh_password', 'become_password'])) - data = dict() - response = self.post(url, data, expect=400) - data['ssh_password'] = 'sshpass' - response = self.post(url, data, expect=400) - data2 = dict(become_password='sudopass') - response = self.post(url, data2, expect=400) - data.update(data2) - response = self.post(url, data, expect=202) - job = Job.objects.get(pk=job.pk) - - # Create jt with no extra_vars - # Launch j1 with runtime extra_vars - # Assign extra_vars to jt backing job - # Relaunch j1 - # j2 should not contain jt extra_vars - def test_relaunch_job_does_not_inherit_jt_extra_vars(self): - jt_extra_vars = { - "hello": "world" - } - j_extra_vars = { - "goodbye": "cruel universe" - } - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success', extra_vars=j_extra_vars) - url = reverse('api:job_relaunch', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.post(url, {}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertTrue(j.status == 'successful') - - self.jt_ops_east_run.extra_vars = jt_extra_vars - self.jt_ops_east_run.save() - - response = self.post(url, {}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertTrue(j.status == 'successful') - - resp_extra_vars = json.loads(response['extra_vars']) - self.assertNotIn("hello", resp_extra_vars) - self.assertEqual(resp_extra_vars, j_extra_vars) - - diff --git a/awx/main/tests/old/jobs/jobs_monolithic.py b/awx/main/tests/old/jobs/jobs_monolithic.py deleted file mode 100644 index 2038377cbc..0000000000 --- a/awx/main/tests/old/jobs/jobs_monolithic.py +++ /dev/null @@ -1,1209 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import contextlib -import json -import socket -import struct -import threading -import time -import urlparse -import os -import unittest2 as unittest - - -# Django -import django.test -from django.conf import settings -from django.core.urlresolvers import reverse -from django.test.utils import override_settings -from django.utils.encoding import smart_text - -# Requests -import requests - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.job_base import BaseJobTestMixin - -__all__ = ['JobTemplateTest', 'JobTest', 'JobTemplateCallbackTest', 'JobTransactionTest', 'JobTemplateSurveyTest'] - -TEST_ASYNC_PLAYBOOK = ''' -- hosts: all - gather_facts: false - tasks: - - name: async task should pass - command: sleep 10 - async: 20 - poll: 1 -''' - -TEST_SIMPLE_REQUIRED_SURVEY = ''' -{ - "name": "Simple", - "description": "Description", - "spec": [ - { - "type": "text", - "question_name": "favorite color", - "question_description": "What is your favorite color?", - "variable": "favorite_color", - "choices": "", - "min": "", - "max": "", - "required": true, - "default": "blue" - } - ] -} -''' - -TEST_SIMPLE_NONREQUIRED_SURVEY = ''' -{ - "name": "Simple", - "description": "Description", - "spec": [ - { - "type": "text", - "question_name": "unladen swallow", - "question_description": "What is the airspeed velocity of an unladen swallow?", - "variable": "unladen_swallow", - "choices": "", - "min": "", - "max": "", - "required": false, - "default": "european" - } - ] -} -''' - -TEST_SURVEY_REQUIREMENTS = ''' -{ - "name": "Simple", - "description": "Description", - "spec": [ - { - "type": "text", - "question_name": "cantbeshort", - "question_description": "What is a long answer", - "variable": "long_answer", - "choices": "", - "min": 5, - "max": "", - "required": false, - "default": "yes" - }, - { - "type": "text", - "question_name": "cantbelong", - "question_description": "What is a short answer", - "variable": "short_answer", - "choices": "", - "min": "", - "max": 5, - "required": false, - "default": "yes" - }, - { - "type": "text", - "question_name": "reqd", - "question_description": "I should be required", - "variable": "reqd_answer", - "choices": "", - "min": "", - "max": "", - "required": true, - "default": "yes" - }, - { - "type": "multiplechoice", - "question_name": "achoice", - "question_description": "Need one of these", - "variable": "single_choice", - "choices": ["one", "two"], - "min": "", - "max": "", - "required": false, - "default": "yes" - }, - { - "type": "multiselect", - "question_name": "mchoice", - "question_description": "Can have multiples of these", - "variable": "multi_choice", - "choices": ["one", "two", "three"], - "min": "", - "max": "", - "required": false, - "default": "yes" - }, - { - "type": "integer", - "question_name": "integerchoice", - "question_description": "I need an int here", - "variable": "int_answer", - "choices": "", - "min": 1, - "max": 5, - "required": false, - "default": "" - }, - { - "type": "integer", - "question_name": "integerchoicewithoutmax", - "question_description": "I need an int here without max", - "variable": "int_answer_no_max", - "choices": "", - "min": 1, - "required": false, - "default": "" - }, - { - "type": "float", - "question_name": "float", - "question_description": "I need a float here", - "variable": "float_answer", - "choices": "", - "min": 2, - "max": 5, - "required": false, - "default": "" - }, - { - "type": "json", - "question_name": "jsonanswer", - "question_description": "This answer should validate as json", - "variable": "json_answer", - "choices": "", - "min": "", - "max": "", - "required": false, - "default": "" - } - ] -} -''' - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobTemplateTest(BaseJobTestMixin, django.test.TransactionTestCase): - - JOB_TEMPLATE_FIELDS = ('id', 'type', 'url', 'related', 'summary_fields', - 'created', 'modified', 'name', 'description', - 'job_type', 'inventory', 'project', 'playbook', - 'become_enabled', 'credential', - 'cloud_credential', 'force_handlers', 'forks', - 'limit', 'verbosity', 'extra_vars', - 'ask_variables_on_launch', 'job_tags', 'skip_tags', - 'start_at_task', 'host_config_key', 'status', - 'next_job_run', 'has_schedules', 'last_job_run', - 'last_job_failed', 'survey_enabled') - - def test_get_job_template_list(self): - self.skipTest('This test makes assumptions about projects being multi-org and needs to be updated/rewritten') - url = reverse('api:job_template_list') - qs = JobTemplate.objects.distinct() - fields = self.JOB_TEMPLATE_FIELDS - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - - # Sue's credentials (superuser) == 200, full list - self.check_get_list(url, self.user_sue, qs, fields) - - # Alex's credentials (admin of all orgs) == 200, full list - self.check_get_list(url, self.user_alex, qs, fields) - - # # Bob is an admin for Eng he can see all Engineering templates - # this is: 2 Engineering templates and 1 Support Template - # Note: He is able to see the scan job from the support organization possibly incorrect - # due to being an org admin for that project and no credential assigned to that template - with self.current_user(self.user_bob): - resp = self.get(url, expect=200) - #print [x['name'] for x in resp['results']] - self.assertEquals(resp['count'], 3) - - # Chuck has permission to see all Eng Job Templates as Lead Engineer - # Note: Since chuck is an org admin he can also see the support scan template - with self.current_user(self.user_chuck): - resp = self.get(url, expect=200) - #print [x['name'] for x in resp['results']] - self.assertEquals(resp['count'], 3) - - # Doug is in engineering but can only run scan jobs so he can only see the one Job Template - with self.current_user(self.user_doug): - resp = self.get(url, expect=200) - #print [x['name'] for x in resp['results']] - self.assertEquals(resp['count'], 1) - - # Juan can't see any job templates in Engineering because he lacks the inventory read permission - with self.current_user(self.user_juan): - resp = self.get(url, expect=200) - #print [x['name'] for x in resp['results']] - self.assertEquals(resp['count'], 0) - - def test_credentials_list(self): - url = reverse('api:credential_list') - # Greg can't see the 'south' credential because the 'southerns' team is inactive - with self.current_user(self.user_greg): - all_credentials = self.get(url, expect=200) - self.assertFalse('south' in [x['username'] for x in all_credentials['results']]) - - url2 = reverse('api:team_detail', args=(self.team_ops_north.id,)) - # Greg shouldn't be able to see the north credential once deleting its team - with self.current_user(self.user_greg): - all_credentials = self.get(url, expect=200) - self.assertTrue('north' in [x['username'] for x in all_credentials['results']]) - self.delete(url2, expect=204) - all_credentials = self.get(url, expect=200) - self.assertFalse('north' in [x['username'] for x in all_credentials['results']]) - # Sue can still see the credential, she's a super user - with self.current_user(self.user_sue): - all_credentials = self.get(url, expect=200) - self.assertTrue('north' in [x['username'] for x in all_credentials['results']]) - - def test_post_job_template_list(self): - self.skipTest('This test makes assumptions about projects being multi-org and needs to be updated/rewritten') - url = reverse('api:job_template_list') - data = dict( - name = 'new job template', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_eng.pk, - project = self.proj_dev.pk, - playbook = self.proj_dev.playbooks[0], - ) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url, data, methods=('post',)) - - # sue can always add job templates. - with self.current_user(self.user_sue): - response = self.post(url, data, expect=201) - detail_url = reverse('api:job_template_detail', - args=(response['id'],)) - self.assertEquals(response['url'], detail_url) - - # Check that all fields provided were set. - jt = JobTemplate.objects.get(pk=response['id']) - self.assertEqual(jt.name, data['name']) - self.assertEqual(jt.job_type, data['job_type']) - self.assertEqual(jt.inventory.pk, data['inventory']) - self.assertEqual(jt.credential, None) - self.assertEqual(jt.project.pk, data['project']) - self.assertEqual(smart_text(jt.playbook), data['playbook']) - - # Test that all required fields are really required. - data['name'] = 'another new job template' - for field in ('name', 'inventory', 'project', 'playbook'): - with self.current_user(self.user_sue): - d = dict(data.items()) - d.pop(field) - response = self.post(url, d, expect=400) - self.assertTrue(field in response, - 'no error for field "%s" in response' % field) - - # Test invalid value for job_type. - with self.current_user(self.user_sue): - d = dict(data.items()) - d['job_type'] = 'world domination' - response = self.post(url, d, expect=400) - self.assertTrue('job_type' in response) - - # Test playbook not in list of project playbooks. - with self.current_user(self.user_sue): - d = dict(data.items()) - d['playbook'] = 'no_playbook_here.yml' - response = self.post(url, d, expect=400) - self.assertTrue('playbook' in response) - - # Test unique constraint on names. - with self.current_user(self.user_sue): - d = dict(data.items()) - d['name'] = 'new job template' - response = self.post(url, d, expect=400) - self.assertTrue('name' in response) - self.assertTrue('Job template with this Name already exists.' in response['name']) - self.assertTrue('__all__' not in response) - - data = dict( - name = 'ops job template', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_ops_west.pk, - project = self.proj_prod.pk, - playbook = self.proj_prod.playbooks[0], - ) - - # randall can't create a job template since his job is to check templates - # as per the ops testers team permission - with self.current_user(self.user_randall): - response = self.post(url, data, expect=403) - - # greg can because he is the head of operations - with self.current_user(self.user_greg): - response = self.post(url, data, expect=201) - - data = dict( - name = 'eng job template', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_eng.pk, - project = self.proj_dev.pk, - playbook = self.proj_dev.playbooks[0], - ) - - # Neither Juan or Doug can create job templates as they have Permissions - # that only allow them to DEPLOY and CHECK respectively - with self.current_user(self.user_juan): - response = self.post(url, data, expect=403) - - with self.current_user(self.user_doug): - response = self.post(url, data, expect=403) - - # Hannibal, despite his questionable social habits has a user Permission - # that allows him to create playbooks - with self.current_user(self.user_hannibal): - response = self.post(url, data, expect=201) - - # FIXME: Check other credentials and optional fields. - - def test_get_job_template_detail(self): - jt = self.jt_eng_run - url = reverse('api:job_template_detail', args=(jt.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - - # sue can read the job template detail. - with self.current_user(self.user_sue): - self.options(url) - self.head(url) - response = self.get(url) - self.assertEqual(response['url'], url) - self.assertEqual(response['cloud_credential'], None) - - # FIXME: Check other credentials and optional fields. - - # TODO: add more tests that show - # the method used to START a JobTemplate follow the exact same permissions as those to create it ... - # and that jobs come back nicely serialized with related resources and so on ... - # that we can drill all the way down and can get at host failure lists, etc ... - - def test_put_job_template_detail(self): - jt = self.jt_eng_run - url = reverse('api:job_template_detail', args=(jt.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url, methods=('put',))# 'patch')) - - # sue can update the job template detail. - with self.current_user(self.user_sue): - data = self.get(url) - data['name'] = '%s-updated' % data['name'] - self.put(url, data) - - # FIXME: Check other credentials and optional fields. - - def test_get_job_template_job_list(self): - jt = self.jt_eng_run - url = reverse('api:job_template_jobs_list', args=(jt.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - - # sue can read the job template job list. - with self.current_user(self.user_sue): - self.options(url) - self.head(url) - response = self.get(url) - qs = jt.jobs.all() - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # FIXME: Check other credentials and optional fields. - - def test_post_job_template_job_list(self): - jt = self.jt_eng_run - url = reverse('api:job_template_jobs_list', args=(jt.pk,)) - data = dict( - credential=self.cred_bob.pk, - ) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url, data, methods=('post',)) - - # sue can create a new job from the template. - with self.current_user(self.user_sue): - self.post(url, data, expect=201) - - # FIXME: Check other credentials and optional fields. - - def test_post_scan_job_template(self): - self.skipTest('This test makes assumptions about projects being multi-org and needs to be updated/rewritten') - url = reverse('api:job_template_list') - data = dict( - name = 'scan job template 1', - job_type = PERM_INVENTORY_SCAN, - inventory = self.inv_eng.pk, - ) - self.create_test_license_file(features=dict(system_tracking=False)) - # Without the system tracking license feature even super users can't create scan jobs - with self.current_user(self.user_sue): - data['credential'] = self.cred_sue.pk - response = self.post(url, data, expect=402) - self.create_test_license_file(features=dict(system_tracking=True)) - # Scan Jobs cannot be created with survey enabled - with self.current_user(self.user_sue): - data['credential'] = self.cred_sue.pk - data['survey_enabled'] = True - response = self.post(url, data, expect=400) - data.pop("survey_enabled") - # Regular users, even those who have access to the inv and cred can't create scan jobs templates - with self.current_user(self.user_doug): - data['credential'] = self.cred_doug.pk - response = self.post(url, data, expect=403) - # Org admins can create scan job templates in their org - with self.current_user(self.user_chuck): - data['credential'] = self.cred_chuck.pk - response = self.post(url, data, expect=201) - detail_url = reverse('api:job_template_detail', - args=(response['id'],)) - # Non Org Admins don't have permission to access it though - with self.current_user(self.user_doug): - self.get(detail_url, expect=403) - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobTest(BaseJobTestMixin, django.test.TransactionTestCase): - - def test_get_job_list(self): - url = reverse('api:job_list') - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - - # sue's credentials (superuser) == 200, full list - with self.current_user(self.user_sue): - self.options(url) - self.head(url) - response = self.get(url) - qs = Job.objects.all() - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # FIXME: Check individual job result fields. - # FIXME: Check with other credentials. - - def test_post_job_list(self): - url = reverse('api:job_list') - data = dict( - name='new job without template', - job_type=PERM_INVENTORY_DEPLOY, - inventory=self.inv_ops_east.pk, - project=self.proj_prod.pk, - playbook=self.proj_prod.playbooks[0], - credential=self.cred_ops_east.pk, - ) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url, data, methods=('post',)) - - # sue can create a new job without a template. - with self.current_user(self.user_sue): - self.post(url, data, expect=201) - - # alex can't create a job without a template, only super users can do that - with self.current_user(self.user_alex): - self.post(url, data, expect=403) - - # sue can also create a job here from a template. - jt = self.jt_ops_east_run - data = dict( - name='new job from template', - job_template=jt.pk, - ) - with self.current_user(self.user_sue): - self.post(url, data, expect=201) - - # sue can't create a job when it is hidden due to inactive team - - # FIXME: Check with other credentials and optional fields. - - def test_get_job_detail(self): - #job = self.job_ops_east_run - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success') - url = reverse('api:job_detail', args=(job.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - - # sue can read the job detail. - with self.current_user(self.user_sue): - self.options(url) - self.head(url) - response = self.get(url) - self.assertEqual(response['url'], url) - self.assertEqual(response['cloud_credential'], None) - - # FIXME: Check with other credentials and optional fields. - - def test_put_job_detail(self): - #job = self.job_ops_west_run - job = self.make_job(self.jt_ops_west_run, self.user_sue, 'success') - url = reverse('api:job_detail', args=(job.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url, methods=('put',))# 'patch')) - - # sue can update the job detail only if the job is new. - job.status = 'new' - job.save() - self.assertEqual(job.status, 'new') - with self.current_user(self.user_sue): - data = self.get(url) - data['limit'] = '%s-updated' % data['limit'] - self.put(url, data) - - # sue cannot update the job detail if it is in any other state. - for status in ('pending', 'running', 'successful', 'failed', 'error', - 'canceled'): - job.status = status - job.save() - with self.current_user(self.user_sue): - data = self.get(url) - data['limit'] = '%s-updated' % data['limit'] - self.put(url, data, expect=405) - #patch_data = dict(limit='%s-changed' % data['limit']) - #self.patch(url, patch_data, expect=405) - - # FIXME: Check with other credentials and readonly fields. - - def _test_mainline(self): - url = reverse('api:job_list') - - # job templates - data = self.get('/api/v1/job_templates/', expect=401) - data = self.get('/api/v1/job_templates/', expect=200, auth=self.get_normal_credentials()) - self.assertTrue(data['count'], 2) - - rec = dict( - name = 'job-foo', - credential = self.credential.pk, - inventory = self.inventory.pk, - project = self.project.pk, - job_type = PERM_INVENTORY_DEPLOY - ) - - # org admin can add job type - posted = self.post('/api/v1/job_templates/', rec, expect=201, auth=self.get_normal_credentials()) - self.assertEquals(posted['url'], '/api/v1/job_templates/3/') - - # other_django_user is on a team that can deploy, so can create both deploy and check type jobs - rec['name'] = 'job-foo2' - posted = self.post('/api/v1/job_templates/', rec, expect=201, auth=self.get_other_credentials()) - rec['name'] = 'job-foo3' - rec['job_type'] = PERM_INVENTORY_CHECK - posted = self.post('/api/v1/job_templates/', rec, expect=201, auth=self.get_other_credentials()) - - # other2_django_user has individual permissions to run check mode, but not deploy - # nobody user can't even run check mode - rec['name'] = 'job-foo4' - self.post('/api/v1/job_templates/', rec, expect=403, auth=self.get_nobody_credentials()) - rec['credential'] = self.credential2.pk - posted = self.post('/api/v1/job_templates/', rec, expect=201, auth=self.get_other2_credentials()) - rec['name'] = 'job-foo5' - rec['job_type'] = PERM_INVENTORY_DEPLOY - self.post('/api/v1/job_templates/', rec, expect=403, auth=self.get_nobody_credentials()) - self.post('/api/v1/job_templates/', rec, expect=201, auth=self.get_other2_credentials()) - url = posted['url'] - - # verify we can also get the job template record - got = self.get(url, expect=200, auth=self.get_other2_credentials()) - self.failUnlessEqual(got['url'], '/api/v1/job_templates/6/') - - # TODO: add more tests that show - # the method used to START a JobTemplate follow the exact same permissions as those to create it ... - # and that jobs come back nicely serialized with related resources and so on ... - # that we can drill all the way down and can get at host failure lists, etc ... - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local') -class JobTemplateCallbackTest(BaseJobTestMixin, django.test.LiveServerTestCase): - '''Job template callback tests for empheral hosts.''' - - def setUp(self): - super(JobTemplateCallbackTest, self).setUp() - settings.INTERNAL_API_URL = self.live_server_url - # Monkeypatch socket module DNS lookup functions for testing. - self._original_gethostbyaddr = socket.gethostbyaddr - self._original_getaddrinfo = socket.getaddrinfo - socket.gethostbyaddr = self.gethostbyaddr - socket.getaddrinfo = self.getaddrinfo - - def tearDown(self): - super(JobTemplateCallbackTest, self).tearDown() - socket.gethostbyaddr = self._original_gethostbyaddr - socket.getaddrinfo = self._original_getaddrinfo - - def atoh(self, a): - '''Convert IP address to integer in host byte order.''' - return socket.ntohl(struct.unpack('I', socket.inet_aton(a))[0]) - - def htoa(self, n): - '''Convert integer in host byte order to IP address.''' - return socket.inet_ntoa(struct.pack('I', socket.htonl(n))) - - def get_test_ips_for_host(self, host): - '''Return test IP address(es) for given test hostname.''' - ips = [] - try: - h = Host.objects.exclude(name__endswith='-alias').get(name=host) - # Primary IP for host (both forward/reverse lookups work). - val = self.atoh('127.10.0.0') + h.pk - ips.append(self.htoa(val)) - # Secondary IP for host (both forward/reverse lookups work). - if h.pk % 2 == 0: - val = self.atoh('127.20.0.0') + h.pk - ips.append(self.htoa(val)) - # Additional IP for host (only forward lookups work). - if h.pk % 3 == 0: - val = self.atoh('127.30.0.0') + h.pk - ips.append(self.htoa(val)) - # Additional IP for host (neither forward/reverse lookups work). - if h.pk % 3 == 1: - val = self.atoh('127.40.0.0') + h.pk - ips.append(self.htoa(val)) - except Host.DoesNotExist: - pass - return ips - - def get_test_host_for_ip(self, ip): - '''Return test hostname for given test IP address.''' - if not ip.startswith('127.10.') and not ip.startswith('127.20.'): - return None - val = self.atoh(ip) - try: - return Host.objects.get(pk=(val & 0x0ffff)).name - except Host.DoesNotExist: - return None - - def test_dummy_host_ip_lookup(self): - all_ips = set() - for host in Host.objects.all(): - ips = self.get_test_ips_for_host(host.name) - self.assertTrue(ips) - all_ips.update(ips) - ips = self.get_test_ips_for_host('invalid_host_name') - self.assertFalse(ips) - for ip in all_ips: - host = self.get_test_host_for_ip(ip) - if ip.startswith('127.30.') or ip.startswith('127.40.'): - continue - self.assertTrue(host) - ips = self.get_test_ips_for_host(host) - self.assertTrue(ip in ips) - host = self.get_test_host_for_ip('127.10.254.254') - self.assertFalse(host) - - def gethostbyaddr(self, ip): - if not ip.startswith('127.'): - return self._original_gethostbyaddr(ip) - host = self.get_test_host_for_ip(ip) - if not host: - raise socket.herror('unknown test host') - raddr = '.'.join(list(reversed(ip.split('.'))) + ['in-addr', 'arpa']) - return (host, [raddr], [ip]) - - def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, flags=0): - if family or socktype or proto or flags: - return self._original_getaddrinfo(host, port, family, socktype, - proto, flags) - port = port or 0 - try: - socket.inet_aton(host) - addrs = [host] - except socket.error: - addrs = self.get_test_ips_for_host(host) - addrs = [x for x in addrs if not x.startswith('127.40.')] - if not addrs: - raise socket.gaierror('test host not found') - results = [] - for addr in addrs: - results.append((socket.AF_INET, socket.SOCK_STREAM, - socket.IPPROTO_TCP, '', (addr, port))) - results.append((socket.AF_INET, socket.SOCK_DGRAM, - socket.IPPROTO_UDP, '', (addr, port))) - return results - - def test_job_template_callback(self): - # Set ansible_ssh_host for certain hosts, update name to be an alias. - for host in Host.objects.all(): - ips = self.get_test_ips_for_host(host.name) - for ip in ips: - if ip.startswith('127.40.'): - host.name = '%s-alias' % host.name - host_vars = host.variables_dict - host_vars['ansible_ssh_host'] = ip - host.variables = json.dumps(host_vars) - host.save() - - # Find a valid job template to use to test the callback. - job_template = None - qs = JobTemplate.objects.filter(job_type='run', - credential__isnull=False) - qs = qs.exclude(host_config_key='') - for jt in qs: - if not jt.can_start_without_user_input(): - continue - job_template = jt - break - self.assertTrue(job_template) - url = reverse('api:job_template_callback', args=(job_template.pk,)) - data = dict(host_config_key=job_template.host_config_key) - - # Test a POST to start a new job. - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(variables__icontains='ansible_ssh_host') - host = host_qs[0] - host_ip = self.get_test_ips_for_host(host.name)[0] - jobs_qs = job_template.jobs.filter(launch_type='callback').order_by('-pk') - self.assertEqual(jobs_qs.count(), 0) - - # Create the job itself. - result = self.post(url, data, expect=201, remote_addr=host_ip) - - # Establish that we got back what we expect, and made the changes - # that we expect. - self.assertTrue('Location' in result.response, result.response) - self.assertEqual(jobs_qs.count(), 1) - job = jobs_qs[0] - self.assertEqual(urlparse.urlsplit(result.response['Location']).path, - job.get_absolute_url()) - self.assertEqual(job.launch_type, 'callback') - self.assertEqual(job.limit, host.name) - self.assertEqual(job.hosts.count(), 1) - self.assertEqual(job.hosts.all()[0], host) - - # Create the job itself using URL-encoded form data instead of JSON. - result = self.post(url, data, expect=201, remote_addr=host_ip, data_type='form') - - # Establish that we got back what we expect, and made the changes - # that we expect. - self.assertTrue('Location' in result.response, result.response) - self.assertEqual(jobs_qs.count(), 2) - job = jobs_qs[0] - self.assertEqual(urlparse.urlsplit(result.response['Location']).path, - job.get_absolute_url()) - self.assertEqual(job.launch_type, 'callback') - self.assertEqual(job.limit, host.name) - self.assertEqual(job.hosts.count(), 1) - self.assertEqual(job.hosts.all()[0], host) - - # Run the callback job again with extra vars and verify their presence - data.update(dict(extra_vars=dict(key="value"))) - result = self.post(url, data, expect=201, remote_addr=host_ip) - jobs_qs = job_template.jobs.filter(launch_type='callback').order_by('-pk') - job = jobs_qs[0] - self.assertTrue("key" in job.extra_vars) - - # GET as unauthenticated user will prompt for authentication. - self.get(url, expect=401, remote_addr=host_ip) - - # Test GET (as super user) to validate host. - with self.current_user(self.user_sue): - response = self.get(url, expect=200, remote_addr=host_ip) - self.assertEqual(response['host_config_key'], - job_template.host_config_key) - self.assertEqual(response['matching_hosts'], [host.name]) - - # POST but leave out the host_config_key. - self.post(url, {}, expect=403, remote_addr=host_ip) - - # Try with REMOTE_ADDR empty. - self.post(url, data, expect=400, remote_addr='') - - # Try with REMOTE_ADDR set to an unknown address. - self.post(url, data, expect=400, remote_addr='127.127.0.1') - - # Create a pending job that will block creation of another job - j = job_template.create_job(limit=job.limit, launch_type='callback') - j.status = 'pending' - j.save() - # This should fail since there is already a pending/waiting callback job on that job template involving that host - self.post(url, data, expect=400, remote_addr=host_ip) - j.delete() # Remove that so it's not hanging around - - # Try using an alternate IP for the host (but one that also resolves - # via reverse lookup). - host = None - host_ip = None - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(variables__icontains='ansible_ssh_host') - for h in host_qs: - ips = self.get_test_ips_for_host(h.name) - for ip in ips: - if ip.startswith('127.20.'): - host = h - host_ip = ip - break - if host_ip: - break - self.assertTrue(host) - self.assertEqual(jobs_qs.count(), 3) - self.post(url, data, expect=201, remote_addr=host_ip) - self.assertEqual(jobs_qs.count(), 4) - job = jobs_qs[0] - self.assertEqual(job.launch_type, 'callback') - self.assertEqual(job.limit, host.name) - self.assertEqual(job.hosts.count(), 1) - self.assertEqual(job.hosts.all()[0], host) - - # Try using an IP for the host that doesn't resolve via reverse lookup, - # but can be found by doing a forward lookup on the host name. - host = None - host_ip = None - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(variables__icontains='ansible_ssh_host') - for h in host_qs: - ips = self.get_test_ips_for_host(h.name) - for ip in ips: - if ip.startswith('127.30.'): - host = h - host_ip = ip - break - if host_ip: - break - self.assertTrue(host) - self.assertEqual(jobs_qs.count(), 4) - self.post(url, data, expect=201, remote_addr=host_ip) - self.assertEqual(jobs_qs.count(), 5) - job = jobs_qs[0] - self.assertEqual(job.launch_type, 'callback') - self.assertEqual(job.limit, host.name) - self.assertEqual(job.hosts.count(), 1) - self.assertEqual(job.hosts.all()[0], host) - - # Try using address only specified via ansible_ssh_host. - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.filter(variables__icontains='ansible_ssh_host') - host = host_qs[0] - host_ip = host.variables_dict['ansible_ssh_host'] - self.assertEqual(jobs_qs.count(), 5) - self.post(url, data, expect=201, remote_addr=host_ip) - self.assertEqual(jobs_qs.count(), 6) - job = jobs_qs[0] - self.assertEqual(job.launch_type, 'callback') - self.assertEqual(job.limit, host.name) - self.assertEqual(job.hosts.count(), 1) - self.assertEqual(job.hosts.all()[0], host) - - # Set a limit on the job template to verify the callback job limit is - # set to the intersection of this limit and the host name. - # job_template.limit = 'bakers:slicers:packagers' - # job_template.save(update_fields=['limit']) - JobTemplate.objects.filter(pk=job_template.pk).update(limit='bakers:slicers:packagers') - job_template = JobTemplate.objects.get(pk=job_template.pk) - - # Try when hostname is also an IP address, even if a different one is - # specified via ansible_ssh_host. - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(variables__icontains='ansible_ssh_host') - host = None - host_ip = None - for h in host_qs: - ips = self.get_test_ips_for_host(h.name) - if len(ips) > 1: - host = h - host.name = list(ips)[0] - host_vars = host.variables_dict - host_vars['ansible_ssh_host'] = list(ips)[1] - host.variables = json.dumps(host_vars) - host.save() - host_ip = list(ips)[0] - break - self.assertTrue(host) - self.assertEqual(jobs_qs.count(), 6) - self.post(url, data, expect=201, remote_addr=host_ip) - self.assertEqual(jobs_qs.count(), 7) - job = jobs_qs[0] - self.assertEqual(job.launch_type, 'callback') - self.assertEqual(job.limit, host.name) - self.assertEqual(job.hosts.count(), 1) - self.assertEqual(job.hosts.all()[0], host) - - # Find a new job template to use. - job_template = None - qs = JobTemplate.objects.filter(job_type='check', - credential__isnull=False) - qs = qs.exclude(host_config_key='') - for jt in qs: - if not jt.can_start_without_user_input(): - continue - job_template = jt - break - self.assertTrue(job_template) - url = reverse('api:job_template_callback', args=(job_template.pk,)) - data = dict(host_config_key=job_template.host_config_key) - - # Should get an error when multiple hosts match to the same IP. - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(name__endswith='-alias') - for host in host_qs: - host_vars = host.variables_dict - host_vars['ansible_ssh_host'] = '127.50.0.1' - host.variables = json.dumps(host_vars) - host.save() - host = host_qs[0] - host_ip = host.variables_dict['ansible_ssh_host'] - self.post(url, data, expect=400, remote_addr=host_ip) - - # Find a job template to run that doesn't have a credential. - job_template = None - qs = JobTemplate.objects.filter(job_type='run', - credential__isnull=True) - qs = qs.exclude(host_config_key='') - for jt in qs: - job_template = jt - break - self.assertTrue(job_template) - url = reverse('api:job_template_callback', args=(job_template.pk,)) - data = dict(host_config_key=job_template.host_config_key) - - # Test POST to start a new job when the template has no credential. - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(variables__icontains='ansible_ssh_host') - host = host_qs[0] - host_ip = self.get_test_ips_for_host(host.name)[0] - self.post(url, data, expect=400, remote_addr=host_ip) - - # Find a job template to run that has a credential but would require - # user input. - job_template = None - qs = JobTemplate.objects.filter(job_type='run', - credential__isnull=False) - qs = qs.exclude(host_config_key='') - for jt in qs: - if jt.can_start_without_user_input(): - continue - job_template = jt - break - self.assertTrue(job_template) - url = reverse('api:job_template_callback', args=(job_template.pk,)) - data = dict(host_config_key=job_template.host_config_key) - - # Test POST to start a new job when the credential would require user - # input. - host_qs = job_template.inventory.hosts.order_by('pk') - host_qs = host_qs.exclude(variables__icontains='ansible_ssh_host') - host = host_qs[0] - host_ip = self.get_test_ips_for_host(host.name)[0] - self.post(url, data, expect=400, remote_addr=host_ip) - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local') -class JobTransactionTest(BaseJobTestMixin, django.test.LiveServerTestCase): - '''Job test of transaction locking using the celery task backend.''' - - def setUp(self): - super(JobTransactionTest, self).setUp() - settings.INTERNAL_API_URL = self.live_server_url - - def tearDown(self): - super(JobTransactionTest, self).tearDown() - - def _job_detail_polling_thread(self, url, auth, errors): - time.sleep(1) - while True: - time.sleep(0.1) - try: - response = requests.get(url, auth=auth) - response.raise_for_status() - data = json.loads(response.content) - if data.get('status', '') not in ('new', 'pending', 'running'): - break - except Exception as e: - errors.append(e) - break - - @contextlib.contextmanager - def poll_job_detail(self, url, auth, errors): - try: - t = threading.Thread(target=self._job_detail_polling_thread, - args=(url, auth, errors)) - t.start() - yield - finally: - t.join(20) - - def test_for_job_deadlocks(self): - if 'postgresql' not in settings.DATABASES['default']['ENGINE']: - self.skipTest('Not using PostgreSQL') - # Create lots of extra test hosts to trigger job event callbacks - #job = self.job_eng_run - job = self.make_job(self.jt_eng_run, self.user_sue, 'new') - inv = job.inventory - for x in xrange(50): - h = inv.hosts.create(name='local-%d' % x) - for g in inv.groups.all(): - g.hosts.add(h) - - job_detail_url = reverse('api:job_detail', args=(job.pk,)) - job_detail_url = urlparse.urljoin(self.live_server_url, job_detail_url) - auth = ('sue', self._user_passwords['sue']) - errors = [] - with self.poll_job_detail(job_detail_url, auth, errors): - with self.current_user(self.user_sue): - url = reverse('api:job_start', args=(job.pk,)) - response = self.get(url) - self.assertTrue(response['can_start']) - self.assertFalse(response['passwords_needed_to_start']) - response = self.post(url, {}, expect=201) - job = Job.objects.get(pk=job.pk) - self.assertEqual(job.status, 'successful', job.result_stdout) - self.assertFalse(errors) - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobTemplateSurveyTest(BaseJobTestMixin, django.test.TransactionTestCase): - def setUp(self): - super(JobTemplateSurveyTest, self).setUp() - # TODO: Test non-enterprise license - self.create_test_license_file() - - def tearDown(self): - super(JobTemplateSurveyTest, self).tearDown() - - def test_post_job_template_survey(self): - url = reverse('api:job_template_list') - data = dict( - name = 'launched job template', - job_type = PERM_INVENTORY_DEPLOY, - inventory = self.inv_eng.pk, - project = self.proj_dev.pk, - playbook = self.proj_dev.playbooks[0], - credential = self.cred_sue.pk, - survey_enabled = True, - ) - with self.current_user(self.user_sue): - response = self.post(url, data, expect=201) - new_jt_id = response['id'] - detail_url = reverse('api:job_template_detail', - args=(new_jt_id,)) - self.assertEquals(response['url'], detail_url) - url = reverse('api:job_template_survey_spec', args=(new_jt_id,)) - launch_url = reverse('api:job_template_launch', args=(new_jt_id,)) - with self.current_user(self.user_sue): - # If no survey spec is available, survey_enabled on launch endpoint - # should return, and should be able to launch template without error. - response = self.get(launch_url) - self.assertFalse(response['survey_enabled']) - self.post(launch_url, {}, expect=201) - # Now post a survey spec and check that the answer is set in the - # job's extra vars. - self.post(url, json.loads(TEST_SIMPLE_REQUIRED_SURVEY), expect=200) - response = self.get(launch_url) - self.assertTrue(response['survey_enabled']) - self.assertTrue('favorite_color' in response['variables_needed_to_start']) - response = self.post(launch_url, dict(extra_vars=dict(favorite_color="green")), expect=201) - job = Job.objects.get(pk=response["job"]) - job_extra = json.loads(job.extra_vars) - self.assertTrue("favorite_color" in job_extra) - - # launch job template with required survey without providing survey data - with self.current_user(self.user_sue): - self.post(url, json.loads(TEST_SIMPLE_REQUIRED_SURVEY), expect=200) - response = self.get(launch_url) - self.assertTrue('favorite_color' in response['variables_needed_to_start']) - response = self.post(launch_url, dict(extra_vars=dict()), expect=400) - self.assertIn("'favorite_color' value missing", response['variables_needed_to_start']) - - # launch job template with required survey without providing survey data and without - # even providing extra_vars - with self.current_user(self.user_sue): - self.post(url, json.loads(TEST_SIMPLE_REQUIRED_SURVEY), expect=200) - response = self.get(launch_url) - self.assertTrue('favorite_color' in response['variables_needed_to_start']) - response = self.post(launch_url, {}, expect=400) - self.assertIn("'favorite_color' value missing", response['variables_needed_to_start']) - - with self.current_user(self.user_sue): - response = self.post(url, json.loads(TEST_SIMPLE_NONREQUIRED_SURVEY), expect=200) - response = self.get(launch_url) - self.assertTrue(len(response['variables_needed_to_start']) == 0) - - with self.current_user(self.user_sue): - response = self.post(url, json.loads(TEST_SURVEY_REQUIREMENTS), expect=200) - # Just the required answer should work - self.post(launch_url, dict(extra_vars=dict(reqd_answer="foo")), expect=201) - # Short answer but requires a long answer - self.post(launch_url, dict(extra_vars=dict(long_answer='a', reqd_answer="foo")), expect=400) - # Long answer but requires a short answer - self.post(launch_url, dict(extra_vars=dict(short_answer='thisissomelongtext', reqd_answer="foo")), expect=400) - # Long answer but missing required answer - self.post(launch_url, dict(extra_vars=dict(long_answer='thisissomelongtext')), expect=400) - # Integer that's not big enough - self.post(launch_url, dict(extra_vars=dict(int_answer=0, reqd_answer="foo")), expect=400) - # Integer that's too big - self.post(launch_url, dict(extra_vars=dict(int_answer=10, reqd_answer="foo")), expect=400) - # Integer that's just riiiiight - self.post(launch_url, dict(extra_vars=dict(int_answer=3, reqd_answer="foo")), expect=201) - # Integer bigger than min with no max defined - self.post(launch_url, dict(extra_vars=dict(int_answer_no_max=3, reqd_answer="foo")), expect=201) - # Integer answer that's the wrong type - self.post(launch_url, dict(extra_vars=dict(int_answer="test", reqd_answer="foo")), expect=400) - # Float that's too big - self.post(launch_url, dict(extra_vars=dict(float_answer=10.5, reqd_answer="foo")), expect=400) - # Float that's too small - self.post(launch_url, dict(extra_vars=dict(float_answer=1.995, reqd_answer="foo")), expect=400) - # float that's just riiiiight - self.post(launch_url, dict(extra_vars=dict(float_answer=2.01, reqd_answer="foo")), expect=201) - # float answer that's the wrong type - self.post(launch_url, dict(extra_vars=dict(float_answer="test", reqd_answer="foo")), expect=400) - # Wrong choice in single choice - self.post(launch_url, dict(extra_vars=dict(reqd_answer="foo", single_choice="three")), expect=400) - # Wrong choice in multi choice - self.post(launch_url, dict(extra_vars=dict(reqd_answer="foo", multi_choice=["four"])), expect=400) - # Wrong type for multi choicen - self.post(launch_url, dict(extra_vars=dict(reqd_answer="foo", multi_choice="two")), expect=400) - # Right choice in single choice - self.post(launch_url, dict(extra_vars=dict(reqd_answer="foo", single_choice="two")), expect=201) - # Right choices in multi choice - self.post(launch_url, dict(extra_vars=dict(reqd_answer="foo", multi_choice=["one", "two"])), expect=201) - # Nested json - self.post(launch_url, dict(extra_vars=dict(json_answer=dict(test="val", num=1), reqd_answer="foo")), expect=201) - - # Bob can access and update the survey because he's an org-admin - with self.current_user(self.user_bob): - self.post(url, json.loads(TEST_SURVEY_REQUIREMENTS), expect=200) - - # Chuck is the lead engineer and has the right permissions to edit it also - with self.current_user(self.user_chuck): - self.post(url, json.loads(TEST_SURVEY_REQUIREMENTS), expect=200) - - # Doug shouldn't be able to access this playbook - with self.current_user(self.user_doug): - self.post(url, json.loads(TEST_SURVEY_REQUIREMENTS), expect=403) - - # Neither can juan because he doesn't have the job template create permission - with self.current_user(self.user_juan): - self.post(url, json.loads(TEST_SURVEY_REQUIREMENTS), expect=403) - - # Bob and chuck can read the template - with self.current_user(self.user_bob): - self.get(url, expect=200) - - with self.current_user(self.user_chuck): - self.get(url, expect=200) - - # Doug and Juan can't - with self.current_user(self.user_doug): - self.get(url, expect=403) - - with self.current_user(self.user_juan): - self.get(url, expect=403) diff --git a/awx/main/tests/old/jobs/start_cancel.py b/awx/main/tests/old/jobs/start_cancel.py deleted file mode 100644 index 1067a618f3..0000000000 --- a/awx/main/tests/old/jobs/start_cancel.py +++ /dev/null @@ -1,248 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved - -# Python -from __future__ import absolute_import -import os -import unittest2 as unittest - -# Django -from django.core.urlresolvers import reverse -from django.conf import settings - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseLiveServerTest -from awx.main.tests.job_base import BaseJobTestMixin - -__all__ = ['JobStartCancelTest',] - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class JobStartCancelTest(BaseJobTestMixin, BaseLiveServerTest): - - def test_job_start(self): - #job = self.job_ops_east_run - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success') - url = reverse('api:job_start', args=(job.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - self.check_invalid_auth(url, methods=('post',)) - - # Sue can start a job (when passwords are already saved) as long as the - # status is new. Reverse list so "new" will be last. - for status in reversed([x[0] for x in Job.STATUS_CHOICES]): - if status == 'waiting': - continue - job.status = status - job.save() - with self.current_user(self.user_sue): - response = self.get(url) - if status == 'new': - self.assertTrue(response['can_start']) - self.assertFalse(response['passwords_needed_to_start']) - # response = self.post(url, {}, expect=202) - # job = Job.objects.get(pk=job.pk) - # self.assertEqual(job.status, 'successful', - # job.result_stdout) - else: - self.assertFalse(response['can_start']) - response = self.post(url, {}, expect=405) - - # Test with a job that prompts for SSH and sudo become passwords. - #job = self.job_sup_run - job = self.make_job(self.jt_sup_run, self.user_sue, 'new') - url = reverse('api:job_start', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - self.assertTrue(response['can_start']) - self.assertEqual(set(response['passwords_needed_to_start']), - set(['ssh_password', 'become_password'])) - data = dict() - response = self.post(url, data, expect=400) - data['ssh_password'] = 'sshpass' - response = self.post(url, data, expect=400) - data2 = dict(become_password='sudopass') - response = self.post(url, data2, expect=400) - data.update(data2) - response = self.post(url, data, expect=202) - job = Job.objects.get(pk=job.pk) - # FIXME: Test run gets the following error in this case: - # fatal: [hostname] => sudo output closed while waiting for password prompt: - #self.assertEqual(job.status, 'successful') - - # Test with a job that prompts for SSH unlock key, given the wrong key. - #job = self.jt_ops_west_run.create_job( - # credential=self.cred_greg, - # created_by=self.user_sue, - #) - job = self.make_job(self.jt_ops_west_run, self.user_sue, 'new') - job.credential = self.cred_greg - job.save() - - url = reverse('api:job_start', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - self.assertTrue(response['can_start']) - self.assertEqual(set(response['passwords_needed_to_start']), - set(['ssh_key_unlock'])) - data = dict() - response = self.post(url, data, expect=400) - # The job should start but fail. - data['ssh_key_unlock'] = 'sshunlock' - response = self.post(url, data, expect=202) - # job = Job.objects.get(pk=job.pk) - # self.assertEqual(job.status, 'failed') - - # Test with a job that prompts for SSH unlock key, given the right key. - from awx.main.tests.data.ssh import TEST_SSH_KEY_DATA_UNLOCK - # job = self.jt_ops_west_run.create_job( - # credential=self.cred_greg, - # created_by=self.user_sue, - # ) - job = self.make_job(self.jt_ops_west_run, self.user_sue, 'new') - job.credential = self.cred_greg - job.save() - url = reverse('api:job_start', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - self.assertTrue(response['can_start']) - self.assertEqual(set(response['passwords_needed_to_start']), - set(['ssh_key_unlock'])) - data = dict() - response = self.post(url, data, expect=400) - data['ssh_key_unlock'] = TEST_SSH_KEY_DATA_UNLOCK - response = self.post(url, data, expect=202) - # job = Job.objects.get(pk=job.pk) - # self.assertEqual(job.status, 'successful') - - # FIXME: Test with other users, test when passwords are required. - - def test_job_cancel(self): - #job = self.job_ops_east_run - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new') - url = reverse('api:job_cancel', args=(job.pk,)) - - # Test with no auth and with invalid login. - self.check_invalid_auth(url) - self.check_invalid_auth(url, methods=('post',)) - - # sue can cancel the job, but only when it is pending or running. - for status in [x[0] for x in Job.STATUS_CHOICES]: - if status == 'waiting': - continue - job.status = status - job.save() - with self.current_user(self.user_sue): - response = self.get(url) - if status in ('new', 'pending', 'waiting', 'running'): - self.assertTrue(response['can_cancel']) - response = self.post(url, {}, expect=202) - else: - self.assertFalse(response['can_cancel']) - response = self.post(url, {}, expect=405) - - # FIXME: Test with other users. - - def test_get_job_results(self): - # Start/run a job and then access its results via the API. - #job = self.job_ops_east_run - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new') - job.signal_start() - - # Check that the job detail has been updated. - url = reverse('api:job_detail', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - self.assertEqual(response['status'], 'successful', - response['result_traceback']) - self.assertTrue(response['result_stdout']) - - # Test job events for completed job. - url = reverse('api:job_job_events_list', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - qs = job.job_events.all() - self.assertTrue(qs.count()) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # Test individual job event detail records. - host_ids = set() - for job_event in job.job_events.all(): - if job_event.host: - host_ids.add(job_event.host.pk) - url = reverse('api:job_event_detail', args=(job_event.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - - # Also test job event list for each host. - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - for host in Host.objects.filter(pk__in=host_ids): - url = reverse('api:host_job_events_list', args=(host.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - qs = host.job_events.all() - self.assertTrue(qs.count()) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # Test job event list for groups. - for group in self.inv_ops_east.groups.all(): - url = reverse('api:group_job_events_list', args=(group.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - qs = group.job_events.all() - self.assertTrue(qs.count(), group) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # Test global job event list. - url = reverse('api:job_event_list') - with self.current_user(self.user_sue): - response = self.get(url) - qs = JobEvent.objects.all() - self.assertTrue(qs.count()) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # Test job host summaries for completed job. - url = reverse('api:job_job_host_summaries_list', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - qs = job.job_host_summaries.all() - self.assertTrue(qs.count()) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - # Every host referenced by a job_event should be present as a job - # host summary record. - self.assertEqual(host_ids, - set(qs.values_list('host__pk', flat=True))) - - # Test individual job host summary records. - for job_host_summary in job.job_host_summaries.all(): - url = reverse('api:job_host_summary_detail', - args=(job_host_summary.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - - # Test job host summaries for each host. - for host in Host.objects.filter(pk__in=host_ids): - url = reverse('api:host_job_host_summaries_list', args=(host.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - qs = host.job_host_summaries.all() - self.assertTrue(qs.count()) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) - - # Test job host summaries for groups. - for group in self.inv_ops_east.groups.all(): - url = reverse('api:group_job_host_summaries_list', args=(group.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - qs = group.job_host_summaries.all() - self.assertTrue(qs.count()) - self.check_pagination_and_size(response, qs.count()) - self.check_list_ids(response, qs) diff --git a/awx/main/tests/old/jobs/survey_password.py b/awx/main/tests/old/jobs/survey_password.py deleted file mode 100644 index d1241eab11..0000000000 --- a/awx/main/tests/old/jobs/survey_password.py +++ /dev/null @@ -1,240 +0,0 @@ -# Python -import json - -# Django -from django.core.urlresolvers import reverse - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTest, QueueStartStopTestMixin - -__all__ = ['SurveyPasswordRedactedTest'] - -PASSWORD="5m/h" -ENCRYPTED_STR='$encrypted$' - -TEST_PLAYBOOK = u'''--- -- name: test success - hosts: test-group - gather_facts: True - tasks: - - name: should pass - command: echo {{ %s }} -''' % ('spot_speed') - - -TEST_SIMPLE_SURVEY = ''' -{ - "name": "Simple", - "description": "Description", - "spec": [ - { - "type": "password", - "question_name": "spots speed", - "question_description": "How fast can spot run?", - "variable": "%s", - "choices": "", - "min": "", - "max": "", - "required": false, - "default": "%s" - } - ] -} -''' % ('spot_speed', PASSWORD) - -TEST_COMPLEX_SURVEY = ''' -{ - "name": "Simple", - "description": "Description", - "spec": [ - { - "type": "password", - "question_name": "spots speed", - "question_description": "How fast can spot run?", - "variable": "spot_speed", - "choices": "", - "min": "", - "max": "", - "required": false, - "default": "0m/h" - }, - { - "type": "password", - "question_name": "ssn", - "question_description": "What's your social security number?", - "variable": "ssn", - "choices": "", - "min": "", - "max": "", - "required": false, - "default": "999-99-9999" - }, - { - "type": "password", - "question_name": "bday", - "question_description": "What's your birth day?", - "variable": "bday", - "choices": "", - "min": "", - "max": "", - "required": false, - "default": "1/1/1970" - } - ] -} -''' - - -TEST_SINGLE_PASSWORDS = [ - { - 'description': 'Single instance with a . after', - 'text' : 'See spot. See spot run. See spot run %s. That is a fast run.' % PASSWORD, - 'passwords': [PASSWORD], - 'occurances': 1, - }, - { - 'description': 'Single instance with , after', - 'text': 'Spot goes %s, at a fast pace' % PASSWORD, - 'passwords': [PASSWORD], - 'occurances': 1, - }, - { - 'description': 'Single instance with a space after', - 'text': 'Is %s very fast?' % PASSWORD, - 'passwords': [PASSWORD], - 'occurances': 1, - }, - { - 'description': 'Many instances, also with newline', - 'text': 'I think %s is very very fast. If I ran %s for 4 hours how many hours would I ' - 'run?.\nTrick question. %s for 4 hours would result in running for 4 hours' % (PASSWORD, PASSWORD, PASSWORD), - 'passwords': [PASSWORD], - 'occurances': 3, - }, -] -passwd = 'my!@#$%^pass&*()_+' -TEST_SINGLE_PASSWORDS.append({ - 'description': 'password includes characters not in a-z 0-9 range', - 'passwords': [passwd], - 'text': 'Text is fun yeah with passwords %s.' % passwd, - 'occurances': 1 -}) - -# 3 because 3 password fields in spec TEST_COMPLEX_SURVEY -TEST_MULTIPLE_PASSWORDS = [] -passwds = [ '65km/s', '545-83-4534', '7/4/2002'] -TEST_MULTIPLE_PASSWORDS.append({ - 'description': '3 different passwords each used once', - 'text': 'Spot runs %s. John has an ss of %s and is born on %s.' % (passwds[0], passwds[1], passwds[2]), - 'passwords': passwds, - 'occurances': 3, -}) - -TESTS = { - 'simple': { - 'survey' : json.loads(TEST_SIMPLE_SURVEY), - 'tests' : TEST_SINGLE_PASSWORDS, - }, - 'complex': { - 'survey' : json.loads(TEST_COMPLEX_SURVEY), - 'tests' : TEST_MULTIPLE_PASSWORDS, - } -} - - -class SurveyPasswordBaseTest(BaseTest, QueueStartStopTestMixin): - def setUp(self): - super(SurveyPasswordBaseTest, self).setUp() - self.setup_instances() - self.setup_users() - - def check_passwords_redacted(self, test, response): - self.assertIsNotNone(response['content']) - for password in test['passwords']: - self.check_not_found(response['content'], password, test['description'], word_boundary=True) - - self.check_found(response['content'], ENCRYPTED_STR, test['occurances'], test['description']) - - # TODO: A more complete test would ensure that the variable value isn't found - def check_extra_vars_redacted(self, test, response): - self.assertIsNotNone(response) - # Ensure that all extra_vars of type password have the value '$encrypted$' - vars = [] - for question in test['survey']['spec']: - if question['type'] == 'password': - vars.append(question['variable']) - - extra_vars = json.loads(response['extra_vars']) - for var in vars: - self.assertIn(var, extra_vars, 'Variable "%s" should exist in "%s"' % (var, extra_vars)) - self.assertEqual(extra_vars[var], ENCRYPTED_STR) - - def _get_url_job_stdout(self, job): - url = reverse('api:job_stdout', args=(job.pk,)) - return self.get(url, expect=200, auth=self.get_super_credentials(), accept='application/json') - - def _get_url_job_details(self, job): - url = reverse('api:job_detail', args=(job.pk,)) - return self.get(url, expect=200, auth=self.get_super_credentials(), accept='application/json') - - -class SurveyPasswordRedactedTest(SurveyPasswordBaseTest): - ''' - Transpose TEST[]['tests'] to the below format. A more flat format." - [ - { - 'text': '...', - 'description': '...', - ..., - 'job': '...', - 'survey': '...' - }, - ] - ''' - def setup_test(self, test_name): - blueprint = TESTS[test_name] - self.tests[test_name] = [] - - job_template = self.make_job_template(survey_enabled=True, survey_spec=blueprint['survey']) - for test in blueprint['tests']: - test = dict(test) - extra_vars = {} - - # build extra_vars from spec variables and passwords - for x in range(0, len(blueprint['survey']['spec'])): - question = blueprint['survey']['spec'][x] - extra_vars[question['variable']] = test['passwords'][x] - - job = self.make_job(job_template=job_template) - job.extra_vars = json.dumps(extra_vars) - job.result_stdout_text = test['text'] - job.save() - test['job'] = job - test['survey'] = blueprint['survey'] - self.tests[test_name].append(test) - - def setUp(self): - super(SurveyPasswordRedactedTest, self).setUp() - - self.tests = {} - self.setup_test('simple') - self.setup_test('complex') - - # should redact single variable survey - def test_redact_stdout_simple_survey(self): - for test in self.tests['simple']: - response = self._get_url_job_stdout(test['job']) - self.check_passwords_redacted(test, response) - - # should redact multiple variables survey - def test_redact_stdout_complex_survey(self): - for test in self.tests['complex']: - response = self._get_url_job_stdout(test['job']) - self.check_passwords_redacted(test, response) - - # should redact values in extra_vars - def test_redact_job_extra_vars(self): - for test in self.tests['simple']: - response = self._get_url_job_details(test['job']) - self.check_extra_vars_redacted(test, response) diff --git a/awx/main/tests/old/projects.py b/awx/main/tests/old/projects.py deleted file mode 100644 index d933ddd0f3..0000000000 --- a/awx/main/tests/old/projects.py +++ /dev/null @@ -1,1703 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import getpass -import json -import os -import re -import time -import subprocess -import tempfile -import urlparse - -# Django -from django.conf import settings -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse -from django.test.utils import override_settings -from django.utils.timezone import now - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTransactionTest -from awx.main.tests.data.ssh import ( - #TEST_SSH_KEY_DATA, - TEST_SSH_KEY_DATA_LOCKED, - TEST_SSH_KEY_DATA_UNLOCK, - #TEST_OPENSSH_KEY_DATA, - #TEST_OPENSSH_KEY_DATA_LOCKED, -) -from awx.main.utils import decrypt_field, update_scm_url - -TEST_PLAYBOOK = '''- hosts: mygroup - gather_facts: false - tasks: - - name: woohoo - command: test 1 = 1 -''' - - -class ProjectsTest(BaseTransactionTest): - - # tests for users, projects, and teams - - def collection(self): - return reverse('api:project_list') - - def setUp(self): - super(ProjectsTest, self).setUp() - self.setup_instances() - self.setup_users() - - self.organizations = self.make_organizations(self.super_django_user, 10) - self.projects = self.make_projects(self.normal_django_user, 10, TEST_PLAYBOOK) - - # add projects to organizations in a more or less arbitrary way - for project in self.projects[0:2]: - self.organizations[0].projects.add(project) - for project in self.projects[3:8]: - self.organizations[1].projects.add(project) - for project in self.projects[9:10]: - self.organizations[2].projects.add(project) - #self.organizations[0].projects.add(self.projects[-1]) - #self.organizations[9].projects.add(self.projects[-2]) - - # get the URL for various organization records - self.a_detail_url = "%s%s" % (self.collection(), self.organizations[0].pk) - self.b_detail_url = "%s%s" % (self.collection(), self.organizations[1].pk) - self.c_detail_url = "%s%s" % (self.collection(), self.organizations[2].pk) - - # configuration: - # admin_user is an admin and regular user in all organizations - # other_user is all organizations - # normal_user is a user in organization 0, and an admin of organization 1 - - for x in self.organizations: - # NOTE: superuser does not have to be explicitly added to admin group - # x.admins.add(self.super_django_user) - x.member_role.members.add(self.super_django_user) - - self.organizations[0].member_role.members.add(self.normal_django_user) - self.organizations[1].admin_role.members.add(self.normal_django_user) - - self.team1 = Team.objects.create( - name = 'team1', organization = self.organizations[0] - ) - - self.team2 = Team.objects.create( - name = 'team2', organization = self.organizations[0] - ) - - # create some teams in the first org - #self.team1.projects.add(self.projects[0]) - self.projects[0].admin_role.parents.add(self.team1.member_role) - #self.team1.projects.add(self.projects[0]) - self.team2.member_role.children.add(self.projects[1].admin_role) - self.team2.member_role.children.add(self.projects[2].admin_role) - self.team2.member_role.children.add(self.projects[3].admin_role) - self.team2.member_role.children.add(self.projects[4].admin_role) - self.team2.member_role.children.add(self.projects[5].admin_role) - self.team1.save() - self.team2.save() - self.team1.member_role.members.add(self.normal_django_user) - self.team2.member_role.members.add(self.other_django_user) - - def test_playbooks(self): - def write_test_file(project, name, content): - full_path = os.path.join(project.get_project_path(), name) - if not os.path.exists(os.path.dirname(full_path)): - os.makedirs(os.path.dirname(full_path)) - f = file(full_path, 'wb') - f.write(content) - f.close() - # Invalid local_path - project = self.projects[0] - project.local_path = 'path_does_not_exist' - project.save() - self.assertFalse(project.get_project_path()) - self.assertEqual(len(project.playbooks), 0) - # Simple playbook - project = self.projects[1] - self.assertEqual(len(project.playbooks), 1) - write_test_file(project, 'foo.yml', TEST_PLAYBOOK) - self.assertEqual(len(project.playbooks), 2) - # Other files - project = self.projects[2] - self.assertEqual(len(project.playbooks), 1) - write_test_file(project, 'foo.txt', 'not a playbook') - self.assertEqual(len(project.playbooks), 1) - # Empty playbook - project = self.projects[3] - self.assertEqual(len(project.playbooks), 1) - write_test_file(project, 'blah.yml', '') - self.assertEqual(len(project.playbooks), 1) - # Invalid YAML (now allowed to show) - project = self.projects[4] - self.assertEqual(len(project.playbooks), 1) - write_test_file(project, 'blah.yml', TEST_PLAYBOOK + '----') - self.assertEqual(len(project.playbooks), 2) - # No hosts or includes - project = self.projects[5] - self.assertEqual(len(project.playbooks), 1) - playbook_content = TEST_PLAYBOOK.replace('hosts', 'hoists') - write_test_file(project, 'blah.yml', playbook_content) - self.assertEqual(len(project.playbooks), 1) - # Playbook in roles folder - project = self.projects[6] - self.assertEqual(len(project.playbooks), 1) - write_test_file(project, 'roles/blah.yml', TEST_PLAYBOOK) - self.assertEqual(len(project.playbooks), 1) - # Playbook in tasks folder - project = self.projects[7] - self.assertEqual(len(project.playbooks), 1) - write_test_file(project, 'tasks/blah.yml', TEST_PLAYBOOK) - self.assertEqual(len(project.playbooks), 1) - - def test_api_config(self): - # superuser can read all config data. - url = reverse('api:api_v1_config_view') - response = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertTrue('project_base_dir' in response) - self.assertEqual(response['project_base_dir'], settings.PROJECTS_ROOT) - self.assertTrue('project_local_paths' in response) - self.assertEqual(set(response['project_local_paths']), - set(Project.get_local_path_choices())) - - # return local paths are only the ones not used by any active project. - qs = Project.objects - used_paths = qs.values_list('local_path', flat=True) - self.assertFalse(set(response['project_local_paths']) & set(used_paths)) - for project in self.projects: - local_path = project.local_path - response = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertTrue(local_path not in response['project_local_paths']) - project.delete() - response = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertTrue(local_path in response['project_local_paths']) - - # org admin can read config and will get project fields. - response = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertTrue('project_base_dir' in response) - self.assertTrue('project_local_paths' in response) - - # regular user can read configuration, but won't have project fields. - response = self.get(url, expect=200, auth=self.get_nobody_credentials()) - self.assertFalse('project_base_dir' in response) - self.assertFalse('project_local_paths' in response) - - # anonymous/invalid user can't access config. - self.get(url, expect=401) - self.get(url, expect=401, auth=self.get_invalid_credentials()) - - def test_dashboard(self): - url = reverse('api:dashboard_view') - # superuser can read dashboard. - self.get(url, expect=200, auth=self.get_super_credentials()) - # org admin can read dashboard. - self.get(url, expect=200, auth=self.get_normal_credentials()) - # regular user can read dashboard. - self.get(url, expect=200, auth=self.get_nobody_credentials()) - # anonymous/invalid user can't access dashboard. - self.get(url, expect=401) - self.get(url, expect=401, auth=self.get_invalid_credentials()) - # FIXME: Test that data on dashboard is filtered based on RBAC. - - def test_mainline(self): - - # ===================================================================== - # PROJECTS - LISTING - - # can get projects list - projects = reverse('api:project_list') - # invalid auth - self.get(projects, expect=401) - self.get(projects, expect=401, auth=self.get_invalid_credentials()) - # super user - results = self.get(projects, expect=200, auth=self.get_super_credentials()) - self.assertEquals(results['count'], 10) - # org admin - results = self.get(projects, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(results['count'], 6) - # user on a team - results = self.get(projects, expect=200, auth=self.get_other_credentials()) - self.assertEquals(results['count'], 5) - # user not on any teams - results = self.get(projects, expect=200, auth=self.get_nobody_credentials()) - self.assertEquals(results['count'], 0) - - # can add projects (super user) - project_dir = tempfile.mkdtemp(dir=settings.PROJECTS_ROOT) - self._temp_paths.append(project_dir) - project_data = { - 'name': 'My Test Project', - 'description': 'Does amazing things', - 'local_path': os.path.basename(project_dir), - 'scm_type': 'git', # must not be manual in order to schedule - 'scm_url': 'http://192.168.100.128.git', - 'scm_update_on_launch': '', - 'scm_delete_on_update': None, - 'scm_clean': False, - 'organization': self.organizations[0].pk, - } - # Adding a project with scm_type=None should work, but scm_type will be - # changed to an empty string. Other boolean fields should accept null - # or an empty string for False, but save the value as a boolean. - response = self.post(projects, project_data, expect=201, - auth=self.get_super_credentials()) - self.assertEqual(response['scm_type'], u'git') - self.assertEqual(response['scm_update_on_launch'], False) - self.assertEqual(response['scm_delete_on_update'], False) - self.assertEqual(response['scm_clean'], False) - - # can edit project using same local path. - project_detail = reverse('api:project_detail', args=(response['id'],)) - project_data = self.get(project_detail, expect=200, - auth=self.get_super_credentials()) - response = self.put(project_detail, project_data, expect=200, - auth=self.get_super_credentials()) - - # cannot update using local_path from another project. - project_data['local_path'] = self.projects[2].local_path - response = self.put(project_detail, project_data, expect=400, - auth=self.get_super_credentials()) - - # cannot update using a path that doesn't exist. - project_data['local_path'] = 'my_secret_invisible_project_path' - response = self.put(project_detail, project_data, expect=400, - auth=self.get_super_credentials()) - - # ===================================================================== - # PROJECTS - ACCESS - project = reverse('api:project_detail', args=(self.projects[3].pk,)) - self.get(project, expect=200, auth=self.get_super_credentials()) - self.get(project, expect=200, auth=self.get_normal_credentials()) - self.get(project, expect=200, auth=self.get_other_credentials()) - self.get(project, expect=403, auth=self.get_nobody_credentials()) - - # can create a schedule for a project (doesn't matter if the project - # has SCM for this test). - project_schedules = reverse('api:project_schedules_list', args=(self.projects[3].pk,)) - with self.current_user(self.super_django_user): - response = self.get(project_schedules, expect=200) - self.assertEqual(response['count'], 0) - dtstart = now().replace(microsecond=0).strftime('%Y%m%dT%H%M%SZ') - data = { - 'name': 'test schedule', - 'rrule': 'DTSTART:%s RRULE:FREQ=DAILY;INTERVAL=1' % dtstart, - } - response = self.post(project_schedules, data, expect=201) - schedule_url = response['url'] - response = self.get(schedule_url, expect=200) - - # can delete projects, schedule should be "deleted" along with project. - self.delete(project, expect=204, auth=self.get_normal_credentials()) - self.get(project, expect=404, auth=self.get_normal_credentials()) - with self.current_user(self.super_django_user): - self.get(schedule_url, expect=404) - - # can list playbooks for projects - proj_playbooks = reverse('api:project_playbooks', args=(self.projects[2].pk,)) - got = self.get(proj_playbooks, expect=200, auth=self.get_super_credentials()) - self.assertEqual(got, self.projects[2].playbooks) - - # ===================================================================== - # TEAMS - - all_teams = reverse('api:team_list') - team1 = reverse('api:team_detail', args=(self.team1.pk,)) - - # can list teams - got = self.get(all_teams, expect=200, auth=self.get_super_credentials()) - self.assertEquals(got['count'], 2) - # FIXME: for other accounts, also check filtering - - # can get teams - got = self.get(team1, expect=200, auth=self.get_super_credentials()) - self.assertEquals(got['url'], reverse('api:team_detail', args=(self.team1.pk,))) - got = self.get(team1, expect=200, auth=self.get_normal_credentials()) - got = self.get(team1, expect=403, auth=self.get_other_credentials()) - self.team1.member_role.members.add(User.objects.get(username='other')) - self.team1.save() - got = self.get(team1, expect=200, auth=self.get_other_credentials()) - got = self.get(team1, expect=403, auth=self.get_nobody_credentials()) - - new_team = dict(name='newTeam', description='blarg', organization=self.organizations[0].pk) - new_team2 = dict(name='newTeam2', description='blarg', organization=self.organizations[0].pk) - new_team3 = dict(name='newTeam3', description='bad wolf', organization=self.organizations[0].pk) - - # can add teams - posted1 = self.post(all_teams, data=new_team, expect=201, auth=self.get_super_credentials()) - self.post(all_teams, data=new_team, expect=400, auth=self.get_super_credentials()) - # normal user is not an admin of organizations[0], but is for [1]. - posted3 = self.post(all_teams, data=new_team2, expect=403, auth=self.get_normal_credentials()) - new_team2['organization'] = self.organizations[1].pk - posted3 = self.post(all_teams, data=new_team2, expect=201, auth=self.get_normal_credentials()) - self.post(all_teams, data=new_team2, expect=400, auth=self.get_normal_credentials()) - self.post(all_teams, data=new_team3, expect=403, auth=self.get_other_credentials()) - posted1['url'] - url3 = posted3['url'] - posted1['url'] - - new_team = Team.objects.create(name='newTeam4', organization=self.organizations[1]) - url = reverse('api:team_detail', args=(new_team.pk,)) - - # can delete teams - self.delete(url, expect=401) - self.delete(url, expect=403, auth=self.get_nobody_credentials()) - self.delete(url, expect=403, auth=self.get_other_credentials()) - self.delete(url, expect=204, auth=self.get_normal_credentials()) - self.delete(url3, expect=204, auth=self.get_super_credentials()) - - # ===================================================================== - # ORGANIZATION TEAMS - - # can list organization teams (filtered by user) -- this is an org admin function - org_teams = reverse('api:organization_teams_list', args=(self.organizations[1].pk,)) - self.get(org_teams, expect=401) - data2 = self.get(org_teams, expect=403, auth=self.get_nobody_credentials()) - self.get(org_teams, expect=403, auth=self.get_other_credentials()) - self.get(org_teams, expect=200, auth=self.get_normal_credentials()) - self.get(org_teams, expect=200, auth=self.get_super_credentials()) - - # can add teams to organizations - new_team1 = dict(name='super new team A') - # also tests that sub posts overwrite the related field: - new_team2 = dict(name='super new team B', organization=34567) - new_team3 = dict(name='super new team C') - - self.post(org_teams, new_team1, expect=401) - self.post(org_teams, new_team1, expect=403, auth=self.get_nobody_credentials()) - self.post(org_teams, new_team1, expect=403, auth=self.get_other_credentials()) - data2 = self.post(org_teams, new_team2, expect=201, auth=self.get_normal_credentials()) - self.post(org_teams, new_team3, expect=201, auth=self.get_super_credentials()) - - # can remove teams from organizations - data2['disassociate'] = 1 - url = data2['url'] - self.post(org_teams, data2, expect=204, auth=self.get_normal_credentials()) - got = self.get(url, expect=404, auth=self.get_normal_credentials()) - - - # ===================================================================== - # TEAM PROJECTS - - team = Team.objects.filter( organization__pk=self.organizations[1].pk)[0] - team_projects = reverse('api:team_projects_list', args=(team.pk,)) - - p1 = self.projects[0] - team.member_role.children.add(p1.admin_role) - team.save() - - got = self.get(team_projects, expect=200, auth=self.get_super_credentials()) - - # FIXME: project postablility tests somewhat incomplete. - # add tests to show we can create new projects on the subresource and so on. - - self.assertEquals(got['count'], 1) - - # ===================================================================== - # TEAMS USER MEMBERSHIP - - team = Team.objects.filter( organization__pk=self.organizations[1].pk)[0] - team_users = reverse('api:team_users_list', args=(team.pk,)) - for x in team.member_role.members.all(): - team.member_role.members.remove(x) - team.save() - - # can list uses on teams - self.get(team_users, expect=401) - self.get(team_users, expect=401, auth=self.get_invalid_credentials()) - self.get(team_users, expect=403, auth=self.get_nobody_credentials()) - self.get(team_users, expect=403, auth=self.get_other_credentials()) - self.get(team_users, expect=200, auth=self.get_normal_credentials()) - self.get(team_users, expect=200, auth=self.get_super_credentials()) - - # can add users to teams (but only users I can see - as an org admin, can now see all users) - all_users = self.get(reverse('api:user_list'), expect=200, auth=self.get_normal_credentials()) - for x in all_users['results']: - self.post(team_users, data=x, expect=403, auth=self.get_nobody_credentials()) - self.post(team_users, data=dict(x, is_superuser=False), - expect=204, auth=self.get_normal_credentials()) - # The normal admin user can't create a super user vicariously through the team/project - self.post(team_users, data=dict(username='attempted_superuser_create', password='thepassword', - is_superuser=True), expect=403, auth=self.get_normal_credentials()) - # ... but a superuser can - self.post(team_users, data=dict(username='attempted_superuser_create', password='thepassword', - is_superuser=True), expect=201, auth=self.get_super_credentials()) - - self.assertEqual(Team.objects.get(pk=team.pk).member_role.members.count(), all_users['count'] + 1) - - # can remove users from teams - for x in all_users['results']: - y = dict(id=x['id'], disassociate=1) - self.post(team_users, data=y, expect=403, auth=self.get_nobody_credentials()) - self.post(team_users, data=y, expect=204, auth=self.get_normal_credentials()) - - self.assertEquals(Team.objects.get(pk=team.pk).member_role.members.count(), 1) # Leaving just the super user we created - - # ===================================================================== - # USER TEAMS - - # from a user, can see what teams they are on (related resource) - other = User.objects.get(username = 'other') - url = reverse('api:user_teams_list', args=(other.pk,)) - self.get(url, expect=401) - self.get(url, expect=401, auth=self.get_invalid_credentials()) - self.get(url, expect=403, auth=self.get_nobody_credentials()) - self.organizations[1].member_role.members.add(other) - # Normal user can only see some teams that other user is a part of, - # since normal user is not an admin of that organization. - my_teams1 = self.get(url, expect=200, auth=self.get_normal_credentials()) - my_teams2 = self.get(url, expect=200, auth=self.get_other_credentials()) - - my_teams1 = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEqual(my_teams1['count'], 1) - # Other user should be able to see all his own teams. - my_teams2 = self.get(url, expect=200, auth=self.get_other_credentials()) - self.assertEqual(my_teams2['count'], 2) - - # ===================================================================== - # USER PROJECTS - - url = reverse('api:user_projects_list', args=(other.pk,)) - - # from a user, can see what projects they can see based on team association - # though this resource doesn't do anything else - got = self.get(url, expect=200, auth=self.get_other_credentials()) - self.assertEquals(got['count'], 5) - got = self.get(url, expect=403, auth=self.get_nobody_credentials()) - got = self.get(url, expect=401, auth=self.get_invalid_credentials()) - got = self.get(url, expect=401) - got = self.get(url, expect=200, auth=self.get_super_credentials()) - - - -@override_settings(CELERY_ALWAYS_EAGER=True, - CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, - ANSIBLE_TRANSPORT='local', - PROJECT_UPDATE_IDLE_TIMEOUT=60, - PROJECT_UPDATE_VVV=True) -class ProjectUpdatesTest(BaseTransactionTest): - - def setUp(self): - super(ProjectUpdatesTest, self).setUp() - self.setup_instances() - self.start_queue() - self.setup_users() - - def tearDown(self): - super(ProjectUpdatesTest, self).tearDown() - self.terminate_queue() - - def create_project(self, **kwargs): - cred_fields = ['scm_username', 'scm_password', 'scm_key_data', - 'scm_key_unlock'] - if set(cred_fields) & set(kwargs.keys()): - kw = { - 'kind': 'scm', - 'user': self.super_django_user, - } - for field in cred_fields: - if field not in kwargs: - continue - if field.startswith('scm_key_'): - kw[field.replace('scm_key_', 'ssh_key_')] = kwargs.pop(field) - else: - kw[field.replace('scm_', '')] = kwargs.pop(field) - u = kw['user'] - del kw['user'] - credential = Credential.objects.create(**kw) - credential.admin_role.members.add(u) - kwargs['credential'] = credential - project = Project.objects.create(**kwargs) - project_path = project.get_project_path(check_if_exists=False) - self._temp_paths.append(project_path) - return project - - def test_update_scm_url(self): - # Handle all of the URL formats supported by the SCM systems: - urls_to_test = [ - # (scm type, original url, new url, new url with username, new url with username and password) - - # git: https://www.kernel.org/pub/software/scm/git/docs/git-clone.html#URLS - # - ssh://[user@]host.xz[:port]/path/to/repo.git/ - ( - 'git', 'ssh://host.xz/path/to/repo.git/', None, - 'ssh://testuser@host.xz/path/to/repo.git/', - 'ssh://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ssh://host.xz:1022/path/to/repo.git', None, - 'ssh://testuser@host.xz:1022/path/to/repo.git', - 'ssh://testuser:testpass@host.xz:1022/path/to/repo.git'), - ( - 'git', 'ssh://user@host.xz/path/to/repo.git/', None, - 'ssh://testuser@host.xz/path/to/repo.git/', - 'ssh://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ssh://user@host.xz:1022/path/to/repo.git', None, - 'ssh://testuser@host.xz:1022/path/to/repo.git', 'ssh://testuser:testpass@host.xz:1022/path/to/repo.git'), - ( - 'git', 'ssh://user:pass@host.xz/path/to/repo.git/', None, - 'ssh://testuser:pass@host.xz/path/to/repo.git/', 'ssh://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ssh://user:pass@host.xz:1022/path/to/repo.git', None, - 'ssh://testuser:pass@host.xz:1022/path/to/repo.git', 'ssh://testuser:testpass@host.xz:1022/path/to/repo.git'), - # - git://host.xz[:port]/path/to/repo.git/ (doesn't really support authentication) - ( - 'git', 'git://host.xz/path/to/repo.git/', None, - 'git://testuser@host.xz/path/to/repo.git/', 'git://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'git://host.xz:9418/path/to/repo.git', None, - 'git://testuser@host.xz:9418/path/to/repo.git', 'git://testuser:testpass@host.xz:9418/path/to/repo.git'), - ( - 'git', 'git://user@host.xz/path/to/repo.git/', None, - 'git://testuser@host.xz/path/to/repo.git/', 'git://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'git://user@host.xz:9418/path/to/repo.git', None, - 'git://testuser@host.xz:9418/path/to/repo.git', 'git://testuser:testpass@host.xz:9418/path/to/repo.git'), - # - http[s]://host.xz[:port]/path/to/repo.git/ - ( - 'git', 'http://host.xz/path/to/repo.git/', None, - 'http://testuser@host.xz/path/to/repo.git/', 'http://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'http://host.xz:8080/path/to/repo.git', None, - 'http://testuser@host.xz:8080/path/to/repo.git', 'http://testuser:testpass@host.xz:8080/path/to/repo.git'), - ( - 'git', 'http://user@host.xz/path/to/repo.git/', None, - 'http://testuser@host.xz/path/to/repo.git/', 'http://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'http://user@host.xz:8080/path/to/repo.git', None, - 'http://testuser@host.xz:8080/path/to/repo.git', 'http://testuser:testpass@host.xz:8080/path/to/repo.git'), - ( - 'git', 'http://user:pass@host.xz/path/to/repo.git/', None, - 'http://testuser:pass@host.xz/path/to/repo.git/', 'http://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'http://user:pass@host.xz:8080/path/to/repo.git', None, - 'http://testuser:pass@host.xz:8080/path/to/repo.git', 'http://testuser:testpass@host.xz:8080/path/to/repo.git'), - ( - 'git', 'https://host.xz/path/to/repo.git/', None, - 'https://testuser@host.xz/path/to/repo.git/', 'https://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'https://host.xz:8443/path/to/repo.git', None, - 'https://testuser@host.xz:8443/path/to/repo.git', 'https://testuser:testpass@host.xz:8443/path/to/repo.git'), - ( - 'git', 'https://user@host.xz/path/to/repo.git/', None, - 'https://testuser@host.xz/path/to/repo.git/', 'https://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'https://user@host.xz:8443/path/to/repo.git', None, - 'https://testuser@host.xz:8443/path/to/repo.git', 'https://testuser:testpass@host.xz:8443/path/to/repo.git'), - ( - 'git', 'https://user:pass@host.xz/path/to/repo.git/', None, - 'https://testuser:pass@host.xz/path/to/repo.git/', 'https://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'https://user:pass@host.xz:8443/path/to/repo.git', None, - 'https://testuser:pass@host.xz:8443/path/to/repo.git', 'https://testuser:testpass@host.xz:8443/path/to/repo.git'), - # - ftp[s]://host.xz[:port]/path/to/repo.git/ - ( - 'git', 'ftp://host.xz/path/to/repo.git/', None, - 'ftp://testuser@host.xz/path/to/repo.git/', 'ftp://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ftp://host.xz:8021/path/to/repo.git', None, - 'ftp://testuser@host.xz:8021/path/to/repo.git', 'ftp://testuser:testpass@host.xz:8021/path/to/repo.git'), - ( - 'git', 'ftp://user@host.xz/path/to/repo.git/', None, - 'ftp://testuser@host.xz/path/to/repo.git/', 'ftp://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ftp://user@host.xz:8021/path/to/repo.git', None, - 'ftp://testuser@host.xz:8021/path/to/repo.git', 'ftp://testuser:testpass@host.xz:8021/path/to/repo.git'), - ( - 'git', 'ftp://user:pass@host.xz/path/to/repo.git/', None, - 'ftp://testuser:pass@host.xz/path/to/repo.git/', 'ftp://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ftp://user:pass@host.xz:8021/path/to/repo.git', None, - 'ftp://testuser:pass@host.xz:8021/path/to/repo.git', 'ftp://testuser:testpass@host.xz:8021/path/to/repo.git'), - ( - 'git', 'ftps://host.xz/path/to/repo.git/', None, - 'ftps://testuser@host.xz/path/to/repo.git/', 'ftps://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ftps://host.xz:8990/path/to/repo.git', None, - 'ftps://testuser@host.xz:8990/path/to/repo.git', 'ftps://testuser:testpass@host.xz:8990/path/to/repo.git'), - ( - 'git', 'ftps://user@host.xz/path/to/repo.git/', None, - 'ftps://testuser@host.xz/path/to/repo.git/', 'ftps://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ftps://user@host.xz:8990/path/to/repo.git', None, - 'ftps://testuser@host.xz:8990/path/to/repo.git', 'ftps://testuser:testpass@host.xz:8990/path/to/repo.git'), - ( - 'git', 'ftps://user:pass@host.xz/path/to/repo.git/', None, - 'ftps://testuser:pass@host.xz/path/to/repo.git/', 'ftps://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'ftps://user:pass@host.xz:8990/path/to/repo.git', None, - 'ftps://testuser:pass@host.xz:8990/path/to/repo.git', 'ftps://testuser:testpass@host.xz:8990/path/to/repo.git'), - # - rsync://host.xz/path/to/repo.git/ - ( - 'git', 'rsync://host.xz/path/to/repo.git/', ValueError, ValueError, ValueError), - # - [user@]host.xz:path/to/repo.git/ (SCP style) - ( - 'git', 'host.xz:path/to/repo.git/', 'git+ssh://host.xz/path/to/repo.git/', - 'git+ssh://testuser@host.xz/path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'user@host.xz:path/to/repo.git/', 'git+ssh://user@host.xz/path/to/repo.git/', - 'git+ssh://testuser@host.xz/path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'user:pass@host.xz:path/to/repo.git/', 'git+ssh://user:pass@host.xz/path/to/repo.git/', - 'git+ssh://testuser:pass@host.xz/path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz/path/to/repo.git/'), - ( - 'git', 'host.xz:~/path/to/repo.git/', 'git+ssh://host.xz/~/path/to/repo.git/', - 'git+ssh://testuser@host.xz/~/path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz/~/path/to/repo.git/'), - ( - 'git', 'user@host.xz:~/path/to/repo.git/', 'git+ssh://user@host.xz/~/path/to/repo.git/', - 'git+ssh://testuser@host.xz/~/path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz/~/path/to/repo.git/'), - ( - 'git', 'user:pass@host.xz:~/path/to/repo.git/', 'git+ssh://user:pass@host.xz/~/path/to/repo.git/', - 'git+ssh://testuser:pass@host.xz/~/path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz/~/path/to/repo.git/'), - ( - 'git', 'host.xz:/path/to/repo.git/', 'git+ssh://host.xz//path/to/repo.git/', - 'git+ssh://testuser@host.xz//path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz//path/to/repo.git/'), - ( - 'git', 'user@host.xz:/path/to/repo.git/', 'git+ssh://user@host.xz//path/to/repo.git/', - 'git+ssh://testuser@host.xz//path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz//path/to/repo.git/'), - ( - 'git', 'user:pass@host.xz:/path/to/repo.git/', 'git+ssh://user:pass@host.xz//path/to/repo.git/', - 'git+ssh://testuser:pass@host.xz//path/to/repo.git/', 'git+ssh://testuser:testpass@host.xz//path/to/repo.git/'), - # - /path/to/repo.git/ (local file) - ('git', '/path/to/repo.git', ValueError, ValueError, ValueError), - ('git', 'path/to/repo.git', ValueError, ValueError, ValueError), - # - file:///path/to/repo.git/ - ('git', 'file:///path/to/repo.git', ValueError, ValueError, ValueError), - ('git', 'file://localhost/path/to/repo.git', ValueError, ValueError, ValueError), - # Invalid SSH URLs: - ('git', 'ssh:github.com:ansible/ansible-examples.git', ValueError, ValueError, ValueError), - ('git', 'ssh://github.com:ansible/ansible-examples.git', ValueError, ValueError, ValueError), - # Special case for github URLs: - ('git', 'git@github.com:ansible/ansible-examples.git', 'git+ssh://git@github.com/ansible/ansible-examples.git', ValueError, ValueError), - ('git', 'bob@github.com:ansible/ansible-examples.git', ValueError, ValueError, ValueError), - # Special case for bitbucket URLs: - ('git', 'ssh://git@bitbucket.org/foo/bar.git', None, ValueError, ValueError), - ('git', 'ssh://git@altssh.bitbucket.org:443/foo/bar.git', None, ValueError, ValueError), - ('git', 'ssh://hg@bitbucket.org/foo/bar.git', ValueError, ValueError, ValueError), - ('git', 'ssh://hg@altssh.bitbucket.org:443/foo/bar.git', ValueError, ValueError, ValueError), - - # hg: http://www.selenic.com/mercurial/hg.1.html#url-paths - # - local/filesystem/path[#revision] - ('hg', '/path/to/repo', ValueError, ValueError, ValueError), - ('hg', 'path/to/repo/', ValueError, ValueError, ValueError), - ('hg', '/path/to/repo#rev', ValueError, ValueError, ValueError), - ('hg', 'path/to/repo/#rev', ValueError, ValueError, ValueError), - # - file://local/filesystem/path[#revision] - ('hg', 'file:///path/to/repo', ValueError, ValueError, ValueError), - ('hg', 'file://localhost/path/to/repo/', ValueError, ValueError, ValueError), - ('hg', 'file:///path/to/repo#rev', ValueError, ValueError, ValueError), - ('hg', 'file://localhost/path/to/repo/#rev', ValueError, ValueError, ValueError), - # - http://[user[:pass]@]host[:port]/[path][#revision] - ( - 'hg', 'http://host.xz/path/to/repo/', None, - 'http://testuser@host.xz/path/to/repo/', 'http://testuser:testpass@host.xz/path/to/repo/'), - ( - 'hg', 'http://host.xz:8080/path/to/repo', None, - 'http://testuser@host.xz:8080/path/to/repo', 'http://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'hg', 'http://user@host.xz/path/to/repo/', None, - 'http://testuser@host.xz/path/to/repo/', 'http://testuser:testpass@host.xz/path/to/repo/'), - ( - 'hg', 'http://user@host.xz:8080/path/to/repo', None, - 'http://testuser@host.xz:8080/path/to/repo', 'http://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'hg', 'http://user:pass@host.xz/path/to/repo/', None, 'http://testuser:pass@host.xz/path/to/repo/', - 'http://testuser:testpass@host.xz/path/to/repo/'), - ( - 'hg', 'http://user:pass@host.xz:8080/path/to/repo', None, - 'http://testuser:pass@host.xz:8080/path/to/repo', - 'http://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'hg', 'http://host.xz/path/to/repo/#rev', None, - 'http://testuser@host.xz/path/to/repo/#rev', 'http://testuser:testpass@host.xz/path/to/repo/#rev'), - ( - 'hg', 'http://host.xz:8080/path/to/repo#rev', None, - 'http://testuser@host.xz:8080/path/to/repo#rev', 'http://testuser:testpass@host.xz:8080/path/to/repo#rev'), - ( - 'hg', 'http://user@host.xz/path/to/repo/#rev', None, - 'http://testuser@host.xz/path/to/repo/#rev', 'http://testuser:testpass@host.xz/path/to/repo/#rev'), - ( - 'hg', 'http://user@host.xz:8080/path/to/repo#rev', None, - 'http://testuser@host.xz:8080/path/to/repo#rev', 'http://testuser:testpass@host.xz:8080/path/to/repo#rev'), - ( - 'hg', 'http://user:pass@host.xz/path/to/repo/#rev', None, - 'http://testuser:pass@host.xz/path/to/repo/#rev', 'http://testuser:testpass@host.xz/path/to/repo/#rev'), - ( - 'hg', 'http://user:pass@host.xz:8080/path/to/repo#rev', None, - 'http://testuser:pass@host.xz:8080/path/to/repo#rev', 'http://testuser:testpass@host.xz:8080/path/to/repo#rev'), - # - https://[user[:pass]@]host[:port]/[path][#revision] - ( - 'hg', 'https://host.xz/path/to/repo/', None, - 'https://testuser@host.xz/path/to/repo/', 'https://testuser:testpass@host.xz/path/to/repo/'), - ( - 'hg', 'https://host.xz:8443/path/to/repo', None, - 'https://testuser@host.xz:8443/path/to/repo', 'https://testuser:testpass@host.xz:8443/path/to/repo'), - ( - 'hg', 'https://user@host.xz/path/to/repo/', None, - 'https://testuser@host.xz/path/to/repo/', 'https://testuser:testpass@host.xz/path/to/repo/'), - ( - 'hg', 'https://user@host.xz:8443/path/to/repo', None, - 'https://testuser@host.xz:8443/path/to/repo', 'https://testuser:testpass@host.xz:8443/path/to/repo'), - ( - 'hg', 'https://user:pass@host.xz/path/to/repo/', None, - 'https://testuser:pass@host.xz/path/to/repo/', 'https://testuser:testpass@host.xz/path/to/repo/'), - ( - 'hg', 'https://user:pass@host.xz:8443/path/to/repo', None, - 'https://testuser:pass@host.xz:8443/path/to/repo', 'https://testuser:testpass@host.xz:8443/path/to/repo'), - ( - 'hg', 'https://host.xz/path/to/repo/#rev', None, - 'https://testuser@host.xz/path/to/repo/#rev', 'https://testuser:testpass@host.xz/path/to/repo/#rev'), - ( - 'hg', 'https://host.xz:8443/path/to/repo#rev', None, - 'https://testuser@host.xz:8443/path/to/repo#rev', 'https://testuser:testpass@host.xz:8443/path/to/repo#rev'), - ( - 'hg', 'https://user@host.xz/path/to/repo/#rev', None, - 'https://testuser@host.xz/path/to/repo/#rev', 'https://testuser:testpass@host.xz/path/to/repo/#rev'), - ( - 'hg', 'https://user@host.xz:8443/path/to/repo#rev', None, - 'https://testuser@host.xz:8443/path/to/repo#rev', 'https://testuser:testpass@host.xz:8443/path/to/repo#rev'), - ( - 'hg', 'https://user:pass@host.xz/path/to/repo/#rev', None, - 'https://testuser:pass@host.xz/path/to/repo/#rev', 'https://testuser:testpass@host.xz/path/to/repo/#rev'), - ( - 'hg', 'https://user:pass@host.xz:8443/path/to/repo#rev', None, - 'https://testuser:pass@host.xz:8443/path/to/repo#rev', 'https://testuser:testpass@host.xz:8443/path/to/repo#rev'), - # - ssh://[user@]host[:port]/[path][#revision] - # Password is always stripped out for hg when using SSH. - ('hg', 'ssh://host.xz/path/to/repo/', None, 'ssh://testuser@host.xz/path/to/repo/', 'ssh://testuser@host.xz/path/to/repo/'), - ('hg', 'ssh://host.xz:1022/path/to/repo', None, 'ssh://testuser@host.xz:1022/path/to/repo', 'ssh://testuser@host.xz:1022/path/to/repo'), - ('hg', 'ssh://user@host.xz/path/to/repo/', None, 'ssh://testuser@host.xz/path/to/repo/', 'ssh://testuser@host.xz/path/to/repo/'), - ('hg', 'ssh://user@host.xz:1022/path/to/repo', None, 'ssh://testuser@host.xz:1022/path/to/repo', 'ssh://testuser@host.xz:1022/path/to/repo'), - ( - 'hg', 'ssh://user:pass@host.xz/path/to/repo/', - 'ssh://user@host.xz/path/to/repo/', - 'ssh://testuser@host.xz/path/to/repo/', - 'ssh://testuser@host.xz/path/to/repo/'), - ( - 'hg', 'ssh://user:pass@host.xz:1022/path/to/repo', - 'ssh://user@host.xz:1022/path/to/repo', - 'ssh://testuser@host.xz:1022/path/to/repo', - 'ssh://testuser@host.xz:1022/path/to/repo'), - ( - 'hg', 'ssh://host.xz/path/to/repo/#rev', None, - 'ssh://testuser@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev'), - ( - 'hg', 'ssh://host.xz:1022/path/to/repo#rev', None, - 'ssh://testuser@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev'), - ( - 'hg', 'ssh://user@host.xz/path/to/repo/#rev', None, - 'ssh://testuser@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev'), - ( - 'hg', 'ssh://user@host.xz:1022/path/to/repo#rev', None, - 'ssh://testuser@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev'), - ( - 'hg', 'ssh://user:pass@host.xz/path/to/repo/#rev', - 'ssh://user@host.xz/path/to/repo/#rev', - 'ssh://testuser@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev'), - ( - 'hg', 'ssh://user:pass@host.xz:1022/path/to/repo#rev', 'ssh://user@host.xz:1022/path/to/repo#rev', - 'ssh://testuser@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev'), - # Special case for bitbucket URLs: - ('hg', 'ssh://hg@bitbucket.org/foo/bar', None, ValueError, ValueError), - ('hg', 'ssh://hg@altssh.bitbucket.org:443/foo/bar', None, ValueError, ValueError), - ('hg', 'ssh://bob@bitbucket.org/foo/bar', ValueError, ValueError, ValueError), - ('hg', 'ssh://bob@altssh.bitbucket.org:443/foo/bar', ValueError, ValueError, ValueError), - - # svn: http://svnbook.red-bean.com/en/1.7/svn-book.html#svn.advanced.reposurls - # - file:/// Direct repository access (on local disk) - ('svn', 'file:///path/to/repo', ValueError, ValueError, ValueError), - ('svn', 'file://localhost/path/to/repo/', ValueError, ValueError, ValueError), - # - http:// Access via WebDAV protocol to Subversion-aware Apache server - ( - 'svn', 'http://host.xz/path/to/repo/', None, - 'http://testuser@host.xz/path/to/repo/', 'http://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'http://host.xz:8080/path/to/repo', None, - 'http://testuser@host.xz:8080/path/to/repo', 'http://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'svn', 'http://user@host.xz/path/to/repo/', None, - 'http://testuser@host.xz/path/to/repo/', 'http://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'http://user@host.xz:8080/path/to/repo', None, - 'http://testuser@host.xz:8080/path/to/repo', 'http://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'svn', 'http://user:pass@host.xz/path/to/repo/', None, - 'http://testuser:pass@host.xz/path/to/repo/', 'http://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'http://user:pass@host.xz:8080/path/to/repo', None, - 'http://testuser:pass@host.xz:8080/path/to/repo', 'http://testuser:testpass@host.xz:8080/path/to/repo'), - # - https:// Same as http://, but with SSL encryption - ( - 'svn', 'https://host.xz/path/to/repo/', None, - 'https://testuser@host.xz/path/to/repo/', 'https://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'https://host.xz:8080/path/to/repo', None, - 'https://testuser@host.xz:8080/path/to/repo', 'https://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'svn', 'https://user@host.xz/path/to/repo/', None, - 'https://testuser@host.xz/path/to/repo/', 'https://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'https://user@host.xz:8080/path/to/repo', None, - 'https://testuser@host.xz:8080/path/to/repo', 'https://testuser:testpass@host.xz:8080/path/to/repo'), - ( - 'svn', 'https://user:pass@host.xz/path/to/repo/', None, - 'https://testuser:pass@host.xz/path/to/repo/', 'https://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'https://user:pass@host.xz:8080/path/to/repo', None, - 'https://testuser:pass@host.xz:8080/path/to/repo', 'https://testuser:testpass@host.xz:8080/path/to/repo'), - # - svn:// Access via custom protocol to an svnserve server - ( - 'svn', 'svn://host.xz/path/to/repo/', None, - 'svn://testuser@host.xz/path/to/repo/', 'svn://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'svn://host.xz:3690/path/to/repo', None, - 'svn://testuser@host.xz:3690/path/to/repo', 'svn://testuser:testpass@host.xz:3690/path/to/repo'), - ( - 'svn', 'svn://user@host.xz/path/to/repo/', None, - 'svn://testuser@host.xz/path/to/repo/', 'svn://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'svn://user@host.xz:3690/path/to/repo', None, - 'svn://testuser@host.xz:3690/path/to/repo', 'svn://testuser:testpass@host.xz:3690/path/to/repo'), - ( - 'svn', 'svn://user:pass@host.xz/path/to/repo/', None, - 'svn://testuser:pass@host.xz/path/to/repo/', 'svn://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'svn://user:pass@host.xz:3690/path/to/repo', None, - 'svn://testuser:pass@host.xz:3690/path/to/repo', 'svn://testuser:testpass@host.xz:3690/path/to/repo'), - # - svn+ssh:// Same as svn://, but through an SSH tunnel - ( - 'svn', 'svn+ssh://host.xz/path/to/repo/', None, - 'svn+ssh://testuser@host.xz/path/to/repo/', 'svn+ssh://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'svn+ssh://host.xz:1022/path/to/repo', None, - 'svn+ssh://testuser@host.xz:1022/path/to/repo', 'svn+ssh://testuser:testpass@host.xz:1022/path/to/repo'), - ( - 'svn', 'svn+ssh://user@host.xz/path/to/repo/', None, - 'svn+ssh://testuser@host.xz/path/to/repo/', 'svn+ssh://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'svn+ssh://user@host.xz:1022/path/to/repo', None, - 'svn+ssh://testuser@host.xz:1022/path/to/repo', 'svn+ssh://testuser:testpass@host.xz:1022/path/to/repo'), - ( - 'svn', 'svn+ssh://user:pass@host.xz/path/to/repo/', None, - 'svn+ssh://testuser:pass@host.xz/path/to/repo/', 'svn+ssh://testuser:testpass@host.xz/path/to/repo/'), - ( - 'svn', 'svn+ssh://user:pass@host.xz:1022/path/to/repo', None, - 'svn+ssh://testuser:pass@host.xz:1022/path/to/repo', 'svn+ssh://testuser:testpass@host.xz:1022/path/to/repo'), - ] - - # Some invalid URLs. - for scm_type in ('git', 'svn', 'hg'): - urls_to_test.append((scm_type, 'host', ValueError, ValueError, ValueError)) - urls_to_test.append((scm_type, '/path', ValueError, ValueError, ValueError)) - urls_to_test.append((scm_type, 'mailto:joe@example.com', ValueError, ValueError, ValueError)) - urls_to_test.append((scm_type, 'telnet://host.xz/path/to/repo', ValueError, ValueError, ValueError)) - - def is_exception(e): - return bool(isinstance(e, Exception) or (isinstance(e, type) and issubclass(e, Exception))) - - for url_opts in urls_to_test: - scm_type, url, new_url, new_url_u, new_url_up = url_opts - new_url = new_url or url - new_url_u = new_url_u or url - new_url_up = new_url_up or url - - # Check existing URL as-is. - if is_exception(new_url): - self.assertRaises(new_url, update_scm_url, scm_type, url) - else: - updated_url = update_scm_url(scm_type, url) - self.assertEqual(new_url, updated_url) - if updated_url.startswith('git+ssh://'): - new_url2 = new_url.replace('git+ssh://', '', 1).replace('/', ':', 1) - updated_url2 = update_scm_url(scm_type, url, scp_format=True) - self.assertEqual(new_url2, updated_url2) - - # Check URL with username replaced. - if is_exception(new_url_u): - self.assertRaises(new_url_u, update_scm_url, scm_type, url, username='testuser') - else: - updated_url = update_scm_url(scm_type, url, username='testuser') - self.assertEqual(new_url_u, updated_url) - if updated_url.startswith('git+ssh://'): - new_url2 = new_url_u.replace('git+ssh://', '', 1).replace('/', ':', 1) - updated_url2 = update_scm_url(scm_type, url, username='testuser', scp_format=True) - self.assertEqual(new_url2, updated_url2) - - # Check URL with username and password replaced. - if is_exception(new_url_up): - self.assertRaises(new_url_up, update_scm_url, scm_type, url, username='testuser', password='testpass') - else: - updated_url = update_scm_url(scm_type, url, username='testuser', password='testpass') - self.assertEqual(new_url_up, updated_url) - if updated_url.startswith('git+ssh://'): - new_url2 = new_url_up.replace('git+ssh://', '', 1).replace('/', ':', 1) - updated_url2 = update_scm_url(scm_type, url, username='testuser', password='testpass', scp_format=True) - self.assertEqual(new_url2, updated_url2) - - def is_public_key_in_authorized_keys(self): - auth_keys = set() - auth_keys_path = os.path.expanduser('~/.ssh/authorized_keys') - if os.path.exists(auth_keys_path): - for line in file(auth_keys_path, 'r'): - if line.strip(): - key = tuple(line.strip().split()[:2]) - auth_keys.add(key) - pub_keys = set() - rsa_key_path = os.path.expanduser('~/.ssh/id_rsa.pub') - if os.path.exists(rsa_key_path): - for line in file(rsa_key_path, 'r'): - if line.strip(): - key = tuple(line.strip().split()[:2]) - pub_keys.add(key) - dsa_key_path = os.path.expanduser('~/.ssh/id_dsa.pub') - if os.path.exists(dsa_key_path): - for line in file(dsa_key_path, 'r'): - if line.strip(): - key = tuple(line.strip().split()[:2]) - pub_keys.add(key) - return bool(auth_keys & pub_keys) - - def check_project_update(self, project, should_fail=False, **kwargs): - pu = kwargs.pop('project_update', None) - should_error = kwargs.pop('should_error', False) - if not pu: - pu = project.update(**kwargs) - self.assertTrue(pu) - pu = ProjectUpdate.objects.get(pk=pu.pk) - if should_error: - self.assertEqual(pu.status, 'error', - pu.result_stdout + pu.result_traceback) - elif should_fail: - self.assertEqual(pu.status, 'failed', - pu.result_stdout + pu.result_traceback) - elif should_fail is False: - self.assertEqual(pu.status, 'successful', - pu.result_stdout + pu.result_traceback) - else: - #print pu.result_stdout - pass # If should_fail is None, we don't care. - # Get the SCM URL from the job args, if it starts with a '/' we aren't - # handling the URL correctly. - if not should_error: - scm_url_in_args_re = re.compile(r'\\(?:\\\\)??"scm_url\\(?:\\\\)??": \\(?:\\\\)??"(.*?)\\(?:\\\\)??"') - match = scm_url_in_args_re.search(pu.job_args) - self.assertTrue(match, pu.job_args) - scm_url_in_args = match.groups()[0] - self.assertFalse(scm_url_in_args.startswith('/'), scm_url_in_args) - #return pu - # Make sure scm_password doesn't show up anywhere in args or output - # from project update. - if project.credential: - scm_password = kwargs.get('scm_password', - decrypt_field(project.credential, - 'password')) - if scm_password: - self.assertFalse(scm_password in pu.job_args, pu.job_args) - self.assertFalse(scm_password in json.dumps(pu.job_env), - json.dumps(pu.job_env)) - # FIXME: Not filtering password from stdout since saving it - # directly to a file. - #self.assertFalse(scm_password in pu.result_stdout, - # pu.result_stdout) - self.assertFalse(scm_password in pu.result_traceback, - pu.result_traceback) - # Make sure scm_key_unlock doesn't show up anywhere in args or output - # from project update. - if project.credential: - scm_key_unlock = kwargs.get('scm_key_unlock', - decrypt_field(project.credential, - 'ssh_key_unlock')) - if scm_key_unlock: - self.assertFalse(scm_key_unlock in pu.job_args, pu.job_args) - self.assertFalse(scm_key_unlock in json.dumps(pu.job_env), - json.dumps(pu.job_env)) - self.assertFalse(scm_key_unlock in pu.result_stdout, - pu.result_stdout) - self.assertFalse(scm_key_unlock in pu.result_traceback, - pu.result_traceback) - project = Project.objects.get(pk=project.pk) - self.assertEqual(project.last_update, pu) - self.assertEqual(project.last_update_failed, pu.failed) - return pu - - def change_file_in_project(self, project): - project_path = project.get_project_path() - self.assertTrue(project_path) - for root, dirs, files in os.walk(project_path): - for f in files: - if f.startswith('.') or f == 'yadayada.txt': - continue - path_parts = os.path.relpath(root, project_path).split(os.sep) - if any([x.startswith('.') and x != '.' for x in path_parts]): - continue - path = os.path.join(root, f) - before = file(path, 'rb').read() - #print 'changed', path - file(path, 'wb').write('CHANGED FILE') - after = file(path, 'rb').read() - return path, before, after - self.fail('no file found to change!') - - def check_project_scm(self, project): - project = Project.objects.get(pk=project.pk) - project_path = project.get_project_path(check_if_exists=False) - # If project could be auto-updated on creation, the project dir should - # already exist, otherwise run an initial checkout. - if project.scm_type: - self.assertTrue(project.last_update) - self.check_project_update(project, - project_update=project.last_update) - self.assertTrue(os.path.exists(project_path)) - else: - self.assertFalse(os.path.exists(project_path)) - self.check_project_update(project) - self.assertTrue(os.path.exists(project_path)) - - # TODO: Removed pending resolution of: https://github.com/ansible/ansible/issues/6582 - # # Stick a new untracked file in the project. - untracked_path = os.path.join(project_path, 'yadayada.txt') - self.assertFalse(os.path.exists(untracked_path)) - file(untracked_path, 'wb').write('yabba dabba doo') - self.assertTrue(os.path.exists(untracked_path)) - # # Update to existing checkout (should leave untracked file alone). - # self.check_project_update(project) - # self.assertTrue(os.path.exists(untracked_path)) - # # Change file then update (with scm_clean=False). Modified file should - # # not be changed. - # self.assertFalse(project.scm_clean) - # modified_path, before, after = self.change_file_in_project(project) - # # Mercurial still returns successful if a modified file is present. - # should_fail = bool(project.scm_type != 'hg') - # self.check_project_update(project, should_fail=should_fail) - # content = file(modified_path, 'rb').read() - # self.assertEqual(content, after) - # self.assertTrue(os.path.exists(untracked_path)) - # # Set scm_clean=True then try to update again. Modified file should - # # have been replaced with the original. Untracked file should still be - # # present. - # project.scm_clean = True - # project.save() - # self.check_project_update(project) - # content = file(modified_path, 'rb').read() - # self.assertEqual(content, before) - # self.assertTrue(os.path.exists(untracked_path)) - # # If scm_type or scm_url changes, scm_delete_on_next_update should be - # # set, causing project directory (including untracked file) to be - # # completely blown away, but only for the next update.. - # self.assertFalse(project.scm_delete_on_update) - # self.assertFalse(project.scm_delete_on_next_update) - # scm_type = project.scm_type - # project.scm_type = '' - # project.save() - # self.assertTrue(project.scm_delete_on_next_update) - # project.scm_type = scm_type - # project.save() - # self.check_project_update(project) - # self.assertFalse(os.path.exists(untracked_path)) - # # Check that the flag is cleared after the update, and that an - # # untracked file isn't blown away. - # project = Project.objects.get(pk=project.pk) - # self.assertFalse(project.scm_delete_on_next_update) - # file(untracked_path, 'wb').write('yabba dabba doo') - # self.assertTrue(os.path.exists(untracked_path)) - # self.check_project_update(project) - # self.assertTrue(os.path.exists(untracked_path)) - - - # Set scm_delete_on_update=True then update again. Project directory - # (including untracked file) should be completely blown away. - self.assertFalse(project.scm_delete_on_update) - project.scm_delete_on_update = True - project.save() - self.check_project_update(project) - self.assertFalse(os.path.exists(untracked_path)) - # Change username/password for private projects and verify the update - # fails (but doesn't cause the task to hang). - scm_url_parts = urlparse.urlsplit(project.scm_url) - # FIXME: Implement these tests again with new credentials! - if 0 and project.scm_username and project.scm_password: - scm_username = project.scm_username - should_still_fail = not (getpass.getuser() == scm_username and - scm_url_parts.hostname == 'localhost' and - 'ssh' in scm_url_parts.scheme and - self.is_public_key_in_authorized_keys()) - # Clear username only. - project = Project.objects.get(pk=project.pk) - project.scm_username = '' - project.save() - self.check_project_update(project, should_fail=should_still_fail) - # Try invalid username. - project = Project.objects.get(pk=project.pk) - project.scm_username = 'not a\\ valid\' user" name' - project.save() - self.check_project_update(project, should_fail=True) - # Clear username and password. - project = Project.objects.get(pk=project.pk) - project.scm_username = '' - project.scm_password = '' - project.save() - self.check_project_update(project, should_fail=should_still_fail) - # Set username, but no password. - project = Project.objects.get(pk=project.pk) - project.scm_username = scm_username - project.save() - self.check_project_update(project, should_fail=should_still_fail) - # Set username, with invalid password. - project = Project.objects.get(pk=project.pk) - project.scm_password = 'not a\\ valid\' "password' - project.save() - if project.scm_type == 'svn': - self.check_project_update(project, should_fail=True) # should_still_fail) - else: - self.check_project_update(project, should_fail=should_still_fail) - # Test that we can delete project updates. - for pu in project.project_updates.all(): - pu_url = reverse('api:project_update_detail', args=(pu.pk,)) - with self.current_user(self.super_django_user): - self.delete(pu_url, expect=204) - - def test_create_project_with_scm(self): - scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS', - 'https://github.com/ansible/ansible.github.com.git') - if not all([scm_url]): - self.skipTest('no public git repo defined for https!') - projects_url = reverse('api:project_list') - credentials_url = reverse('api:credential_list') - org = self.make_organizations(self.super_django_user, 1)[0] - # Test basic project creation without a credential. - project_data = { - 'name': 'my public git project over https', - 'scm_type': 'git', - 'scm_url': scm_url, - 'organization': org.id, - } - with self.current_user(self.super_django_user): - self.post(projects_url, project_data, expect=201) - # Test with an invalid URL. - project_data = { - 'name': 'my local git project', - 'scm_type': 'git', - 'scm_url': 'file:///path/to/repo.git', - 'organization': org.id, - } - with self.current_user(self.super_django_user): - self.post(projects_url, project_data, expect=400) - # Test creation with a credential. - credential_data = { - 'name': 'my scm credential', - 'kind': 'scm', - 'user': self.super_django_user.pk, - 'username': 'testuser', - 'password': 'testpass', - } - with self.current_user(self.super_django_user): - response = self.post(credentials_url, credential_data, expect=201) - credential_id = response['id'] - project_data = { - 'name': 'my git project over https with credential', - 'scm_type': 'git', - 'scm_url': scm_url, - 'credential': credential_id, - 'organization': org.id, - } - with self.current_user(self.super_django_user): - self.post(projects_url, project_data, expect=201) - # Test creation with an invalid credential type. - ssh_credential_data = { - 'name': 'my ssh credential', - 'kind': 'ssh', - 'user': self.super_django_user.pk, - 'username': 'testuser', - 'password': 'testpass', - } - with self.current_user(self.super_django_user): - response = self.post(credentials_url, ssh_credential_data, - expect=201) - ssh_credential_id = response['id'] - project_data = { - 'name': 'my git project with invalid credential type', - 'scm_type': 'git', - 'scm_url': scm_url, - 'credential': ssh_credential_id, - 'organization': org.id, - } - with self.current_user(self.super_django_user): - self.post(projects_url, project_data, expect=400) - # Test special case for github/bitbucket URLs. - project_data = { - 'name': 'my github project over ssh', - 'scm_type': 'git', - 'scm_url': 'ssh://git@github.com/ansible/ansible.github.com.git', - 'credential': credential_id, - 'organization': org.id, - } - with self.current_user(self.super_django_user): - self.post(projects_url, project_data, expect=201) - - def test_delete_project_update_as_org_admin(self): - scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS', - 'https://github.com/ansible/ansible.github.com.git') - if not all([scm_url]): - self.skipTest('no public git repo defined for https!') - projects_url = reverse('api:project_list') - org = self.make_organizations(self.super_django_user, 1)[0] - project_data = { - 'name': 'my public git project over https', - 'scm_type': 'git', - 'scm_url': scm_url, - 'organization': org.id, - } - org.admin_role.members.add(self.normal_django_user) - with self.current_user(self.super_django_user): - del_proj = self.post(projects_url, project_data, expect=201) - del_proj = Project.objects.get(pk=del_proj["id"]) - org.projects.add(del_proj) - pu = self.check_project_update(del_proj) - pu_url = reverse('api:project_update_detail', args=(pu.id,)) - self.delete(pu_url, expect=403, auth=self.get_other_credentials()) - self.delete(pu_url, expect=204, auth=self.get_normal_credentials()) - - def test_public_git_project_over_https(self): - scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS', - 'https://github.com/ansible/ansible.github.com.git') - if not all([scm_url]): - self.skipTest('no public git repo defined for https!') - project = self.create_project( - name='my public git project over https', - scm_type='git', - scm_url=scm_url, - ) - self.check_project_scm(project) - # Test passing username/password for public project. Though they're not - # needed, the update should still work. - scm_username = getattr(settings, 'TEST_GIT_USERNAME', '') - scm_password = getattr(settings, 'TEST_GIT_PASSWORD', '') - if scm_username or scm_password: - project2 = self.create_project( - name='my other public git project over https', - scm_type='git', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_update(project2) - - def test_private_git_project_over_https(self): - scm_url = getattr(settings, 'TEST_GIT_PRIVATE_HTTPS', '') - scm_username = getattr(settings, 'TEST_GIT_USERNAME', '') - scm_password = getattr(settings, 'TEST_GIT_PASSWORD', '') - if not all([scm_url, scm_username, scm_password]): - self.skipTest('no private git repo defined for https!') - project = self.create_project( - name='my private git project over https', - scm_type='git', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_scm(project) - - def test_private_git_project_over_ssh(self): - scm_url = getattr(settings, 'TEST_GIT_PRIVATE_SSH', '') - scm_key_data = getattr(settings, 'TEST_GIT_KEY_DATA', '') - scm_username = getattr(settings, 'TEST_GIT_USERNAME', '') - scm_password = 'blahblahblah' # getattr(settings, 'TEST_GIT_PASSWORD', '') - if not all([scm_url, scm_key_data, scm_username, scm_password]): - self.skipTest('no private git repo defined for ssh!') - project = self.create_project( - name='my private git project over ssh', - scm_type='git', - scm_url=scm_url, - scm_key_data=scm_key_data, - ) - self.check_project_scm(project) - # Test project using SSH username/password instead of key. Should fail - # because of bad password, but never hang. - project2 = self.create_project( - name='my other private git project over ssh', - scm_type='git', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - bool('github.com' in scm_url and scm_username != 'git') - self.check_project_update(project2, should_fail=None) # , should_error=should_error) - - def test_scm_key_unlock_on_project_update(self): - scm_url = 'git@github.com:ansible/ansible.github.com.git' - project = self.create_project( - name='my git project over ssh with encrypted key', - scm_type='git', - scm_url=scm_url, - scm_key_data=TEST_SSH_KEY_DATA_LOCKED, - scm_key_unlock=TEST_SSH_KEY_DATA_UNLOCK, - ) - url = reverse('api:project_update_view', args=(project.pk,)) - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertTrue(response['can_update']) - with self.current_user(self.super_django_user): - response = self.post(url, {}, expect=202) - project_update = project.project_updates.order_by('-pk')[0] - self.check_project_update(project, should_fail=None, - project_update=project_update) - # Verify that we responded to ssh-agent prompt. - self.assertTrue('Identity added' in project_update.result_stdout, - project_update.result_stdout) - # Try again with a bad unlock password. - project = self.create_project( - name='my git project over ssh with encrypted key and bad pass', - scm_type='git', - scm_url=scm_url, - scm_key_data=TEST_SSH_KEY_DATA_LOCKED, - scm_key_unlock='not the right password', - ) - with self.current_user(self.super_django_user): - response = self.get(url, expect=200) - self.assertTrue(response['can_update']) - with self.current_user(self.super_django_user): - response = self.post(url, {}, expect=202) - project_update = project.project_updates.order_by('-pk')[0] - self.check_project_update(project, should_fail=None, - project_update=project_update) - # Verify response to ssh-agent prompt, did not accept password. - self.assertTrue('Bad passphrase' in project_update.result_stdout, - project_update.result_stdout) - self.assertFalse('Identity added' in project_update.result_stdout, - project_update.result_stdout) - - def create_local_git_repo(self): - repo_dir = tempfile.mkdtemp() - self._temp_paths.append(repo_dir) - handle, playbook_path = tempfile.mkstemp(suffix='.yml', dir=repo_dir) - test_playbook_file = os.fdopen(handle, 'w') - test_playbook_file.write(TEST_PLAYBOOK) - test_playbook_file.close() - subprocess.check_call(['git', 'init', '.'], cwd=repo_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - subprocess.check_call(['git', 'add', os.path.basename(playbook_path)], - cwd=repo_dir, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - subprocess.check_call(['git', 'commit', '-m', 'blah'], cwd=repo_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return repo_dir - - def _test_git_project_from_local_path(self): - repo_dir = self.create_local_git_repo() - project = self.create_project( - name='my git project from local path', - scm_type='git', - scm_url=repo_dir, - ) - self.check_project_scm(project) - - def test_git_project_via_ssh_loopback(self): - scm_username = getattr(settings, 'TEST_SSH_LOOPBACK_USERNAME', '') - scm_password = getattr(settings, 'TEST_SSH_LOOPBACK_PASSWORD', '') - if not all([scm_username, scm_password]): - self.skipTest('no ssh loopback username/password defined!') - repo_dir = self.create_local_git_repo() - scm_url = 'ssh://localhost%s' % repo_dir - project = self.create_project( - name='my git project via ssh loopback', - scm_type='git', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_scm(project) - - def test_public_hg_project_over_https(self): - scm_url = getattr(settings, 'TEST_HG_PUBLIC_HTTPS', - 'https://bitbucket.org/cchurch/django-hotrunner') - if not all([scm_url]): - self.skipTest('no public hg repo defined for https!') - project = self.create_project( - name='my public hg project over https', - scm_type='hg', - scm_url=scm_url, - ) - self.check_project_scm(project) - # Test passing username/password for public project. Though they're not - # needed, the update should still work. - scm_username = getattr(settings, 'TEST_HG_USERNAME', '') - scm_password = getattr(settings, 'TEST_HG_PASSWORD', '') - if scm_username or scm_password: - project2 = self.create_project( - name='my other public hg project over https', - scm_type='hg', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_update(project2) - - def test_private_hg_project_over_https(self): - scm_url = getattr(settings, 'TEST_HG_PRIVATE_HTTPS', '') - scm_username = getattr(settings, 'TEST_HG_USERNAME', '') - scm_password = getattr(settings, 'TEST_HG_PASSWORD', '') - if not all([scm_url, scm_username, scm_password]): - self.skipTest('no private hg repo defined for https!') - project = self.create_project( - name='my private hg project over https', - scm_type='hg', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_scm(project) - - def test_private_hg_project_over_ssh(self): - scm_url = getattr(settings, 'TEST_HG_PRIVATE_SSH', '') - scm_key_data = getattr(settings, 'TEST_HG_KEY_DATA', '') - if not all([scm_url, scm_key_data]): - self.skipTest('no private hg repo defined for ssh!') - project = self.create_project( - name='my private hg project over ssh', - scm_type='hg', - scm_url=scm_url, - scm_key_data=scm_key_data, - ) - self.check_project_scm(project) - # hg doesn't support password for ssh:// urls. - - def create_local_hg_repo(self): - repo_dir = tempfile.mkdtemp() - self._temp_paths.append(repo_dir) - handle, playbook_path = tempfile.mkstemp(suffix='.yml', dir=repo_dir) - test_playbook_file = os.fdopen(handle, 'w') - test_playbook_file.write(TEST_PLAYBOOK) - test_playbook_file.close() - subprocess.check_call(['hg', 'init', '.'], cwd=repo_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - subprocess.check_call(['hg', 'add', os.path.basename(playbook_path)], - cwd=repo_dir, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - subprocess.check_call(['hg', 'commit', '-m', 'blah'], cwd=repo_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - return repo_dir - - def _test_hg_project_from_local_path(self): - repo_dir = self.create_local_hg_repo() - project = self.create_project( - name='my hg project from local path', - scm_type='hg', - scm_url=repo_dir, - ) - self.check_project_scm(project) - - def _test_hg_project_via_ssh_loopback(self): - # hg doesn't support password for ssh:// urls. - scm_username = getattr(settings, 'TEST_SSH_LOOPBACK_USERNAME', '') - if not all([scm_username]): - self.skipTest('no ssh loopback username defined!') - if not self.is_public_key_in_authorized_keys(): - self.skipTest('ssh loopback for hg requires public key in authorized keys') - repo_dir = self.create_local_hg_repo() - scm_url = 'ssh://localhost/%s' % repo_dir - project = self.create_project( - name='my hg project via ssh loopback', - scm_type='hg', - scm_url=scm_url, - scm_username=scm_username, - ) - self.check_project_scm(project) - - def test_public_svn_project_over_https(self): - scm_url = getattr(settings, 'TEST_SVN_PUBLIC_HTTPS', - 'https://github.com/ansible/ansible.github.com') - if not all([scm_url]): - self.skipTest('no public svn repo defined for https!') - project = self.create_project( - name='my public svn project over https', - scm_type='svn', - scm_url=scm_url, - ) - self.check_project_scm(project) - - def test_private_svn_project_over_https(self): - scm_url = getattr(settings, 'TEST_SVN_PRIVATE_HTTPS', '') - scm_username = getattr(settings, 'TEST_SVN_USERNAME', '') - scm_password = getattr(settings, 'TEST_SVN_PASSWORD', '') - if not all([scm_url, scm_username, scm_password]): - self.skipTest('no private svn repo defined for https!') - project = self.create_project( - name='my private svn project over https', - scm_type='svn', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_scm(project) - - def create_local_svn_repo(self): - repo_dir = tempfile.mkdtemp() - self._temp_paths.append(repo_dir) - subprocess.check_call(['svnadmin', 'create', '.'], cwd=repo_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - handle, playbook_path = tempfile.mkstemp(suffix='.yml', dir=repo_dir) - test_playbook_file = os.fdopen(handle, 'w') - test_playbook_file.write(TEST_PLAYBOOK) - test_playbook_file.close() - subprocess.check_call(['svn', 'import', '-m', 'blah', - os.path.basename(playbook_path), - 'file://%s/%s' % (repo_dir, os.path.basename(playbook_path))], - cwd=repo_dir, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - return repo_dir - - def _test_svn_project_from_local_path(self): - repo_dir = self.create_local_svn_repo() - scm_url = 'file://%s' % repo_dir - project = self.create_project( - name='my svn project from local path', - scm_type='svn', - scm_url=scm_url, - ) - self.check_project_scm(project) - - def test_svn_project_via_ssh_loopback(self): - scm_username = getattr(settings, 'TEST_SSH_LOOPBACK_USERNAME', '') - scm_password = getattr(settings, 'TEST_SSH_LOOPBACK_PASSWORD', '') - if not all([scm_username, scm_password]): - self.skipTest('no ssh loopback username/password defined!') - repo_dir = self.create_local_svn_repo() - scm_url = 'svn+ssh://localhost%s' % repo_dir - project = self.create_project( - name='my svn project via ssh loopback', - scm_type='svn', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - ) - self.check_project_scm(project) - - def create_test_job_template(self, **kwargs): - opts = { - 'name': 'test-job-template %s' % str(now()), - 'inventory': self.inventory, - 'project': self.project, - 'credential': self.credential, - 'job_type': 'run', - } - try: - opts['playbook'] = self.project.playbooks[0] - except (AttributeError, IndexError): - pass - opts.update(kwargs) - self.job_template = JobTemplate.objects.create(**opts) - return self.job_template - - def create_test_job(self, **kwargs): - job_template = kwargs.pop('job_template', None) - if job_template: - self.job = job_template.create_job(**kwargs) - else: - opts = { - 'name': 'test-job %s' % str(now()), - 'inventory': self.inventory, - 'project': self.project, - 'credential': self.credential, - 'job_type': 'run', - } - try: - opts['playbook'] = self.project.playbooks[0] - except (AttributeError, IndexError): - pass - opts.update(kwargs) - self.job = Job.objects.create(**opts) - return self.job - - # TODO: We need to test this another way due to concurrency conflicts - # Will add some tests for the task runner system - def _test_update_on_launch(self): - scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS', - 'https://github.com/ansible/ansible.github.com.git') - if not all([scm_url]): - self.skipTest('no public git repo defined for https!') - self.organization = self.make_organizations(self.super_django_user, 1)[0] - self.inventory = Inventory.objects.create(name='test-inventory', - description='description for test-inventory', - organization=self.organization) - self.host = self.inventory.hosts.create(name='host.example.com', - inventory=self.inventory) - self.group = self.inventory.groups.create(name='test-group', - inventory=self.inventory) - self.group.hosts.add(self.host) - self.credential = Credential.objects.create(name='test-creds') - self.credential.admin_role.members.add(self.super_django_user) - self.project = self.create_project( - name='my public git project over https', - scm_type='git', - scm_url=scm_url, - scm_update_on_launch=True) - # First update triggered by saving a new project with SCM. - self.assertEqual(self.project.project_updates.count(), 1) - self.check_project_update(self.project) - self.assertEqual(self.project.project_updates.count(), 2) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - time.sleep(10) # Need some time to wait for the dependency to run - job = Job.objects.get(pk=job.pk) - self.assertTrue(job.status in ('successful', 'failed')) - self.assertEqual(self.project.project_updates.count(), 3) - - def _test_update_on_launch_with_project_passwords(self): - scm_url = getattr(settings, 'TEST_GIT_PRIVATE_HTTPS', '') - scm_username = getattr(settings, 'TEST_GIT_USERNAME', '') - scm_password = getattr(settings, 'TEST_GIT_PASSWORD', '') - if not all([scm_url, scm_username, scm_password]): - self.skipTest('no private git repo defined for https!') - self.organization = self.make_organizations(self.super_django_user, 1)[0] - self.inventory = Inventory.objects.create(name='test-inventory', - description='description for test-inventory', - organization=self.organization) - self.host = self.inventory.hosts.create(name='host.example.com', - inventory=self.inventory) - self.group = self.inventory.groups.create(name='test-group', - inventory=self.inventory) - self.group.hosts.add(self.host) - self.credential = Credential.objects.create(name='test-creds') - self.credential.admin_role.members.add(self.super_django_user) - self.project = self.create_project( - name='my private git project over https', - scm_type='git', - scm_url=scm_url, - scm_username=scm_username, - scm_password=scm_password, - scm_update_on_launch=True, - ) - self.check_project_update(self.project) - self.assertEqual(self.project.project_updates.count(), 2) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.start()) - self.assertTrue(job.status in ('pending', 'waiting'), job.status) - job = Job.objects.get(pk=job.pk) - self.assertTrue(job.status in ('successful', 'failed'), - job.result_stdout + job.result_traceback) - self.assertEqual(self.project.project_updates.count(), 3) - # Try again but set a bad project password - the job should flag an - # error because the project update failed. - cred = self.project.credential - cred.password = 'bad scm password' - cred.save() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.start()) - self.assertTrue(job.status in ('pending', 'waiting'), job.status) - job = Job.objects.get(pk=job.pk) - # FIXME: Not quite sure why the project update still returns successful - # in this case? - #self.assertEqual(job.status, 'error', - # '\n'.join([job.result_stdout, job.result_traceback])) - self.assertEqual(self.project.project_updates.count(), 4) diff --git a/awx/main/tests/old/schedules.py b/awx/main/tests/old/schedules.py deleted file mode 100644 index 8e114d7950..0000000000 --- a/awx/main/tests/old/schedules.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import datetime - -# Django -from django.core.urlresolvers import reverse -from django.utils.timezone import now - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTest - -__all__ = ['ScheduleTest'] - -YESTERDAY = (datetime.date.today() - datetime.timedelta(1)).strftime('%Y%m%dT075000Z') -UNTIL_SCHEDULE = "DTSTART:%s RRULE:FREQ=MINUTELY;INTERVAL=1;UNTIL=30230401T075000Z" % YESTERDAY -EXPIRED_SCHEDULES = ["DTSTART:19340331T055000Z RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5"] -INFINITE_SCHEDULES = ["DTSTART:30340331T055000Z RRULE:FREQ=MINUTELY;INTERVAL=10"] -GOOD_SCHEDULES = ["DTSTART:20500331T055000Z RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5", - "DTSTART:20240331T075000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1", - "DTSTART:%s RRULE:FREQ=MINUTELY;INTERVAL=1;UNTIL=20230401T075000Z" % YESTERDAY, - "DTSTART:20140331T075000Z RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR", - "DTSTART:20140331T075000Z RRULE:FREQ=WEEKLY;INTERVAL=5;BYDAY=MO", - "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=6", - "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=4;BYDAY=SU", - "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR", - "DTSTART:20140331T075000Z RRULE:FREQ=MONTHLY;INTERVAL=1;BYSETPOS=-1;BYDAY=MO,TU,WE,TH,FR,SA,SU", - "DTSTART:20140331T075000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=4;BYMONTHDAY=1", - "DTSTART:20140331T075000Z RRULE:FREQ=YEARLY;INTERVAL=1;BYSETPOS=-1;BYMONTH=8;BYDAY=SU", - "DTSTART:20140331T075000Z RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20230401T075000Z;BYDAY=MO,WE,FR", - "DTSTART:20140331T075000Z RRULE:FREQ=HOURLY;INTERVAL=1;UNTIL=20230610T075000Z", - "DTSTART:20140411T040000Z RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20140411T040000Z;BYDAY=WE"] -BAD_SCHEDULES = ["", "DTSTART:20140331T055000 RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5", - "RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5", - "FREQ=MINUTELY;INTERVAL=10;COUNT=5", - "DTSTART:20240331T075000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=10000000", - "DTSTART;TZID=US-Eastern:19961105T090000 RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5", - "DTSTART:20140331T055000Z RRULE:FREQ=SECONDLY;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=SECONDLY", - "DTSTART:20140331T055000Z RRULE:FREQ=YEARLY;BYDAY=20MO;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=MONTHLY;BYMONTHDAY=10,15;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=YEARLY;BYMONTH=1,2;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=YEARLY;BYYEARDAY=120;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=YEARLY;BYWEEKNO=10;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=HOURLY;INTERVAL=1 DTSTART:20140331T055000Z RRULE:FREQ=HOURLY;INTERVAL=1", - "DTSTART:20140331T055000Z RRULE:FREQ=HOURLY;INTERVAL=1 RRULE:FREQ=HOURLY;INTERVAL=1"] - - -class ScheduleTest(BaseTest): - - def setUp(self): - super(ScheduleTest, self).setUp() - self.start_rabbit() - self.setup_instances() - self.setup_users() - self.organizations = self.make_organizations(self.super_django_user, 2) - self.organizations[0].admin_role.members.add(self.normal_django_user) - self.organizations[0].member_role.members.add(self.other_django_user) - self.organizations[0].member_role.members.add(self.normal_django_user) - - self.diff_org_user = self.make_user('fred') - self.organizations[1].member_role.members.add(self.diff_org_user) - - self.cloud_source = Credential.objects.create(kind='awx', username='Dummy', password='Dummy') - self.cloud_source.admin_role.members.add(self.super_django_user) - - self.first_inventory = Inventory.objects.create(name='test_inventory', description='for org 0', organization=self.organizations[0]) - self.first_inventory.hosts.create(name='host_1') - self.first_inventory_group = self.first_inventory.groups.create(name='group_1') - self.first_inventory_source = self.first_inventory_group.inventory_source - self.first_inventory_source.source = 'ec2' - self.first_inventory_source.save() - - self.first_inventory.read_role.members.add(self.other_django_user) - - self.second_inventory = Inventory.objects.create(name='test_inventory_2', description='for org 0', organization=self.organizations[0]) - self.second_inventory.hosts.create(name='host_2') - self.second_inventory_group = self.second_inventory.groups.create(name='group_2') - self.second_inventory_source = self.second_inventory_group.inventory_source - self.second_inventory_source.source = 'ec2' - self.second_inventory_source.save() - - self.first_schedule = Schedule.objects.create(name='test_schedule_1', unified_job_template=self.first_inventory_source, - enabled=True, rrule=GOOD_SCHEDULES[0]) - self.second_schedule = Schedule.objects.create(name='test_schedule_2', unified_job_template=self.second_inventory_source, - enabled=True, rrule=GOOD_SCHEDULES[0]) - - self.without_valid_source_inventory = Inventory.objects.create(name='without valid source', description='for org 0', organization=self.organizations[0]) - self.without_valid_source_inventory.hosts.create(name='host_3') - self.without_valid_source_inventory_group = self.without_valid_source_inventory.groups.create(name='not valid source') - self.without_valid_source_inventory_source = self.without_valid_source_inventory_group.inventory_source - - def tearDown(self): - super(ScheduleTest, self).tearDown() - self.stop_rabbit() - - def test_schedules_list(self): - url = reverse('api:schedule_list') - enabled_schedules = Schedule.objects.filter(enabled=True).distinct() - empty_schedules = Schedule.objects.none() - org_1_schedules = Schedule.objects.filter(unified_job_template=self.first_inventory_source) - - #Super user can see everything - self.check_get_list(url, self.super_django_user, enabled_schedules) - - # Unauth user should have no access - self.check_invalid_auth(url) - - # regular org user with read permission can see only their schedules - self.check_get_list(url, self.other_django_user, org_1_schedules) - - # other org user with no read perm can't see anything - self.check_get_list(url, self.diff_org_user, empty_schedules) - - def test_post_new_schedule(self): - first_url = reverse('api:inventory_source_schedules_list', args=(self.first_inventory_source.pk,)) - reverse('api:inventory_source_schedules_list', args=(self.second_inventory_source.pk,)) - - new_schedule = dict(name='newsched_1', description='newsched', enabled=True, rrule=GOOD_SCHEDULES[0]) - - # No auth should fail - self.check_invalid_auth(first_url, new_schedule, methods=('post',)) - - # Super user can post a new schedule - with self.current_user(self.super_django_user): - self.post(first_url, data=new_schedule, expect=201) - - # #admin can post - admin_schedule = dict(name='newsched_2', description='newsched', enabled=True, rrule=GOOD_SCHEDULES[0]) - self.post(first_url, data=admin_schedule, expect=201, auth=self.get_normal_credentials()) - - #normal user without write access can't post - unauth_schedule = dict(name='newsched_3', description='newsched', enabled=True, rrule=GOOD_SCHEDULES[0]) - with self.current_user(self.other_django_user): - self.post(first_url, data=unauth_schedule, expect=403) - - #give normal user write access and then they can post - self.first_inventory.admin_role.members.add(self.other_django_user) - auth_schedule = unauth_schedule - with self.current_user(self.other_django_user): - self.post(first_url, data=auth_schedule, expect=201) - - # another org user shouldn't be able to post a schedule to this org's schedule - diff_user_schedule = dict(name='newsched_4', description='newsched', enabled=True, rrule=GOOD_SCHEDULES[0]) - with self.current_user(self.diff_org_user): - self.post(first_url, data=diff_user_schedule, expect=403) - - def test_post_schedule_to_non_cloud_source(self): - invalid_inv_url = reverse('api:inventory_source_schedules_list', args=(self.without_valid_source_inventory_source.pk,)) - new_schedule = dict(name='newsched_1', description='newsched', enabled=True, rrule=GOOD_SCHEDULES[0]) - - with self.current_user(self.super_django_user): - self.post(invalid_inv_url, data=new_schedule, expect=400) - - def test_update_existing_schedule(self): - first_url = reverse('api:inventory_source_schedules_list', args=(self.first_inventory_source.pk,)) - - new_schedule = dict(name='edit_schedule', description='going to change', enabled=True, rrule=EXPIRED_SCHEDULES[0]) - with self.current_user(self.normal_django_user): - data = self.post(first_url, new_schedule, expect=201) - self.assertEquals(data['next_run'], None) - new_schedule_url = reverse('api:schedule_detail', args=(data['id'],)) - - data['rrule'] = GOOD_SCHEDULES[0] - with self.current_user(self.normal_django_user): - data = self.put(new_schedule_url, data=data, expect=200) - self.assertNotEqual(data['next_run'], None) - #TODO: Test path needed for non org-admin users, but rather regular users who have permission to create the JT associated with the Schedule - - def test_infinite_schedule(self): - first_url = reverse('api:inventory_source_schedules_list', args=(self.first_inventory_source.pk,)) - - new_schedule = dict(name='inf_schedule', description='going forever', enabled=True, rrule=INFINITE_SCHEDULES[0]) - with self.current_user(self.normal_django_user): - data = self.post(first_url, new_schedule, expect=201) - self.assertEquals(data['dtend'], None) - - long_schedule = dict(name='long_schedule', description='going for a long time', enabled=True, rrule=UNTIL_SCHEDULE) - with self.current_user(self.normal_django_user): - data = self.post(first_url, long_schedule, expect=201) - self.assertNotEquals(data['dtend'], None) - - def test_schedule_filtering(self): - first_url = reverse('api:inventory_source_schedules_list', args=(self.first_inventory_source.pk,)) - - start_time = now() + datetime.timedelta(minutes=5) - dtstart_str = start_time.strftime("%Y%m%dT%H%M%SZ") - new_schedule = dict(name="filter_schedule_1", enabled=True, rrule="DTSTART:%s RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5" % dtstart_str) - with self.current_user(self.normal_django_user): - self.post(first_url, new_schedule, expect=201) - self.assertTrue(Schedule.objects.enabled().between(now(), now() + datetime.timedelta(minutes=10)).count(), 1) - - start_time = now() - dtstart_str = start_time.strftime("%Y%m%dT%H%M%SZ") - new_schedule_middle = dict(name="runnable_schedule", enabled=True, rrule="DTSTART:%s RRULE:FREQ=MINUTELY;INTERVAL=10;COUNT=5" % dtstart_str) - with self.current_user(self.normal_django_user): - self.post(first_url, new_schedule_middle, expect=201) - self.assertTrue(Schedule.objects.enabled().between(now() - datetime.timedelta(minutes=10), now() + datetime.timedelta(minutes=10)).count(), 1) - - def test_rrule_validation(self): - first_url = reverse('api:inventory_source_schedules_list', args=(self.first_inventory_source.pk,)) - with self.current_user(self.normal_django_user): - for good_rule in GOOD_SCHEDULES: - sched_dict = dict(name=good_rule, enabled=True, rrule=good_rule) - self.post(first_url, sched_dict, expect=201) - for bad_rule in BAD_SCHEDULES: - sched_dict = dict(name=bad_rule, enabled=True, rrule=bad_rule) - self.post(first_url, sched_dict, expect=400) diff --git a/awx/main/tests/old/scripts.py b/awx/main/tests/old/scripts.py deleted file mode 100644 index 7af019fd7c..0000000000 --- a/awx/main/tests/old/scripts.py +++ /dev/null @@ -1,402 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import json -import os -import subprocess -import sys -import urlparse - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseLiveServerTest - -__all__ = ['InventoryScriptTest'] - - -class BaseScriptTest(BaseLiveServerTest): - ''' - Base class for tests that run external scripts to access the API. - ''' - - def setUp(self): - super(BaseScriptTest, self).setUp() - self._sys_path = [x for x in sys.path] - self._environ = dict(os.environ.items()) - self._temp_files = [] - - def tearDown(self): - super(BaseScriptTest, self).tearDown() - sys.path = self._sys_path - for k,v in self._environ.items(): - if os.environ.get(k, None) != v: - os.environ[k] = v - for k,v in os.environ.items(): - if k not in self._environ.keys(): - del os.environ[k] - for tf in self._temp_files: - if os.path.exists(tf): - os.remove(tf) - - def run_script(self, name, *args, **options): - ''' - Run an external script and capture its stdout/stderr and return code. - ''' - #stdin_fileobj = options.pop('stdin_fileobj', None) - pargs = [name] - for k,v in options.items(): - pargs.append('%s%s' % ('-' if len(k) == 1 else '--', k)) - if v is not True: - pargs.append(str(v)) - for arg in args: - pargs.append(str(arg)) - proc = subprocess.Popen(pargs, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, stderr = proc.communicate() - return proc.returncode, stdout, stderr - - -class InventoryScriptTest(BaseScriptTest): - ''' - Test helper to run management command as standalone script. - ''' - - def setUp(self): - super(InventoryScriptTest, self).setUp() - self.start_rabbit() - self.setup_instances() - self.setup_users() - self.organizations = self.make_organizations(self.super_django_user, 2) - self.projects = self.make_projects(self.normal_django_user, 2) - self.organizations[0].projects.add(self.projects[1]) - self.organizations[1].projects.add(self.projects[0]) - self.inventories = [] - self.hosts = [] - self.groups = [] - for n, organization in enumerate(self.organizations): - inventory = Inventory.objects.create(name='inventory-%d' % n, - description='description for inventory %d' % n, - organization=organization, - variables=json.dumps({'n': n}) if n else '') - self.inventories.append(inventory) - hosts = [] - for x in xrange(10): - if n > 0: - variables = json.dumps({'ho': 'hum-%d' % x}) - else: - variables = '' - host = inventory.hosts.create(name='host-%02d-%02d.example.com' % (n, x), - inventory=inventory, - variables=variables) - #if x in (3, 7): - # host.delete() - # continue - hosts.append(host) - - - # add localhost just to make sure it's thrown into all (Ansible github bug) - local = inventory.hosts.create(name='localhost', inventory=inventory, variables={}) - hosts.append(local) - - self.hosts.extend(hosts) - groups = [] - for x in xrange(5): - if n > 0: - variables = json.dumps({'gee': 'whiz-%d' % x}) - else: - variables = '' - group = inventory.groups.create(name='group-%d' % x, - inventory=inventory, - variables=variables) - #if x == 2: - # #group.delete() - # #continue - groups.append(group) - group.hosts.add(hosts[x]) - group.hosts.add(hosts[x + 5]) - if n > 0 and x == 4: - group.parents.add(groups[3]) - if x == 4: - group.hosts.add(local) - self.groups.extend(groups) - - hosts[3].delete() - hosts[7].delete() - groups[2].delete() - - - - - def tearDown(self): - super(InventoryScriptTest, self).tearDown() - self.stop_rabbit() - - def run_inventory_script(self, *args, **options): - rest_api_url = self.live_server_url - parts = urlparse.urlsplit(rest_api_url) - username, password = self.get_super_credentials() - netloc = '%s:%s@%s' % (username, password, parts.netloc) - rest_api_url = urlparse.urlunsplit([parts.scheme, netloc, parts.path, - parts.query, parts.fragment]) - os.environ.setdefault('REST_API_URL', rest_api_url) - #os.environ.setdefault('REST_API_TOKEN', - # self.super_django_user.auth_token.key) - name = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'plugins', - 'inventory', 'awxrest.py') - return self.run_script(name, *args, **options) - - def test_without_inventory_id(self): - rc, stdout, stderr = self.run_inventory_script(list=True) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - rc, stdout, stderr = self.run_inventory_script(host=self.hosts[0].name) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - - def test_list_with_inventory_id_as_argument(self): - inventory = self.inventories[0] - rc, stdout, stderr = self.run_inventory_script(list=True, - inventory=inventory.pk) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - groups = inventory.groups - groupnames = [ x for x in groups.values_list('name', flat=True)] - - # it's ok for all to be here because due to an Ansible inventory workaround - # 127.0.0.1/localhost must show up in the all group - groupnames.append('all') - self.assertEqual(set(data.keys()), set(groupnames)) - - # Groups for this inventory should only have hosts, and no group - # variable data or parent/child relationships. - for k,v in data.items(): - if k != 'all': - assert isinstance(v, dict) - assert isinstance(v['children'], (list,tuple)) - assert isinstance(v['hosts'], (list,tuple)) - assert isinstance(v['vars'], (dict)) - group = inventory.groups.get(name=k) - hosts = group.hosts - hostnames = hosts.values_list('name', flat=True) - self.assertEqual(set(v['hosts']), set(hostnames)) - else: - assert v['hosts'] == ['host-00-02.example.com', 'localhost'] - - # Command line argument for inventory ID should take precedence over - # environment variable. - inventory_pks = set(map(lambda x: x.pk, self.inventories)) - invalid_id = [x for x in xrange(9999) if x not in inventory_pks][0] - os.environ['INVENTORY_ID'] = str(invalid_id) - rc, stdout, stderr = self.run_inventory_script(list=True, - inventory=inventory.pk) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - - def test_list_with_inventory_id_in_environment(self): - inventory = self.inventories[1] - os.environ['INVENTORY_ID'] = str(inventory.pk) - rc, stdout, stderr = self.run_inventory_script(list=True) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - groups = inventory.groups - groupnames = list(groups.values_list('name', flat=True)) + ['all'] - self.assertEqual(set(data.keys()), set(groupnames)) - # Groups for this inventory should have hosts, variable data, and one - # parent/child relationship. - for k,v in data.items(): - assert isinstance(v, dict) - if k == 'all': - self.assertEqual(v.get('vars', {}), inventory.variables_dict) - continue - group = inventory.groups.get(name=k) - hosts = group.hosts - hostnames = hosts.values_list('name', flat=True) - self.assertEqual(set(v.get('hosts', [])), set(hostnames)) - if group.variables: - self.assertEqual(v.get('vars', {}), group.variables_dict) - if k == 'group-3': - children = group.children - childnames = children.values_list('name', flat=True) - self.assertEqual(set(v.get('children', [])), set(childnames)) - else: - assert len(v['children']) == 0 - - def test_list_with_hostvars_inline(self): - inventory = self.inventories[1] - rc, stdout, stderr = self.run_inventory_script(list=True, - inventory=inventory.pk, - hostvars=True) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - groups = inventory.groups - groupnames = list(groups.values_list('name', flat=True)) - groupnames.extend(['all', '_meta']) - self.assertEqual(set(data.keys()), set(groupnames)) - all_hostnames = set() - # Groups for this inventory should have hosts, variable data, and one - # parent/child relationship. - for k,v in data.items(): - assert isinstance(v, dict) - if k == 'all': - self.assertEqual(v.get('vars', {}), inventory.variables_dict) - continue - if k == '_meta': - continue - group = inventory.groups.get(name=k) - hosts = group.hosts - hostnames = hosts.values_list('name', flat=True) - all_hostnames.update(hostnames) - assert set(v.get('hosts', [])) == set(hostnames) - if group.variables: - assert v.get('vars', {}) == group.variables_dict - if k == 'group-3': - children = group.children - childnames = children.values_list('name', flat=True) - assert set(v.get('children', [])) == set(childnames) - else: - assert len(v['children']) == 0 - # Check hostvars in ['_meta']['hostvars'] dict. - for hostname in all_hostnames: - assert hostname in data['_meta']['hostvars'] - host = inventory.hosts.get(name=hostname) - self.assertEqual(data['_meta']['hostvars'][hostname], - host.variables_dict) - # Hostvars can also be requested via environment variable. - os.environ['INVENTORY_HOSTVARS'] = str(True) - rc, stdout, stderr = self.run_inventory_script(list=True, - inventory=inventory.pk) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - assert '_meta' in data - - def test_valid_host(self): - # Host without variable data. - inventory = self.inventories[0] - host = inventory.hosts.all()[2] - os.environ['INVENTORY_ID'] = str(inventory.pk) - rc, stdout, stderr = self.run_inventory_script(host=host.name) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - self.assertEqual(data, {}) - # Host with variable data. - inventory = self.inventories[1] - host = inventory.hosts.all()[4] - os.environ['INVENTORY_ID'] = str(inventory.pk) - rc, stdout, stderr = self.run_inventory_script(host=host.name) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - self.assertEqual(data, host.variables_dict) - - def test_invalid_host(self): - # Valid host, but not part of the specified inventory. - inventory = self.inventories[0] - host = Host.objects.exclude(inventory=inventory)[0] - os.environ['INVENTORY_ID'] = str(inventory.pk) - rc, stdout, stderr = self.run_inventory_script(host=host.name) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - # Invalid hostname not in database. - rc, stdout, stderr = self.run_inventory_script(host='blah.example.com') - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - - def test_with_invalid_inventory_id(self): - inventory_pks = set(map(lambda x: x.pk, self.inventories)) - invalid_id = [x for x in xrange(1, 9999) if x not in inventory_pks][0] - os.environ['INVENTORY_ID'] = str(invalid_id) - rc, stdout, stderr = self.run_inventory_script(list=True) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - os.environ['INVENTORY_ID'] = 'not_an_int' - rc, stdout, stderr = self.run_inventory_script(list=True) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - os.environ['INVENTORY_ID'] = str(invalid_id) - rc, stdout, stderr = self.run_inventory_script(host=self.hosts[1].name) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - os.environ['INVENTORY_ID'] = 'not_an_int' - rc, stdout, stderr = self.run_inventory_script(host=self.hosts[2].name) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - - def test_with_deleted_inventory(self): - inventory = self.inventories[0] - pk = inventory.pk - inventory.delete() - os.environ['INVENTORY_ID'] = str(pk) - rc, stdout, stderr = self.run_inventory_script(list=True) - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - - def test_without_list_or_host_argument(self): - inventory = self.inventories[0] - os.environ['INVENTORY_ID'] = str(inventory.pk) - rc, stdout, stderr = self.run_inventory_script() - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - - def test_with_both_list_and_host_arguments(self): - inventory = self.inventories[0] - os.environ['INVENTORY_ID'] = str(inventory.pk) - rc, stdout, stderr = self.run_inventory_script(list=True, host='blah') - self.assertNotEqual(rc, 0, stderr) - self.assertEqual(json.loads(stdout), {'failed': True}) - - def test_with_disabled_hosts(self): - inventory = self.inventories[1] - for host in inventory.hosts.filter(enabled=True): - host.enabled = False - host.save(update_fields=['enabled']) - os.environ['INVENTORY_ID'] = str(inventory.pk) - # Load inventory list as normal (only enabled hosts). - rc, stdout, stderr = self.run_inventory_script(list=True) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - groups = inventory.groups - groupnames = list(groups.values_list('name', flat=True)) + ['all'] - self.assertEqual(set(data.keys()), set(groupnames)) - for k,v in data.items(): - assert isinstance(v, dict) - if k == 'all': - self.assertEqual(v.get('vars', {}), inventory.variables_dict) - continue - group = inventory.groups.get(name=k) - hosts = group.hosts.filter(enabled=True) - hostnames = hosts.values_list('name', flat=True) - self.assertEqual(set(v.get('hosts', [])), set(hostnames)) - self.assertFalse(hostnames) - if group.variables: - self.assertEqual(v.get('vars', {}), group.variables_dict) - if k == 'group-3': - children = group.children - childnames = children.values_list('name', flat=True) - self.assertEqual(set(v.get('children', [])), set(childnames)) - else: - assert len(v['children']) == 0 - # Load inventory list with all hosts. - rc, stdout, stderr = self.run_inventory_script(list=True, all=True) - self.assertEqual(rc, 0, stderr) - data = json.loads(stdout) - groups = inventory.groups - groupnames = list(groups.values_list('name', flat=True)) + ['all'] - self.assertEqual(set(data.keys()), set(groupnames)) - for k,v in data.items(): - assert isinstance(v, dict) - if k == 'all': - self.assertEqual(v.get('vars', {}), inventory.variables_dict) - continue - group = inventory.groups.get(name=k) - hosts = group.hosts - hostnames = hosts.values_list('name', flat=True) - self.assertEqual(set(v.get('hosts', [])), set(hostnames)) - assert hostnames - if group.variables: - self.assertEqual(v.get('vars', {}), group.variables_dict) - if k == 'group-3': - children = group.children - childnames = children.values_list('name', flat=True) - self.assertEqual(set(v.get('children', [])), set(childnames)) - else: - assert len(v['children']) == 0 diff --git a/awx/main/tests/old/settings.py b/awx/main/tests/old/settings.py deleted file mode 100644 index a1614dac38..0000000000 --- a/awx/main/tests/old/settings.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2016 Ansible, Inc. -# All Rights Reserved. - -import pytest - -from awx.main.tests.base import BaseTest -from awx.main.models import * # noqa - -from django.core.urlresolvers import reverse -from django.test.utils import override_settings - -TEST_TOWER_SETTINGS_MANIFEST = { - "TEST_SETTING_INT": { - "name": "An Integer Field", - "description": "An Integer Field", - "default": 1, - "type": "int", - "category": "test" - }, - "TEST_SETTING_STRING": { - "name": "A String Field", - "description": "A String Field", - "default": "test", - "type": "string", - "category": "test" - }, - "TEST_SETTING_BOOL": { - "name": "A Bool Field", - "description": "A Bool Field", - "default": True, - "type": "bool", - "category": "test" - }, - "TEST_SETTING_LIST": { - "name": "A List Field", - "description": "A List Field", - "default": ["A", "Simple", "List"], - "type": "list", - "category": "test" - }, - "TEST_SETTING_JSON": { - "name": "A JSON Field", - "description": "A JSON Field", - "default": {"key": "value", "otherkey": ["list", "of", "things"]}, - "type": "json", - "category": "test" - } -} - - -@override_settings(TOWER_SETTINGS_MANIFEST=TEST_TOWER_SETTINGS_MANIFEST) -@pytest.mark.skip(reason="Settings deferred to 3.1") -class SettingsPlaceholder(BaseTest): - - def setUp(self): - super(SettingsTest, self).setUp() - self.setup_instances() - self.setup_users() - - def get_settings(self, expected_count=5): - result = self.get(reverse('api:settings_list'), expect=200) - self.assertEqual(result['count'], expected_count) - return result['results'] - - def get_individual_setting(self, setting): - all_settings = self.get_settings() - setting_actual = None - for setting_item in all_settings: - if setting_item['key'] == setting: - setting_actual = setting_item - break - self.assertIsNotNone(setting_actual) - return setting_actual - - def set_setting(self, key, value): - self.post(reverse('api:settings_list'), data={"key": key, "value": value}, expect=201) - - def test_get_settings(self): - # Regular user should see nothing (no user settings yet) - with self.current_user(self.normal_django_user): - self.get_settings(expected_count=0) - # anonymous user should get a 401 - self.get(reverse('api:settings_list'), expect=401) - # super user can see everything - with self.current_user(self.super_django_user): - self.get_settings(expected_count=len(TEST_TOWER_SETTINGS_MANIFEST)) - - def set_and_reset_setting(self, key, values, expected_values=()): - settings_reset = reverse('api:settings_reset') - setting = self.get_individual_setting(key) - self.assertEqual(setting['value'], TEST_TOWER_SETTINGS_MANIFEST[key]['default']) - for n, value in enumerate(values): - self.set_setting(key, value) - setting = self.get_individual_setting(key) - if len(expected_values) > n: - self.assertEqual(setting['value'], expected_values[n]) - else: - self.assertEqual(setting['value'], value) - self.post(settings_reset, data={"key": key}, expect=204) - setting = self.get_individual_setting(key) - self.assertEqual(setting['value'], TEST_TOWER_SETTINGS_MANIFEST[key]['default']) - - def test_set_and_reset_settings(self): - with self.current_user(self.super_django_user): - self.set_and_reset_setting('TEST_SETTING_INT', (2, 0)) - self.set_and_reset_setting('TEST_SETTING_STRING', ('blah', '', u'\u2620')) - self.set_and_reset_setting('TEST_SETTING_BOOL', (True, False)) - # List values are always saved as strings. - self.set_and_reset_setting('TEST_SETTING_LIST', ([4, 5, 6], [], [2]), (['4', '5', '6'], [], ['2'])) - self.set_and_reset_setting('TEST_SETTING_JSON', ({"k": "v"}, {}, [], [7, 8], 'str')) - - def test_clear_all_settings(self): - settings_list = reverse('api:settings_list') - with self.current_user(self.super_django_user): - self.set_setting('TEST_SETTING_INT', 2) - self.set_setting('TEST_SETTING_STRING', "foo") - self.set_setting('TEST_SETTING_BOOL', False) - self.set_setting('TEST_SETTING_LIST', [1,2,3]) - self.set_setting('TEST_SETTING_JSON', '{"key": "new value"}') - all_settings = self.get_settings() - for setting_entry in all_settings: - self.assertNotEqual(setting_entry['value'], - TEST_TOWER_SETTINGS_MANIFEST[setting_entry['key']]['default']) - self.delete(settings_list, expect=200) - all_settings = self.get_settings() - for setting_entry in all_settings: - self.assertEqual(setting_entry['value'], - TEST_TOWER_SETTINGS_MANIFEST[setting_entry['key']]['default']) diff --git a/awx/main/tests/old/tasks.py b/awx/main/tests/old/tasks.py deleted file mode 100644 index eba9bf552b..0000000000 --- a/awx/main/tests/old/tasks.py +++ /dev/null @@ -1,1244 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -from distutils.version import LooseVersion as Version -import glob -import json -import os -import shutil -import subprocess -import tempfile -import unittest2 as unittest - -# Django -from django.conf import settings -from django.utils.timezone import now - -# Django-CRUM -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.data.ssh import ( - TEST_SSH_KEY_DATA, - TEST_SSH_KEY_DATA_LOCKED, - TEST_OPENSSH_KEY_DATA, - TEST_SSH_CERT_KEY, - TEST_SSH_KEY_DATA_UNLOCK, -) - -TEST_PLAYBOOK = u''' -- name: test success - hosts: test-group - gather_facts: False - tasks: - - name: should pass \u2623 - command: test 1 = 1 - - name: should also pass - command: test 2 = 2 -''' - -TEST_PLAYBOOK2 = '''- name: test failed - hosts: test-group - gather_facts: False - tasks: - - name: should fail - command: test 1 = 0 -''' - -TEST_PLAYBOOK_WITH_TAGS = u''' -- name: test with tags - hosts: test-group - gather_facts: False - tasks: - - name: should fail but skipped using --start-at-task="start here" - command: test 1 = 0 - tags: runme - - name: start here - command: test 1 = 1 - tags: runme - - name: should fail but skipped using --skip-tags=skipme - command: test 1 = 0 - tags: skipme - - name: should fail but skipped without runme tag - command: test 1 = 0 -''' - -TEST_EXTRA_VARS_PLAYBOOK = ''' -- name: test extra vars - hosts: test-group - gather_facts: false - tasks: - - fail: msg="{{item}} is not defined" - when: "{{item}} is not defined" - with_items: - - tower_job_id - - tower_job_launch_type - - tower_job_template_id - - tower_job_template_name - - tower_user_id - - tower_user_name -''' - -TEST_ENV_PLAYBOOK = ''' -- name: test env vars - hosts: test-group - gather_facts: False - tasks: - - shell: 'test -n "${%(env_var1)s}"' - - shell: 'test -n "${%(env_var2)s}"' -''' - -TEST_IGNORE_ERRORS_PLAYBOOK = ''' -- name: test ignore errors - hosts: test-group - gather_facts: False - tasks: - - name: should fail - command: test 1 = 0 - ignore_errors: true -''' - -TEST_ASYNC_OK_PLAYBOOK = ''' -- name: test async ok - hosts: test-group - gather_facts: false - tasks: - - debug: msg="one task before async" - - name: async task should pass - command: sleep 4 - async: 16 - poll: 1 -''' - -TEST_ASYNC_FAIL_PLAYBOOK = ''' -- name: test async fail - hosts: test-group - gather_facts: false - tasks: - - debug: msg="one task before async" - - name: async task should fail - shell: sleep 4; test 1 = 0 - async: 16 - poll: 1 -''' - -TEST_ASYNC_TIMEOUT_PLAYBOOK = ''' -- name: test async timeout - hosts: test-group - gather_facts: false - tasks: - - debug: msg="one task before async" - - name: async task should timeout - command: sleep 16 - async: 8 - poll: 1 -''' - -TEST_ASYNC_NOWAIT_PLAYBOOK = ''' -- name: test async no wait - hosts: test-group - gather_facts: false - tasks: - - name: async task should run in background - command: sleep 4 - async: 8 - poll: 0 -''' - -TEST_PROOT_PLAYBOOK = ''' -- name: test bubblewrap environment - hosts: test-group - gather_facts: false - connection: local - tasks: - - name: list projects directory - command: ls -1 "{{ projects_root }}" - register: projects_ls - - name: check that only one project directory is visible - assert: - that: - - "projects_ls.stdout_lines|length == 1" - - "projects_ls.stdout_lines[0] == '{{ project_path }}'" - - name: list job output directory - command: ls -1 "{{ joboutput_root }}" - register: joboutput_ls - - name: check that we see an empty job output directory - assert: - that: - - "not joboutput_ls.stdout" - - name: check for other project path - stat: path={{ other_project_path }} - register: other_project_stat - - name: check that other project path was not found - assert: - that: - - "not other_project_stat.stat.exists" - - name: check for temp path - stat: path={{ temp_path }} - register: temp_stat - - name: check that temp path was not found - assert: - that: - - "not temp_stat.stat.exists" - - name: check for supervisor log path - stat: path={{ supervisor_log_path }} - register: supervisor_log_stat - when: supervisor_log_path is defined - - name: check that supervisor log path was not found - assert: - that: - - "not supervisor_log_stat.stat.exists" - when: supervisor_log_path is defined - - name: try to run a tower-manage command - command: tower-manage validate - ignore_errors: true - register: tower_manage_validate - - name: check that tower-manage command failed - assert: - that: - - "tower_manage_validate|failed" -''' - -TEST_PLAYBOOK_WITH_ROLES = ''' -- name: test with roles - hosts: test-group - gather_facts: false - roles: - - some_stuff - - more_stuff - - {role: stuff, tags: stuff} -''' - -TEST_ROLE_PLAYBOOK = ''' -- name: some task in a role - command: test 1 = 1 -''' - -TEST_ROLE_PLAYBOOKS = { - 'some_stuff': TEST_ROLE_PLAYBOOK, - 'more_stuff': TEST_ROLE_PLAYBOOK, - 'stuff': TEST_ROLE_PLAYBOOK, -} - -TEST_VAULT_PLAYBOOK = '''$ANSIBLE_VAULT;1.1;AES256 -35623233333035633365383330323835353564346534363762366465316263363463396162656432 -6562643539396330616265616532656466353639303338650a313466333663646431646663333739 -32623935316439343636633462373633653039646336376361386439386661366434333830383634 -6266613530626633390a363532373562353262323863343830343865303663306335643430396239 -63393963623537326366663332656132653465646332343234656237316537643135313932623237 -66313863396463343232383131633531363239396636363165646562396261626633326561313837 -32383634326230656230386237333561373630343233353239613463626538356338326633386434 -36396639313030336165366266646431306665336662663732313762663938666239663233393964 -30393733393331383132306463656636396566373961383865643562383564356363''' - -TEST_VAULT_PASSWORD = '1234' - - -@unittest.skipIf(os.environ.get('SKIP_SLOW_TESTS', False), 'Skipping slow test') -class RunJobTest(BaseJobExecutionTest): - ''' - Test cases for RunJob celery task. - ''' - - def setUp(self): - 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() - if self.test_project_path: - shutil.rmtree(self.test_project_path, True) - - def create_test_credential(self, **kwargs): - self.credential = self.make_credential(**kwargs) - return self.credential - - def create_test_cloud_credential(self, **kwargs): - opts = { - 'name': 'test-cloud-cred', - 'kind': 'aws', - 'user': self.super_django_user, - 'username': '', - 'password': '', - } - opts.update(kwargs) - user = opts['user'] - del opts['user'] - self.cloud_credential = Credential.objects.create(**opts) - self.cloud_credential.admin_role.members.add(user) - return self.cloud_credential - - def create_test_project(self, playbook_content, role_playbooks=None): - self.project = self.make_projects(self.normal_django_user, 1, - playbook_content, role_playbooks)[0] - self.organization.projects.add(self.project) - - def create_test_job_template(self, **kwargs): - opts = { - 'name': 'test-job-template %s' % str(now()), - 'inventory': self.inventory, - 'project': self.project, - 'credential': self.credential, - 'cloud_credential': self.cloud_credential, - 'job_type': 'run', - } - try: - opts['playbook'] = self.project.playbooks[0] - except (AttributeError, IndexError): - pass - opts.update(kwargs) - self.job_template = self.make_job_template(**opts) - return self.job_template - - def create_test_job(self, **kwargs): - with impersonate(self.super_django_user): - job_template = kwargs.pop('job_template', None) - if job_template: - self.job = job_template.create_job(**kwargs) - else: - opts = { - 'inventory': self.inventory, - 'project': self.project, - 'credential': self.credential, - 'cloud_credential': self.cloud_credential, - 'job_type': 'run', - } - try: - opts['playbook'] = self.project.playbooks[0] - except (AttributeError, IndexError): - pass - opts.update(kwargs) - self.job = Job.objects.create(**opts) - return self.job - - def check_job_events(self, job, runner_status='ok', plays=1, tasks=1, - async=False, async_timeout=False, async_nowait=False, - check_ignore_errors=False, async_tasks=0, - has_roles=False): - job_events = job.job_events.all() - if False and async: - print - qs = self.super_django_user.get_queryset(JobEvent) - for je in qs.filter(job=job): - print(je.get_event_display2()) - print(je.event, je, je.failed) - print(je.event_data) - for job_event in job_events: - unicode(job_event) # For test coverage. - job_event.save() - job_event.get_event_display2() - should_be_failed = bool(runner_status not in ('ok', 'skipped')) - should_be_changed = bool(runner_status in ('ok', 'failed') and - job.job_type == 'run') - host_pks = set([self.host.pk]) - qs = job_events.filter(event='playbook_on_start') - self.assertEqual(qs.count(), 1) - for evt in qs: - self.assertFalse(evt.host, evt) - self.assertFalse(evt.play, evt) - self.assertFalse(evt.task, evt) - self.assertFalse(evt.role, evt) - self.assertEqual(evt.failed, should_be_failed) - if not async: - self.assertEqual(evt.changed, should_be_changed) - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - self.assertEqual(set(evt.hosts.values_list('pk', flat=True)), - host_pks) - qs = job_events.filter(event='playbook_on_play_start') - self.assertEqual(qs.count(), plays) - for evt in qs: - self.assertFalse(evt.host, evt) - self.assertTrue(evt.play, evt) - self.assertFalse(evt.task, evt) - self.assertFalse(evt.role, evt) - self.assertEqual(evt.failed, should_be_failed) - self.assertEqual(evt.play, evt.event_data['name']) - # All test playbooks have a play name set explicitly. - self.assertNotEqual(evt.event_data['name'], evt.event_data['pattern']) - if not async: - self.assertEqual(evt.changed, should_be_changed) - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - self.assertEqual(set(evt.hosts.values_list('pk', flat=True)), - host_pks) - qs = job_events.filter(event='playbook_on_task_start') - self.assertEqual(qs.count(), tasks) - for n, evt in enumerate(qs): - self.assertFalse(evt.host, evt) - self.assertTrue(evt.play, evt) - self.assertTrue(evt.task, evt) - if has_roles: - self.assertTrue(evt.role, evt) - else: - self.assertFalse(evt.role, evt) - if async and async_tasks < tasks and n == 0: - self.assertFalse(evt.failed) - else: - self.assertEqual(evt.failed, should_be_failed) - if not async: - self.assertEqual(evt.changed, should_be_changed) - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - self.assertEqual(set(evt.hosts.values_list('pk', flat=True)), - host_pks) - if check_ignore_errors: - qs = job_events.filter(event='runner_on_failed') - else: - qs = job_events.filter(event=('runner_on_%s' % runner_status)) - if async and async_timeout: - pass - elif async: - self.assertTrue(qs.count()) - else: - self.assertEqual(qs.count(), tasks) - for evt in qs: - self.assertEqual(evt.host, self.host) - self.assertTrue(evt.play, evt) - self.assertTrue(evt.task, evt) - self.assertTrue(evt.host_name) - if has_roles: - self.assertTrue(evt.role, evt) - else: - self.assertFalse(evt.role, evt) - self.assertEqual(evt.failed, should_be_failed) - if not async: - self.assertEqual(evt.changed, should_be_changed) - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - self.assertEqual(set(evt.hosts.values_list('pk', flat=True)), - host_pks) - if async: - qs = job_events.filter(event='runner_on_async_poll') - for evt in qs: - self.assertEqual(evt.host, self.host) - self.assertTrue(evt.play, evt) - self.assertTrue(evt.task, evt) - self.assertEqual(evt.failed, False) # should_be_failed) - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - self.assertEqual(set(evt.hosts.values_list('pk', flat=True)), - host_pks) - qs = job_events.filter(event=('runner_on_async_%s' % runner_status)) - # Ansible 1.2 won't call the on_runner_async_failed callback when a - # timeout occurs, so skip this check for now. - if not async_timeout and not async_nowait: - self.assertEqual(qs.count(), async_tasks) - for evt in qs: - self.assertEqual(evt.host, self.host) - self.assertTrue(evt.play, evt) - self.assertTrue(evt.task, evt) - self.assertEqual(evt.failed, should_be_failed) - if getattr(settings, 'CAPTURE_JOB_EVENT_HOSTS', False): - self.assertEqual(set(evt.hosts.values_list('pk', flat=True)), - host_pks) - qs = job_events.filter(event__startswith='runner_') - if check_ignore_errors: - qs = qs.exclude(event='runner_on_failed') - else: - qs = qs.exclude(event=('runner_on_%s' % runner_status)) - if runner_status == 'skipped': - # NOTE: Ansible >= 1.8.2 emits a runner_on_ok event in some cases - # of runner_on_skipped. We may need to revisit this if this assumption - # is not universal - qs = qs.exclude(event='runner_on_ok') - - if async: - if runner_status == 'failed': - qs = qs.exclude(event='runner_on_ok') - qs = qs.exclude(event='runner_on_async_poll') - qs = qs.exclude(event=('runner_on_async_%s' % runner_status)) - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.check_job_events(job, 'ok', 1, 2) - for job_host_summary in job.job_host_summaries.all(): - unicode(job_host_summary) # For test coverage. - self.assertFalse(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertFalse(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertFalse(group.has_active_failures) - self.assertFalse(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 1) - self.assertEqual(job.failed_hosts.count(), 0) - self.assertEqual(job.changed_hosts.count(), 1) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - self.assertEqual(job.processed_hosts.count(), 1) - 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') - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.check_job_events(job, 'skipped', 1, 2) - for job_host_summary in job.job_host_summaries.all(): - self.assertFalse(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertFalse(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertFalse(group.has_active_failures) - self.assertFalse(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 0) - self.assertEqual(job.failed_hosts.count(), 0) - self.assertEqual(job.changed_hosts.count(), 0) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 1) - self.assertEqual(job.processed_hosts.count(), 1) - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'failed') - self.check_job_events(job, 'failed', 1, 1) - for job_host_summary in job.job_host_summaries.all(): - self.assertTrue(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertTrue(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertTrue(group.has_active_failures) - self.assertTrue(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 0) - self.assertEqual(job.failed_hosts.count(), 1) - self.assertEqual(job.changed_hosts.count(), 0) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - self.assertEqual(job.processed_hosts.count(), 1) - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.check_job_events(job, 'ok', 1, 1, check_ignore_errors=True) - for job_host_summary in job.job_host_summaries.all(): - self.assertFalse(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertFalse(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertFalse(group.has_active_failures) - self.assertFalse(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 1) - self.assertEqual(job.failed_hosts.count(), 0) - self.assertEqual(job.changed_hosts.count(), 1) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - self.assertEqual(job.processed_hosts.count(), 1) - - def test_update_has_active_failures_when_inventory_changes(self): - self.test_run_job_that_fails() - # Add host to new group (should set has_active_failures) - new_group = self.inventory.groups.create(name='new group') - self.assertFalse(new_group.has_active_failures) - new_group.hosts.add(self.host) - new_group = Group.objects.get(pk=new_group.pk) - self.assertTrue(new_group.has_active_failures) - # Remove host from new group (should clear has_active_failures) - new_group.hosts.remove(self.host) - new_group = Group.objects.get(pk=new_group.pk) - self.assertFalse(new_group.has_active_failures) - # Add existing group to new group (should set flag) - new_group.children.add(self.group) - new_group = Group.objects.get(pk=new_group.pk) - self.assertTrue(new_group.has_active_failures) - # Remove existing group from new group (should clear flag) - new_group.children.remove(self.group) - new_group = Group.objects.get(pk=new_group.pk) - self.assertFalse(new_group.has_active_failures) - # Delete host (should clear flag on parent group and inventory) - self.host.delete() - self.group = Group.objects.get(pk=self.group.pk) - self.assertFalse(self.group.has_active_failures) - self.inventory = Inventory.objects.get(pk=self.inventory.pk) - self.assertFalse(self.inventory.has_active_failures) - - def test_update_has_active_failures_when_job_removed(self): - job = self.test_run_job_that_fails() - # Delete (should clear flags). - job.delete() - self.host = Host.objects.get(pk=self.host.pk) - self.assertFalse(self.host.has_active_failures) - self.group = Group.objects.get(pk=self.group.pk) - self.assertFalse(self.group.has_active_failures) - self.inventory = Inventory.objects.get(pk=self.inventory.pk) - self.assertFalse(self.inventory.has_active_failures) - - def test_update_host_last_job_when_job_removed(self): - job1 = self.test_run_job() - job2 = self.test_run_job() - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job2) - self.assertEqual(self.host.last_job_host_summary.job, job2) - # Delete job2 (should update host to point to job1). - job2.delete() - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job1) - self.assertEqual(self.host.last_job_host_summary.job, job1) - # Delete job1 (should update host.last_job to None). - job1.delete() - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, None) - 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') - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - # Since we don't actually run the task, the --check should indicate - # everything is successful. - self.check_job_result(job, 'successful') - self.check_job_events(job, 'skipped', 1, 1) - for job_host_summary in job.job_host_summaries.all(): - self.assertFalse(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertFalse(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertFalse(group.has_active_failures) - self.assertFalse(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 0) - self.assertEqual(job.failed_hosts.count(), 0) - self.assertEqual(job.changed_hosts.count(), 0) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 1) - self.assertEqual(job.processed_hosts.count(), 1) - - def _cancel_job_callback(self): - job = Job.objects.get(pk=self.job.pk) - self.assertTrue(job.cancel()) - 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. - job = self.create_test_job(job_template=job_template, save=False) - job.save() - self.assertEqual(job.status, 'new') - self.assertEqual(job.cancel_flag, False) - self.assertFalse(job.passwords_needed_to_start) - job.cancel_flag = True - job.save() - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'canceled', expect_stdout=False) - self.assertEqual(job.cancel_flag, True) - # Calling cancel afterwards just returns the cancel flag. - self.assertTrue(job.cancel()) - # Read attribute for test coverage. - job.celery_task - job.celery_task_id = '' - job.save() - self.assertEqual(job.celery_task, None) - # Unable to start job again. - 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, - forks=3, verbosity=2, - extra_vars=u'{\n\t"abc": 1234\n}') - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('"--force-handlers"' in job.job_args) - self.assertTrue('"--forks=3"' in job.job_args) - self.assertTrue('"-vv"' in job.job_args) - self.assertTrue('"-e"' in job.job_args) - # Test with extra_vars as key=value (old format, still supported by - # -e option to ansible-playbook). - job_template2 = self.create_test_job_template(extra_vars='foo=1') - job2 = self.create_test_job(job_template=job_template2) - self.assertEqual(job2.status, 'new') - self.assertTrue(job2.signal_start()) - job2 = Job.objects.get(pk=job2.pk) - self.check_job_result(job2, 'successful') - # Test with extra_vars as YAML (should be converted to JSON in args). - job_template3 = self.create_test_job_template(extra_vars='abc: 1234') - job3 = self.create_test_job(job_template=job_template3) - self.assertEqual(job3.status, 'new') - self.assertTrue(job3.signal_start()) - job3 = Job.objects.get(pk=job3.pk) - 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) - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.assertTrue(len(job.job_args) > 1024) - self.check_job_result(job, 'successful') - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'failed') - self.assertTrue('"-l"' in job.job_args) - - def test_limit_option_with_group_pattern_and_ssh_key(self): - self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA) - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template(limit='test-group:&test-group2') - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertFalse('"--private-key=' in job.job_args) - 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', - start_at_task='start here') - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('"-t"' in job.job_args) - self.assertTrue('"--skip-tags=' in job.job_args) - self.assertTrue('"--start-at-task=' in job.job_args) - - def test_ssh_username_and_password(self): - self.create_test_credential(username='sshuser', password='sshpass') - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('"-u"' in job.job_args) - self.assertTrue('"--ask-pass"' in job.job_args) - - def test_ssh_ask_password(self): - self.create_test_credential(password='ASK') - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertTrue(job.passwords_needed_to_start) - self.assertTrue('ssh_password' in job.passwords_needed_to_start) - self.assertFalse(job.signal_start()) - self.assertEqual(job.status, 'new') - self.assertTrue(job.signal_start(ssh_password='sshpass')) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('"--ask-pass"' in job.job_args) - - def test_become_username_and_password(self): - self.create_test_credential(become_method='sudo', - become_username='sudouser', - become_password='sudopass') - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - # Job may fail if current user doesn't have password-less become - # privileges, but we're mainly checking the command line arguments. - self.check_job_result(job, ('successful', 'failed')) - self.assertTrue('"--become-user"' in job.job_args) - self.assertTrue('"--become-method"' in job.job_args) - self.assertTrue('"--ask-become-pass"' in job.job_args) - - def test_become_ask_password(self): - self.create_test_credential(become_password='ASK') - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertTrue(job.passwords_needed_to_start) - self.assertTrue('become_password' in job.passwords_needed_to_start) - self.assertFalse(job.signal_start()) - self.assertTrue(job.signal_start(become_password='sudopass')) - job = Job.objects.get(pk=job.pk) - # Job may fail if current user doesn't have password-less become - # privileges, but we're mainly checking the command line arguments. - self.assertTrue(job.status in ('successful', 'failed')) - self.assertTrue('"--ask-become-pass"' in job.job_args) - self.assertFalse('"--become-user"' in job.job_args) - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - # Job may fail if current user doesn't have password-less become - # privileges, but we're mainly checking the command line arguments. - self.assertTrue(job.status in ('successful', 'failed')) - self.assertTrue('"--become"' in job.job_args) - self.assertFalse('"--become-user"' in job.job_args) - self.assertFalse('"--become-method"' in job.job_args) - - def test_become_enabled_with_username_and_password(self): - self.create_test_credential(become_method='sudo', - become_username='sudouser', - become_password='sudopass') - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - # Job may fail if current user doesn't have password-less become - # privileges, but we're mainly checking the command line arguments. - self.check_job_result(job, ('successful', 'failed')) - self.assertTrue('"--become-user"' in job.job_args) - self.assertTrue('"--become-method"' in job.job_args) - self.assertTrue('"--ask-become-pass"' in job.job_args) - self.assertTrue('"--become"' in job.job_args) - - def test_unlocked_ssh_key(self): - self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA) - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertFalse('"--private-key=' in job.job_args) - self.assertTrue('ssh-agent' in job.job_args) - - def test_openssh_key_format(self): - ssh_ver = get_ssh_version() - openssh_keys_supported = ssh_ver != "unknown" and Version(ssh_ver) >= Version("6.5") - self.create_test_credential(ssh_key_data=TEST_OPENSSH_KEY_DATA) - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - if openssh_keys_supported: - self.check_job_result(job, 'successful') - self.assertFalse('"--private-key=' in job.job_args) - self.assertTrue('ssh-agent' in job.job_args) - else: - self.check_job_result(job, 'error', expect_traceback=True) - - def test_locked_ssh_key_with_password(self): - self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA_LOCKED, - ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK) - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('ssh-agent' in job.job_args) - self.assertTrue('Bad passphrase' not in job.result_stdout) - - def test_locked_ssh_key_with_bad_password(self): - self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA_LOCKED, - ssh_key_unlock='not the passphrase') - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'failed') - self.assertTrue('ssh-agent' in job.job_args) - self.assertTrue('Bad passphrase' in job.result_stdout) - - def test_locked_ssh_key_ask_password(self): - self.create_test_credential(ssh_key_data=TEST_SSH_KEY_DATA_LOCKED, - ssh_key_unlock='ASK') - self.create_test_project(TEST_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertTrue(job.passwords_needed_to_start) - self.assertTrue('ssh_key_unlock' in job.passwords_needed_to_start) - self.assertFalse(job.signal_start()) - job.status = 'failed' - job.save() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertTrue(job.signal_start(ssh_key_unlock=TEST_SSH_KEY_DATA_UNLOCK)) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('ssh-agent' in job.job_args) - self.assertTrue('Bad passphrase' not in job.result_stdout) - - def test_vault_password(self): - self.create_test_credential(vault_password=TEST_VAULT_PASSWORD) - self.create_test_project(TEST_VAULT_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('"--ask-vault-pass"' in job.job_args) - - def test_vault_ask_password(self): - self.create_test_credential(vault_password='ASK') - self.create_test_project(TEST_VAULT_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertTrue(job.passwords_needed_to_start) - self.assertTrue('vault_password' in job.passwords_needed_to_start) - self.assertFalse(job.signal_start()) - self.assertEqual(job.status, 'new') - self.assertTrue(job.signal_start(vault_password=TEST_VAULT_PASSWORD)) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue('"--ask-vault-pass"' in job.job_args) - - def test_vault_bad_password(self): - self.create_test_credential(vault_password='not it') - self.create_test_project(TEST_VAULT_PLAYBOOK) - job_template = self.create_test_job_template() - job = self.create_test_job(job_template=job_template) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'failed') - self.assertTrue('"--ask-vault-pass"' in job.job_args) - - def _test_cloud_credential_environment_variables(self, kind): - if kind == 'aws': - env_var1 = 'AWS_ACCESS_KEY' - env_var2 = 'AWS_SECRET_KEY' - elif kind == 'rax': - env_var1 = 'RAX_USERNAME' - env_var2 = 'RAX_API_KEY' - elif kind == 'gce': - env_var1 = 'GCE_EMAIL' - env_var2 = 'GCE_PEM_FILE_PATH' - elif kind == 'azure': - env_var1 = 'AZURE_SUBSCRIPTION_ID' - env_var2 = 'AZURE_CERT_PATH' - elif kind == 'vmware': - env_var1 = 'VMWARE_USER' - env_var2 = 'VMWARE_PASSWORD' - self.create_test_cloud_credential(name='%s cred' % kind, kind=kind, - username='my %s access' % kind, - password='my %s secret' % kind, - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.assertTrue(env_var1 in job.job_env) - self.assertTrue(env_var2 in job.job_env) - - def test_aws_cloud_credential_environment_variables(self): - self._test_cloud_credential_environment_variables('aws') - - def test_rax_cloud_credential_environment_variables(self): - self._test_cloud_credential_environment_variables('rax') - - def test_gce_cloud_credential_environment_variables(self): - self._test_cloud_credential_environment_variables('gce') - - def test_azure_cloud_credential_environment_variables(self): - self._test_cloud_credential_environment_variables('azure') - - def test_vmware_cloud_credential_environment_variables(self): - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.check_job_events(job, 'ok', 1, 2, async=True, async_tasks=1) - for job_host_summary in job.job_host_summaries.all(): - self.assertFalse(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, - job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertFalse(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertFalse(group.has_active_failures) - self.assertFalse(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 1) - self.assertEqual(job.failed_hosts.count(), 0) - self.assertEqual(job.changed_hosts.count(), 1) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - self.assertEqual(job.processed_hosts.count(), 1) - - def test_run_async_job_that_fails(self): - # 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'failed') - self.check_job_events(job, 'failed', 1, 2, async=True, async_tasks=1) - for job_host_summary in job.job_host_summaries.all(): - self.assertTrue(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, - job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertTrue(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertTrue(group.has_active_failures) - self.assertTrue(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 1) # FIXME: Is this right? - self.assertEqual(job.failed_hosts.count(), 1) - self.assertEqual(job.changed_hosts.count(), 0) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - self.assertEqual(job.processed_hosts.count(), 1) - - def test_run_async_job_that_times_out(self): - # 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'failed') - self.check_job_events(job, 'failed', 1, 2, async=True, - async_timeout=True, async_tasks=1) - for job_host_summary in job.job_host_summaries.all(): - self.assertTrue(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, - job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertTrue(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertTrue(group.has_active_failures) - self.assertTrue(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 1) # FIXME: Is this right? - self.assertEqual(job.failed_hosts.count(), 1) - self.assertEqual(job.changed_hosts.count(), 0) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.check_job_events(job, 'ok', 1, 1, async=True, async_nowait=True) - for job_host_summary in job.job_host_summaries.all(): - self.assertFalse(job_host_summary.failed) - self.assertEqual(job_host_summary.host.last_job_host_summary, - job_host_summary) - self.host = Host.objects.get(pk=self.host.pk) - self.assertEqual(self.host.last_job, job) - self.assertFalse(self.host.has_active_failures) - for group in self.host.all_groups: - self.assertFalse(group.has_active_failures) - self.assertFalse(self.host.inventory.has_active_failures) - self.assertEqual(job.successful_hosts.count(), 1) - self.assertEqual(job.failed_hosts.count(), 0) - self.assertEqual(job.changed_hosts.count(), 0) - self.assertEqual(job.unreachable_hosts.count(), 0) - self.assertEqual(job.skipped_hosts.count(), 0) - 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - self.check_job_events(job, 'ok', 1, 3, has_roles=True) - - @unittest.skipUnless(settings.BROKER_URL == 'redis://localhost/', - 'Non-default Redis setup.') - def test_run_job_with_bubblewrap(self): - # Only run test if bubblewrap is installed - cmd = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--version'] - try: - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - proc.communicate() - has_bubblewrap = bool(proc.returncode == 0) - except (OSError, ValueError): - has_bubblewrap = False - if not has_bubblewrap: - self.skipTest('bubblewrap is not installed') - # Enable bubblewrap for this test. - settings.AWX_PROOT_ENABLED = True - # Hide local settings path. - 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. - temp_path = tempfile.mkdtemp() - self._temp_paths.append(temp_path) - # Find a file in supervisor logs that should not be visible. - try: - supervisor_log_path = glob.glob('/var/log/supervisor/*')[0] - except IndexError: - supervisor_log_path = None - # Create our test project and job template. - self.create_test_project(TEST_PROOT_PLAYBOOK) - project_path = self.project.local_path - job_template = self.create_test_job_template() - extra_vars = { - 'projects_root': settings.PROJECTS_ROOT, - 'joboutput_root': settings.JOBOUTPUT_ROOT, - 'project_path': project_path, - 'other_project_path': other_project_path, - 'temp_path': temp_path, - } - if supervisor_log_path: - extra_vars['supervisor_log_path'] = supervisor_log_path - job = self.create_test_job(job_template=job_template, verbosity=3, - extra_vars=json.dumps(extra_vars)) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.check_job_result(job, 'successful') - - def test_run_job_with_bubblewrap_not_installed(self): - # Enable bubblewrap for this test, specify invalid bubblewrap 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) - self.assertEqual(job.status, 'new') - self.assertFalse(job.passwords_needed_to_start) - self.assertTrue(job.signal_start()) - job = Job.objects.get(pk=job.pk) - self.assertEqual(job.status, 'error') - diff --git a/awx/main/tests/old/users.py b/awx/main/tests/old/users.py deleted file mode 100644 index 154441d6fa..0000000000 --- a/awx/main/tests/old/users.py +++ /dev/null @@ -1,1073 +0,0 @@ -# Copyright (c) 2015 Ansible, Inc. -# All Rights Reserved. - -# Python -import urllib -from mock import patch - -# Django -from django.conf import settings -from django.contrib.auth.models import User, Group -from django.db.models import Q -from django.core.urlresolvers import reverse -from django.test.utils import override_settings - -# AWX -from awx.main.models import * # noqa -from awx.main.tests.base import BaseTest - -__all__ = ['AuthTokenTimeoutTest', 'AuthTokenLimitTest', 'AuthTokenProxyTest', 'UsersTest', 'LdapTest'] - - -class AuthTokenTimeoutTest(BaseTest): - def setUp(self): - super(AuthTokenTimeoutTest, self).setUp() - self.setup_users() - self.setup_instances() - - def test_auth_token_timeout_exists(self): - auth_token_url = reverse('api:auth_token_view') - dashboard_url = reverse('api:dashboard_view') - - data = dict(zip(('username', 'password'), self.get_super_credentials())) - auth = self.post(auth_token_url, data, expect=200) - kwargs = { - 'HTTP_X_AUTH_TOKEN': 'Token %s' % auth['token'] - } - - response = self._generic_rest(dashboard_url, expect=200, method='get', return_response_object=True, client_kwargs=kwargs) - self.assertIn('Auth-Token-Timeout', response) - self.assertEqual(response['Auth-Token-Timeout'], str(settings.AUTH_TOKEN_EXPIRATION)) - - -class AuthTokenLimitTest(BaseTest): - def setUp(self): - super(AuthTokenLimitTest, self).setUp() - self.setup_users() - self.setup_instances() - - @override_settings(AUTH_TOKEN_PER_USER=1) - @patch.object(awx.main.models.organization.AuthToken, 'get_request_hash') - def test_invalidate_first_session(self, mock_get_request_hash): - auth_token_url = reverse('api:auth_token_view') - user_me_url = reverse('api:user_me_list') - - data = dict(zip(('username', 'password'), self.get_normal_credentials())) - - mock_get_request_hash.return_value = "session_1" - response = self.post(auth_token_url, data, expect=200, auth=None) - auth_token1 = { - 'token': response['token'] - } - self.get(user_me_url, expect=200, auth=auth_token1) - - mock_get_request_hash.return_value = "session_2" - response = self.post(auth_token_url, data, expect=200, auth=None) - auth_token2 = { - 'token': response['token'] - } - self.get(user_me_url, expect=200, auth=auth_token2) - - # Ensure our get_request_hash mock is working - self.assertNotEqual(auth_token1['token'], auth_token2['token']) - - mock_get_request_hash.return_value = "session_1" - response = self.get(user_me_url, expect=401, auth=auth_token1) - self.assertEqual(AuthToken.reason_long('limit_reached'), response['detail']) - - -class AuthTokenProxyTest(BaseTest): - ''' - Ensure ips from the X-Forwarded-For get honored and used in auth tokens - ''' - def check_token_and_expires_exist(self, response): - self.assertTrue('token' in response) - self.assertTrue('expires' in response) - - def check_me_is_admin(self, response): - self.assertEquals(response['results'][0]['username'], 'admin') - self.assertEquals(response['count'], 1) - - def save_remote_host_headers(self): - self._remote_host_headers = settings.REMOTE_HOST_HEADERS[:] - - def restore_remote_host_headers(self): - if getattr(self, '_remote_host_headers', None): - settings.REMOTE_HOST_HEADERS = self._remote_host_headers - - def setUp(self): - super(AuthTokenProxyTest, self).setUp() - self.setup_users() - self.setup_instances() - self.organizations = self.make_organizations(self.super_django_user, 2) - self.organizations[0].admin_role.members.add(self.normal_django_user) - - self.assertIn('REMOTE_ADDR', settings.REMOTE_HOST_HEADERS) - self.assertIn('REMOTE_HOST', settings.REMOTE_HOST_HEADERS) - - if 'HTTP_X_FORWARDED_FOR' not in settings.REMOTE_HOST_HEADERS: - self.save_remote_host_headers() - settings.REMOTE_HOST_HEADERS.insert(0, 'HTTP_X_FORWARDED_FOR') - - def tearDown(self): - super(AuthTokenProxyTest, self).tearDown() - self.restore_remote_host_headers() - - def _request_auth_token(self, remote_addr): - auth_token_url = reverse('api:auth_token_view') - client_kwargs = {'HTTP_X_FORWARDED_FOR': remote_addr} - - # Request a new auth token from the remote address specified via 'HTTP_X_FORWARDED_FOR' - data = dict(zip(('username', 'password'), self.get_super_credentials())) - response = self.post(auth_token_url, data, expect=200, auth=None, remote_addr=None, client_kwargs=client_kwargs) - self.check_token_and_expires_exist(response) - auth_token = response['token'] - - return auth_token - - def _get_me(self, expect, auth, remote_addr, client_kwargs=None): - user_me_url = reverse('api:user_me_list') - return self.get(user_me_url, expect=expect, auth=auth, remote_addr=remote_addr, client_kwargs=client_kwargs) - - def test_honor_ip(self): - remote_addr = '192.168.75.1' - - auth_token = self._request_auth_token(remote_addr) - - # Verify we can access our own user information, from the remote address specified via HTTP_X_FORWARDED_FOR - client_kwargs = {'HTTP_X_FORWARDED_FOR': remote_addr} - response = self._get_me(expect=200, auth=auth_token, remote_addr=remote_addr, client_kwargs=client_kwargs) - self.check_me_is_admin(response) - - # Verify we can access our own user information, from the remote address - response = self._get_me(expect=200, auth=auth_token, remote_addr=remote_addr) - self.check_me_is_admin(response) - - def test_honor_ip_fail(self): - remote_addr = '192.168.75.1' - remote_addr_diff = '192.168.75.2' - - auth_token = self._request_auth_token(remote_addr) - - # Verify we can access our own user information, from the remote address specified via HTTP_X_FORWARDED_FOR - client_kwargs = {'HTTP_X_FORWARDED_FOR': remote_addr_diff} - self._get_me(expect=401, auth=auth_token, remote_addr=remote_addr, client_kwargs=client_kwargs) - self._get_me(expect=401, auth=auth_token, remote_addr=remote_addr_diff) - - # should use ip address from other headers when HTTP_X_FORWARDED_FOR is blank - def test_blank_header_fallback(self): - remote_addr = '192.168.75.1' - - auth_token = self._request_auth_token(remote_addr) - - client_kwargs = {'HTTP_X_FORWARDED_FOR': ''} - response = self._get_me(expect=200, auth=auth_token, remote_addr=remote_addr, client_kwargs=client_kwargs) - self.check_me_is_admin(response) - - -class UsersTest(BaseTest): - - def collection(self): - return reverse('api:user_list') - - def setUp(self): - self.setup_instances() - super(UsersTest, self).setUp() - self.setup_users() - self.organizations = self.make_organizations(self.super_django_user, 2) - self.organizations[0].admin_role.members.add(self.normal_django_user) - self.organizations[0].member_role.members.add(self.other_django_user) - self.organizations[0].member_role.members.add(self.normal_django_user) - self.organizations[1].member_role.members.add(self.other_django_user) - - def test_user_creation_fails_without_password(self): - url = reverse('api:user_list') - new_user = dict(username='blippy') - self.post(url, expect=400, data=new_user, auth=self.get_super_credentials()) - - def test_only_super_user_or_org_admin_can_add_users(self): - url = reverse('api:user_list') - new_user = dict(username='blippy', password='hippy') - new_user2 = dict(username='blippy2', password='hippy2') - self.post(url, expect=401, data=new_user, auth=None) - self.post(url, expect=401, data=new_user, auth=self.get_invalid_credentials()) - self.post(url, expect=403, data=new_user, auth=self.get_other_credentials()) - self.post(url, expect=201, data=new_user, auth=self.get_super_credentials()) - self.post(url, expect=400, data=new_user, auth=self.get_super_credentials()) - # org admin cannot create orphaned users - self.post(url, expect=403, data=new_user2, auth=self.get_normal_credentials()) - # org admin can create org users - org_url = reverse('api:organization_users_list', args=(self.organizations[0].pk,)) - self.post(org_url, expect=201, data=new_user2, auth=self.get_normal_credentials()) - self.post(org_url, expect=400, data=new_user2, auth=self.get_normal_credentials()) - # Normal user cannot add users after his org is marked inactive. - self.organizations[0].delete() - new_user3 = dict(username='blippy3') - self.post(url, expect=403, data=new_user3, auth=self.get_normal_credentials()) - - def test_only_super_user_can_use_superuser_flag(self): - url = reverse('api:user_list') - new_super_user = dict(username='nommy', password='cookie', is_superuser=True) - self.post(url, expect=401, data=new_super_user, auth=self.get_invalid_credentials()) - self.post(url, expect=403, data=new_super_user, auth=self.get_other_credentials()) - self.post(url, expect=403, data=new_super_user, auth=self.get_normal_credentials()) - self.post(url, expect=201, data=new_super_user, auth=self.get_super_credentials()) - new_super_user2 = dict(username='nommy2', password='cookie', is_superuser=None) - self.post(url, expect=201, data=new_super_user2, auth=self.get_super_credentials()) - - def test_auth_token_login(self): - auth_token_url = reverse('api:auth_token_view') - user_me_url = reverse('api:user_me_list') - api_url = reverse('api:api_root_view') - api_v1_url = reverse('api:api_v1_root_view') - - # Always returns a 405 for any GET request, regardless of credentials. - self.get(auth_token_url, expect=405, auth=None) - self.get(auth_token_url, expect=405, auth=self.get_invalid_credentials()) - self.get(auth_token_url, expect=405, auth=self.get_normal_credentials()) - - # /api/ and /api/v1 should work and ignore any credenials passed. - self.get(api_url, expect=200, auth=None) - self.get(api_url, expect=200, auth=self.get_invalid_credentials()) - self.get(api_url, expect=200, auth=self.get_normal_credentials()) - self.get(api_v1_url, expect=200, auth=None) - self.get(api_v1_url, expect=200, auth=self.get_invalid_credentials()) - self.get(api_v1_url, expect=200, auth=self.get_normal_credentials()) - - # Posting without username/password fields or invalid username/password - # returns a 400 error. - data = {} - self.post(auth_token_url, data, expect=400) - data = dict(zip(('username', 'password'), self.get_invalid_credentials())) - self.post(auth_token_url, data, expect=400) - - # A valid username/password should give us an auth token. - data = dict(zip(('username', 'password'), self.get_normal_credentials())) - response = self.post(auth_token_url, data, expect=200, auth=None) - self.assertTrue('token' in response) - self.assertTrue('expires' in response) - self.assertEqual(response['token'], self.normal_django_user.auth_tokens.all()[0].key) - auth_token = response['token'] - - # Verify we can access our own user information with the auth token. - response = self.get(user_me_url, expect=200, auth=auth_token) - self.assertEquals(response['results'][0]['username'], 'normal') - self.assertEquals(response['count'], 1) - - # If basic auth is passed via the Authorization header and the UI also - # passes token auth via the X-Auth-Token header, the API should favor - # the X-Auth-Token value. - mixed_auth = { - 'basic': self.get_super_credentials(), - 'token': auth_token, - } - response = self.get(user_me_url, expect=200, auth=mixed_auth) - self.assertEquals(response['results'][0]['username'], 'normal') - self.assertEquals(response['count'], 1) - - # If we simulate a different remote address, should not be able to use - # the first auth token. - remote_addr = '127.0.0.2' - response = self.get(user_me_url, expect=401, auth=auth_token, - remote_addr=remote_addr) - self.assertEqual(response['detail'], AuthToken.reason_long('invalid_token')) - - # The WWW-Authenticate header should specify Token auth, since that - # auth method was used in the request. - response_header = response.response.get('WWW-Authenticate', '') - self.assertEqual(response_header.split()[0], 'Token') - - # Request a new auth token from the new remote address. - data = dict(zip(('username', 'password'), self.get_normal_credentials())) - response = self.post(auth_token_url, data, expect=200, auth=None, - remote_addr=remote_addr) - self.assertTrue('token' in response) - self.assertTrue('expires' in response) - self.assertEqual(response['token'], self.normal_django_user.auth_tokens.all()[1].key) - auth_token2 = response['token'] - - # Verify we can access our own user information with the second auth - # token from the other remote address. - response = self.get(user_me_url, expect=200, auth=auth_token2, - remote_addr=remote_addr) - self.assertEquals(response['results'][0]['username'], 'normal') - self.assertEquals(response['count'], 1) - - # The second auth token also can't be used from the first address, but - # the first auth token is still valid from its address. - response = self.get(user_me_url, expect=401, auth=auth_token2) - self.assertEqual(response['detail'], 'Invalid token') - response_header = response.response.get('WWW-Authenticate', '') - self.assertEqual(response_header.split()[0], 'Token') - response = self.get(user_me_url, expect=200, auth=auth_token) - - # A request without authentication should ask for Basic by default. - response = self.get(user_me_url, expect=401) - response_header = response.response.get('WWW-Authenticate', '') - self.assertEqual(response_header.split()[0], 'Basic') - - # A request that attempts Basic auth should request Basic auth again. - response = self.get(user_me_url, expect=401, - auth=('invalid', 'password')) - response_header = response.response.get('WWW-Authenticate', '') - self.assertEqual(response_header.split()[0], 'Basic') - - # Invalidate a key (simulate expiration), now token auth should fail - # with the first token, but still work with the second. - self.normal_django_user.auth_tokens.get(key=auth_token).invalidate() - response = self.get(user_me_url, expect=401, auth=auth_token) - self.assertEqual(response['detail'], 'Token is expired') - response = self.get(user_me_url, expect=200, auth=auth_token2, - remote_addr=remote_addr) - - # Token auth should be denied if the user is inactive. - self.normal_django_user.delete() - response = self.get(user_me_url, expect=401, auth=auth_token2, - remote_addr=remote_addr) - assert response['detail'] == 'Invalid token' - - def test_ordinary_user_can_modify_some_fields_about_himself_but_not_all_and_passwords_work(self): - - detail_url = reverse('api:user_detail', args=(self.other_django_user.pk,)) - data = self.get(detail_url, expect=200, auth=self.get_other_credentials()) - - # can change first_name, last_name, etc - data['last_name'] = "NewLastName" - self.put(detail_url, data, expect=200, auth=self.get_other_credentials()) - - # can't change username - data['username'] = 'newUsername' - self.put(detail_url, data, expect=403, auth=self.get_other_credentials()) - - # if superuser, CAN change lastname and username and such - self.put(detail_url, data, expect=200, auth=self.get_super_credentials()) - - # and user can still login - creds = self.get_other_credentials() - creds = ('newUsername', creds[1]) - data = self.get(detail_url, expect=200, auth=creds) - - # user can change their password (submit as text) and can still login - # and password is not stored as plaintext - - data['password'] = 'newPassWord1234Changed' - self.put(detail_url, data, expect=200, auth=creds) - creds = (creds[0], data['password']) - self.get(detail_url, expect=200, auth=creds) - - # make another nobody user, and make sure they can't send any edits - obj = User.objects.create(username='new_user') - obj.set_password('new_user') - obj.save() - hacked = dict(password='asdf') - self.put(detail_url, hacked, expect=403, auth=('new_user', 'new_user')) - hacked = dict(username='asdf') - self.put(detail_url, hacked, expect=403, auth=('new_user', 'new_user')) - - # password is not stored in plaintext - self.assertTrue(User.objects.get(pk=self.normal_django_user.pk).password != data['password']) - - def test_user_created_with_password_can_login(self): - - # this is something an org admin can do... - url = reverse('api:user_list') - data = dict(username='username', password='password') - data2 = dict(username='username2', password='password2') - - # but a regular user cannot create users - self.post(url, expect=403, data=data2, auth=self.get_other_credentials()) - # org admins cannot create orphaned users - self.post(url, expect=403, data=data2, auth=self.get_normal_credentials()) - - # a super user can create new users - self.post(url, expect=201, data=data, auth=self.get_super_credentials()) - # verify that the login works... - self.get(url, expect=200, auth=('username', 'password')) - - # verify that if you post a user with a pk, you do not alter that user's password info - mod = dict(id=self.super_django_user.pk, username='change', password='change') - self.post(url, expect=201, data=mod, auth=self.get_super_credentials()) - orig = User.objects.get(pk=self.super_django_user.pk) - self.assertTrue(orig.username != 'change') - - def test_user_delete_non_existant_user(self): - user_pk = self.normal_django_user.pk - fake_pk = user_pk + 1000 - self.assertFalse(User.objects.filter(pk=fake_pk).exists(), "We made up a fake pk and it happened to exist") - url = reverse('api:user_detail', args=(fake_pk,)) - self.delete(url, expect=404, auth=self.get_super_credentials()) - - def test_password_not_shown_in_get_operations_for_list_or_detail(self): - url = reverse('api:user_detail', args=(self.super_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertTrue('password' not in data) - - url = reverse('api:user_list') - data = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertTrue('password' not in data['results'][0]) - - def test_user_list_filtered(self): - url = reverse('api:user_list') - data3 = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertEquals(data3['count'], 4) - # Normal user is an org admin, can see all users. - data2 = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data2['count'], 4) - # Unless the setting ORG_ADMINS_CAN_SEE_ALL_USERS is False, in which case - # he can only see users in his org, and the system admin - settings.ORG_ADMINS_CAN_SEE_ALL_USERS = False - data2 = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data2['count'], 3) - # Other use can only see users in his org. - data1 = self.get(url, expect=200, auth=self.get_other_credentials()) - self.assertEquals(data1['count'], 3) - # Normal user can no longer see all users after the organization he - # admins is marked inactive, nor can he see any other users that were - # in that org, so he only sees himself and the system admin. - self.organizations[0].delete() - data3 = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data3['count'], 2) - - # Test no longer relevant since we've moved away from active / inactive. - # However there was talk about keeping is_active for users, so this test will - # be relevant if that comes to pass. - anoek 2016-03-22 - # def test_super_user_can_delete_a_user_but_only_marked_inactive(self): - # user_pk = self.normal_django_user.pk - # url = reverse('api:user_detail', args=(user_pk,)) - # self.delete(url, expect=204, auth=self.get_super_credentials()) - # self.get(url, expect=404, auth=self.get_super_credentials()) - # obj = User.objects.get(pk=user_pk) - # self.assertEquals(obj.is_active, False) - - def test_non_org_admin_user_cannot_delete_any_user_including_himself(self): - url1 = reverse('api:user_detail', args=(self.super_django_user.pk,)) - url2 = reverse('api:user_detail', args=(self.normal_django_user.pk,)) - url3 = reverse('api:user_detail', args=(self.other_django_user.pk,)) - self.delete(url1, expect=403, auth=self.get_other_credentials()) - self.delete(url2, expect=403, auth=self.get_other_credentials()) - self.delete(url3, expect=403, auth=self.get_other_credentials()) - - def test_there_exists_an_obvious_url_where_a_user_may_find_his_user_record(self): - url = reverse('api:user_me_list') - data = self.get(url, expect=401, auth=None) - data = self.get(url, expect=401, auth=self.get_invalid_credentials()) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['results'][0]['username'], 'normal') - self.assertEquals(data['count'], 1) - data = self.get(url, expect=200, auth=self.get_other_credentials()) - self.assertEquals(data['results'][0]['username'], 'other') - self.assertEquals(data['count'], 1) - data = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertEquals(data['results'][0]['username'], 'admin') - self.assertEquals(data['count'], 1) - - def test_superuser_can_change_admin_only_fields_about_himself(self): - url = reverse('api:user_detail', args=(self.super_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_super_credentials()) - data['username'] += '2' - data['first_name'] += ' Awesome' - data['last_name'] += ', Jr.' - self.put(url, data, expect=200, - auth=self.get_super_credentials()) - # FIXME: Test if super user mark himself as no longer a super user, or - # delete himself. - - def test_user_related_resources(self): - - # organizations the user is a member of, should be 1 - url = reverse('api:user_organizations_list', - args=(self.normal_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['count'], 1) - # also accessible via superuser - data = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertEquals(data['count'], 1) - # and also by other user... - data = self.get(url, expect=200, auth=self.get_other_credentials()) - # but not by nobody user - data = self.get(url, expect=403, auth=self.get_nobody_credentials()) - - # organizations the user is an admin of, should be 1 - url = reverse('api:user_admin_of_organizations_list', - args=(self.normal_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['count'], 1) - # also accessible via superuser - data = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertEquals(data['count'], 1) - # and also by other user - data = self.get(url, expect=200, auth=self.get_other_credentials()) - # but not by nobody user - data = self.get(url, expect=403, auth=self.get_nobody_credentials()) - - # teams the user is on, should be 0 - url = reverse('api:user_teams_list', args=(self.normal_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['count'], 0) - # also accessible via superuser - data = self.get(url, expect=200, auth=self.get_super_credentials()) - self.assertEquals(data['count'], 0) - # and also by other user - data = self.get(url, expect=200, auth=self.get_other_credentials()) - # but not by nobody user - data = self.get(url, expect=403, auth=self.get_nobody_credentials()) - - # verify org admin can still read other user data too - url = reverse('api:user_organizations_list', - args=(self.other_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['count'], 1) - url = reverse('api:user_admin_of_organizations_list', - args=(self.other_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['count'], 0) - url = reverse('api:user_teams_list', - args=(self.other_django_user.pk,)) - data = self.get(url, expect=200, auth=self.get_normal_credentials()) - self.assertEquals(data['count'], 0) - - # FIXME: add test that shows posting a user w/o id to /organizations/2/users/ can create a new one & associate - # FIXME: add test that shows posting a user w/o id to /organizations/2/admins/ can create a new one & associate - # FIXME: add test that shows posting a projects w/o id to /organizations/2/projects/ can create a new one & associate - - def test_user_list_ordering(self): - base_url = reverse('api:user_list') - base_qs = User.objects.distinct() - - # Check list view with ordering by name. - url = '%s?order_by=username' % base_url - qs = base_qs.order_by('username') - self.check_get_list(url, self.super_django_user, qs, check_order=True) - - # Check list view with ordering by username in reverse. - url = '%s?order=-username' % base_url - qs = base_qs.order_by('-username') - self.check_get_list(url, self.super_django_user, qs, check_order=True) - - # Check list view with multiple ordering fields. - url = '%s?order_by=-pk,username' % base_url - qs = base_qs.order_by('-pk', 'username') - self.check_get_list(url, self.super_django_user, qs, check_order=True) - - # Check list view with invalid field name. - url = '%s?order_by=invalidfieldname' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - # Check list view with no field name. - url = '%s?order_by=' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - def test_user_list_filtering(self): - # Also serves as general-purpose testing for custom API filters. - base_url = reverse('api:user_list') - base_qs = User.objects.distinct() - - # Filter by username. - url = '%s?username=normal' % base_url - qs = base_qs.filter(username='normal') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __exact suffix. - url = '%s?username__exact=normal' % base_url - qs = base_qs.filter(username__exact='normal') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __iexact suffix. - url = '%s?username__iexact=NORMAL' % base_url - qs = base_qs.filter(username__iexact='NORMAL') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __contains suffix. - url = '%s?username__contains=dmi' % base_url - qs = base_qs.filter(username__contains='dmi') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __icontains suffix. - url = '%s?username__icontains=DMI' % base_url - qs = base_qs.filter(username__icontains='DMI') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __startswith suffix. - url = '%s?username__startswith=no' % base_url - qs = base_qs.filter(username__startswith='no') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __istartswith suffix. - url = '%s?username__istartswith=NO' % base_url - qs = base_qs.filter(username__istartswith='NO') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __endswith suffix. - url = '%s?username__endswith=al' % base_url - qs = base_qs.filter(username__endswith='al') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __iendswith suffix. - url = '%s?username__iendswith=AL' % base_url - qs = base_qs.filter(username__iendswith='AL') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __regex suffix. - url = '%s?username__regex=%s' % (base_url, urllib.quote_plus(r'^admin|no.+$')) - qs = base_qs.filter(username__regex=r'^admin|no.+$') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __iregex suffix. - url = '%s?username__iregex=%s' % (base_url, urllib.quote_plus(r'^ADMIN|NO.+$')) - qs = base_qs.filter(username__iregex=r'^ADMIN|NO.+$') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by invalid regex value. - url = '%s?username__regex=%s' % (base_url, urllib.quote_plus('[')) - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - # Filter by multiple usernames (AND). - url = '%s?username=normal&username=nobody' % base_url - qs = base_qs.filter(username='normal', username__exact='nobody') - self.assertFalse(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by multiple usernames (OR). - url = '%s?or__username=normal&or__username=nobody' % base_url - qs = base_qs.filter(Q(username='normal') | Q(username='nobody')) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Exclude by username. - url = '%s?not__username=normal' % base_url - qs = base_qs.exclude(username='normal') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Exclude by multiple usernames. - url = '%s?not__username=normal¬__username=nobody' % base_url - qs = base_qs.filter(~Q(username='normal') & ~Q(username='nobody')) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Exclude by multiple usernames with OR. - url = '%s?or__not__username=normal&or__not__username=nobody' % base_url - qs = base_qs.filter(~Q(username='normal') | ~Q(username='nobody')) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Exclude by username with suffix. - url = '%s?not__username__startswith=no' % base_url - qs = base_qs.exclude(username__startswith='no') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by multiple username specs. - url = '%s?username__startswith=no&username__endswith=al' % base_url - qs = base_qs.filter(username__startswith='no', username__endswith='al') - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by is_superuser (when True). - url = '%s?is_superuser=True' % base_url - qs = base_qs.filter(is_superuser=True) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by is_superuser (when False). - url = '%s?is_superuser=False' % base_url - qs = base_qs.filter(is_superuser=False) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by is_superuser (when 1). - url = '%s?is_superuser=1' % base_url - qs = base_qs.filter(is_superuser=True) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by is_superuser (when 0). - url = '%s?is_superuser=0' % base_url - qs = base_qs.filter(is_superuser=False) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by is_superuser (when TRUE). - url = '%s?is_superuser=TRUE' % base_url - qs = base_qs.filter(is_superuser=True) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by invalid value for boolean field. - url = '%s?is_superuser=notatbool' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - # Filter by custom __int suffix on boolean field. - url = '%s?is_superuser__int=1' % base_url - qs = base_qs.filter(is_superuser=True) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by is_staff (field not exposed via API). FIXME: Should - # eventually not be allowed! - url = '%s?is_staff=true' % base_url - qs = base_qs.filter(is_staff=True) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by pk/id - url = '%s?pk=%d' % (base_url, self.normal_django_user.pk) - qs = base_qs.filter(pk=self.normal_django_user.pk) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - url = '%s?id=%d' % (base_url, self.normal_django_user.pk) - qs = base_qs.filter(id=self.normal_django_user.pk) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by pk gt/gte/lt/lte. - url = '%s?pk__gt=0' % base_url - qs = base_qs.filter(pk__gt=0) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - url = '%s?pk__gte=0' % base_url - qs = base_qs.filter(pk__gt=0) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - url = '%s?pk__lt=999' % base_url - qs = base_qs.filter(pk__lt=999) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - url = '%s?pk__lte=999' % base_url - qs = base_qs.filter(pk__lte=999) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by invalid value for integer field. - url = '%s?pk=notanint' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - # Filter by int using custom __int suffix. - url = '%s?pk__int=%d' % (base_url, self.super_django_user.pk) - qs = base_qs.filter(pk=self.super_django_user.pk) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - # Filter by username with __in list. - url = '%s?username__in=normal,admin' % base_url - qs = base_qs.filter(username__in=('normal', 'admin')) - self.assertTrue(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - - url = '%s?email_address=nobody@example.com' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - # Filter by invalid query string field names. - url = '%s?__' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - url = '%s?not__' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - url = '%s?__foo' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - url = '%s?username__' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - url = '%s?username+=normal' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - # Filter with some unicode characters included in field name and value. - url = u'%s?username=arrr\u2620' % base_url - qs = base_qs.filter(username=u'arrr\u2620') - self.assertFalse(qs.count()) - self.check_get_list(url, self.super_django_user, qs) - url = u'%s?user\u2605name=normal' % base_url - self.check_get_list(url, self.super_django_user, base_qs, expect=400) - - def test_user_list_pagination(self): - base_url = reverse('api:user_list') - base_qs = User.objects.distinct() - - # Check list view with page size of 1. - url = '%s?order_by=username&page_size=1' % base_url - qs = base_qs.order_by('username') - self.check_get_list(url, self.super_django_user, qs, check_order=True, - limit=1) - - # Check list view with page size of 1, remaining pages. - qs = base_qs.order_by('username') - for n in xrange(1, base_qs.count()): - url = '%s?order_by=username&page_size=1&page=%d' % (base_url, n + 1) - self.check_get_list(url, self.super_django_user, qs, - check_order=True, offset=n, limit=1) - - # Check list view with page size of 2. - qs = base_qs.order_by('username') - for n in xrange(0, base_qs.count(), 2): - url = '%s?order_by=username&page_size=2&page=%d' % (base_url, (n / 2) + 1) - self.check_get_list(url, self.super_django_user, qs, - check_order=True, offset=n, limit=2) - - # Check list view with page size of 0 (to allow getting count of items - # matching a given filter). # FIXME: Make this work at some point! - #url = '%s?order_by=username&page_size=0' % base_url - #qs = base_qs.order_by('username') - #self.check_get_list(url, self.super_django_user, qs, check_order=True, - # limit=0) - - def test_user_list_searching(self): - base_url = reverse('api:user_list') - base_qs = User.objects.distinct() - - # Check search query parameter. - url = '%s?search=no' % base_url - qs = base_qs.filter(username__icontains='no') - self.check_get_list(url, self.super_django_user, qs) - - # Check search query parameter. - url = '%s?search=example' % base_url - qs = base_qs.filter(email__icontains='example') - self.check_get_list(url, self.super_django_user, qs) - - -class LdapTest(BaseTest): - - def use_test_setting(self, name, default=None, from_name=None): - from_name = from_name or name - setattr(settings, 'AUTH_LDAP_%s' % name, - getattr(settings, 'TEST_AUTH_LDAP_%s' % from_name, default)) - - def setUp(self): - super(LdapTest, self).setUp() - self.create_test_license_file(features={'ldap': True}) - # Skip tests if basic LDAP test settings aren't defined. - if not getattr(settings, 'TEST_AUTH_LDAP_SERVER_URI', None): - self.skipTest('no test LDAP auth server defined') - self.ldap_username = getattr(settings, 'TEST_AUTH_LDAP_USERNAME', None) - if not self.ldap_username: - self.skipTest('no test LDAP username defined') - self.ldap_password = getattr(settings, 'TEST_AUTH_LDAP_PASSWORD', None) - if not self.ldap_password: - self.skipTest('no test LDAP password defined') - # Set test LDAP settings that are always needed. - for name in ('SERVER_URI', 'BIND_DN', 'BIND_PASSWORD', 'USE_TLS', 'CONNECTION_OPTIONS'): - self.use_test_setting(name) - - def check_login(self, username=None, password=None, should_fail=False): - self.assertEqual(Group.objects.count(), 0) - username = username or self.ldap_username - password = password or self.ldap_password - result = self.client.login(username=username, password=password) - self.assertNotEqual(result, should_fail) - self.assertEqual(Group.objects.count(), 0) - if not should_fail: - user = User.objects.get(username=username) - self.assertTrue(user.profile) - self.assertTrue(user.profile.ldap_dn) - return user - - def test_ldap_auth(self): - for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'GROUP_TYPE', 'GROUP_SEARCH'): - self.use_test_setting(name) - self.assertEqual(User.objects.filter(username=self.ldap_username).count(), 0) - # Test logging in, user should be created with no flags or fields set. - user = self.check_login() - self.assertTrue(user.is_active) - self.assertFalse(user.has_usable_password()) - self.assertFalse(user.is_superuser) - self.assertFalse(user.first_name) - self.assertFalse(user.last_name) - self.assertFalse(user.email) - # Test logging in with bad username or password. - self.check_login(username='not a valid user', should_fail=True) - self.check_login(password='not a valid pass', should_fail=True) - # Test using a flat DN instead of user search. - self.use_test_setting('USER_DN_TEMPLATE', None) - if settings.AUTH_LDAP_USER_DN_TEMPLATE: - user = self.check_login() - del settings.AUTH_LDAP_USER_DN_TEMPLATE - # Test user attributes assigned from LDAP. - self.use_test_setting('USER_ATTR_MAP', {}) - if settings.AUTH_LDAP_USER_ATTR_MAP: - user = self.check_login() - for attr in settings.AUTH_LDAP_USER_ATTR_MAP.keys(): - self.assertTrue(getattr(user, attr)) - # Turn on group search fields. - for name in ('GROUP_SEARCH', 'GROUP_TYPE'): - self.use_test_setting(name) - # Test that user must be in required group. - self.use_test_setting('REQUIRE_GROUP', from_name='REQUIRE_GROUP_FAIL') - if settings.AUTH_LDAP_REQUIRE_GROUP: - user = self.check_login(should_fail=True) - self.use_test_setting('REQUIRE_GROUP') - user = self.check_login() - # Test that user must not be in deny group. - self.use_test_setting('DENY_GROUP', from_name='DENY_GROUP_FAIL') - if settings.AUTH_LDAP_DENY_GROUP: - user = self.check_login(should_fail=True) - self.use_test_setting('DENY_GROUP') - user = self.check_login() - # Check that user flags are set from group membership. - self.use_test_setting('USER_FLAGS_BY_GROUP') - if settings.AUTH_LDAP_USER_FLAGS_BY_GROUP: - user = self.check_login() - for attr in settings.AUTH_LDAP_USER_FLAGS_BY_GROUP.keys(): - self.assertTrue(getattr(user, attr)) - # Check that LDAP login fails when not enabled by license, but using a - # local password will work in either case. - user.set_password('local pass') - user.save() - self.check_login() - self.check_login(password='local pass') - self.create_test_license_file(features={'ldap': False}) - self.check_login(should_fail=True) - self.check_login(password='local pass') - - def test_ldap_organization_mapping(self): - for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP', - 'GROUP_SEARCH', 'GROUP_TYPE', 'USER_FLAGS_BY_GROUP'): - self.use_test_setting(name) - self.assertEqual(User.objects.filter(username=self.ldap_username).count(), 0) - self.use_test_setting('ORGANIZATION_MAP', {}) - self.use_test_setting('ORGANIZATION_MAP_RESULT', {}) - for org_name in settings.AUTH_LDAP_ORGANIZATION_MAP.keys(): - self.assertEqual(Organization.objects.filter(name=org_name).count(), 0) - user = self.check_login() - for org_name in settings.AUTH_LDAP_ORGANIZATION_MAP.keys(): - self.assertEqual(Organization.objects.filter(name=org_name).count(), 1) - for org_name, org_result in settings.AUTH_LDAP_ORGANIZATION_MAP_RESULT.items(): - org = Organization.objects.get(name=org_name) - if org_result.get('admins', False): - self.assertTrue(user in org.admin_role.members.all()) - else: - self.assertFalse(user in org.admin_role.members.all()) - if org_result.get('users', False): - self.assertTrue(user in org.member_role.members.all()) - else: - self.assertFalse(user in org.member_role.members.all()) - # Try again with different test mapping. - self.use_test_setting('ORGANIZATION_MAP', {}, - from_name='ORGANIZATION_MAP_2') - self.use_test_setting('ORGANIZATION_MAP_RESULT', {}, - from_name='ORGANIZATION_MAP_2_RESULT') - user = self.check_login() - for org_name in settings.AUTH_LDAP_ORGANIZATION_MAP.keys(): - self.assertEqual(Organization.objects.filter(name=org_name).count(), 1) - for org_name, org_result in settings.AUTH_LDAP_ORGANIZATION_MAP_RESULT.items(): - org = Organization.objects.get(name=org_name) - if org_result.get('admins', False): - self.assertTrue(user in org.admin_role.members.all()) - else: - self.assertFalse(user in org.admin_role.members.all()) - if org_result.get('users', False): - self.assertTrue(user in org.member_role.members.all()) - else: - self.assertFalse(user in org.member_role.members.all()) - - def test_ldap_team_mapping(self): - for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP', - 'GROUP_SEARCH', 'GROUP_TYPE', 'USER_FLAGS_BY_GROUP'): - self.use_test_setting(name) - self.assertEqual(User.objects.filter(username=self.ldap_username).count(), 0) - self.use_test_setting('TEAM_MAP', {}) - self.use_test_setting('TEAM_MAP_RESULT', {}) - for team_name, team_opts in settings.AUTH_LDAP_TEAM_MAP.items(): - self.assertEqual(Team.objects.filter(name=team_name).count(), 0) - self.assertEqual(Organization.objects.filter(name=team_opts['organization']).count(), 0) - user = self.check_login() - for team_name, team_opts in settings.AUTH_LDAP_TEAM_MAP.items(): - self.assertEqual(Team.objects.filter(name=team_name, organization__name=team_opts['organization']).count(), 1) - for team_name, team_result in settings.AUTH_LDAP_TEAM_MAP_RESULT.items(): - team = Team.objects.get(name=team_name) - if team_result.get('users', False): - self.assertTrue(user in team.member_role.members.all()) - else: - self.assertFalse(user in team.member_role.members.all()) - # Try again with different test mapping. - self.use_test_setting('TEAM_MAP', {}, from_name='TEAM_MAP_2') - self.use_test_setting('TEAM_MAP_RESULT', {}, - from_name='TEAM_MAP_2_RESULT') - user = self.check_login() - for team_name, team_opts in settings.AUTH_LDAP_TEAM_MAP.items(): - self.assertEqual(Team.objects.filter(name=team_name, organization__name=team_opts['organization']).count(), 1) - for team_name, team_result in settings.AUTH_LDAP_TEAM_MAP_RESULT.items(): - team = Team.objects.get(name=team_name) - if team_result.get('users', False): - self.assertTrue(user in team.member_role.members.all()) - else: - self.assertFalse(user in team.member_role.members.all()) - - def test_prevent_changing_ldap_user_fields(self): - for name in ('USER_SEARCH', 'ALWAYS_UPDATE_USER', 'USER_ATTR_MAP', - 'GROUP_SEARCH', 'GROUP_TYPE', 'USER_FLAGS_BY_GROUP'): - self.use_test_setting(name) - user = self.check_login() - self.setup_users() - config_url = reverse('api:api_v1_config_view') - with self.current_user(self.super_django_user): - response = self.get(config_url, expect=200) - user_ldap_fields = response.get('user_ldap_fields', []) - self.assertTrue(user_ldap_fields) - user_url = reverse('api:user_detail', args=(user.pk,)) - for user_field in user_ldap_fields: - with self.current_user(self.super_django_user): - data = self.get(user_url, expect=200) - if user_field == 'password': - data[user_field] = 'my new password' - with self.current_user(self.super_django_user): - self.put(user_url, data, expect=200) - user = User.objects.get(pk=user.pk) - self.assertFalse(user.has_usable_password()) - with self.current_user(self.super_django_user): - self.patch(user_url, {'password': 'try again'}, expect=200) - user = User.objects.get(pk=user.pk) - self.assertFalse(user.has_usable_password()) - elif user_field in data: - value = data[user_field] - if isinstance(value, bool): - value = not value - else: - value = unicode(value).upper() - data[user_field] = value - with self.current_user(self.super_django_user): - self.put(user_url, data, expect=400) - patch_data = {user_field: data[user_field]} - with self.current_user(self.super_django_user): - self.patch(user_url, patch_data, expect=400) - # Install a license with LDAP disabled; ldap fields should not be in - # config and all user fields should be changeable. - self.create_test_license_file(features={'ldap': False}) - with self.current_user(self.super_django_user): - response = self.get(config_url, expect=200) - self.assertFalse('user_ldap_fields' in response) - for user_field in user_ldap_fields: - with self.current_user(self.super_django_user): - data = self.get(user_url, expect=200) - if user_field == 'password': - data[user_field] = 'my new password' - with self.current_user(self.super_django_user): - self.put(user_url, data, expect=200) - user = User.objects.get(pk=user.pk) - self.assertTrue(user.has_usable_password()) - self.assertTrue(user.check_password, 'my new password') - with self.current_user(self.super_django_user): - self.patch(user_url, {'password': 'try again'}, expect=200) - user = User.objects.get(pk=user.pk) - self.assertTrue(user.has_usable_password()) - self.assertTrue(user.check_password, 'try again') - elif user_field in data: - value = data[user_field] - if isinstance(value, bool): - value = not value - else: - value = unicode(value).upper() - data[user_field] = value - with self.current_user(self.super_django_user): - self.put(user_url, data, expect=200) - patch_data = {user_field: data[user_field]} - with self.current_user(self.super_django_user): - self.patch(user_url, patch_data, expect=200)