mirror of
https://github.com/ZwareBear/awx.git
synced 2026-03-20 07:43:35 -05:00
Merge pull request #3935 from jladdjr/at-3532-typo
Minor typo fix (pointing workflow node template at unsupported unified job template)
This commit is contained in:
@@ -1918,7 +1918,7 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer):
|
|||||||
raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")})
|
raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")})
|
||||||
|
|
||||||
if survey_enabled and job_type == PERM_INVENTORY_SCAN:
|
if survey_enabled and job_type == PERM_INVENTORY_SCAN:
|
||||||
raise serializers.ValidationError({'survey_enabled': _('Survey Enabled can not be used with scan jobs.')})
|
raise serializers.ValidationError({'survey_enabled': _('Survey Enabled cannot be used with scan jobs.')})
|
||||||
|
|
||||||
return super(JobTemplateSerializer, self).validate(attrs)
|
return super(JobTemplateSerializer, self).validate(attrs)
|
||||||
|
|
||||||
@@ -2354,7 +2354,7 @@ class WorkflowJobTemplateNodeSerializer(WorkflowNodeBaseSerializer):
|
|||||||
ujt_obj = attrs.get('unified_job_template', None)
|
ujt_obj = attrs.get('unified_job_template', None)
|
||||||
if isinstance(ujt_obj, (WorkflowJobTemplate, SystemJobTemplate)):
|
if isinstance(ujt_obj, (WorkflowJobTemplate, SystemJobTemplate)):
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
"unified_job_template": _("Can not nest a %s inside a WorkflowJobTemplate") % ujt_obj.__class__.__name__})
|
"unified_job_template": _("Cannot nest a %s inside a WorkflowJobTemplate") % ujt_obj.__class__.__name__})
|
||||||
return super(WorkflowJobTemplateNodeSerializer, self).validate(attrs)
|
return super(WorkflowJobTemplateNodeSerializer, self).validate(attrs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1138,7 +1138,7 @@ class ProjectUpdateDetail(RetrieveDestroyAPIView):
|
|||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.unified_job_nodes.filter(workflow_job__status__in=ACTIVE_STATES).exists():
|
if obj.unified_job_nodes.filter(workflow_job__status__in=ACTIVE_STATES).exists():
|
||||||
raise PermissionDenied(detail=_('Can not delete job resource when associated workflow job is running.'))
|
raise PermissionDenied(detail=_('Cannot delete job resource when associated workflow job is running.'))
|
||||||
return super(ProjectUpdateDetail, self).destroy(request, *args, **kwargs)
|
return super(ProjectUpdateDetail, self).destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -2289,7 +2289,7 @@ class InventoryUpdateDetail(RetrieveDestroyAPIView):
|
|||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.unified_job_nodes.filter(workflow_job__status__in=ACTIVE_STATES).exists():
|
if obj.unified_job_nodes.filter(workflow_job__status__in=ACTIVE_STATES).exists():
|
||||||
raise PermissionDenied(detail=_('Can not delete job resource when associated workflow job is running.'))
|
raise PermissionDenied(detail=_('Cannot delete job resource when associated workflow job is running.'))
|
||||||
return super(InventoryUpdateDetail, self).destroy(request, *args, **kwargs)
|
return super(InventoryUpdateDetail, self).destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -3180,7 +3180,7 @@ class JobDetail(RetrieveUpdateDestroyAPIView):
|
|||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.unified_job_nodes.filter(workflow_job__status__in=ACTIVE_STATES).exists():
|
if obj.unified_job_nodes.filter(workflow_job__status__in=ACTIVE_STATES).exists():
|
||||||
raise PermissionDenied(detail=_('Can not delete job resource when associated workflow job is running.'))
|
raise PermissionDenied(detail=_('Cannot delete job resource when associated workflow job is running.'))
|
||||||
return super(JobDetail, self).destroy(request, *args, **kwargs)
|
return super(JobDetail, self).destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -353,17 +353,17 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
|
|||||||
def _extra_job_type_errors(self, data):
|
def _extra_job_type_errors(self, data):
|
||||||
"""
|
"""
|
||||||
Used to enforce 2 special cases around scan jobs and prompting
|
Used to enforce 2 special cases around scan jobs and prompting
|
||||||
- the inventory can not be changed on a scan job template
|
- the inventory cannot be changed on a scan job template
|
||||||
- scan jobs can not be switched to run/check type and vice versa
|
- scan jobs cannot be switched to run/check type and vice versa
|
||||||
"""
|
"""
|
||||||
errors = {}
|
errors = {}
|
||||||
if 'job_type' in data and self.ask_job_type_on_launch:
|
if 'job_type' in data and self.ask_job_type_on_launch:
|
||||||
if ((self.job_type == PERM_INVENTORY_SCAN and not data['job_type'] == PERM_INVENTORY_SCAN) or
|
if ((self.job_type == PERM_INVENTORY_SCAN and not data['job_type'] == PERM_INVENTORY_SCAN) or
|
||||||
(data['job_type'] == PERM_INVENTORY_SCAN and not self.job_type == PERM_INVENTORY_SCAN)):
|
(data['job_type'] == PERM_INVENTORY_SCAN and not self.job_type == PERM_INVENTORY_SCAN)):
|
||||||
errors['job_type'] = _('Can not override job_type to or from a scan job.')
|
errors['job_type'] = _('Cannot override job_type to or from a scan job.')
|
||||||
if (self.job_type == PERM_INVENTORY_SCAN and ('inventory' in data) and self.ask_inventory_on_launch and
|
if (self.job_type == PERM_INVENTORY_SCAN and ('inventory' in data) and self.ask_inventory_on_launch and
|
||||||
self.inventory != data['inventory']):
|
self.inventory != data['inventory']):
|
||||||
errors['inventory'] = _('Inventory can not be changed at runtime for scan jobs.')
|
errors['inventory'] = _('Inventory cannot be changed at runtime for scan jobs.')
|
||||||
return errors
|
return errors
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class WorkflowNodeBase(CreatedModifiedModel):
|
|||||||
prompts_dict = self.prompts_dict()
|
prompts_dict = self.prompts_dict()
|
||||||
if not hasattr(ujt_obj, '_ask_for_vars_dict'):
|
if not hasattr(ujt_obj, '_ask_for_vars_dict'):
|
||||||
if prompts_dict:
|
if prompts_dict:
|
||||||
return {'ignored': {'all': 'Can not use prompts on unified_job_template that is not type of job template'}}
|
return {'ignored': {'all': 'Cannot use prompts on unified_job_template that is not type of job template'}}
|
||||||
else:
|
else:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ def apply_roles(roles, objects, persisted):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
if not persisted:
|
if not persisted:
|
||||||
raise RuntimeError('roles can not be used when persisted=False')
|
raise RuntimeError('roles cannot be used when persisted=False')
|
||||||
|
|
||||||
for role in roles:
|
for role in roles:
|
||||||
obj_role, sep, member_role = role.partition(':')
|
obj_role, sep, member_role = role.partition(':')
|
||||||
@@ -359,7 +359,7 @@ def generate_workflow_job_template_nodes(workflow_job_template,
|
|||||||
|
|
||||||
workflow_job_template_nodes = kwargs.get('workflow_job_template_nodes', [])
|
workflow_job_template_nodes = kwargs.get('workflow_job_template_nodes', [])
|
||||||
if len(workflow_job_template_nodes) > 0 and not persisted:
|
if len(workflow_job_template_nodes) > 0 and not persisted:
|
||||||
raise RuntimeError('workflow job template nodes can not be used when persisted=False')
|
raise RuntimeError('workflow job template nodes cannot be used when persisted=False')
|
||||||
|
|
||||||
new_nodes = []
|
new_nodes = []
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ def test_rbac_stream_user_roles(activity_stream_entry, organization, org_admin,
|
|||||||
def test_stream_access_cant_change(activity_stream_entry, organization, org_admin, settings):
|
def test_stream_access_cant_change(activity_stream_entry, organization, org_admin, settings):
|
||||||
settings.ACTIVITY_STREAM_ENABLED = True
|
settings.ACTIVITY_STREAM_ENABLED = True
|
||||||
access = ActivityStreamAccess(org_admin)
|
access = ActivityStreamAccess(org_admin)
|
||||||
# These should always return false because the activity stream can not be edited
|
# These should always return false because the activity stream cannot be edited
|
||||||
assert not access.can_add(activity_stream_entry)
|
assert not access.can_add(activity_stream_entry)
|
||||||
assert not access.can_change(activity_stream_entry, {'organization': None})
|
assert not access.can_change(activity_stream_entry, {'organization': None})
|
||||||
assert not access.can_delete(activity_stream_entry)
|
assert not access.can_delete(activity_stream_entry)
|
||||||
|
|||||||
@@ -281,11 +281,11 @@ def test_job_relaunch_resource_access(job_with_links, user):
|
|||||||
job_with_links.inventory.use_role.members.add(both_user)
|
job_with_links.inventory.use_role.members.add(both_user)
|
||||||
assert both_user.can_access(Job, 'start', job_with_links)
|
assert both_user.can_access(Job, 'start', job_with_links)
|
||||||
|
|
||||||
# Confirm that a user with credential access alone can not launch
|
# Confirm that a user with credential access alone cannot launch
|
||||||
job_with_links.credential.use_role.members.add(credential_user)
|
job_with_links.credential.use_role.members.add(credential_user)
|
||||||
assert not credential_user.can_access(Job, 'start', job_with_links)
|
assert not credential_user.can_access(Job, 'start', job_with_links)
|
||||||
|
|
||||||
# Confirm that a user with inventory access alone can not launch
|
# Confirm that a user with inventory access alone cannot launch
|
||||||
job_with_links.inventory.use_role.members.add(inventory_user)
|
job_with_links.inventory.use_role.members.add(inventory_user)
|
||||||
assert not inventory_user.can_access(Job, 'start', job_with_links)
|
assert not inventory_user.can_access(Job, 'start', job_with_links)
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class TestJobTemplateCopyEdit:
|
|||||||
SHOULD be able to edit that job template, for nonsensitive changes
|
SHOULD be able to edit that job template, for nonsensitive changes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Attach credential to JT that org admin can not use
|
# Attach credential to JT that org admin cannot use
|
||||||
jt_copy_edit.credential = machine_credential
|
jt_copy_edit.credential = machine_credential
|
||||||
jt_copy_edit.save()
|
jt_copy_edit.save()
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ class TestAccessListCapabilities:
|
|||||||
assert direct_access_list[0]['role']['user_capabilities']['unattach'] == 'foobar'
|
assert direct_access_list[0]['role']['user_capabilities']['unattach'] == 'foobar'
|
||||||
|
|
||||||
def test_user_access_list_direct_access_capability(self, rando, get):
|
def test_user_access_list_direct_access_capability(self, rando, get):
|
||||||
"When a user views their own access list, they can not unattach their admin role"
|
"When a user views their own access list, they cannot unattach their admin role"
|
||||||
response = get(reverse('api:user_access_list', args=(rando.id,)), rando)
|
response = get(reverse('api:user_access_list', args=(rando.id,)), rando)
|
||||||
direct_access_list = response.data['results'][0]['summary_fields']['direct_access']
|
direct_access_list = response.data['results'][0]['summary_fields']['direct_access']
|
||||||
assert not direct_access_list[0]['role']['user_capabilities']['unattach']
|
assert not direct_access_list[0]['role']['user_capabilities']['unattach']
|
||||||
@@ -271,7 +271,7 @@ def test_user_roles_unattach_functional(organization, alice, bob, get):
|
|||||||
organization.member_role.members.add(alice)
|
organization.member_role.members.add(alice)
|
||||||
organization.member_role.members.add(bob)
|
organization.member_role.members.add(bob)
|
||||||
response = get(reverse('api:user_roles_list', args=(alice.id,)), bob)
|
response = get(reverse('api:user_roles_list', args=(alice.id,)), bob)
|
||||||
# Org members can not revoke the membership of other members
|
# Org members cannot revoke the membership of other members
|
||||||
assert not response.data['results'][0]['summary_fields']['user_capabilities']['unattach']
|
assert not response.data['results'][0]['summary_fields']['user_capabilities']['unattach']
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase):
|
|||||||
self.post(launch_url, {'credential_id': self.cred_sue.pk}, expect=403)
|
self.post(launch_url, {'credential_id': self.cred_sue.pk}, expect=403)
|
||||||
|
|
||||||
def test_no_project_fail(self):
|
def test_no_project_fail(self):
|
||||||
# Job Templates without projects can not be launched
|
# Job Templates without projects cannot be launched
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
self.data['name'] = "missing proj"
|
self.data['name'] = "missing proj"
|
||||||
response = self.post(self.url, self.data, expect=201)
|
response = self.post(self.url, self.data, expect=201)
|
||||||
@@ -170,7 +170,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase):
|
|||||||
self.post(launch_url2, {}, expect=400)
|
self.post(launch_url2, {}, expect=400)
|
||||||
|
|
||||||
def test_no_inventory_fail(self):
|
def test_no_inventory_fail(self):
|
||||||
# Job Templates without inventory can not be launched
|
# Job Templates without inventory cannot be launched
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
self.data['name'] = "missing inv"
|
self.data['name'] = "missing inv"
|
||||||
response = self.post(self.url, self.data, expect=201)
|
response = self.post(self.url, self.data, expect=201)
|
||||||
|
|||||||
@@ -486,7 +486,7 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TransactionTestCase):
|
|||||||
data['credential'] = self.cred_sue.pk
|
data['credential'] = self.cred_sue.pk
|
||||||
response = self.post(url, data, expect=402)
|
response = self.post(url, data, expect=402)
|
||||||
self.create_test_license_file(features=dict(system_tracking=True))
|
self.create_test_license_file(features=dict(system_tracking=True))
|
||||||
# Scan Jobs can not be created with survey enabled
|
# Scan Jobs cannot be created with survey enabled
|
||||||
with self.current_user(self.user_sue):
|
with self.current_user(self.user_sue):
|
||||||
data['credential'] = self.cred_sue.pk
|
data['credential'] = self.cred_sue.pk
|
||||||
data['survey_enabled'] = True
|
data['survey_enabled'] = True
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ When verifying acceptance we should ensure the following statements are true
|
|||||||
Job failures during the time period should be predictable and not catastrophic.
|
Job failures during the time period should be predictable and not catastrophic.
|
||||||
* Node downtime testing should also include recoverability testing. Killing single services and ensuring the system can
|
* Node downtime testing should also include recoverability testing. Killing single services and ensuring the system can
|
||||||
return itself to a working state
|
return itself to a working state
|
||||||
* Persistent failure should be tested by killing single services in such a way that the cluster node can not be recovered
|
* Persistent failure should be tested by killing single services in such a way that the cluster node cannot be recovered
|
||||||
and ensuring that the node is properly taken offline
|
and ensuring that the node is properly taken offline
|
||||||
* Network partitioning failures will be important also. In order to test this
|
* Network partitioning failures will be important also. In order to test this
|
||||||
- Disallow a single node from communicating with the other nodes but allow it to communicate with the database
|
- Disallow a single node from communicating with the other nodes but allow it to communicate with the database
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Independent jobs are ran in order of creation time, earliest first. Jobs with de
|
|||||||
|
|
||||||
## Task Manager Architecture
|
## Task Manager Architecture
|
||||||
|
|
||||||
The task manager has a single entry point, `Scheduler().schedule()`. The method may be called in parallel, at any time, as many times as the user wants. The `schedule()` function tries to aquire a single, global, lock using the Instance table first record in the database. If the lock can not be aquired the method returns. The failure to aquire the lock indicates that there is another instance currently running `schedule()`.
|
The task manager has a single entry point, `Scheduler().schedule()`. The method may be called in parallel, at any time, as many times as the user wants. The `schedule()` function tries to aquire a single, global, lock using the Instance table first record in the database. If the lock cannot be aquired the method returns. The failure to aquire the lock indicates that there is another instance currently running `schedule()`.
|
||||||
|
|
||||||
### Hybrid Scheduler: Periodic + Event
|
### Hybrid Scheduler: Periodic + Event
|
||||||
The `schedule()` function is ran (a) periodically by a celery task and (b) on job creation or completion. The task manager system would behave correctly if ran, exclusively, via (a) or (b). We chose to trigger `schedule()` via both mechanisms because of the nice properties I will now mention. (b) reduces the time from launch to running, resulting a better user experience. (a) is a fail-safe in case we miss code-paths, in the present and future, that change the 3 scheduling considerations for which we should call `schedule()` (i.e. adding new nodes to tower changes the capacity, obscure job error handling that fails a job)
|
The `schedule()` function is ran (a) periodically by a celery task and (b) on job creation or completion. The task manager system would behave correctly if ran, exclusively, via (a) or (b). We chose to trigger `schedule()` via both mechanisms because of the nice properties I will now mention. (b) reduces the time from launch to running, resulting a better user experience. (a) is a fail-safe in case we miss code-paths, in the present and future, that change the 3 scheduling considerations for which we should call `schedule()` (i.e. adding new nodes to tower changes the capacity, obscure job error handling that fails a job)
|
||||||
|
|||||||
Reference in New Issue
Block a user