diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 798e39e66f..3f674dae7a 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1400,7 +1400,7 @@ class ProjectUpdateSerializer(UnifiedJobSerializer, ProjectOptionsSerializer): class Meta: model = ProjectUpdate - fields = ('*', 'project', 'job_type') + fields = ('*', 'project', 'job_type', '-controller_node') def get_related(self, obj): res = super(ProjectUpdateSerializer, self).get_related(obj) @@ -2110,7 +2110,8 @@ class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSeri class Meta: model = InventoryUpdate - fields = ('*', 'inventory_source', 'license_error', 'source_project_update') + fields = ('*', 'inventory_source', 'license_error', 'source_project_update', + '-controller_node',) def get_related(self, obj): res = super(InventoryUpdateSerializer, self).get_related(obj) @@ -3257,7 +3258,8 @@ class AdHocCommandSerializer(UnifiedJobSerializer): model = AdHocCommand fields = ('*', 'job_type', 'inventory', 'limit', 'credential', 'module_name', 'module_args', 'forks', 'verbosity', 'extra_vars', - 'become_enabled', 'diff_mode', '-unified_job_template', '-description') + 'become_enabled', 'diff_mode', '-unified_job_template', '-description', + '-controller_node',) extra_kwargs = { 'name': { 'read_only': True, @@ -3366,7 +3368,7 @@ class SystemJobSerializer(UnifiedJobSerializer): class Meta: model = SystemJob - fields = ('*', 'system_job_template', 'job_type', 'extra_vars', 'result_stdout') + fields = ('*', 'system_job_template', 'job_type', 'extra_vars', 'result_stdout', '-controller_node',) def get_related(self, obj): res = super(SystemJobSerializer, self).get_related(obj) diff --git a/awx/main/tests/functional/api/test_job.py b/awx/main/tests/functional/api/test_job.py index 1d5739731d..00114e044d 100644 --- a/awx/main/tests/functional/api/test_job.py +++ b/awx/main/tests/functional/api/test_job.py @@ -9,7 +9,13 @@ from rest_framework.exceptions import PermissionDenied from awx.api.versioning import reverse from awx.api.views import RelatedJobsPreventDeleteMixin, UnifiedJobDeletionMixin -from awx.main.models import JobTemplate, User, Job +from awx.main.models import ( + JobTemplate, + User, + Job, + AdHocCommand, + ProjectUpdate, +) from crum import impersonate @@ -159,3 +165,41 @@ def test_block_related_unprocessed_events(mocker, organization, project, delete, with mock.patch('awx.api.views.now', lambda: time_of_request): with pytest.raises(PermissionDenied): view.perform_destroy(organization) + + +class TestControllerNode(): + @pytest.fixture + def project_update(self, project): + return ProjectUpdate.objects.create(project=project) + + @pytest.fixture + def job(self): + return JobTemplate.objects.create().create_unified_job() + + @pytest.fixture + def adhoc(self, inventory): + return AdHocCommand.objects.create(inventory=inventory) + + @pytest.mark.django_db + def test_field_controller_node_exists(self, sqlite_copy_expert, + admin_user, job, project_update, + inventory_update, adhoc, get, system_job_factory): + system_job = system_job_factory() + + r = get(reverse('api:unified_job_list') + '?id={}'.format(job.id), admin_user, expect=200) + assert 'controller_node' in r.data['results'][0] + + r = get(job.get_absolute_url(), admin_user, expect=200) + assert 'controller_node' in r.data + + r = get(reverse('api:project_update_detail', kwargs={'pk': project_update.pk}), admin_user, expect=200) + assert 'controller_node' not in r.data + + r = get(reverse('api:ad_hoc_command_detail', kwargs={'pk': adhoc.pk}), admin_user, expect=200) + assert 'controller_node' not in r.data + + r = get(reverse('api:inventory_update_detail', kwargs={'pk': inventory_update.pk}), admin_user, expect=200) + assert 'controller_node' not in r.data + + r = get(reverse('api:system_job_detail', kwargs={'pk': system_job.pk}), admin_user, expect=200) + assert 'controller_node' not in r.data diff --git a/awx/main/tests/functional/api/test_unified_jobs_stdout.py b/awx/main/tests/functional/api/test_unified_jobs_stdout.py index 3f1a5760fc..b465f92606 100644 --- a/awx/main/tests/functional/api/test_unified_jobs_stdout.py +++ b/awx/main/tests/functional/api/test_unified_jobs_stdout.py @@ -3,11 +3,8 @@ import base64 import json import re -import shutil -import tempfile from django.conf import settings -from django.db.backends.sqlite3.base import SQLiteCursorWrapper import mock import pytest @@ -31,28 +28,6 @@ def _mk_inventory_update(): return iu -@pytest.fixture(scope='function') -def sqlite_copy_expert(request): - # copy_expert is postgres-specific, and SQLite doesn't support it; mock its - # behavior to test that it writes a file that contains stdout from events - path = tempfile.mkdtemp(prefix='job-event-stdout') - - def write_stdout(self, sql, fd): - # simulate postgres copy_expert support with ORM code - parts = sql.split(' ') - tablename = parts[parts.index('from') + 1] - for cls in (JobEvent, AdHocCommandEvent, ProjectUpdateEvent, - InventoryUpdateEvent, SystemJobEvent): - if cls._meta.db_table == tablename: - for event in cls.objects.order_by('start_line').all(): - fd.write(event.stdout.encode('utf-8')) - - setattr(SQLiteCursorWrapper, 'copy_expert', write_stdout) - request.addfinalizer(lambda: shutil.rmtree(path)) - request.addfinalizer(lambda: delattr(SQLiteCursorWrapper, 'copy_expert')) - return path - - @pytest.mark.django_db @pytest.mark.parametrize('Parent, Child, relation, view', [ [Job, JobEvent, 'job', 'api:job_stdout'], diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index 28d7b65564..4b91564312 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -4,6 +4,8 @@ import mock import json import os import six +import tempfile +import shutil from datetime import timedelta from six.moves import xrange @@ -14,6 +16,7 @@ from django.utils import timezone from django.contrib.auth.models import User from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder +from django.db.backends.sqlite3.base import SQLiteCursorWrapper from jsonbfield.fields import JSONField # AWX @@ -44,6 +47,13 @@ from awx.main.models.notifications import ( NotificationTemplate, Notification ) +from awx.main.models.events import ( + JobEvent, + AdHocCommandEvent, + ProjectUpdateEvent, + InventoryUpdateEvent, + SystemJobEvent, +) from awx.main.models.workflow import WorkflowJobTemplate from awx.main.models.ad_hoc_commands import AdHocCommand from awx.main.models.oauth import OAuth2Application as Application @@ -729,3 +739,26 @@ def oauth_application(admin): name='test app', user=admin, client_type='confidential', authorization_grant_type='password' ) + + +@pytest.fixture +def sqlite_copy_expert(request): + # copy_expert is postgres-specific, and SQLite doesn't support it; mock its + # behavior to test that it writes a file that contains stdout from events + path = tempfile.mkdtemp(prefix='job-event-stdout') + + def write_stdout(self, sql, fd): + # simulate postgres copy_expert support with ORM code + parts = sql.split(' ') + tablename = parts[parts.index('from') + 1] + for cls in (JobEvent, AdHocCommandEvent, ProjectUpdateEvent, + InventoryUpdateEvent, SystemJobEvent): + if cls._meta.db_table == tablename: + for event in cls.objects.order_by('start_line').all(): + fd.write(event.stdout.encode('utf-8')) + + setattr(SQLiteCursorWrapper, 'copy_expert', write_stdout) + request.addfinalizer(lambda: shutil.rmtree(path)) + request.addfinalizer(lambda: delattr(SQLiteCursorWrapper, 'copy_expert')) + return path +