Fail fast for bad connections with the API and bots.

The API has aggressive retry logic for connecting to a
server, which may make sense for situation where you have
connection blips or server restarts.

When you're first connecting to the API, however, connection
failures are almost certainly a sign of misconfiguration, so
now we fail fast.

The bot lib takes advantage of this API change by catching the
ZulipError exception and exiting gracefully.
This commit is contained in:
Steve Howell 2017-11-21 14:21:04 -08:00
parent a19278da65
commit 043d963a99
2 changed files with 26 additions and 2 deletions

View file

@ -271,6 +271,9 @@ def get_default_config_filename():
" mv ~/.humbugrc ~/.zuliprc\n") " mv ~/.humbugrc ~/.zuliprc\n")
return config_file return config_file
class ZulipError(Exception):
pass
class Client(object): class Client(object):
def __init__(self, email=None, api_key=None, config_file=None, def __init__(self, email=None, api_key=None, config_file=None,
verbose=False, retry_on_errors=True, verbose=False, retry_on_errors=True,
@ -382,6 +385,8 @@ class Client(object):
self.session = None # type: Union[None, requests.Session] self.session = None # type: Union[None, requests.Session]
self.has_connected = False
def ensure_session(self): def ensure_session(self):
# type: () -> None # type: () -> None
@ -507,6 +512,8 @@ class Client(object):
timeout=request_timeout, timeout=request_timeout,
**kwargs) **kwargs)
self.has_connected = True
# On 50x errors, try again after a short sleep # On 50x errors, try again after a short sleep
if str(res.status_code).startswith('5'): if str(res.status_code).startswith('5'):
if error_retry(" (server %s)" % (res.status_code,)): if error_retry(" (server %s)" % (res.status_code,)):
@ -528,6 +535,13 @@ class Client(object):
return {'msg': "Connection error:\n%s" % traceback.format_exc(), return {'msg': "Connection error:\n%s" % traceback.format_exc(),
"result": "connection-error"} "result": "connection-error"}
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
if not self.has_connected:
# If we have never successfully connected to the server, don't
# go into retry logic, because the most likely scenario here is
# that somebody just hasn't started their server, or they passed
# in an invalid site.
raise ZulipError('cannot connect to server ' + self.base_url)
if error_retry(""): if error_retry(""):
continue continue
end_error_retry(False) end_error_retry(False)

View file

@ -17,7 +17,7 @@ if False:
from typing import Any, Optional, List, Dict, IO, Text, Set from typing import Any, Optional, List, Dict, IO, Text, Set
from types import ModuleType from types import ModuleType
from zulip import Client from zulip import Client, ZulipError
def exit_gracefully(signum, frame): def exit_gracefully(signum, frame):
# type: (int, Optional[Any]) -> None # type: (int, Optional[Any]) -> None
@ -95,7 +95,17 @@ class ExternalBotHandler(object):
def __init__(self, client, root_dir, bot_details={}): def __init__(self, client, root_dir, bot_details={}):
# type: (Client, str, Dict[str, Any]) -> None # type: (Client, str, Dict[str, Any]) -> None
# Only expose a subset of our Client's functionality # Only expose a subset of our Client's functionality
user_profile = client.get_profile() try:
user_profile = client.get_profile()
except ZulipError as e:
print('''
ERROR: {}
Have you not started the server?
Or did you mis-specify the URL?
'''.format(e))
sys.exit(1)
self._rate_limit = RateLimit(20, 5) self._rate_limit = RateLimit(20, 5)
self._client = client self._client = client
self._root_dir = root_dir self._root_dir = root_dir