diff --git a/awx/conf/tests/functional/test_api.py b/awx/conf/tests/functional/test_api.py index d7bb06a1bf..869627878a 100644 --- a/awx/conf/tests/functional/test_api.py +++ b/awx/conf/tests/functional/test_api.py @@ -325,17 +325,3 @@ def test_setting_singleton_delete_no_read_only_fields(api_request, dummy_setting ) assert response.data['FOO_BAR'] == 23 - -@pytest.mark.django_db -def test_setting_logging_test(api_request): - with mock.patch('awx.conf.views.AWXProxyHandler.perform_test') as mock_func: - api_request( - 'post', - reverse('api:setting_logging_test'), - data={'LOG_AGGREGATOR_HOST': 'http://foobar', 'LOG_AGGREGATOR_TYPE': 'logstash'} - ) - call = mock_func.call_args_list[0] - args, kwargs = call - given_settings = kwargs['custom_settings'] - assert given_settings.LOG_AGGREGATOR_HOST == 'http://foobar' - assert given_settings.LOG_AGGREGATOR_TYPE == 'logstash' diff --git a/awx/conf/views.py b/awx/conf/views.py index 1f82009291..0bd93bf7f2 100644 --- a/awx/conf/views.py +++ b/awx/conf/views.py @@ -6,8 +6,6 @@ import collections import logging import sys import socket -import os -from urllib.parse import urlparse from socket import SHUT_RDWR # Django @@ -165,18 +163,17 @@ class SettingLoggingTest(GenericAPIView): filter_backends = [] def post(self, request, *args, **kwargs): + # Error if logging is not enabled + enabled = getattr(settings, 'LOG_AGGREGATOR_ENABLED', False) + if not enabled: + return Response({'error': 'Logging not enabled'}, status=status.HTTP_400_BAD_REQUEST) + # Send test message to configured logger based on db settings logging.getLogger('awx').error('AWX Connection Test Message') hostname = getattr(settings, 'LOG_AGGREGATOR_HOST', None) protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', None) - # Check if host is reacheable - host = urlparse(hostname).netloc - response = os.system("ping -c 1 " + host) - if response != 0: - return Response({'error': 'The host is not available'}, status=status.HTTP_400_BAD_REQUEST) - # Check to ensure port is open at host if protocol in ['udp', 'tcp']: port = getattr(settings, 'LOG_AGGREGATOR_PORT', None) @@ -184,14 +181,9 @@ class SettingLoggingTest(GenericAPIView): if not port: return Response({'error': 'Port required for ' + protocol}, status=status.HTTP_400_BAD_REQUEST) else: + # if http/https by this point, domain is reacheable return Response(status=status.HTTP_202_ACCEPTED) - - # Error if logging is not enabled - enabled = getattr(settings, 'LOG_AGGREGATOR_ENABLED', False) - if not enabled: - return Response({'error': 'Logging not enabled'}, status=status.HTTP_400_BAD_REQUEST) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: s.settimeout(.5) s.connect((hostname, int(port))) diff --git a/awx/main/tests/functional/api/test_settings.py b/awx/main/tests/functional/api/test_settings.py index 9bcd813aad..262befcc19 100644 --- a/awx/main/tests/functional/api/test_settings.py +++ b/awx/main/tests/functional/api/test_settings.py @@ -5,17 +5,13 @@ # Python import pytest import os -import time from django.conf import settings -# Mock -from unittest import mock - # AWX from awx.api.versioning import reverse from awx.conf.models import Setting -from awx.main.utils.handlers import AWXProxyHandler, LoggingConnectivityException +from awx.conf.registry import settings_registry TEST_GIF_LOGO = 'data:image/gif;base64,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' # NOQA @@ -237,78 +233,94 @@ def test_ui_settings(get, put, patch, delete, admin): @pytest.mark.django_db -def test_logging_aggregrator_connection_test_requires_superuser(get, post, alice): +def test_logging_aggregator_connection_test_requires_superuser(post, alice): url = reverse('api:setting_logging_test') post(url, {}, user=alice, expect=403) -@pytest.mark.parametrize('key', [ - 'LOG_AGGREGATOR_TYPE', - 'LOG_AGGREGATOR_HOST', -]) @pytest.mark.django_db -def test_logging_aggregrator_connection_test_bad_request(get, post, admin, key): +def test_logging_aggregator_connection_test_not_enabled(post, admin): url = reverse('api:setting_logging_test') resp = post(url, {}, user=admin, expect=400) - assert 'This field is required.' in resp.data.get(key, []) + assert 'Logging not enabled' in resp.data.get('error') -@pytest.mark.django_db -def test_logging_aggregrator_connection_test_valid(mocker, get, post, admin): - with mock.patch.object(AWXProxyHandler, 'perform_test') as perform_test: - url = reverse('api:setting_logging_test') - user_data = { - 'LOG_AGGREGATOR_TYPE': 'logstash', - 'LOG_AGGREGATOR_HOST': 'localhost', - 'LOG_AGGREGATOR_PORT': 8080, - 'LOG_AGGREGATOR_USERNAME': 'logger', - 'LOG_AGGREGATOR_PASSWORD': 'mcstash' - } - post(url, user_data, user=admin, expect=200) - args, kwargs = perform_test.call_args_list[0] - create_settings = kwargs['custom_settings'] - for k, v in user_data.items(): - assert hasattr(create_settings, k) - assert getattr(create_settings, k) == v - - -@pytest.mark.django_db -def test_logging_aggregrator_connection_test_with_masked_password(mocker, patch, post, admin): - url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'}) - patch(url, user=admin, data={'LOG_AGGREGATOR_PASSWORD': 'password123'}, expect=200) - time.sleep(1) # log settings are cached slightly - - with mock.patch.object(AWXProxyHandler, 'perform_test') as perform_test: - url = reverse('api:setting_logging_test') - user_data = { - 'LOG_AGGREGATOR_TYPE': 'logstash', - 'LOG_AGGREGATOR_HOST': 'localhost', - 'LOG_AGGREGATOR_PORT': 8080, - 'LOG_AGGREGATOR_USERNAME': 'logger', - 'LOG_AGGREGATOR_PASSWORD': '$encrypted$' - } - post(url, user_data, user=admin, expect=200) - args, kwargs = perform_test.call_args_list[0] - create_settings = kwargs['custom_settings'] - assert getattr(create_settings, 'LOG_AGGREGATOR_PASSWORD') == 'password123' - - # Update these tests ^^ - # Test the `/api/v2/settings/logging/test` functionality - def test_logging_test(): +def _mock_logging_defaults(): + # Pre-populate settings obj with defaults + class MockSettings: pass + mock_settings_obj = MockSettings() + mock_settings_json = dict() + for key in settings_registry.get_registered_settings(category_slug='logging'): + value = settings_registry.get_setting_field(key).get_default() + setattr(mock_settings_obj, key, value) + mock_settings_json[key] = value + return mock_settings_obj, mock_settings_json + + + +@pytest.mark.parametrize('key, value, error', [ + ['LOG_AGGREGATOR_TYPE', 'logstash', 'Cannot enable log aggregator without providing host.'], + ['LOG_AGGREGATOR_HOST', 'https://logstash', 'Cannot enable log aggregator without providing type.'] +]) +@pytest.mark.django_db +def test_logging_aggregator_missing_settings(put, post, admin, key, value, error): + _, mock_settings = _mock_logging_defaults() + mock_settings['LOG_AGGREGATOR_ENABLED'] = True + mock_settings[key] = value + url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'}) + response = put(url, data=mock_settings, user=admin, expect=400) + assert error in str(response.data) + + +@pytest.mark.parametrize('type, host, port, username, password', [ + ['logstash', 'localhost', 8080, 'logger', 'mcstash'], + ['loggly', 'http://logs-01.loggly.com/inputs/1fd38090-hash-h4a$h-8d80-t0k3n71/tag/http/', None, None, None], + ['splunk', 'https://yoursplunk:8088/services/collector/event', None, None, None], + ['other', '97.221.40.41', 9000, 'logger', 'mcstash'], + ['sumologic', 'https://endpoint5.collection.us2.sumologic.com/receiver/v1/http/Zagnw_f9XGr_zZgd-_EPM0hb8_rUU7_RU8Q==', + None, None, None] +]) +@pytest.mark.django_db +def test_logging_aggregator_valid_settings(put, post, admin, type, host, port, username, password): + _, mock_settings = _mock_logging_defaults() + # type = 'splunk' + # host = 'https://yoursplunk:8088/services/collector/event' + mock_settings['LOG_AGGREGATOR_ENABLED'] = True + mock_settings['LOG_AGGREGATOR_TYPE'] = type + mock_settings['LOG_AGGREGATOR_HOST'] = host + if port: + mock_settings['LOG_AGGREGATOR_PORT'] = port + if username: + mock_settings['LOG_AGGREGATOR_USERNAME'] = username + if password: + mock_settings['LOG_AGGREGATOR_PASSWORD'] = password + url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'}) + response = put(url, data=mock_settings, user=admin, expect=200) + assert type in response.data.get('LOG_AGGREGATOR_TYPE') + assert host in response.data.get('LOG_AGGREGATOR_HOST') + if port: + assert port == response.data.get('LOG_AGGREGATOR_PORT') + if username: + assert username in response.data.get('LOG_AGGREGATOR_USERNAME') + if password: # Note: password should be encrypted + assert '$encrypted$' in response.data.get('LOG_AGGREGATOR_PASSWORD') @pytest.mark.django_db -def test_logging_aggregrator_connection_test_invalid(mocker, get, post, admin): - with mock.patch.object(AWXProxyHandler, 'perform_test') as perform_test: - perform_test.side_effect = LoggingConnectivityException('404: Not Found') - url = reverse('api:setting_logging_test') - resp = post(url, { - 'LOG_AGGREGATOR_TYPE': 'logstash', - 'LOG_AGGREGATOR_HOST': 'localhost', - 'LOG_AGGREGATOR_PORT': 8080 - }, user=admin, expect=500) - assert resp.data == {'error': '404: Not Found'} +def test_logging_aggregator_connection_test_valid(put, post, admin): + _, mock_settings = _mock_logging_defaults() + type = 'other' + host = 'https://localhost' + mock_settings['LOG_AGGREGATOR_ENABLED'] = True + mock_settings['LOG_AGGREGATOR_TYPE'] = type + mock_settings['LOG_AGGREGATOR_HOST'] = host + # POST to save these mock settings + url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'logging'}) + put(url, data=mock_settings, user=admin, expect=200) + # "Test" the logger + url = reverse('api:setting_logging_test') + post(url, {}, user=admin, expect=202) @pytest.mark.django_db diff --git a/awx/main/tests/unit/api/test_logger_test.py b/awx/main/tests/unit/api/test_logger.py similarity index 62% rename from awx/main/tests/unit/api/test_logger_test.py rename to awx/main/tests/unit/api/test_logger.py index 616b2e4c25..d08362259b 100644 --- a/awx/main/tests/unit/api/test_logger_test.py +++ b/awx/main/tests/unit/api/test_logger.py @@ -1,8 +1,9 @@ import pytest -from awx.main.utils.external_logging import construct_rsyslog_conf_template -from awx.conf import settings_registry +from django.conf import settings +from awx.main.utils.external_logging import construct_rsyslog_conf_template +from awx.main.tests.functional.api.test_settings import _mock_logging_defaults ''' # Example User Data @@ -36,48 +37,53 @@ data_loggly = { @pytest.mark.parametrize( 'enabled, type, host, port, protocol, expected_config', [ (True, 'loggly', 'http://logs-01.loggly.com/inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/', None, 'https', - '$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")\ntemplate(name="awx" type="string" string="%msg%")\nmodule(load="omhttp")\naction(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")'), + '''$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on") +template(name="awx" type="string" string="%msg%")\nmodule(load="omhttp") +action(type="omhttp" server="logs-01.loggly.com" serverport="80" usehttps="off" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' + + 'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/")'), (True, 'other', 'localhost', 9000, 'udp', - '$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")\ntemplate(name="awx" type="string" string="%msg%")\naction(type="omfwd" target="localhost" port="9000" protocol="udp" action.resumeRetryCount="-1" template="awx")'), + '''$IncludeConfig /etc/rsyslog.conf +input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on") +template(name="awx" type="string" string="%msg%") +action(type="omfwd" target="localhost" port="9000" protocol="udp" action.resumeRetryCount="-1" template="awx")'''), (True, 'other', 'localhost', 9000, 'tcp', - '$IncludeConfig /etc/rsyslog.conf\ninput(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on")\ntemplate(name="awx" type="string" string="%msg%")\naction(type="omfwd" target="localhost" port="9000" protocol="tcp" action.resumeRetryCount="-1" template="awx")'), + '''$IncludeConfig /etc/rsyslog.conf +input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on") +template(name="awx" type="string" string="%msg%") +action(type="omfwd" target="localhost" port="9000" protocol="tcp" action.resumeRetryCount="-1" template="awx")'''), (False, 'loggly', 'http://logs-01.loggly.com/inputs/1fd38090-2af1-4e1e-8d80-492899da0f71/tag/http/', 8080, 'https', - '$IncludeConfig /etc/rsyslog.conf'), + '''$IncludeConfig /etc/rsyslog.conf'''), (True, 'splunk', 'https://yoursplunk:8088/services/collector/event', None, None, '''$IncludeConfig /etc/rsyslog.conf input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on") template(name="awx" type="string" string="%msg%") module(load="omhttp") -action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'''), +action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' + + 'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'), (True, 'splunk', 'https://yoursplunk/services/collector/event', 8088, None, '''$IncludeConfig /etc/rsyslog.conf input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on") template(name="awx" type="string" string="%msg%") module(load="omhttp") -action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'''), +action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' + + 'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'), (True, 'splunk', 'https://yoursplunk/services/collector/event', 8088, 'https', '''$IncludeConfig /etc/rsyslog.conf input(type="imuxsock" Socket="/var/run/rsyslog/rsyslog.sock" unlink="on") template(name="awx" type="string" string="%msg%") module(load="omhttp") -action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'''), +action(type="omhttp" server="yoursplunk" serverport="8088" usehttps="on" skipverifyhost="off" action.resumeRetryCount="-1" template="awx" ''' + + 'errorfile="/var/log/tower/external.err" healthchecktimeout="20000" restpath="services/collector/event")'), ] ) def test_rsyslog_conf_template(enabled, type, host, port, protocol, expected_config): - # Mock settings object - class MockSettings: - pass - mock_settings = MockSettings() - - # Pre-populate settings obj with defaults - for key in settings_registry.get_registered_settings(category_slug='logging'): - value = settings_registry.get_setting_field(key).get_default() - setattr(mock_settings, key, value) - + mock_settings, _ = _mock_logging_defaults() # Set test settings - setattr(mock_settings, 'LOGGING_SOCK', '/var/run/rsyslog/rsyslog.sock') + logging_defaults = getattr(settings, 'LOGGING') + setattr(mock_settings, 'LOGGING', logging_defaults) + setattr(mock_settings, 'LOGGING["handlers"]["external_logger"]["address"]', '/var/run/rsyslog/rsyslog.sock') setattr(mock_settings, 'LOG_AGGREGATOR_ENABLED', enabled) setattr(mock_settings, 'LOG_AGGREGATOR_TYPE', type) setattr(mock_settings, 'LOG_AGGREGATOR_HOST', host) @@ -91,4 +97,3 @@ def test_rsyslog_conf_template(enabled, type, host, port, protocol, expected_con # check validity of created template assert tmpl in expected_config - diff --git a/awx/main/tests/unit/utils/test_reload.py b/awx/main/tests/unit/utils/test_reload.py index 87c2689da8..525a90e6aa 100644 --- a/awx/main/tests/unit/utils/test_reload.py +++ b/awx/main/tests/unit/utils/test_reload.py @@ -8,7 +8,7 @@ def test_produce_supervisor_command(mocker): mock_process.communicate = communicate_mock Popen_mock = mocker.MagicMock(return_value=mock_process) with mocker.patch.object(reload.subprocess, 'Popen', Popen_mock): - reload._supervisor_service_command("restart") + reload.supervisor_service_command("restart") reload.subprocess.Popen.assert_called_once_with( ['supervisorctl', 'restart', 'tower-processes:*',], stderr=-1, stdin=-1, stdout=-1) diff --git a/awx/main/utils/external_logging.py b/awx/main/utils/external_logging.py index 49f74e4146..74325a3b12 100644 --- a/awx/main/utils/external_logging.py +++ b/awx/main/utils/external_logging.py @@ -12,7 +12,6 @@ def construct_rsyslog_conf_template(settings=settings): host = getattr(settings, 'LOG_AGGREGATOR_HOST', '') port = getattr(settings, 'LOG_AGGREGATOR_PORT', '') protocol = getattr(settings, 'LOG_AGGREGATOR_PROTOCOL', '') - import pdb; pdb.set_trace() if protocol.startswith('http'): scheme = 'https' # urlparse requires '//' to be provided if scheme is not specified @@ -29,7 +28,7 @@ def construct_rsyslog_conf_template(settings=settings): port = settings.LOG_AGGREGATOR_PORT parts.extend([ - 'input(type="imuxsock" Socket="' + settings.LOGGING['handlers']['external_logger'] + '" unlink="on")', + 'input(type="imuxsock" Socket="' + settings.LOGGING['handlers']['external_logger']['address'] + '" unlink="on")', 'template(name="awx" type="string" string="%msg%")', ]) if protocol.startswith('http'): @@ -66,7 +65,8 @@ def construct_rsyslog_conf_template(settings=settings): ) tmpl = '\n'.join(parts) return tmpl - + + def reconfigure_rsyslog(): tmpl = construct_rsyslog_conf_template() with open('/var/lib/awx/rsyslog/rsyslog.conf', 'w') as f: diff --git a/awx/main/utils/handlers.py b/awx/main/utils/handlers.py index 7f393711d0..a883058df1 100644 --- a/awx/main/utils/handlers.py +++ b/awx/main/utils/handlers.py @@ -12,7 +12,7 @@ from django.conf import settings class RSysLogHandler(logging.handlers.SysLogHandler): def emit(self, msg): - if not os.path.exists(settings.LOGGING['handlers']['external_logger']): + if not os.path.exists(settings.LOGGING['handlers']['external_logger']['address']): return return super(RSysLogHandler, self).emit(msg) diff --git a/awx/ui/client/src/configuration/forms/system-form/configuration-system.controller.js b/awx/ui/client/src/configuration/forms/system-form/configuration-system.controller.js index 520a0d5516..14b62e2ef9 100644 --- a/awx/ui/client/src/configuration/forms/system-form/configuration-system.controller.js +++ b/awx/ui/client/src/configuration/forms/system-form/configuration-system.controller.js @@ -199,7 +199,7 @@ export default [ $scope.$parent.vm.testTooltip = i18n._('Save and enable log aggregation before testing the log aggregator.'); } else { $scope.$parent.vm.disableTestButton = false; - $scope.$parent.vm.testTooltip = i18n._('Send a test response to the configured log aggregator.'); + $scope.$parent.vm.testTooltip = i18n._('Send a test log message to the configured log aggregator.'); } }); @@ -220,7 +220,7 @@ export default [ }) .catch(({ data, status }) => { $scope.$parent.vm.disableTestButton = false; - if (status === 400 || status == 500) { + if (status === 400 || status === 500) { ngToast.danger({ dismissButton: false, dismissOnTimeout: true, diff --git a/docs/logging_integration.md b/docs/logging_integration.md index 7b0eba9eb9..f21dd9b2e6 100644 --- a/docs/logging_integration.md +++ b/docs/logging_integration.md @@ -213,7 +213,6 @@ with the traceback message. Log messages should be sent outside of the request-response cycle. For example, Loggly examples use -`requests_futures.sessions.FuturesSession`, which does some -threading work to fire the message without interfering with other +rsyslog, which handles these messages without interfering with other operations. A timeout on the part of the log aggregation service should not cause Tower operations to hang. diff --git a/requirements/README.md b/requirements/README.md index 4ce399a7e9..af672ae20e 100644 --- a/requirements/README.md +++ b/requirements/README.md @@ -4,7 +4,7 @@ The `requirements.txt` and `requirements_ansible.txt` files are generated from ` ## How To Use -Commands should from inside `./requirements` directory of the awx repository. +Commands should be run from inside the `./requirements` directory of the awx repository. Make sure you have `patch, awk, python3, python2, python3-venv, python2-virtualenv, pip2, pip3` installed. The development container image should have all these. diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 3136ca3171..00fbee267d 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -101,9 +101,8 @@ python3-saml==1.9.0 # via -r /awx_devel/requirements/requirements.in pytz==2019.3 # via django, irc, tempora, twilio pyyaml==5.3.1 # via -r /awx_devel/requirements/requirements.in, ansible-runner, djangorestframework-yaml, kubernetes redis==3.4.1 # via -r /awx_devel/requirements/requirements.in -requests-futures==1.0.0 # via -r /awx_devel/requirements/requirements.in requests-oauthlib==1.3.0 # via kubernetes, msrest, social-auth-core -requests==2.23.0 # via -r /awx_devel/requirements/requirements.in, adal, azure-keyvault, django-oauth-toolkit, kubernetes, msrest, requests-futures, requests-oauthlib, slackclient, social-auth-core, twilio +requests==2.23.0 # via -r /awx_devel/requirements/requirements.in, adal, azure-keyvault, django-oauth-toolkit, kubernetes, msrest, requests-oauthlib, slackclient, social-auth-core, twilio rsa==4.0 # via google-auth ruamel.yaml.clib==0.2.0 # via ruamel.yaml ruamel.yaml==0.16.10 # via openshift diff --git a/tools/docker-compose/supervisor.conf b/tools/docker-compose/supervisor.conf index a28a230da1..75d8c05fce 100644 --- a/tools/docker-compose/supervisor.conf +++ b/tools/docker-compose/supervisor.conf @@ -84,7 +84,7 @@ stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 [group:tower-processes] -programs=awx-dispatcher,awx-receiver,awx-runworker,awx-uwsgi,awx-daphne,awx-nginx,awx-wsbroadcast,awx-rsyslogd +programs=awx-dispatcher,awx-receiver,awx-uwsgi,awx-daphne,awx-nginx,awx-wsbroadcast,awx-rsyslogd priority=5 [unix_http_server]