Merge pull request #2430 from chrismeyersfsu/fix-do_not_touch_my_iso

deny topology changes to iso instances via api
This commit is contained in:
Chris Meyers 2018-07-09 11:04:19 -04:00 committed by GitHub
commit 6a610d633f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 2 deletions

View File

@ -189,9 +189,22 @@ class InstanceGroupMembershipMixin(object):
ig_obj.save()
return response
def is_valid_relation(self, parent, sub, created=False):
if sub.is_isolated():
return {'error': _('Isolated instances may not be added or removed from instances groups via the API.')}
return None
def unattach_validate(self, request):
(sub_id, res) = super(InstanceGroupMembershipMixin, self).unattach_validate(request)
if res:
return res
sub = get_object_or_400(self.model, pk=sub_id)
attach_errors = self.is_valid_relation(None, sub)
if attach_errors:
return (sub_id, Response(attach_errors, status=status.HTTP_400_BAD_REQUEST))
def unattach(self, request, *args, **kwargs):
response = super(InstanceGroupMembershipMixin, self).unattach(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)

View File

@ -120,6 +120,8 @@ class Instance(BaseModel):
def is_controller(self):
return Instance.objects.filter(rampart_groups__controller__instances=self).exists()
def is_isolated(self):
return self.rampart_groups.filter(controller__isnull=False).exists()
def refresh_capacity(self):
cpu = get_cpu_capacity()

View File

@ -2,6 +2,7 @@ import pytest
from awx.api.versioning import reverse
from awx.main.models import (
Instance,
InstanceGroup,
ProjectUpdate,
)
@ -14,6 +15,12 @@ def tower_instance_group():
return ig
@pytest.fixture
def instance():
instance = Instance.objects.create(hostname='iso')
return instance
@pytest.fixture
def instance_group(job_factory):
ig = InstanceGroup(name="east")
@ -22,9 +29,11 @@ def instance_group(job_factory):
@pytest.fixture
def isolated_instance_group(instance_group):
def isolated_instance_group(instance_group, instance):
ig = InstanceGroup(name="iso", controller=instance_group)
ig.save()
ig.instances.set([instance])
ig.save()
return ig
@ -113,3 +122,22 @@ def test_prevent_delete_iso_and_control_groups(delete, isolated_instance_group,
controller_url = reverse("api:instance_group_detail", kwargs={'pk': isolated_instance_group.controller.pk})
delete(iso_url, None, admin, expect=403)
delete(controller_url, None, admin, expect=403)
@pytest.mark.django_db
def test_prevent_isolated_instance_added_to_non_isolated_instance_group(post, admin, instance, instance_group, isolated_instance_group):
url = reverse("api:instance_group_instance_list", kwargs={'pk': instance_group.pk})
assert True is instance.is_isolated()
resp = post(url, {'associate': True, 'id': instance.id}, admin, expect=400)
assert u"Isolated instances may not be added or removed from instances groups via the API." == resp.data['error']
@pytest.mark.django_db
def test_prevent_isolated_instance_removal_from_isolated_instance_group(post, admin, instance, instance_group, isolated_instance_group):
url = reverse("api:instance_group_instance_list", kwargs={'pk': isolated_instance_group.pk})
assert True is instance.is_isolated()
resp = post(url, {'disassociate': True, 'id': instance.id}, admin, expect=400)
assert u"Isolated instances may not be added or removed from instances groups via the API." == resp.data['error']