mirror of
https://github.com/ansible/awx.git
synced 2026-03-15 07:57:29 -02:30
AC-132 More updates after testing various SCM username/password/key options.
This commit is contained in:
@@ -7,6 +7,7 @@ import distutils.version
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
@@ -64,8 +65,10 @@ class BaseTask(Task):
|
|||||||
Create a temporary file containing the SSH private key.
|
Create a temporary file containing the SSH private key.
|
||||||
'''
|
'''
|
||||||
ssh_key_data = ''
|
ssh_key_data = ''
|
||||||
if hasattr(instance, 'scm_key_data'):
|
if hasattr(instance, 'project'):
|
||||||
ssh_key_data = decrypt_field(instance, 'scm_key_data')
|
project = instance.project
|
||||||
|
if hasattr(project, 'scm_key_data'):
|
||||||
|
ssh_key_data = decrypt_field(project, 'scm_key_data')
|
||||||
elif hasattr(instance, 'credential'):
|
elif hasattr(instance, 'credential'):
|
||||||
credential = instance.credential
|
credential = instance.credential
|
||||||
if hasattr(credential, 'ssh_key_data'):
|
if hasattr(credential, 'ssh_key_data'):
|
||||||
@@ -82,9 +85,13 @@ class BaseTask(Task):
|
|||||||
|
|
||||||
def build_passwords(self, instance, **kwargs):
|
def build_passwords(self, instance, **kwargs):
|
||||||
'''
|
'''
|
||||||
Build a dictionary of passwords responding to prompts.
|
Build a dictionary of passwords for responding to prompts.
|
||||||
'''
|
'''
|
||||||
return {}
|
return {
|
||||||
|
'yes': 'yes',
|
||||||
|
'no': 'no',
|
||||||
|
'': '',
|
||||||
|
}
|
||||||
|
|
||||||
def build_env(self, instance, **kwargs):
|
def build_env(self, instance, **kwargs):
|
||||||
'''
|
'''
|
||||||
@@ -222,7 +229,7 @@ class RunJob(BaseTask):
|
|||||||
'''
|
'''
|
||||||
Build a dictionary of passwords for SSH private key, SSH user and sudo.
|
Build a dictionary of passwords for SSH private key, SSH user and sudo.
|
||||||
'''
|
'''
|
||||||
passwords = {}
|
passwords = super(RunJob, self).build_passwords(job, **kwargs)
|
||||||
creds = job.credential
|
creds = job.credential
|
||||||
if creds:
|
if creds:
|
||||||
for field in ('ssh_key_unlock', 'ssh_password', 'sudo_password'):
|
for field in ('ssh_key_unlock', 'ssh_password', 'sudo_password'):
|
||||||
@@ -339,13 +346,15 @@ class RunProjectUpdate(BaseTask):
|
|||||||
|
|
||||||
name = 'run_project_update'
|
name = 'run_project_update'
|
||||||
model = ProjectUpdate
|
model = ProjectUpdate
|
||||||
idle_timeout = 30
|
#idle_timeout = 30
|
||||||
|
|
||||||
def build_passwords(self, project_update, **kwargs):
|
def build_passwords(self, project_update, **kwargs):
|
||||||
'''
|
'''
|
||||||
Build a dictionary of passwords for SSH private key.
|
Build a dictionary of passwords for SSH private key unlock and SCM
|
||||||
|
username/password.
|
||||||
'''
|
'''
|
||||||
passwords = {}
|
passwords = super(RunProjectUpdate, self).build_passwords(project_update,
|
||||||
|
**kwargs)
|
||||||
project = project_update.project
|
project = project_update.project
|
||||||
value = decrypt_field(project, 'scm_key_unlock')
|
value = decrypt_field(project, 'scm_key_unlock')
|
||||||
if value not in ('', 'ASK'):
|
if value not in ('', 'ASK'):
|
||||||
@@ -360,6 +369,7 @@ class RunProjectUpdate(BaseTask):
|
|||||||
'''
|
'''
|
||||||
env = super(RunProjectUpdate, self).build_env(project_update, **kwargs)
|
env = super(RunProjectUpdate, self).build_env(project_update, **kwargs)
|
||||||
env['ANSIBLE_ASK_SUDO_PASS'] = str(False)
|
env['ANSIBLE_ASK_SUDO_PASS'] = str(False)
|
||||||
|
env['DISPLAY'] = '' # Prevent stupid password popup when running tests.
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def update_url_auth(self, url, username=None, password=None):
|
def update_url_auth(self, url, username=None, password=None):
|
||||||
@@ -384,24 +394,32 @@ class RunProjectUpdate(BaseTask):
|
|||||||
# Since we specify -vvv and tasks use async polling, we should get some
|
# Since we specify -vvv and tasks use async polling, we should get some
|
||||||
# output regularly...
|
# output regularly...
|
||||||
args.append('-%s' % ('v' * 3))
|
args.append('-%s' % ('v' * 3))
|
||||||
|
extra_vars = {}
|
||||||
project = project_update.project
|
project = project_update.project
|
||||||
scm_url = project.scm_url
|
scm_url = project.scm_url
|
||||||
if project.scm_username and project.scm_password not in ('ASK', ''):
|
if project.scm_username and project.scm_password not in ('ASK', ''):
|
||||||
scm_url = self.update_url_auth(scm_url, project.scm_username,
|
if project.scm_type == 'svn':
|
||||||
decrypt_field(project, 'scm_password'))
|
extra_vars['scm_username'] = project.scm_username
|
||||||
|
extra_vars['scm_password'] = decrypt_field(project, 'scm_password')
|
||||||
|
else:
|
||||||
|
scm_url = self.update_url_auth(scm_url, project.scm_username,
|
||||||
|
decrypt_field(project, 'scm_password'))
|
||||||
elif project.scm_username:
|
elif project.scm_username:
|
||||||
scm_url = self.update_url_auth(scm_url, project.scm_username)
|
if project.scm_type == 'svn':
|
||||||
|
extra_vars['scm_username'] = project.scm_username
|
||||||
|
else:
|
||||||
|
scm_url = self.update_url_auth(scm_url, project.scm_username)
|
||||||
# FIXME: Need to hide password in saved job_args and result_stdout!
|
# FIXME: Need to hide password in saved job_args and result_stdout!
|
||||||
scm_branch = project.scm_branch or {'hg': 'tip'}.get(project.scm_type, 'HEAD')
|
scm_branch = project.scm_branch or {'hg': 'tip'}.get(project.scm_type, 'HEAD')
|
||||||
scm_delete_on_update = project.scm_delete_on_update or project.scm_delete_on_next_update
|
scm_delete_on_update = project.scm_delete_on_update or project.scm_delete_on_next_update
|
||||||
extra_vars = {
|
extra_vars.update({
|
||||||
'project_path': project.get_project_path(check_if_exists=False),
|
'project_path': project.get_project_path(check_if_exists=False),
|
||||||
'scm_type': project.scm_type,
|
'scm_type': project.scm_type,
|
||||||
'scm_url': scm_url,
|
'scm_url': scm_url,
|
||||||
'scm_branch': scm_branch,
|
'scm_branch': scm_branch,
|
||||||
'scm_clean': project.scm_clean,
|
'scm_clean': project.scm_clean,
|
||||||
'scm_delete_on_update': scm_delete_on_update,
|
'scm_delete_on_update': scm_delete_on_update,
|
||||||
}
|
})
|
||||||
args.extend(['-e', json.dumps(extra_vars)])
|
args.extend(['-e', json.dumps(extra_vars)])
|
||||||
args.append('project_update.yml')
|
args.append('project_update.yml')
|
||||||
|
|
||||||
@@ -418,9 +436,8 @@ class RunProjectUpdate(BaseTask):
|
|||||||
def get_password_prompts(self):
|
def get_password_prompts(self):
|
||||||
d = super(RunProjectUpdate, self).get_password_prompts()
|
d = super(RunProjectUpdate, self).get_password_prompts()
|
||||||
d.update({
|
d.update({
|
||||||
r'Username for.*:': 'scm_username',
|
# FIXME: Configure whether we should auto accept host keys?
|
||||||
r'Password for.*:': 'scm_password',
|
r'Are you sure you want to continue connecting \(yes/no\)\?': 'yes',
|
||||||
r'Are you sure you want to continue connecting (yes/no)\?': 'yes', # FIXME: Should we really do this?
|
|
||||||
})
|
})
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@@ -441,3 +458,4 @@ class RunProjectUpdate(BaseTask):
|
|||||||
Hook for actions after project_update has completed.
|
Hook for actions after project_update has completed.
|
||||||
'''
|
'''
|
||||||
# Start any jobs waiting on this update to finish.
|
# Start any jobs waiting on this update to finish.
|
||||||
|
|
||||||
|
|||||||
@@ -721,6 +721,36 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
|||||||
project.save()
|
project.save()
|
||||||
self.check_project_update(project)
|
self.check_project_update(project)
|
||||||
self.assertFalse(os.path.exists(untracked_path))
|
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).
|
||||||
|
if project.scm_username and project.scm_password not in ('', 'ASK'):
|
||||||
|
scm_username = project.scm_username
|
||||||
|
# Clear username only.
|
||||||
|
project = Project.objects.get(pk=project.pk)
|
||||||
|
project.scm_username = ''
|
||||||
|
project.save()
|
||||||
|
self.check_project_update(project, should_fail=True)
|
||||||
|
# Try invalid username.
|
||||||
|
project = Project.objects.get(pk=project.pk)
|
||||||
|
project.scm_username = 'notavalidusername'
|
||||||
|
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=True)
|
||||||
|
# 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=True)
|
||||||
|
# Set username, with invalid password.
|
||||||
|
project = Project.objects.get(pk=project.pk)
|
||||||
|
project.scm_password = 'notavalidpassword'
|
||||||
|
project.save()
|
||||||
|
self.check_project_update(project, should_fail=True)
|
||||||
|
|
||||||
def test_public_git_project_over_https(self):
|
def test_public_git_project_over_https(self):
|
||||||
scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS',
|
scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS',
|
||||||
@@ -871,3 +901,4 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
|||||||
self.assertTrue('scm_key_unlock' in response['passwords_needed_to_update'])
|
self.assertTrue('scm_key_unlock' in response['passwords_needed_to_update'])
|
||||||
with self.current_user(self.super_django_user):
|
with self.current_user(self.super_django_user):
|
||||||
response = self.post(url, {'scm_key_unlock': TEST_SSH_KEY_DATA_UNLOCK}, expect=202)
|
response = self.post(url, {'scm_key_unlock': TEST_SSH_KEY_DATA_UNLOCK}, expect=202)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
# scm_branch: HEAD
|
# scm_branch: HEAD
|
||||||
# scm_clean: true/false
|
# scm_clean: true/false
|
||||||
# scm_delete_on_update: true/false
|
# scm_delete_on_update: true/false
|
||||||
|
# scm_username: username (only for svn)
|
||||||
|
# scm_password: password (only for svn)
|
||||||
|
|
||||||
- hosts: all
|
- hosts: all
|
||||||
connection: local
|
connection: local
|
||||||
@@ -27,4 +29,9 @@
|
|||||||
|
|
||||||
- name: update project using svn
|
- name: update project using svn
|
||||||
subversion: dest={{project_path}} repo={{scm_url}} revision={{scm_branch}} force={{scm_clean}}
|
subversion: dest={{project_path}} repo={{scm_url}} revision={{scm_branch}} force={{scm_clean}}
|
||||||
when: scm_type == 'svn'
|
when: scm_type == 'svn' and not scm_username|default('')
|
||||||
|
|
||||||
|
- name: update project using svn with auth
|
||||||
|
subversion: dest={{project_path}} repo={{scm_url}} revision={{scm_branch}} force={{scm_clean}} username={{scm_username}} password={{scm_password}}
|
||||||
|
when: scm_type == 'svn' and scm_username|default('')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user