diff --git a/awx/main/models/__init__.py b/awx/main/models/__init__.py index ec823d1add..640206291c 100644 --- a/awx/main/models/__init__.py +++ b/awx/main/models/__init__.py @@ -299,6 +299,8 @@ class CommonTask(PrimordialModel): def start(self, **kwargs): task_class = self._get_task_class() + if not self.can_start: + return False needed = self._get_passwords_needed_to_start() opts = dict([(field, kwargs.get(field, '')) for field in needed]) if not all(opts.values()): @@ -1431,6 +1433,24 @@ class ProjectUpdate(CommonTask): def _get_passwords_needed_to_start(self): return self.project.scm_passwords_needed + def _update_parent_instance(self): + parent_instance = self._get_parent_instance() + if parent_instance: + if self.status in ('pending', 'waiting', 'running'): + if parent_instance.current_update != self: + parent_instance.current_update = self + parent_instance.save(update_fields=['current_update']) + elif self.status in ('successful', 'failed', 'error', 'canceled'): + if parent_instance.current_update == self: + parent_instance.current_update = None + parent_instance.last_update = self + parent_instance.last_update_failed = self.failed + if not self.failed and parent_instance.scm_delete_on_next_update: + parent_instance.scm_delete_on_next_update = False + parent_instance.save(update_fields=['current_update', + 'last_update', + 'last_update_failed', + 'scm_delete_on_next_update']) class Permission(CommonModelNameNotUnique): ''' @@ -1582,13 +1602,13 @@ class JobTemplate(CommonModel): ''' needed = [] if self.credential: - needed.extend(self.credential.passwords_needed) + for pw in self.credential.passwords_needed: + if pw == 'password': + needed.append('ssh_password') + else: + needed.append(pw) if self.project.scm_update_on_launch: needed.extend(self.project.scm_passwords_needed) - for inventory_source in self.inventory.inventory_sources.filter(active=True, update_on_launch=True): - for pw in inventory_source.source_passwords_needed: - if pw not in needed: - needed.append(pw) return bool(self.credential and not len(needed)) class Job(CommonTask): @@ -1701,13 +1721,13 @@ class Job(CommonTask): '''Return list of password field names needed to start the job.''' needed = [] if self.credential: - needed.extend(self.credential.passwords_needed) + for pw in self.credential.passwords_needed: + if pw == 'password': + needed.append('ssh_password') + else: + needed.append(pw) if self.project.scm_update_on_launch: needed.extend(self.project.scm_passwords_needed) - for inventory_source in self.inventory.inventory_sources.filter(active=True, update_on_launch=True): - for pw in inventory_source.source_passwords_needed: - if pw not in needed: - needed.append(pw) return needed def _get_task_class(self): diff --git a/awx/main/tasks.py b/awx/main/tasks.py index dbaa055957..62872ee1d1 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -301,8 +301,11 @@ class RunJob(BaseTask): passwords = super(RunJob, self).build_passwords(job, **kwargs) creds = job.credential if creds: - for field in ('ssh_key_unlock', 'password', 'sudo_password'): - value = kwargs.get(field, decrypt_field(creds, field)) + for field in ('ssh_key_unlock', 'ssh_password', 'sudo_password'): + if field == 'ssh_password': + value = kwargs.get(field, decrypt_field(creds, 'password')) + else: + value = kwargs.get(field, decrypt_field(creds, field)) if value not in ('', 'ASK'): passwords[field] = value return passwords @@ -524,7 +527,7 @@ class RunProjectUpdate(BaseTask): value = kwargs.get('scm_key_unlock', decrypt_field(project.credential, 'ssh_key_unlock')) if value not in ('', 'ASK'): passwords['scm_key_unlock'] = value - passwords['scm_username'] = project.scm_username + passwords['scm_username'] = project.credential.username passwords['scm_password'] = kwargs.get('scm_password', decrypt_field(project.credential, 'password')) return passwords @@ -549,9 +552,9 @@ class RunProjectUpdate(BaseTask): scm_type = project.scm_type scm_url = update_scm_url(scm_type, project.scm_url) scm_url_parts = urlparse.urlsplit(scm_url) - scm_username = kwargs.get('passwords', {}).get('username', '') + scm_username = kwargs.get('passwords', {}).get('scm_username', '') scm_username = scm_username or scm_url_parts.username or '' - scm_password = kwargs.get('passwords', {}).get('password', '') + scm_password = kwargs.get('passwords', {}).get('scm_password', '') scm_password = scm_password or scm_url_parts.password or '' if scm_username and scm_password not in ('ASK', ''): if scm_type == 'svn': @@ -633,6 +636,7 @@ class RunProjectUpdate(BaseTask): output_replacements = [] before_url = self._build_scm_url_extra_vars(project_update, **kwargs)[0] + scm_username = kwargs.get('passwords', {}).get('scm_username', '') scm_password = kwargs.get('passwords', {}).get('scm_password', '') pwdict = dict(kwargs.get('passwords', {}).items()) for pw_name, pw_val in pwdict.items(): @@ -645,13 +649,13 @@ class RunProjectUpdate(BaseTask): if after_url != before_url: output_replacements.append((before_url, after_url)) project = project_update.project - if project.scm_type == 'svn' and project.scm_username and scm_password: + if project.scm_type == 'svn' and scm_username and scm_password: d_before = { - 'username': project.scm_username, + 'username': scm_username, 'password': scm_password, } d_after = { - 'username': project.scm_username, + 'username': scm_username, 'password': '*'*len(scm_password), } pattern1 = "username=\"%(username)s\" password=\"%(password)s\"" diff --git a/awx/main/tests/jobs.py b/awx/main/tests/jobs.py index 48c3b1574f..25bb337a1f 100644 --- a/awx/main/tests/jobs.py +++ b/awx/main/tests/jobs.py @@ -677,9 +677,9 @@ class JobTest(BaseJobTestMixin, django.test.TestCase): self.assertEqual(job.status, 'new') with self.current_user(self.user_sue): data = self.get(url) - data['name'] = '%s-updated' % data['name'] + data['limit'] = '%s-updated' % data['limit'] response = self.put(url, data) - #patch_data = dict(name='%s-changed' % data['name']) + #patch_data = dict(limit='%s-changed' % data['limit']) #response = self.patch(url, patch_data) # sue cannot update the job detail if it is in any other state. @@ -689,9 +689,9 @@ class JobTest(BaseJobTestMixin, django.test.TestCase): job.save() with self.current_user(self.user_sue): data = self.get(url) - data['name'] = '%s-updated' % data['name'] + data['limit'] = '%s-updated' % data['limit'] self.put(url, data, expect=405) - #patch_data = dict(name='%s-changed' % data['name']) + #patch_data = dict(limit='%s-changed' % data['limit']) #self.patch(url, patch_data, expect=405) # FIXME: Check with other credentials and readonly fields. @@ -777,7 +777,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.LiveServerTestCase): # 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]): + for status in reversed([x[0] for x in TASK_STATUS_CHOICES]): if status == 'waiting': continue job.status = status @@ -865,7 +865,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.LiveServerTestCase): 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]: + for status in [x[0] for x in TASK_STATUS_CHOICES]: if status == 'waiting': continue job.status = status diff --git a/awx/main/tests/projects.py b/awx/main/tests/projects.py index a7883d3814..4cd5c43607 100644 --- a/awx/main/tests/projects.py +++ b/awx/main/tests/projects.py @@ -631,6 +631,22 @@ class ProjectUpdatesTest(BaseTransactionTest): self.setup_users() 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) + credential = Credential.objects.create(**kw) + kwargs['credential'] = credential project = Project.objects.create(**kwargs) project_path = project.get_project_path(check_if_exists=False) self._temp_project_dirs.append(project_path) @@ -877,28 +893,32 @@ class ProjectUpdatesTest(BaseTransactionTest): #return pu # Make sure scm_password doesn't show up anywhere in args or output # from project update. - scm_password = kwargs.get('scm_password', - decrypt_field(project, 'scm_password')) - if scm_password not in ('', 'ASK'): - 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)) - self.assertFalse(scm_password in pu.result_stdout, - pu.result_stdout) - self.assertFalse(scm_password in pu.result_traceback, - pu.result_traceback) + if project.credential: + scm_password = kwargs.get('scm_password', + decrypt_field(project.credential, + 'password')) + if scm_password not in ('', 'ASK'): + 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)) + 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. - scm_key_unlock = kwargs.get('scm_key_unlock', - decrypt_field(project, 'scm_key_unlock')) - if scm_key_unlock not in ('', 'ASK'): - 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) + if project.credential: + scm_key_unlock = kwargs.get('scm_key_unlock', + decrypt_field(project.credential, + 'ssh_key_unlock')) + if scm_key_unlock not in ('', 'ASK'): + 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) @@ -994,7 +1014,7 @@ class ProjectUpdatesTest(BaseTransactionTest): # 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) - if project.scm_username and project.scm_password not in ('', 'ASK'): + if 0 and project.scm_username and project.scm_password not in ('', 'ASK'): scm_username = project.scm_username should_still_fail = not (getpass.getuser() == scm_username and scm_url_parts.hostname == 'localhost' and diff --git a/awx/main/tests/tasks.py b/awx/main/tests/tasks.py index 304a1cce1d..137772c4cc 100644 --- a/awx/main/tests/tasks.py +++ b/awx/main/tests/tasks.py @@ -229,7 +229,6 @@ class RunJobTest(BaseCeleryTest): self.job = job_template.create_job(**kwargs) else: opts = { - 'name': 'test-job %s' % str(now()), 'inventory': self.inventory, 'project': self.project, 'credential': self.credential, @@ -544,7 +543,6 @@ class RunJobTest(BaseCeleryTest): self.inventory = Inventory.objects.get(pk=self.inventory.pk) self.assertFalse(self.inventory.has_active_failures) # Un-mark job as inactive (need to force update of flag) - job.name = '_'.join(job.name.split('_')[3:]) or 'undeleted job' job.active = True job.save() # Need to manually update last_job on host... @@ -720,7 +718,7 @@ class RunJobTest(BaseCeleryTest): self.assertTrue('--ask-pass' in self.run_job_args) def test_ssh_ask_password(self): - self.create_test_credential(ssh_password='ASK') + 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) diff --git a/awx/settings/local_settings.py.example b/awx/settings/local_settings.py.example index 0d765844d9..8695153868 100644 --- a/awx/settings/local_settings.py.example +++ b/awx/settings/local_settings.py.example @@ -258,7 +258,7 @@ except OSError: TEST_GIT_USERNAME = '' TEST_GIT_PASSWORD = '' TEST_GIT_KEY_DATA = TEST_SSH_KEY_DATA -TEST_GIT_PUBLIC_HTTPS = 'https://github.com/ansible/ansible-examples.git' +TEST_GIT_PUBLIC_HTTPS = 'https://github.com/ansible/ansible.github.com.git' TEST_GIT_PRIVATE_HTTPS = 'https://github.com/ansible/ansible-doc.git' TEST_GIT_PRIVATE_SSH = 'git@github.com:ansible/ansible-doc.git' @@ -271,7 +271,7 @@ TEST_HG_PRIVATE_SSH = '' TEST_SVN_USERNAME = '' TEST_SVN_PASSWORD = '' -TEST_SVN_PUBLIC_HTTPS = 'https://github.com/ansible/ansible-examples' +TEST_SVN_PUBLIC_HTTPS = 'https://github.com/ansible/ansible.github.com' TEST_SVN_PRIVATE_HTTPS = 'https://github.com/ansible/ansible-doc' # To test repo access via SSH login to localhost.