Merge pull request #10929 from ansible/validate-control-only-nodes

Validate that control-only Instance nodes cannot change IG membership
This commit is contained in:
Jeff Bradberry 2021-09-01 09:24:40 -04:00 committed by GitHub
commit 81fe39f060
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 3 deletions

View File

@ -402,6 +402,11 @@ class InstanceInstanceGroupsList(InstanceGroupMembershipMixin, SubListCreateAtta
parent_model = models.Instance
relationship = 'rampart_groups'
def is_valid_relation(self, parent, sub, created=False):
if parent.node_type == 'control':
return {'msg': _(f"Cannot change instance group membership of control-only node: {parent.hostname}.")}
return None
class InstanceGroupList(ListCreateAPIView):
@ -444,6 +449,11 @@ class InstanceGroupInstanceList(InstanceGroupMembershipMixin, SubListAttachDetac
relationship = "instances"
search_fields = ('hostname',)
def is_valid_relation(self, parent, sub, created=False):
if sub.node_type == 'control':
return {'msg': _(f"Cannot change instance group membership of control-only node: {sub.hostname}.")}
return None
class ScheduleList(ListCreateAPIView):

View File

@ -68,13 +68,23 @@ class InstanceGroupMembershipMixin(object):
membership.
"""
def attach_validate(self, request):
parent = self.get_parent_object()
sub_id, res = super().attach_validate(request)
if res: # handle an error
return sub_id, res
sub = get_object_or_400(self.model, pk=sub_id)
attach_errors = self.is_valid_relation(parent, sub)
if attach_errors:
return sub_id, Response(attach_errors, status=status.HTTP_400_BAD_REQUEST)
return sub_id, res
def attach(self, request, *args, **kwargs):
response = super(InstanceGroupMembershipMixin, self).attach(request, *args, **kwargs)
sub_id, res = self.attach_validate(request)
if status.is_success(response.status_code):
if self.parent_model is Instance:
ig_obj = get_object_or_400(self.model, pk=sub_id)
inst_name = ig_obj.hostname
inst_name = self.get_parent_object().hostname
else:
inst_name = get_object_or_400(self.model, pk=sub_id).hostname
with transaction.atomic():
@ -91,11 +101,12 @@ class InstanceGroupMembershipMixin(object):
return response
def unattach_validate(self, request):
parent = self.get_parent_object()
(sub_id, res) = super(InstanceGroupMembershipMixin, self).unattach_validate(request)
if res:
return (sub_id, res)
sub = get_object_or_400(self.model, pk=sub_id)
attach_errors = self.is_valid_relation(None, sub)
attach_errors = self.is_valid_relation(parent, sub)
if attach_errors:
return (sub_id, Response(attach_errors, status=status.HTTP_400_BAD_REQUEST))
return (sub_id, res)

View File

@ -23,6 +23,14 @@ def instance():
return Instance.objects.create(hostname='instance')
@pytest.fixture
def node_type_instance():
def fn(hostname, node_type):
return Instance.objects.create(hostname=hostname, node_type=node_type)
return fn
@pytest.fixture
def instance_group(job_factory):
ig = InstanceGroup(name="east")
@ -198,3 +206,41 @@ def test_containerized_group_default_fields(instance_group, kube_credential):
assert ig.policy_instance_list == []
assert ig.policy_instance_minimum == 0
assert ig.policy_instance_percentage == 0
@pytest.mark.django_db
@pytest.mark.parametrize('node_type', ['control', 'hybrid', 'execution'])
def test_instance_attach_to_instance_group(post, instance_group, node_type_instance, admin, node_type):
instance = node_type_instance(hostname=node_type, node_type=node_type)
url = reverse(f'api:instance_group_instance_list', kwargs={'pk': instance_group.pk})
post(url, {'associate': True, 'id': instance.id}, admin, expect=204 if node_type != 'control' else 400)
@pytest.mark.django_db
@pytest.mark.parametrize('node_type', ['control', 'hybrid', 'execution'])
def test_instance_unattach_from_instance_group(post, instance_group, node_type_instance, admin, node_type):
instance = node_type_instance(hostname=node_type, node_type=node_type)
instance_group.instances.add(instance)
url = reverse(f'api:instance_group_instance_list', kwargs={'pk': instance_group.pk})
post(url, {'disassociate': True, 'id': instance.id}, admin, expect=204 if node_type != 'control' else 400)
@pytest.mark.django_db
@pytest.mark.parametrize('node_type', ['control', 'hybrid', 'execution'])
def test_instance_group_attach_to_instance(post, instance_group, node_type_instance, admin, node_type):
instance = node_type_instance(hostname=node_type, node_type=node_type)
url = reverse(f'api:instance_instance_groups_list', kwargs={'pk': instance.pk})
post(url, {'associate': True, 'id': instance_group.id}, admin, expect=204 if node_type != 'control' else 400)
@pytest.mark.django_db
@pytest.mark.parametrize('node_type', ['control', 'hybrid', 'execution'])
def test_instance_group_unattach_from_instance(post, instance_group, node_type_instance, admin, node_type):
instance = node_type_instance(hostname=node_type, node_type=node_type)
instance_group.instances.add(instance)
url = reverse(f'api:instance_instance_groups_list', kwargs={'pk': instance.pk})
post(url, {'disassociate': True, 'id': instance_group.id}, admin, expect=204 if node_type != 'control' else 400)