diff --git a/awx/api/serializers.py b/awx/api/serializers.py index c774898d0d..aa63d0997e 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -4627,6 +4627,11 @@ class InstanceGroupSerializer(BaseSerializer): raise serializers.ValidationError(_('{} is not a valid hostname of an existing instance.').format(instance_name)) return value + def validate_name(self, value): + if self.instance and self.instance.name == 'tower' and value != 'tower': + raise serializers.ValidationError(_('tower instance group name may not be changed.')) + return value + def get_jobs_qs(self): # Store running jobs queryset in context, so it will be shared in ListView if 'running_jobs' not in self.context: diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py index 3dfd554f11..cd78d0de33 100644 --- a/awx/main/tests/functional/api/test_instance_group.py +++ b/awx/main/tests/functional/api/test_instance_group.py @@ -87,7 +87,7 @@ def test_delete_instance_group_jobs_running(delete, instance_group_jobs_running, @pytest.mark.django_db -def test_modify_delete_tower_instance_group_prevented(delete, options, tower_instance_group, user, patch, put): +def test_delete_rename_tower_instance_group_prevented(delete, options, tower_instance_group, instance_group, user, patch): url = reverse("api:instance_group_detail", kwargs={'pk': tower_instance_group.pk}) super_user = user('bob', True) @@ -99,6 +99,13 @@ def test_modify_delete_tower_instance_group_prevented(delete, options, tower_ins assert 'GET' in resp.data['actions'] assert 'PUT' in resp.data['actions'] + # Rename 'tower' instance group denied + patch(url, {'name': 'tower_prime'}, super_user, expect=400) + + # Rename, other instance group OK + url = reverse("api:instance_group_detail", kwargs={'pk': instance_group.pk}) + patch(url, {'name': 'foobar'}, super_user, expect=200) + @pytest.mark.django_db def test_prevent_delete_iso_and_control_groups(delete, isolated_instance_group, admin):