Changing label functions to account for new relationships

Removing unreferenced get_orphaned_labels

Forcing forks and job_slice_count to be >=0
This commit is contained in:
John Westcott IV
2022-09-15 14:09:01 -04:00
committed by Alan Rominger
parent 64dad61b29
commit b501b30db4
3 changed files with 127 additions and 48 deletions

View File

@@ -3633,8 +3633,8 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
skip_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None) skip_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
diff_mode = serializers.BooleanField(required=False, allow_null=True, default=None) diff_mode = serializers.BooleanField(required=False, allow_null=True, default=None)
verbosity = serializers.ChoiceField(allow_null=True, required=False, default=None, choices=VERBOSITY_CHOICES) verbosity = serializers.ChoiceField(allow_null=True, required=False, default=None, choices=VERBOSITY_CHOICES)
forks = serializers.IntegerField(required=False, allow_null=True, default=None) forks = serializers.IntegerField(required=False, allow_null=True, min_value=0, default=None)
job_slice_count = serializers.IntegerField(required=False, allow_null=True, default=None) job_slice_count = serializers.IntegerField(required=False, allow_null=True, min_value=0, default=None)
timeout = serializers.IntegerField(required=False, allow_null=True, default=None) timeout = serializers.IntegerField(required=False, allow_null=True, default=None)
exclude_errors = () exclude_errors = ()
@@ -4141,8 +4141,8 @@ class JobLaunchSerializer(BaseSerializer):
verbosity = serializers.ChoiceField(required=False, choices=VERBOSITY_CHOICES, write_only=True) verbosity = serializers.ChoiceField(required=False, choices=VERBOSITY_CHOICES, write_only=True)
execution_environment = serializers.PrimaryKeyRelatedField(queryset=ExecutionEnvironment.objects.all(), required=False) execution_environment = serializers.PrimaryKeyRelatedField(queryset=ExecutionEnvironment.objects.all(), required=False)
labels = serializers.PrimaryKeyRelatedField(many=True, queryset=Label.objects.all(), required=False) labels = serializers.PrimaryKeyRelatedField(many=True, queryset=Label.objects.all(), required=False)
forks = serializers.IntegerField(required=False, write_only=True, default=1) forks = serializers.IntegerField(required=False, write_only=True, min_value=0, default=1)
job_slice_count = serializers.IntegerField(required=False, write_only=True, default=0) job_slice_count = serializers.IntegerField(required=False, write_only=True, min_value=0, default=0)
timeout = serializers.IntegerField(required=False, write_only=True, default=0) timeout = serializers.IntegerField(required=False, write_only=True, default=0)
instance_groups = serializers.PrimaryKeyRelatedField(many=True, queryset=InstanceGroup.objects.all(), required=False) instance_groups = serializers.PrimaryKeyRelatedField(many=True, queryset=InstanceGroup.objects.all(), required=False)

View File

@@ -10,6 +10,8 @@ from awx.api.versioning import reverse
from awx.main.models.base import CommonModelNameNotUnique from awx.main.models.base import CommonModelNameNotUnique
from awx.main.models.unified_jobs import UnifiedJobTemplate, UnifiedJob from awx.main.models.unified_jobs import UnifiedJobTemplate, UnifiedJob
from awx.main.models.inventory import Inventory from awx.main.models.inventory import Inventory
from awx.main.models.schedules import Schedule
from awx.main.models.workflow import WorkflowJobTemplateNode, WorkflowJobNode
__all__ = ('Label',) __all__ = ('Label',)
@@ -34,16 +36,22 @@ class Label(CommonModelNameNotUnique):
def get_absolute_url(self, request=None): def get_absolute_url(self, request=None):
return reverse('api:label_detail', kwargs={'pk': self.pk}, request=request) return reverse('api:label_detail', kwargs={'pk': self.pk}, request=request)
@staticmethod
def get_orphaned_labels():
return Label.objects.filter(organization=None, unifiedjobtemplate_labels__isnull=True, inventory_labels__isnull=True)
def is_detached(self): def is_detached(self):
return Label.objects.filter(id=self.id, unifiedjob_labels__isnull=True, unifiedjobtemplate_labels__isnull=True, inventory_labels__isnull=True).exists() return Label.objects.filter(
id=self.id,
unifiedjob_labels__isnull=True,
unifiedjobtemplate_labels__isnull=True,
inventory_labels__isnull=True,
schedule_labels__isnull=True,
workflowjobtemplatenode_labels__isnull=True,
workflowjobnode_labels=True,
).exists()
def is_candidate_for_detach(self): def is_candidate_for_detach(self):
count = UnifiedJob.objects.filter(labels__in=[self.id]).count() # Both Jobs and WFJobs
c1 = UnifiedJob.objects.filter(labels__in=[self.id]).count() count += UnifiedJobTemplate.objects.filter(labels__in=[self.id]).count() # Both JTs and WFJT
c2 = UnifiedJobTemplate.objects.filter(labels__in=[self.id]).count() count += Inventory.objects.filter(labels__in=[self.id]).count()
c3 = Inventory.objects.filter(labels__in=[self.id]).count() count += Schedule.objects.filter(labels__in=[self.id]).count()
return (c1 + c2 + c3 - 1) == 0 count += WorkflowJobTemplateNode.objects.filter(labels__in=[self.id]).count()
count += WorkflowJobNode.objects.filter(labels__in=[self.id]).count()
return (count - 1) == 0

