diff --git a/src/Pipfile b/src/Pipfile index 377252e05..43b919c08 100644 --- a/src/Pipfile +++ b/src/Pipfile @@ -25,6 +25,7 @@ django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"} boto3 = "*" typing-extensions ='*' django-login-required-middleware = "*" +gevent = "*" fred-epplib = {git = "https://github.com/cisagov/epplib.git", ref = "master"} geventconnpool = {git = "https://github.com/rasky/geventconnpool.git", ref = "1bbb93a714a331a069adf27265fe582d9ba7ecd4"} diff --git a/src/Pipfile.lock b/src/Pipfile.lock index c8ca68d5c..a7314de46 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "a3a996c98e72cee37bc89a3b95fab6ae4b396d5663eb4fe66a80684154bc90e0" + "sha256": "67b51a57b7d9d7d70d1eeca3515e169cd614d575a7213f31251f9dde43e1f748" }, "pipfile-spec": 6, "requires": {}, @@ -433,6 +433,7 @@ "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" ], + "index": "pypi", "markers": "python_version >= '3.8'", "version": "==23.9.1" }, diff --git a/src/epplibwrapper/client.py b/src/epplibwrapper/client.py index b0d79c300..50c3c3a3e 100644 --- a/src/epplibwrapper/client.py +++ b/src/epplibwrapper/client.py @@ -36,6 +36,7 @@ except Exception: exc_info=True, ) + class EPPLibWrapper: """ A wrapper over epplib's client. @@ -69,9 +70,7 @@ class EPPLibWrapper: # Pool size "size": settings.EPP_CONNECTION_POOL_SIZE, # Which errors the pool should look out for - "exc_classes": ( - TransportError, - ), + "exc_classes": (TransportError,), # Occasionally pings the registry to keep the connection alive. # Value in seconds => (keepalive / size) "keepalive": settings.POOL_KEEP_ALIVE, @@ -133,7 +132,7 @@ class EPPLibWrapper: # try to prevent use of this method without appropriate safeguards if not cleaned: raise ValueError("Please sanitize user input before sending it.") - + # Reopen the pool if its closed if not self.pool_status.pool_running: # We want to reopen the connection pool, @@ -160,12 +159,12 @@ class EPPLibWrapper: else: # don't try again raise err - def start_connection_pool(self, restart_pool_if_exists = True): - """Starts a connection pool for the registry. + def start_connection_pool(self, restart_pool_if_exists=True): + """Starts a connection pool for the registry. - restart_pool_if_exists -> bool: + restart_pool_if_exists -> bool: If an instance of the pool already exists, - then then that instance will be killed first. + then then that instance will be killed first. It is generally recommended to keep this enabled.""" # Since we reuse the same creds for each pool, we can test on # one socket, and if successful, then we know we can connect. @@ -179,13 +178,13 @@ class EPPLibWrapper: return else: self.pool_status.connection_success = True - + # If this function is reinvoked, then ensure # that we don't have duplicate data sitting around. if self._pool is not None and restart_pool_if_exists: logger.info("Connection pool restarting...") self.kill_pool() - + self._pool = EPPConnectionPool( client=self._client, login=self._login, options=self.pool_options ) @@ -193,7 +192,7 @@ class EPPLibWrapper: self.pool_status.pool_hanging = False logger.info("Connection pool started") - + def kill_pool(self): """Kills the existing pool. Use this instead of self._pool = None, as that doesn't clear @@ -203,9 +202,7 @@ class EPPLibWrapper: self._pool = None self.pool_status.pool_running = False return - logger.info( - "kill_pool() was invoked but there was no pool to delete" - ) + logger.info("kill_pool() was invoked but there was no pool to delete") def _test_registry_connection_success(self): """Check that determines if our login diff --git a/src/epplibwrapper/errors.py b/src/epplibwrapper/errors.py index 91c8721d8..884b453fa 100644 --- a/src/epplibwrapper/errors.py +++ b/src/epplibwrapper/errors.py @@ -79,7 +79,7 @@ class RegistryError(Exception): def is_client_error(self): return self.code is not None and (self.code >= 2000 and self.code <= 2308) - + def is_not_retryable(self): pass diff --git a/src/epplibwrapper/tests/test_pool.py b/src/epplibwrapper/tests/test_pool.py index 8752c9eab..f77607d56 100644 --- a/src/epplibwrapper/tests/test_pool.py +++ b/src/epplibwrapper/tests/test_pool.py @@ -22,6 +22,7 @@ logger = logging.getLogger(__name__) @patch("djangooidc.views.CLIENT", autospec=True) class TestConnectionPool(MockEppLib): """Tests for our connection pooling behaviour""" + def setUp(self): """ Background: @@ -33,9 +34,7 @@ class TestConnectionPool(MockEppLib): # Current pool size "size": 1, # Which errors the pool should look out for - "exc_classes": ( - TransportError, - ), + "exc_classes": (TransportError,), # Occasionally pings the registry to keep the connection alive. # Value in seconds => (keepalive / size) "keepalive": 60, @@ -43,7 +42,7 @@ class TestConnectionPool(MockEppLib): def tearDown(self): super().tearDown() - + def user_info(*args): return { "sub": "TEST", @@ -69,16 +68,16 @@ class TestConnectionPool(MockEppLib): # in client.py. They should remain unchanged, # and if they aren't, something went wrong. expected_login = commands.Login( - cl_id='nothing', - password='nothing', + cl_id="nothing", + password="nothing", obj_uris=[ - 'urn:ietf:params:xml:ns:domain-1.0', - 'urn:ietf:params:xml:ns:contact-1.0' + "urn:ietf:params:xml:ns:domain-1.0", + "urn:ietf:params:xml:ns:contact-1.0", ], new_pw=None, - version='1.0', - lang='en', - ext_uris=[] + version="1.0", + lang="en", + ext_uris=[], ) # Key/cert will generate a new file everytime. @@ -94,7 +93,7 @@ class TestConnectionPool(MockEppLib): ).__dict__ except Exception as err: self.fail(err) - + # We don't care about checking if the objects are both of # the same reference, we only care about data parity, so # we do a dict conversion. @@ -118,15 +117,14 @@ class TestConnectionPool(MockEppLib): @skip("not implemented yet") def test_pool_timesout(self): """The pool timesout and restarts""" - raise - + raise + @skip("not implemented yet") def test_multiple_users_send_data(self): """Multiple users send data concurrently""" raise - + @skip("not implemented yet") def test_pool_sends_data(self): """A .send is invoked on the pool""" raise - diff --git a/src/epplibwrapper/utility/pool.py b/src/epplibwrapper/utility/pool.py index 3784f3fbc..00bf0ffd0 100644 --- a/src/epplibwrapper/utility/pool.py +++ b/src/epplibwrapper/utility/pool.py @@ -22,6 +22,7 @@ class EPPConnectionPool(ConnectionPool): options (dict): Options for the ConnectionPool base class """ + def __init__(self, client, login, options: dict): # For storing shared credentials self._client = client @@ -51,11 +52,11 @@ class EPPConnectionPool(ConnectionPool): """Creates and returns a socket instance""" socket = Socket(client, login) return socket - + def get_connections(self): """Returns the connection queue""" return self.conn - + def kill_all_connections(self): """Kills all active connections in the pool.""" try: @@ -66,11 +67,9 @@ class EPPConnectionPool(ConnectionPool): self.lock.release() # TODO - connection pool err except Exception as err: - logger.error( - "Could not kill all connections." - ) + logger.error("Could not kill all connections.") raise err - + def repopulate_all_connections(self): """Regenerates the connection pool. If any connections exist, kill them first. @@ -80,6 +79,4 @@ class EPPConnectionPool(ConnectionPool): for i in range(self.size): self.lock.acquire() for i in range(self.size): - gevent.spawn_later(self.SPAWN_FREQUENCY*i, self._addOne) - - + gevent.spawn_later(self.SPAWN_FREQUENCY * i, self._addOne) diff --git a/src/epplibwrapper/utility/pool_status.py b/src/epplibwrapper/utility/pool_status.py index 82c032941..214bf8ac1 100644 --- a/src/epplibwrapper/utility/pool_status.py +++ b/src/epplibwrapper/utility/pool_status.py @@ -1,5 +1,6 @@ class PoolStatus: """A list of Booleans to keep track of Pool Status""" + def __init__(self): self.pool_running = False self.connection_success = False