From a1e075bef82abf1dbfdf734b533c17c978529f2f Mon Sep 17 00:00:00 2001 From: Yashvardhan Nanavati Date: Aug 09 2019 20:16:49 +0000 Subject: Respond with a nicer error when the gating.yaml source is down If you turn on the RemoteRule, greenwave will retrieve the `gating.yaml` from another source, which could be down or returning 500 for X or Y reasons. In this situation, greenwave returns a 500 error without any explanations. This change would have greenwave handle this situation a bit better and return with an error code some error message as to why it cannot proceed as expected. --- diff --git a/greenwave/resources.py b/greenwave/resources.py index 038af19..9f1ddfd 100644 --- a/greenwave/resources.py +++ b/greenwave/resources.py @@ -12,6 +12,7 @@ import json from io import BytesIO import tarfile import subprocess +import socket from urllib.parse import urlparse import xmlrpc.client @@ -129,8 +130,11 @@ class WaiversRetriever(BaseRetriever): def retrieve_scm_from_koji(nvr): """ Retrieve cached rev and namespace from koji using the nvr """ koji_url = current_app.config['KOJI_BASE_URL'] - proxy = xmlrpc.client.ServerProxy(koji_url) - build = proxy.getBuild(nvr) + try: + proxy = xmlrpc.client.ServerProxy(koji_url) + build = proxy.getBuild(nvr) + except (xmlrpc.client.ProtocolError, socket.error) as err: + raise ConnectionError('Could not reach Koji: {}'.format(err)) return retrieve_scm_from_koji_build(nvr, build, koji_url) diff --git a/greenwave/tests/test_retrieve_gating_yaml.py b/greenwave/tests/test_retrieve_gating_yaml.py index d2c5c17..0705957 100644 --- a/greenwave/tests/test_retrieve_gating_yaml.py +++ b/greenwave/tests/test_retrieve_gating_yaml.py @@ -2,6 +2,7 @@ import subprocess import io +import socket import pytest import mock @@ -9,7 +10,7 @@ from werkzeug.exceptions import BadGateway import greenwave.app_factory from greenwave.resources import ( - retrieve_scm_from_koji_build, retrieve_yaml_remote_rule) + retrieve_scm_from_koji_build, retrieve_yaml_remote_rule, retrieve_scm_from_koji) KOJI_URL = 'https://koji.fedoraproject.org/kojihub' @@ -155,3 +156,15 @@ def test_retrieve_yaml_remote_rule_git_archive_error(mock_subp): with pytest.raises(BadGateway, match=expected_error): with app.app_context(): retrieve_yaml_remote_rule('85e796daaa', 'python-requests', 'rpms') + + +@mock.patch('greenwave.resources.xmlrpc.client.ServerProxy') +def test_retrieve_scm_from_koji_build_socket_error(mock_xmlrpc_client): + mock_auth_server = mock_xmlrpc_client.return_value + mock_auth_server.getBuild.side_effect = socket.error('Socket is closed') + app = greenwave.app_factory.create_app() + nvr = 'nethack-3.6.1-3.fc29' + expected_error = 'Could not reach Koji: Socket is closed' + with pytest.raises(socket.error, match=expected_error): + with app.app_context(): + retrieve_scm_from_koji(nvr) diff --git a/greenwave/tests/test_utils.py b/greenwave/tests/test_utils.py index c3abd12..adac2a8 100644 --- a/greenwave/tests/test_utils.py +++ b/greenwave/tests/test_utils.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0+ import pytest +import urllib3 import json @@ -17,7 +18,8 @@ from greenwave.utils import json_error (ConnectionError('ERROR'), 502, 'ERROR'), (ConnectTimeout('TIMEOUT'), 502, 'TIMEOUT'), (Timeout('TIMEOUT'), 504, 'TIMEOUT'), - (InternalServerError(), 500, 'The server encountered an internal error') + (InternalServerError(), 500, 'The server encountered an internal error'), + (urllib3.exceptions.MaxRetryError('MAX_RETRY', '.../gating.yaml'), 502, 'There was an error retrieving the gating.yaml file at .../gating.yaml') ]) def test_json_connection_error(error, expected_status_code, expected_error_message_part): diff --git a/greenwave/utils.py b/greenwave/utils.py index 1166144..a2717a6 100644 --- a/greenwave/utils.py +++ b/greenwave/utils.py @@ -5,6 +5,7 @@ import logging import os import hashlib import datetime +import urllib3 from flask import jsonify, current_app, request from flask.config import Config @@ -34,6 +35,13 @@ def json_error(error): current_app.logger.exception('Timeout error: {}'.format(error)) msg = 'Timeout connecting to upstream server: {}'.format(error) status_code = 504 + elif isinstance(error, urllib3.exceptions.MaxRetryError): + current_app.logger.exception('Connection error: {}'.format(error)) + if error.url.endswith('gating.yaml'): + msg = 'There was an error retrieving the gating.yaml file at {}'.format(error.url) + else: + msg = 'Error connecting to {}'.format(error.url) + status_code = 502 else: current_app.logger.exception('Unexpected server error: {}'.format(error)) msg = 'Server encountered unexpected error'