View File

@@ -1,9 +1,15 @@
import pytest import pytest
from unittest import mock from unittest import mock
from awx.main.models.label import Label from awx.main.models import (
from awx.main.models.unified_jobs import UnifiedJobTemplate, UnifiedJob Label,
from awx.main.models.inventory import Inventory UnifiedJobTemplate,
UnifiedJob,
Inventory,
Schedule,
WorkflowJobTemplateNode,
WorkflowJobNode,
)
mock_query_set = mock.MagicMock() mock_query_set = mock.MagicMock()
@@ -14,12 +20,6 @@ mock_objects = mock.MagicMock(filter=mock.MagicMock(return_value=mock_query_set)
@pytest.mark.django_db @pytest.mark.django_db
@mock.patch('awx.main.models.label.Label.objects', mock_objects) @mock.patch('awx.main.models.label.Label.objects', mock_objects)
class TestLabelFilterMocked: class TestLabelFilterMocked:
def test_get_orphaned_labels(self, mocker):
ret = Label.get_orphaned_labels()
assert mock_query_set == ret
Label.objects.filter.assert_called_with(organization=None, unifiedjobtemplate_labels__isnull=True, inventory_labels__isnull=True)
def test_is_detached(self, mocker): def test_is_detached(self, mocker):
mock_query_set.exists.return_value = True mock_query_set.exists.return_value = True
@@ -27,7 +27,15 @@ class TestLabelFilterMocked:
ret = label.is_detached() ret = label.is_detached()
assert ret is True assert ret is True
Label.objects.filter.assert_called_with(id=37, unifiedjob_labels__isnull=True, unifiedjobtemplate_labels__isnull=True, inventory_labels__isnull=True) Label.objects.filter.assert_called_with(
id=37,
unifiedjob_labels__isnull=True,
unifiedjobtemplate_labels__isnull=True,
inventory_labels__isnull=True,
schedule_labels__isnull=True,
workflowjobtemplatenode_labels__isnull=True,
workflowjobnode_labels=True,
)
mock_query_set.exists.assert_called_with() mock_query_set.exists.assert_called_with()
def test_is_detached_not(self, mocker): def test_is_detached_not(self, mocker):
@@ -37,39 +45,102 @@ class TestLabelFilterMocked:
ret = label.is_detached() ret = label.is_detached()
assert ret is False assert ret is False
Label.objects.filter.assert_called_with(id=37, unifiedjob_labels__isnull=True, unifiedjobtemplate_labels__isnull=True, inventory_labels__isnull=True) Label.objects.filter.assert_called_with(
id=37,
unifiedjob_labels__isnull=True,
unifiedjobtemplate_labels__isnull=True,
inventory_labels__isnull=True,
schedule_labels__isnull=True,
workflowjobtemplatenode_labels__isnull=True,
workflowjobnode_labels=True,
)
mock_query_set.exists.assert_called_with() mock_query_set.exists.assert_called_with()
@pytest.mark.parametrize( @pytest.mark.parametrize(
"jt_count,j_count,inv_count,expected", "jt_count,j_count,inv_count,sched_count,wfnode_count,wfnodej_count,expected",
[ [
(1, 0, 0, True), (1, 0, 0, 0, 0, 0, True),
(0, 1, 0, True), (0, 1, 0, 0, 0, 0, True),
(0, 0, 1, True), (1, 1, 0, 0, 0, 0, False),
(1, 1, 1, False), (0, 0, 1, 0, 0, 0, True),
(1, 0, 1, 0, 0, 0, False),
(0, 1, 1, 0, 0, 0, False),
(1, 1, 1, 0, 0, 0, False),
(0, 0, 0, 1, 0, 0, True),
(1, 0, 0, 1, 0, 0, False),
(0, 1, 0, 1, 0, 0, False),
(1, 1, 0, 1, 0, 0, False),
(0, 0, 1, 1, 0, 0, False),
(1, 0, 1, 1, 0, 0, False),
(0, 1, 1, 1, 0, 0, False),
(1, 1, 1, 1, 0, 0, False),
(0, 0, 0, 0, 1, 0, True),
(1, 0, 0, 0, 1, 0, False),
(0, 1, 0, 0, 1, 0, False),
(1, 1, 0, 0, 1, 0, False),
(0, 0, 1, 0, 1, 0, False),
(1, 0, 1, 0, 1, 0, False),
(0, 1, 1, 0, 1, 0, False),
(1, 1, 1, 0, 1, 0, False),
(0, 0, 0, 1, 1, 0, False),
(1, 0, 0, 1, 1, 0, False),
(0, 1, 0, 1, 1, 0, False),
(1, 1, 0, 1, 1, 0, False),
(0, 0, 1, 1, 1, 0, False),
(1, 0, 1, 1, 1, 0, False),
(0, 1, 1, 1, 1, 0, False),
(1, 1, 1, 1, 1, 0, False),
(0, 0, 0, 0, 0, 1, True),
(1, 0, 0, 0, 0, 1, False),
(0, 1, 0, 0, 0, 1, False),
(1, 1, 0, 0, 0, 1, False),
(0, 0, 1, 0, 0, 1, False),
(1, 0, 1, 0, 0, 1, False),
(0, 1, 1, 0, 0, 1, False),
(1, 1, 1, 0, 0, 1, False),
(0, 0, 0, 1, 0, 1, False),
(1, 0, 0, 1, 0, 1, False),
(0, 1, 0, 1, 0, 1, False),
(1, 1, 0, 1, 0, 1, False),
(0, 0, 1, 1, 0, 1, False),
(1, 0, 1, 1, 0, 1, False),
(0, 1, 1, 1, 0, 1, False),
(1, 1, 1, 1, 0, 1, False),
(0, 0, 0, 0, 1, 1, False),
(1, 0, 0, 0, 1, 1, False),
(0, 1, 0, 0, 1, 1, False),
(1, 1, 0, 0, 1, 1, False),
(0, 0, 1, 0, 1, 1, False),
(1, 0, 1, 0, 1, 1, False),
(0, 1, 1, 0, 1, 1, False),
(1, 1, 1, 0, 1, 1, False),
(0, 0, 0, 1, 1, 1, False),
(1, 0, 0, 1, 1, 1, False),
(0, 1, 0, 1, 1, 1, False),
(1, 1, 0, 1, 1, 1, False),
(0, 0, 1, 1, 1, 1, False),
(1, 0, 1, 1, 1, 1, False),
(0, 1, 1, 1, 1, 1, False),
(1, 1, 1, 1, 1, 1, False),
], ],
) )
def test_is_candidate_for_detach(self, mocker, jt_count, j_count, inv_count, expected): def test_is_candidate_for_detach(self, mocker, jt_count, j_count, inv_count, sched_count, wfnode_count, wfnodej_count, expected):
mock_job_qs = mocker.MagicMock() counts = [jt_count, j_count, inv_count, sched_count, wfnode_count, wfnodej_count]
mock_job_qs.count = mocker.MagicMock(return_value=j_count) models = [UnifiedJobTemplate, UnifiedJob, Inventory, Schedule, WorkflowJobTemplateNode, WorkflowJobNode]
mocker.patch.object(UnifiedJob, 'objects', mocker.MagicMock(filter=mocker.MagicMock(return_value=mock_job_qs))) mockers = []
for index in range(0, len(models)):
mock_jt_qs = mocker.MagicMock() a_mocker = mocker.MagicMock()
mock_jt_qs.count = mocker.MagicMock(return_value=jt_count) a_mocker.count = mocker.MagicMock(return_value=counts[index])
mocker.patch.object(UnifiedJobTemplate, 'objects', mocker.MagicMock(filter=mocker.MagicMock(return_value=mock_jt_qs))) mocker.patch.object(models[index], 'objects', mocker.MagicMock(filter=mocker.MagicMock(return_value=a_mocker)))
mockers.append(a_mocker)
mock_inv_qs = mocker.MagicMock()
mock_inv_qs.count = mocker.MagicMock(return_value=inv_count)
mocker.patch.object(Inventory, 'objects', mocker.MagicMock(filter=mocker.MagicMock(return_value=mock_inv_qs)))
label = Label(id=37) label = Label(id=37)
ret = label.is_candidate_for_detach() ret = label.is_candidate_for_detach()
UnifiedJob.objects.filter.assert_called_with(labels__in=[label.id]) for index in range(0, len(models)):
UnifiedJobTemplate.objects.filter.assert_called_with(labels__in=[label.id]) models[index].objects.filter.assert_called_with(labels__in=[label.id])
Inventory.objects.filter.assert_called_with(labels__in=[label.id]) for index in range(0, len(mockers)):
mock_job_qs.count.assert_called_with() mockers[index].count.assert_called_with()
mock_jt_qs.count.assert_called_with()
mock_inv_qs.count.assert_called_with()
assert ret is expected assert ret is expected