1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-31 06:51:10 +03:00

Add a configurable limit for job forks

This commit is contained in:
Jake McDermott 2020-01-07 10:12:47 -05:00
parent bf3042e85a
commit 0d98a1980e
No known key found for this signature in database
GPG Key ID: 0E56ED990CDFCB4F
6 changed files with 44 additions and 3 deletions

View File

@ -615,6 +615,17 @@ register(
category=_('Jobs'), category=_('Jobs'),
category_slug='jobs', category_slug='jobs',
) )
register(
'MAX_FORKS',
field_class=fields.IntegerField,
allow_null=False,
default=0,
label=_('Maximum number of forks per job.'),
help_text=_('Saving a Job Template with more than this number of forks will result in an error. '
'When set to 0 (the default), no limit is applied.'),
category=_('Jobs'),
category_slug='jobs',
)
register( register(
'LOG_AGGREGATOR_HOST', 'LOG_AGGREGATOR_HOST',

View File

@ -13,6 +13,7 @@ from urllib.parse import urljoin
# Django # Django
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
#from django.core.cache import cache #from django.core.cache import cache
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
@ -293,6 +294,11 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
def resources_needed_to_start(self): def resources_needed_to_start(self):
return [fd for fd in ['project', 'inventory'] if not getattr(self, '{}_id'.format(fd))] return [fd for fd in ['project', 'inventory'] if not getattr(self, '{}_id'.format(fd))]
def clean_forks(self):
if settings.MAX_FORKS > 0 and self.forks > settings.MAX_FORKS:
raise ValidationError(_(f'Maximum number of forks ({settings.MAX_FORKS}) exceeded.'))
return self.forks
def create_job(self, **kwargs): def create_job(self, **kwargs):
''' '''
Create a new job based on this template. Create a new job based on this template.

View File

@ -1656,8 +1656,12 @@ class RunJob(BaseTask):
args.append('--vault-id') args.append('--vault-id')
args.append('{}@prompt'.format(vault_id)) args.append('{}@prompt'.format(vault_id))
if job.forks: # FIXME: Max limit? if job.forks:
args.append('--forks=%d' % job.forks) if settings.MAX_FORKS > 0 and job.forks > settings.MAX_FORKS:
logger.warning(f'Maximum number of forks ({settings.MAX_FORKS}) exceeded.')
args.append('--forks=%d' % settings.MAX_FORKS)
else:
args.append('--forks=%d' % job.forks)
if job.force_handlers: if job.force_handlers:
args.append('--force-handlers') args.append('--force-handlers')
if job.limit: if job.limit:

View File

@ -118,6 +118,22 @@ def test_extra_credential_unique_type_xfail(get, post, organization_factory, job
assert response.data.get('count') == 1 assert response.data.get('count') == 1
@pytest.mark.django_db
def test_create_with_forks_exceeding_maximum_xfail(alice, post, project, inventory, settings):
project.use_role.members.add(alice)
inventory.use_role.members.add(alice)
settings.MAX_FORKS = 10
response = post(reverse('api:job_template_list'), {
'name': 'Some name',
'project': project.id,
'inventory': inventory.id,
'playbook': 'helloworld.yml',
'forks': 11,
}, alice)
assert response.status_code == 400
assert 'Maximum number of forks (10) exceeded' in str(response.data)
@pytest.mark.django_db @pytest.mark.django_db
def test_attach_extra_credential(get, post, organization_factory, job_template_factory, credential): def test_attach_extra_credential(get, post, organization_factory, job_template_factory, credential):
objs = organization_factory("org", superusers=['admin']) objs = organization_factory("org", superusers=['admin'])

View File

@ -58,6 +58,10 @@ export default ['i18n', function(i18n) {
type: 'text', type: 'text',
reset: 'ANSIBLE_FACT_CACHE_TIMEOUT', reset: 'ANSIBLE_FACT_CACHE_TIMEOUT',
}, },
MAX_FORKS: {
type: 'text',
reset: 'MAX_FORKS',
},
PROJECT_UPDATE_VVV: { PROJECT_UPDATE_VVV: {
type: 'toggleSwitch', type: 'toggleSwitch',
}, },

View File

@ -233,7 +233,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
addApiErrors(form.fields[field], field); addApiErrors(form.fields[field], field);
} }
} }
if (defaultMsg) { if (!fieldErrors && defaultMsg) {
Alert(defaultMsg.hdr, defaultMsg.msg); Alert(defaultMsg.hdr, defaultMsg.msg);
} }
} else if (typeof data === 'object' && data !== null) { } else if (typeof data === 'object' && data !== null) {