From e0c4fd4b3a3498def4f4c9c0beed9b78e98d6ffd Mon Sep 17 00:00:00 2001 From: Vismay Golwala Date: Thu, 11 Apr 2019 15:53:08 -0400 Subject: [PATCH] Disallow deleting controller or isolated instance groups Added two new properties to the InstanceGroup model - `is_controller` and `is_isolated`. Used these properties to hide the trash icon for instance groups that are either controller or isolated. Signed-off-by: Vismay Golwala --- awx/api/serializers.py | 11 +++++++++- awx/main/models/ha.py | 8 ++++++++ .../functional/api/test_instance_group.py | 20 +++++++++++++++++++ .../list/instance-groups-list.controller.js | 2 +- 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 84247c210b..0bee016055 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4881,6 +4881,15 @@ class InstanceGroupSerializer(BaseSerializer): read_only=True ) instances = serializers.SerializerMethodField() + is_controller = serializers.BooleanField( + help_text=_('Indicates whether instance group controls any other group'), + read_only=True + ) + is_isolated = serializers.BooleanField( + help_text=_('Indicates whether instances in this group are isolated.' + 'Isolated groups have a designated controller group.'), + read_only=True + ) # NOTE: help_text is duplicated from field definitions, no obvious way of # both defining field details here and also getting the field's help_text policy_instance_percentage = serializers.IntegerField( @@ -4906,7 +4915,7 @@ class InstanceGroupSerializer(BaseSerializer): fields = ("id", "type", "url", "related", "name", "created", "modified", "capacity", "committed_capacity", "consumed_capacity", "percent_capacity_remaining", "jobs_running", "jobs_total", - "instances", "controller", + "instances", "controller", "is_controller", "is_isolated", "policy_instance_percentage", "policy_instance_minimum", "policy_instance_list") def get_related(self, obj): diff --git a/awx/main/models/ha.py b/awx/main/models/ha.py index 4f9fa3b0a2..4b5eb899d7 100644 --- a/awx/main/models/ha.py +++ b/awx/main/models/ha.py @@ -208,6 +208,14 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin): def jobs_total(self): return UnifiedJob.objects.filter(instance_group=self).count() + @property + def is_controller(self): + return self.controlled_groups.exists() + + @property + def is_isolated(self): + return bool(self.controller) + ''' RelatedJobsMixin ''' diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py index 77c3997bd8..a6ea786639 100644 --- a/awx/main/tests/functional/api/test_instance_group.py +++ b/awx/main/tests/functional/api/test_instance_group.py @@ -78,6 +78,26 @@ def instance_group_jobs_successful(instance_group, create_job_factory, create_pr return jobs_successful + project_updates_successful +@pytest.mark.django_db +def test_instance_group_is_controller(instance_group, isolated_instance_group, non_iso_instance): + assert not isolated_instance_group.is_controller + assert instance_group.is_controller + + instance_group.instances.set([non_iso_instance]) + + assert instance_group.is_controller + + +@pytest.mark.django_db +def test_instance_group_is_isolated(instance_group, isolated_instance_group): + assert not instance_group.is_isolated + assert isolated_instance_group.is_isolated + + isolated_instance_group.instances = [] + + assert isolated_instance_group.is_isolated + + @pytest.mark.django_db def test_delete_instance_group_jobs(delete, instance_group_jobs_successful, instance_group, admin): url = reverse("api:instance_group_detail", kwargs={'pk': instance_group.pk}) diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js index 766f968442..25888a18f7 100644 --- a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js @@ -83,7 +83,7 @@ export default ['$scope', '$filter', '$state', 'Alert', 'resolvedModels', 'Datas vm.rowAction = { trash: instance_group => { - return vm.isSuperuser && instance_group.name !== 'tower'; + return vm.isSuperuser && instance_group.name !== 'tower' && !instance_group.is_controller && !instance_group.is_isolated; } };