mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 23:51:09 +03:00
Implement /launch endpoint on job template
Partial implementation of: https://trello.com/c/7uXHs7ze/14-require-admin-to-be-able-to-run-a-job-without-a-job-template
This commit is contained in:
parent
e48309b6aa
commit
58b71ca9f9
@ -130,6 +130,7 @@ permission_urls = patterns('awx.api.views',
|
||||
job_template_urls = patterns('awx.api.views',
|
||||
url(r'^$', 'job_template_list'),
|
||||
url(r'^(?P<pk>[0-9]+)/$', 'job_template_detail'),
|
||||
url(r'^(?P<pk>[0-9]+)launch/$', 'job_template_launch'),
|
||||
url(r'^(?P<pk>[0-9]+)/jobs/$', 'job_template_jobs_list'),
|
||||
url(r'^(?P<pk>[0-9]+)/callback/$', 'job_template_callback'),
|
||||
url(r'^(?P<pk>[0-9]+)/schedules/$', 'job_template_schedules_list'),
|
||||
|
@ -1337,6 +1337,31 @@ class JobTemplateDetail(RetrieveUpdateDestroyAPIView):
|
||||
model = JobTemplate
|
||||
serializer_class = JobTemplateSerializer
|
||||
|
||||
class JobTemplateLaunch(GenericAPIView):
|
||||
|
||||
model = JobTemplate
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
data = {}
|
||||
data['can_start_without_user_input'] = obj.can_start_without_user_input()
|
||||
data['passwords_needed_to_start'] = obj.passwords_needed_to_start
|
||||
data['ask_variables_on_launch'] = obj.ask_variables_on_launch
|
||||
return Response(data)
|
||||
|
||||
def post(self, request, *args, **obj):
|
||||
obj = self.get_object()
|
||||
if not request.user.can_access(self.model, 'start', obj):
|
||||
raise PermissionDenied()
|
||||
new_job = obj.create_unified_job()
|
||||
result = new_job.signal_start(**request.DATA)
|
||||
if not result:
|
||||
data = dict(passwords_needed_to_start=obj.passwords_needed_to_start)
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
data = dict(job=new_job.id)
|
||||
return Response(data, status=status.HTTP_202_ACCEPTED)
|
||||
|
||||
class JobTemplateSchedulesList(SubListCreateAPIView):
|
||||
|
||||
view_name = "Job Template Schedules"
|
||||
|
@ -920,6 +920,26 @@ class JobTemplateAccess(BaseAccess):
|
||||
|
||||
return True
|
||||
|
||||
def can_start(self, obj):
|
||||
reader = TaskSerializer()
|
||||
validation_info = reader.from_file()
|
||||
|
||||
if 'test' in sys.argv or 'jenkins' in sys.argv:
|
||||
validation_info['free_instances'] = 99999999
|
||||
validation_info['time_remaining'] = 99999999
|
||||
validation_info['grace_period_remaining'] = 99999999
|
||||
|
||||
if validation_info.get('time_remaining', None) is None:
|
||||
raise PermissionDenied("license is missing")
|
||||
if validation_info.get("grace_period_remaining") <= 0:
|
||||
raise PermissionDenied("license has expired")
|
||||
if validation_info.get('free_instances', 0) < 0:
|
||||
raise PermissionDenied("Host Count exceeds available instances")
|
||||
|
||||
dep_access = self.user.can_access(Inventory, 'read', obj.inventory) and \
|
||||
self.user.can_access(Project, 'read', obj.project)
|
||||
return self.can_read(obj) and dep_access
|
||||
|
||||
def can_change(self, obj, data):
|
||||
return self.can_read(obj) and self.can_add(data)
|
||||
|
||||
|
@ -189,6 +189,18 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
|
||||
needed.append(pw)
|
||||
return bool(self.credential and not len(needed))
|
||||
|
||||
@property
|
||||
def passwords_needed_to_start(self):
|
||||
'''Return list of password field names needed to start the job.'''
|
||||
needed = []
|
||||
if self.credential:
|
||||
for pw in self.credential.passwords_needed:
|
||||
if pw == 'password':
|
||||
needed.append('ssh_password')
|
||||
else:
|
||||
needed.append(pw)
|
||||
return needed
|
||||
|
||||
def _can_update(self):
|
||||
return self.can_start_without_user_input()
|
||||
|
||||
|
@ -302,7 +302,7 @@ class BaseTestMixin(object):
|
||||
self.assertFalse(response.content)
|
||||
#if return_response_object:
|
||||
# return response
|
||||
if response.status_code not in [ 202, 204, 405 ] and method_name != 'head' and response.content:
|
||||
if response.status_code not in [ 204, 405 ] and method_name != 'head' and response.content:
|
||||
# no JSON responses in these at least for now, 409 should probably return some (FIXME)
|
||||
if response['Content-Type'].startswith('application/json'):
|
||||
obj = json.loads(response.content)
|
||||
|
@ -681,6 +681,32 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase):
|
||||
|
||||
# FIXME: Check other credentials and optional fields.
|
||||
|
||||
def test_launch_job_template(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],
|
||||
)
|
||||
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)
|
||||
|
||||
launch_url = reverse('api:job_template_launch',
|
||||
args=(response['id'],))
|
||||
|
||||
# Invalid auth can't trigger the launch endpoint
|
||||
self.check_invalid_auth(launch_url, {}, methods=('post',))
|
||||
|
||||
with self.current_user(self.user_sue):
|
||||
response = self.post(launch_url, {}, expect=202)
|
||||
j = Job.objects.get(pk=response['job'])
|
||||
self.assertTrue(j.status == 'new')
|
||||
|
||||
class JobTest(BaseJobTestMixin, django.test.TestCase):
|
||||
|
||||
def test_get_job_list(self):
|
||||
|
Loading…
Reference in New Issue
Block a user