Skip to content

Commit

Permalink
feat: Recognize more errors
Browse files Browse the repository at this point in the history
* 429 Too Many requests means the rate limit was hit.
* 401 Unauthorized from gateway means the username/password is invalid.
* SSLError means the key/certificate pair is invalid.
* SSL: WRONG_VERSION_NUMBER in the SSLError means that HTTPS has been used to contact an HTTP server.
* ConnectionTimeout and ReadTimeout may mean the connection is slow.
  • Loading branch information
Glutexo committed Jan 8, 2025
1 parent 453d1d1 commit 7001cef
Showing 1 changed file with 55 additions and 8 deletions.
63 changes: 55 additions & 8 deletions insights/client/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,32 @@ def _api_request_failed(exception, message='The Insights API could not be reache
logger.error(message)


def _is_dns_error(exception):
while isinstance(exception, Exception):
if isinstance(exception, socket.gaierror):
return True
def _is_basic_auth_error(result):
if result.status_code != 401 or result.headers["Content-Type"] != "application/json":
return False

exception = exception.__context__
try:
body = result.json()
except requests.exceptions.JSONDecodeError:
return False

if not isinstance(body, dict) or "errors" not in body or not isinstance(body["errors"], list):
return False

for error in body["errors"]:
if "status" in error and error["status"] == 401 and "meta" in error and isinstance(error["meta"], dict) and "response_by" in error["meta"] and error["meta"]["response_by"] == "gateway":
return True

return False


def _exception_root_cause(exception):
while True:
if not exception.__context__:
return exception
exception = exception.__context__


def _fallback_ip(hostname):
if hostname.endswith("redhat.com"):
if hostname.endswith("stage.redhat.com"):
Expand Down Expand Up @@ -218,7 +234,7 @@ def _http_request(self, url, method, log_response_text=True, log_prefix="", log_
log_message += " attachments={files}".format(files=",".join(attachments))
logger.log(log_level, log_message)
try:
res = self.session.request(url=url, method=method, timeout=self.config.http_timeout, **kwargs)
res = self.session.request(url=url, method=method, timeout=self.config.http_timeout, verify="/home/insights/simple-http-server/cert.pem", **kwargs)
except Exception:
raise
logger.log(log_level, "%sHTTP Status: %d %s", log_prefix, res.status_code, res.reason)
Expand Down Expand Up @@ -554,8 +570,39 @@ def test_connection(self, rc=0):
logger.error(" Additional information may be in %s" % self.config.logging_file)
logger.error("")

if _is_dns_error(result):
self._test_connection(url)
if isinstance(result, REQUEST_FAILED_EXCEPTIONS):
root_cause = _exception_root_cause(result)
if isinstance(result, requests.exceptions.SSLError):
if "[SSL: WRONG_VERSION_NUMBER]" in str(root_cause):
logger.error(" Invalid protocol.")
else:
logger.error(" Invalid key or certificate.")
elif isinstance(result, requests.exceptions.Timeout):
if isinstance(result, requests.exceptions.ConnectTimeout):
logger.error(" Connection timed out.")
elif isinstance(result, requests.exceptions.ReadTimeout):
logger.error(" Read timed out.")
else:
logger.error(" Timeout.") # Cannot happen.
self._test_connection(url)
elif isinstance(result, requests.exceptions.ConnectionError):
if isinstance(root_cause, socket.gaierror):
self._test_connection(url)
elif "Connection refused" in str(root_cause):
logger.error(" Connection refused.")
else:
logger.error(" Connection error.") # Should not happen.
else:
logger.error(" Unknown error %s.", result) # Should not happen.
elif isinstance(result, requests.Response):
if _is_basic_auth_error(result):
logger.error(" Invalid username or password.")
elif result.status_code == requests.codes.too_many_requests:
logger.error(" Too many requests.")
else:
logger.error(" Unknown response %s %s.", result.status_code, result.reason)
else:
logger.error(" Unknown result %s.", result) # Cannot happen.

return 1

Expand Down

0 comments on commit 7001cef

Please sign in to comment.