mirror of
https://github.com/ansible/awx.git
synced 2026-05-17 06:17:36 -02:30
AC-132 Added API support to prompt for SCM passwords when updating a project.
This commit is contained in:
@@ -633,11 +633,13 @@ class Project(CommonModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def needs_scm_password(self):
|
def needs_scm_password(self):
|
||||||
return not self.scm_key_data and self.ssh_password == 'ASK'
|
return self.scm_type and not self.scm_key_data and \
|
||||||
|
self.scm_password == 'ASK'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def needs_scm_key_unlock(self):
|
def needs_scm_key_unlock(self):
|
||||||
return 'ENCRYPTED' in self.scm_key_data and \
|
return self.scm_type and self.scm_key_data and \
|
||||||
|
'ENCRYPTED' in self.scm_key_data and \
|
||||||
(not self.scm_key_unlock or self.scm_key_unlock == 'ASK')
|
(not self.scm_key_unlock or self.scm_key_unlock == 'ASK')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -648,10 +650,14 @@ class Project(CommonModel):
|
|||||||
needed.append(field)
|
needed.append(field)
|
||||||
return needed
|
return needed
|
||||||
|
|
||||||
def update(self):
|
def update(self, **kwargs):
|
||||||
if self.scm_type:
|
if self.scm_type:
|
||||||
|
needed = self.scm_passwords_needed
|
||||||
|
opts = dict([(field, kwargs.get(field, '')) for field in needed])
|
||||||
|
if not all(opts.values()):
|
||||||
|
return
|
||||||
project_update = self.project_updates.create()
|
project_update = self.project_updates.create()
|
||||||
project_update.start()
|
project_update.start(**opts)
|
||||||
return project_update
|
return project_update
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -794,6 +800,10 @@ class ProjectUpdate(PrimordialModel):
|
|||||||
except TaskMeta.DoesNotExist:
|
except TaskMeta.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_passwords_needed_to_start(self):
|
||||||
|
'''Return list of password field names needed to start the job.'''
|
||||||
|
return (self.credential and self.credential.passwords_needed) or []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_start(self):
|
def can_start(self):
|
||||||
return bool(self.status == 'new')
|
return bool(self.status == 'new')
|
||||||
@@ -802,9 +812,13 @@ class ProjectUpdate(PrimordialModel):
|
|||||||
from awx.main.tasks import RunProjectUpdate
|
from awx.main.tasks import RunProjectUpdate
|
||||||
if not self.can_start:
|
if not self.can_start:
|
||||||
return False
|
return False
|
||||||
|
needed = self.project.scm_passwords_needed
|
||||||
|
opts = dict([(field, kwargs.get(field, '')) for field in needed])
|
||||||
|
if not all(opts.values()):
|
||||||
|
return False
|
||||||
self.status = 'pending'
|
self.status = 'pending'
|
||||||
self.save(update_fields=['status'])
|
self.save(update_fields=['status'])
|
||||||
task_result = RunProjectUpdate().delay(self.pk, **kwargs)
|
task_result = RunProjectUpdate().delay(self.pk, **opts)
|
||||||
# Reload project update from database so we don't clobber results
|
# Reload project update from database so we don't clobber results
|
||||||
# from RunProjectUpdate (mainly from tests when using Django 1.4.x).
|
# from RunProjectUpdate (mainly from tests when using Django 1.4.x).
|
||||||
project_update = ProjectUpdate.objects.get(pk=self.pk)
|
project_update = ProjectUpdate.objects.get(pk=self.pk)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class BaseTask(Task):
|
|||||||
expect_passwords = {}
|
expect_passwords = {}
|
||||||
for n, item in enumerate(self.get_password_prompts().items()):
|
for n, item in enumerate(self.get_password_prompts().items()):
|
||||||
expect_list.append(item[0])
|
expect_list.append(item[0])
|
||||||
expect_passwords[n] = passwords.get(item[1], '')
|
expect_passwords[n] = passwords.get(item[1], '') or ''
|
||||||
expect_list.extend([pexpect.TIMEOUT, pexpect.EOF])
|
expect_list.extend([pexpect.TIMEOUT, pexpect.EOF])
|
||||||
while child.isalive():
|
while child.isalive():
|
||||||
result_id = child.expect(expect_list, timeout=2)
|
result_id = child.expect(expect_list, timeout=2)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from django.test.utils import override_settings
|
|||||||
# AWX
|
# AWX
|
||||||
from awx.main.models import *
|
from awx.main.models import *
|
||||||
from awx.main.tests.base import BaseTest, BaseTransactionTest
|
from awx.main.tests.base import BaseTest, BaseTransactionTest
|
||||||
|
from awx.main.tests.tasks import TEST_SSH_KEY_DATA_LOCKED, TEST_SSH_KEY_DATA_UNLOCK
|
||||||
|
|
||||||
TEST_PLAYBOOK = '''- hosts: mygroup
|
TEST_PLAYBOOK = '''- hosts: mygroup
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
@@ -621,7 +622,6 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(ProjectUpdatesTest, self).setUp()
|
super(ProjectUpdatesTest, self).setUp()
|
||||||
self.setup_users()
|
self.setup_users()
|
||||||
#self.skipTest('blah')
|
|
||||||
|
|
||||||
def create_project(self, **kwargs):
|
def create_project(self, **kwargs):
|
||||||
project = Project.objects.create(**kwargs)
|
project = Project.objects.create(**kwargs)
|
||||||
@@ -629,17 +629,10 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
|||||||
self._temp_project_dirs.append(project_path)
|
self._temp_project_dirs.append(project_path)
|
||||||
return project
|
return project
|
||||||
|
|
||||||
def update_url_auth(self, url, username=None, password=None):
|
def check_project_update(self, project, should_fail=False, **kwargs):
|
||||||
parts = urlparse.urlsplit(url)
|
pu = project.update(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def check_project_update(self, project, should_fail=False):
|
|
||||||
#print project.local_path
|
|
||||||
pu = project.update()
|
|
||||||
self.assertTrue(pu)
|
self.assertTrue(pu)
|
||||||
pu = ProjectUpdate.objects.get(pk=pu.pk)
|
pu = ProjectUpdate.objects.get(pk=pu.pk)
|
||||||
#print pu.status
|
|
||||||
#print pu.result_traceback
|
|
||||||
if should_fail:
|
if should_fail:
|
||||||
self.assertEqual(pu.status, 'failed', pu.result_stdout)
|
self.assertEqual(pu.status, 'failed', pu.result_stdout)
|
||||||
else:
|
else:
|
||||||
@@ -647,9 +640,6 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
|||||||
project = Project.objects.get(pk=project.pk)
|
project = Project.objects.get(pk=project.pk)
|
||||||
self.assertEqual(project.last_update, pu)
|
self.assertEqual(project.last_update, pu)
|
||||||
self.assertEqual(project.last_update_failed, pu.failed)
|
self.assertEqual(project.last_update_failed, pu.failed)
|
||||||
#print pu.result_traceback
|
|
||||||
#print pu.result_stdout
|
|
||||||
#print
|
|
||||||
return pu
|
return pu
|
||||||
|
|
||||||
def change_file_in_project(self, project):
|
def change_file_in_project(self, project):
|
||||||
@@ -838,3 +828,46 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
|||||||
scm_password=scm_password,
|
scm_password=scm_password,
|
||||||
)
|
)
|
||||||
self.check_project_scm(project)
|
self.check_project_scm(project)
|
||||||
|
|
||||||
|
def test_prompt_for_scm_password_on_update(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,
|
||||||
|
scm_username='nobody',
|
||||||
|
scm_password='ASK',
|
||||||
|
)
|
||||||
|
url = reverse('main: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'])
|
||||||
|
self.assertTrue('scm_password' in response['passwords_needed_to_update'])
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
response = self.post(url, {}, expect=400)
|
||||||
|
self.assertTrue('scm_password' in response['passwords_needed_to_update'])
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
response = self.post(url, {'scm_password': 'blah'}, expect=202)
|
||||||
|
|
||||||
|
def test_prompt_for_scm_key_unlock_on_update(self):
|
||||||
|
scm_url = 'git@github.com:ansible/ansible.github.com.git'
|
||||||
|
project = self.create_project(
|
||||||
|
name='my public git project over https',
|
||||||
|
scm_type='git',
|
||||||
|
scm_url=scm_url,
|
||||||
|
scm_key_data=TEST_SSH_KEY_DATA_LOCKED,
|
||||||
|
scm_key_unlock='ASK',
|
||||||
|
)
|
||||||
|
url = reverse('main: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'])
|
||||||
|
self.assertTrue('scm_key_unlock' in response['passwords_needed_to_update'])
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
response = self.post(url, {}, expect=400)
|
||||||
|
self.assertTrue('scm_key_unlock' in response['passwords_needed_to_update'])
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
response = self.post(url, {'scm_key_unlock': TEST_SSH_KEY_DATA_UNLOCK}, expect=202)
|
||||||
|
|||||||
@@ -270,16 +270,16 @@ class ProjectUpdateView(GenericAPIView):
|
|||||||
data = dict(
|
data = dict(
|
||||||
can_update=bool(obj.scm_type),
|
can_update=bool(obj.scm_type),
|
||||||
)
|
)
|
||||||
#if obj.scm_type:
|
if obj.scm_type:
|
||||||
# data['passwords_needed_to_update'] = obj.get_passwords_needed_to_start()
|
data['passwords_needed_to_update'] = obj.scm_passwords_needed
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if bool(obj.scm_type):
|
if bool(obj.scm_type):
|
||||||
project_update = obj.update()
|
project_update = obj.update(**request.DATA)
|
||||||
if not project_update:
|
if not project_update:
|
||||||
data = dict(msg='Unable to update project!')
|
data = dict(passwords_needed_to_update=obj.scm_passwords_needed)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
return Response(status=status.HTTP_202_ACCEPTED)
|
return Response(status=status.HTTP_202_ACCEPTED)
|
||||||
|
|||||||
Reference in New Issue
Block a user