mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
Merge pull request #6246 from wwitzel3/issue-5756
Create /inventories/N/update_inventory_sources endpoint.
This commit is contained in:
@@ -16,8 +16,8 @@ from awx.main.utils import get_object_or_400
|
|||||||
logger = logging.getLogger('awx.api.permissions')
|
logger = logging.getLogger('awx.api.permissions')
|
||||||
|
|
||||||
__all__ = ['ModelAccessPermission', 'JobTemplateCallbackPermission',
|
__all__ = ['ModelAccessPermission', 'JobTemplateCallbackPermission',
|
||||||
'TaskPermission', 'ProjectUpdatePermission', 'UserPermission',
|
'TaskPermission', 'ProjectUpdatePermission', 'InventoryInventorySourcesUpdatePermission',
|
||||||
'IsSuperUser']
|
'UserPermission', 'IsSuperUser']
|
||||||
|
|
||||||
|
|
||||||
class ModelAccessPermission(permissions.BasePermission):
|
class ModelAccessPermission(permissions.BasePermission):
|
||||||
@@ -200,6 +200,12 @@ class ProjectUpdatePermission(ModelAccessPermission):
|
|||||||
return check_user_access(request.user, view.model, 'start', project)
|
return check_user_access(request.user, view.model, 'start', project)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryInventorySourcesUpdatePermission(ModelAccessPermission):
|
||||||
|
def check_post_permissions(self, request, view, obj=None):
|
||||||
|
inventory = get_object_or_400(view.model, pk=view.kwargs['pk'])
|
||||||
|
return check_user_access(request.user, view.model, 'update', inventory)
|
||||||
|
|
||||||
|
|
||||||
class UserPermission(ModelAccessPermission):
|
class UserPermission(ModelAccessPermission):
|
||||||
def check_post_permissions(self, request, view, obj=None):
|
def check_post_permissions(self, request, view, obj=None):
|
||||||
if not request.data:
|
if not request.data:
|
||||||
|
|||||||
@@ -1123,6 +1123,7 @@ class InventorySerializer(BaseSerializerWithVariables):
|
|||||||
script = self.reverse('api:inventory_script_view', kwargs={'pk': obj.pk}),
|
script = self.reverse('api:inventory_script_view', kwargs={'pk': obj.pk}),
|
||||||
tree = self.reverse('api:inventory_tree_view', kwargs={'pk': obj.pk}),
|
tree = self.reverse('api:inventory_tree_view', kwargs={'pk': obj.pk}),
|
||||||
inventory_sources = self.reverse('api:inventory_inventory_sources_list', kwargs={'pk': obj.pk}),
|
inventory_sources = self.reverse('api:inventory_inventory_sources_list', kwargs={'pk': obj.pk}),
|
||||||
|
update_inventory_sources = self.reverse('api:inventory_inventory_sources_update', kwargs={'pk': obj.pk}),
|
||||||
activity_stream = self.reverse('api:inventory_activity_stream_list', kwargs={'pk': obj.pk}),
|
activity_stream = self.reverse('api:inventory_activity_stream_list', kwargs={'pk': obj.pk}),
|
||||||
job_templates = self.reverse('api:inventory_job_template_list', kwargs={'pk': obj.pk}),
|
job_templates = self.reverse('api:inventory_job_template_list', kwargs={'pk': obj.pk}),
|
||||||
scan_job_templates = self.reverse('api:inventory_scan_job_template_list', kwargs={'pk': obj.pk}),
|
scan_job_templates = self.reverse('api:inventory_scan_job_template_list', kwargs={'pk': obj.pk}),
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ inventory_urls = patterns('awx.api.views',
|
|||||||
url(r'^(?P<pk>[0-9]+)/script/$', 'inventory_script_view'),
|
url(r'^(?P<pk>[0-9]+)/script/$', 'inventory_script_view'),
|
||||||
url(r'^(?P<pk>[0-9]+)/tree/$', 'inventory_tree_view'),
|
url(r'^(?P<pk>[0-9]+)/tree/$', 'inventory_tree_view'),
|
||||||
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'inventory_inventory_sources_list'),
|
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'inventory_inventory_sources_list'),
|
||||||
|
url(r'^(?P<pk>[0-9]+)/update_inventory_sources/$', 'inventory_inventory_sources_update'),
|
||||||
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'inventory_activity_stream_list'),
|
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'inventory_activity_stream_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/job_templates/$', 'inventory_job_template_list'),
|
url(r'^(?P<pk>[0-9]+)/job_templates/$', 'inventory_job_template_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/scan_job_templates/$', 'inventory_scan_job_template_list'),
|
url(r'^(?P<pk>[0-9]+)/scan_job_templates/$', 'inventory_scan_job_template_list'),
|
||||||
|
|||||||
@@ -2405,6 +2405,51 @@ class InventoryInventorySourcesList(SubListCreateAPIView):
|
|||||||
new_in_14 = True
|
new_in_14 = True
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryInventorySourcesUpdate(RetrieveAPIView):
|
||||||
|
view_name = _('Inventory Sources Update')
|
||||||
|
|
||||||
|
model = Inventory
|
||||||
|
serializer_class = InventorySourceUpdateSerializer
|
||||||
|
permission_classes = (InventoryInventorySourcesUpdatePermission,)
|
||||||
|
is_job_start = True
|
||||||
|
new_in_320 = True
|
||||||
|
|
||||||
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
inventory = self.get_object()
|
||||||
|
update_data = []
|
||||||
|
for inventory_source in inventory.inventory_sources.all():
|
||||||
|
details = {'inventory_source': inventory_source.pk,
|
||||||
|
'can_update': inventory_source.can_update}
|
||||||
|
update_data.append(details)
|
||||||
|
return Response(update_data)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
inventory = self.get_object()
|
||||||
|
update_data = []
|
||||||
|
for inventory_source in inventory.inventory_sources.all():
|
||||||
|
details = {'inventory_source': inventory_source.pk, 'status': None}
|
||||||
|
can_update = inventory_source.can_update
|
||||||
|
project_update = False
|
||||||
|
|
||||||
|
if inventory_source.source == 'scm' and inventory_source.update_on_project_update:
|
||||||
|
if not request.user or not request.user.can_access(Project, 'start', inventory_source.source_project):
|
||||||
|
details['status'] = 'You do not have permission to update project `{}`'.format(inventory_source.source_project.name)
|
||||||
|
can_update = False
|
||||||
|
else:
|
||||||
|
project_update = True
|
||||||
|
|
||||||
|
if can_update:
|
||||||
|
if project_update:
|
||||||
|
details['project_update'] = inventory_source.source_project.update().id
|
||||||
|
details['status'] = 'started'
|
||||||
|
details['inventory_update'] = inventory_source.update().id
|
||||||
|
else:
|
||||||
|
if not details.get('status'):
|
||||||
|
details['status'] = 'Could not start because `can_update` returned False'
|
||||||
|
update_data.append(details)
|
||||||
|
return Response(update_data, status=status.HTTP_202_ACCEPTED)
|
||||||
|
|
||||||
|
|
||||||
class InventorySourceList(ListCreateAPIView):
|
class InventorySourceList(ListCreateAPIView):
|
||||||
|
|
||||||
model = InventorySource
|
model = InventorySource
|
||||||
@@ -2523,7 +2568,7 @@ class InventorySourceUpdateView(RetrieveAPIView):
|
|||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if obj.can_update:
|
if obj.can_update:
|
||||||
if obj.source == 'scm' and obj.update_on_project_update:
|
if obj.source == 'scm' and obj.update_on_project_update:
|
||||||
if not self.request.user or self.request.user.can_access(self.model, 'update', obj):
|
if not self.request.user or not self.request.user.can_access(Project, 'start', obj.source_project):
|
||||||
raise PermissionDenied(detail=_(
|
raise PermissionDenied(detail=_(
|
||||||
'You do not have permission to update project `{}`.'.format(obj.source_project.name)))
|
'You do not have permission to update project `{}`.'.format(obj.source_project.name)))
|
||||||
return self._build_update_response(obj.source_project.update(), request)
|
return self._build_update_response(obj.source_project.update(), request)
|
||||||
|
|||||||
@@ -613,6 +613,10 @@ class InventoryAccess(BaseAccess):
|
|||||||
# inventory to a new organization. Otherwise, just check for admin permission.
|
# inventory to a new organization. Otherwise, just check for admin permission.
|
||||||
return self.check_related('organization', Organization, data, obj=obj) and self.user in obj.admin_role
|
return self.check_related('organization', Organization, data, obj=obj) and self.user in obj.admin_role
|
||||||
|
|
||||||
|
@check_superuser
|
||||||
|
def can_update(self, obj):
|
||||||
|
return self.user in obj.update_role
|
||||||
|
|
||||||
def can_delete(self, obj):
|
def can_delete(self, obj):
|
||||||
is_can_admin = self.can_admin(obj, None)
|
is_can_admin = self.can_admin(obj, None)
|
||||||
if not is_can_admin:
|
if not is_can_admin:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from awx.api.views import (
|
|||||||
ApiVersionRootView,
|
ApiVersionRootView,
|
||||||
JobTemplateLabelList,
|
JobTemplateLabelList,
|
||||||
JobTemplateSurveySpec,
|
JobTemplateSurveySpec,
|
||||||
|
InventoryInventorySourcesUpdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -81,3 +82,38 @@ class TestJobTemplateSurveySpec(object):
|
|||||||
assert response == mock_response_new
|
assert response == mock_response_new
|
||||||
# which there was a better way to do this!
|
# which there was a better way to do this!
|
||||||
assert response.call_args[0][1]['spec'][0]['default'] == '$encrypted$'
|
assert response.call_args[0][1]['spec'][0]['default'] == '$encrypted$'
|
||||||
|
|
||||||
|
|
||||||
|
class TestInventoryInventorySourcesUpdate:
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("can_update, can_access, is_source, is_up_on_proj, expected", [
|
||||||
|
(True, True, "ec2", False, [{'status': 'started', 'inventory_update': 1, 'inventory_source': 1}]),
|
||||||
|
(False, True, "gce", False, [{'status': 'Could not start because `can_update` returned False', 'inventory_source': 1}]),
|
||||||
|
(True, False, "scm", True, [{'status': 'You do not have permission to update project `project`', 'inventory_source': 1}]),
|
||||||
|
])
|
||||||
|
def test_post(self, mocker, can_update, can_access, is_source, is_up_on_proj, expected):
|
||||||
|
class InventoryUpdate:
|
||||||
|
id = 1
|
||||||
|
|
||||||
|
class Project:
|
||||||
|
name = 'project'
|
||||||
|
|
||||||
|
InventorySource = namedtuple('InventorySource', ['source', 'update_on_project_update', 'pk', 'can_update',
|
||||||
|
'update', 'source_project'])
|
||||||
|
|
||||||
|
class InventorySources(object):
|
||||||
|
def all(self):
|
||||||
|
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)]
|
||||||
|
|
||||||
|
Inventory = namedtuple('Inventory', ['inventory_sources'])
|
||||||
|
obj = Inventory(inventory_sources=InventorySources())
|
||||||
|
|
||||||
|
mock_request = mocker.MagicMock()
|
||||||
|
mock_request.user.can_access.return_value = can_access
|
||||||
|
|
||||||
|
with mocker.patch.object(InventoryInventorySourcesUpdate, 'get_object', return_value=obj):
|
||||||
|
view = InventoryInventorySourcesUpdate()
|
||||||
|
response = view.post(mock_request)
|
||||||
|
assert response.data == expected
|
||||||
|
|||||||
Reference in New Issue
Block a user