IMPALA-9240: add HTTP code handling to THttpClient.

Before this change Impala Shell is not checking HTTP return codes when
using the hs2-http protocol. The shell is sending a request message
(e.g. send_CloseOperation) but the HTTP call to send this message may
fail. This will result in a failure when reading the reply (e.g. in
recv_CloseOperation) as there is no reply data to read. This will
typically result in an 'EOFError'.

In code that overrides THttpClient.flush(), check the HTTP code that is
returned after the HTTP call is made. If the code is not 1XX
(informational response) or 2XX (successful) then throw an RPCException.

This change does not contain any attempt to recover from an HTTP failures
but it does allow the failure to be detected and a message to be
printed.

In future it may be possible to retry after certain HTTP errors.

Testing:
- Add a new test for impala-shell that tries to connect to an HTTP
  server that always returns a 503 error. Check that an appropriate
  error message is printed.

Change-Id: I3c105f4b8237b87695324d759ffff81821c08c43
Reviewed-on: http://gerrit.cloudera.org:8080/14924
Reviewed-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
This commit is contained in:
Andrew Sherman
2019-12-11 09:38:27 -08:00
committed by Impala Public Jenkins
parent e081e42f68
commit ed5e7dae94
2 changed files with 62 additions and 3 deletions

View File

@@ -129,6 +129,17 @@ class MissingThriftMethodException(Exception):
return self.value
class CodeCheckingHttpClient(THttpClient):
"""Add HTTP response code handling to THttpClient."""
def flush(self):
THttpClient.flush(self)
# At this point the http call has completed.
if self.code >= 300:
# Report any http response code that is not 1XX (informational response) or
# 2XX (successful).
raise RPCException("HTTP code {}: {}".format(self.code, self.message))
def print_to_stderr(message):
print >> sys.stderr, message
@@ -394,10 +405,11 @@ class ImpalaClient(object):
else:
ssl_ctx.check_hostname = False # Mandated by the SSL lib for CERT_NONE mode.
ssl_ctx.verify_mode = ssl.CERT_NONE
transport = THttpClient(
transport = CodeCheckingHttpClient(
"https://{0}/{1}".format(host_and_port, self.http_path), ssl_context=ssl_ctx)
else:
transport = THttpClient("http://{0}/{1}".format(host_and_port, self.http_path))
transport = CodeCheckingHttpClient("http://{0}/{1}".
format(host_and_port, self.http_path))
if self.use_ldap:
# Set the BASIC auth header

View File

@@ -18,6 +18,7 @@
# specific language governing permissions and limitations
# under the License.
import httplib
import logging
import os
import pexpect
@@ -26,7 +27,9 @@ import re
import signal
import socket
import sys
import threading
from time import sleep
from contextlib import closing
# This import is the actual ImpalaShell class from impala_shell.py.
# We rename it to ImpalaShellClass here because we later import another
@@ -40,7 +43,9 @@ from tests.common.impala_test_suite import ImpalaTestSuite
from tests.common.skip import SkipIfLocal
from tests.common.test_dimensions import create_client_protocol_dimension
from util import (assert_var_substitution, ImpalaShell, get_impalad_port, get_shell_cmd,
get_open_sessions_metric)
get_open_sessions_metric, IMPALA_SHELL_EXECUTABLE)
import SimpleHTTPServer
import SocketServer
QUERY_FILE_PATH = os.path.join(os.environ['IMPALA_HOME'], 'tests', 'shell')
@@ -70,6 +75,20 @@ def tmp_history_file(request):
return tmp.name
class UnavailableRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
"""An HTTP server that always returns 503"""
def do_POST(self):
self.send_response(code=httplib.SERVICE_UNAVAILABLE, message="Service Unavailable")
def get_unused_port():
""" Find an unused port http://stackoverflow.com/questions/1365265 """
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(('', 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return s.getsockname()[1]
class TestImpalaShellInteractive(ImpalaTestSuite):
"""Test the impala shell interactively"""
@@ -842,6 +861,34 @@ class TestImpalaShellInteractive(ImpalaTestSuite):
result = p.get_result()
assert "Fetched 0 row" in result.stderr
def test_http_codes(self, vector):
"""Check that the shell prints a good message when using hs2-http protocol
and the http server returns a 503 error."""
protocol = vector.get_value("protocol")
if protocol != 'hs2-http':
pytest.skip()
# Start an http server that always returns 503.
HOST = "localhost"
PORT = get_unused_port()
httpd = None
http_server_thread = None
try:
httpd = SocketServer.TCPServer((HOST, PORT), UnavailableRequestHandler)
http_server_thread = threading.Thread(target=httpd.serve_forever)
http_server_thread.start()
# Check that we get a message about the 503 error when we try to connect.
shell_args = ["--protocol={0}".format(protocol), "-i{0}:{1}".format(HOST, PORT)]
shell_proc = pexpect.spawn(IMPALA_SHELL_EXECUTABLE, shell_args)
shell_proc.expect("HTTP code 503", timeout=10)
finally:
# Clean up.
if httpd is not None:
httpd.shutdown()
if http_server_thread is not None:
http_server_thread.join()
def run_impala_shell_interactive(vector, input_lines, shell_args=None,
wait_until_connected=True):