From fe36ed94b82d5b582404310b992a0ecb09a2ddb5 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 2 Aug 2017 13:10:55 -0400 Subject: [PATCH 1/3] Ensure hosts list properly when given read access to a smart inventory --- awx/api/views.py | 7 +++++++ awx/main/tests/unit/api/test_views.py | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/awx/api/views.py b/awx/api/views.py index 1bf21696ef..4531cbe5dc 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1959,6 +1959,13 @@ class InventoryHostsList(SubListCreateAttachDetachAPIView): parent_key = 'inventory' capabilities_prefetch = ['inventory.admin'] + def get_queryset(self): + inventory = self.get_parent_object() + if inventory.kind == 'smart': + filter_qs = SmartFilter.query_from_string(inventory.host_filter) + return filter_qs.distinct() + return super(InventoryHostsList, self).get_queryset() + class HostGroupsList(ControlledByScmMixin, SubListCreateAttachDetachAPIView): ''' the list of groups a host is directly a member of ''' diff --git a/awx/main/tests/unit/api/test_views.py b/awx/main/tests/unit/api/test_views.py index 8a0a22aa48..1d82e4a4ea 100644 --- a/awx/main/tests/unit/api/test_views.py +++ b/awx/main/tests/unit/api/test_views.py @@ -9,6 +9,7 @@ from awx.api.views import ( JobTemplateLabelList, JobTemplateSurveySpec, InventoryInventorySourcesUpdate, + InventoryHostsList, HostInsights, ) @@ -204,3 +205,15 @@ class TestHostInsights(): assert resp.data['error'] == 'The Insights Credential for "inventory_name_here" was not found.' assert resp.status_code == 404 + + +class TestInventoryHostsList(object): + + def test_host_list_smart_inventory(self, mocker): + Inventory = namedtuple('Inventory', ['kind', 'host_filter']) + obj = Inventory(kind='smart', host_filter='localhost') + with mock.patch.object(InventoryHostsList, 'get_parent_object', return_value=obj): + with mock.patch('awx.api.views.SmartFilter.query_from_string') as mock_query: + view = InventoryHostsList() + view.get_queryset() + mock_query.assert_called_once_with('localhost') From 7f75ef9ad476f158cbbc417f7f1cab20bee0b735 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 2 Aug 2017 13:11:16 -0400 Subject: [PATCH 2/3] whitespace clean up --- awx/main/tests/unit/api/test_views.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/awx/main/tests/unit/api/test_views.py b/awx/main/tests/unit/api/test_views.py index 1d82e4a4ea..824f5661d3 100644 --- a/awx/main/tests/unit/api/test_views.py +++ b/awx/main/tests/unit/api/test_views.py @@ -113,10 +113,10 @@ class TestInventoryInventorySourcesUpdate: return [InventorySource(pk=1, source=is_source, source_project=Project, update_on_project_update=is_up_on_proj, can_update=can_update, update=lambda:InventoryUpdate)] - + def exclude(self, **kwargs): return self.all() - + Inventory = namedtuple('Inventory', ['inventory_sources', 'kind']) obj = Inventory(inventory_sources=InventorySources(), kind='') @@ -158,14 +158,14 @@ class TestHostInsights(): def test_get_insights_malformed_json_content(self, patch_parent, mocker): view = HostInsights() - + class Response(): status_code = 200 content = 'booo!' def json(self): raise ValueError('we do not care what this is') - + mocker.patch.object(view, '_get_insights', return_value=Response()) (msg, code) = view.get_insights('https://myexample.com/whocares/me/', 'ignore', 'ignore') @@ -180,11 +180,11 @@ class TestHostInsights(): host = Host() host.insights_system_id = None - + mocker.patch.object(view, 'get_object', return_value=host) resp = view.get(None) - + assert resp.data['error'] == 'This host is not recognized as an Insights host.' assert resp.status_code == 404 From eb73248d37e212864c7df67a6bf4899fe69a4030 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 2 Aug 2017 15:13:27 -0400 Subject: [PATCH 3/3] Refactor InventoryHostsList get_queryset to use getattrd --- awx/api/views.py | 5 +---- awx/main/tests/unit/api/test_views.py | 10 +++++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/awx/api/views.py b/awx/api/views.py index 4531cbe5dc..734011f3ef 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1961,10 +1961,7 @@ class InventoryHostsList(SubListCreateAttachDetachAPIView): def get_queryset(self): inventory = self.get_parent_object() - if inventory.kind == 'smart': - filter_qs = SmartFilter.query_from_string(inventory.host_filter) - return filter_qs.distinct() - return super(InventoryHostsList, self).get_queryset() + return getattrd(inventory, self.relationship).all() class HostGroupsList(ControlledByScmMixin, SubListCreateAttachDetachAPIView): diff --git a/awx/main/tests/unit/api/test_views.py b/awx/main/tests/unit/api/test_views.py index 824f5661d3..8d7ccdfc59 100644 --- a/awx/main/tests/unit/api/test_views.py +++ b/awx/main/tests/unit/api/test_views.py @@ -17,6 +17,8 @@ from awx.main.models import ( Host, ) +from awx.main.managers import HostManager + @pytest.fixture def mock_response_new(mocker): @@ -210,10 +212,12 @@ class TestHostInsights(): class TestInventoryHostsList(object): def test_host_list_smart_inventory(self, mocker): - Inventory = namedtuple('Inventory', ['kind', 'host_filter']) - obj = Inventory(kind='smart', host_filter='localhost') + Inventory = namedtuple('Inventory', ['kind', 'host_filter', 'hosts']) + obj = Inventory(kind='smart', host_filter='localhost', hosts=HostManager()) + obj.hosts.instance = obj + with mock.patch.object(InventoryHostsList, 'get_parent_object', return_value=obj): - with mock.patch('awx.api.views.SmartFilter.query_from_string') as mock_query: + with mock.patch('awx.main.utils.filters.SmartFilter.query_from_string') as mock_query: view = InventoryHostsList() view.get_queryset() mock_query.assert_called_once_with('localhost')