mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-14 06:55:08 +02:00
Tests / pool restart on freeze
This commit is contained in:
parent
ea9c05bd8a
commit
a9e4d34099
4 changed files with 150 additions and 2 deletions
|
@ -3,6 +3,8 @@
|
||||||
import logging
|
import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
from gevent import Timeout
|
||||||
|
|
||||||
from epplibwrapper.utility.pool_status import PoolStatus
|
from epplibwrapper.utility.pool_status import PoolStatus
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -84,14 +86,22 @@ class EPPLibWrapper:
|
||||||
def _send(self, command):
|
def _send(self, command):
|
||||||
"""Helper function used by `send`."""
|
"""Helper function used by `send`."""
|
||||||
cmd_type = command.__class__.__name__
|
cmd_type = command.__class__.__name__
|
||||||
|
# Start a timeout to check if the pool is hanging
|
||||||
|
timeout = Timeout(settings.POOL_TIMEOUT)
|
||||||
|
timeout.start()
|
||||||
try:
|
try:
|
||||||
if not self.pool_status.connection_success:
|
if not self.pool_status.connection_success:
|
||||||
raise LoginError(
|
raise LoginError(
|
||||||
"Couldn't connect to the registry after three attempts"
|
"Couldn't connect to the registry after three attempts"
|
||||||
)
|
)
|
||||||
# TODO - add a timeout
|
|
||||||
with self._pool.get() as connection:
|
with self._pool.get() as connection:
|
||||||
response = connection.send(command)
|
response = connection.send(command)
|
||||||
|
except Timeout as t:
|
||||||
|
if t is timeout:
|
||||||
|
# Flag that the pool is frozen,
|
||||||
|
# then restart the pool.
|
||||||
|
self.pool_status.pool_hanging = True
|
||||||
|
self.start_connection_pool()
|
||||||
except (ValueError, ParsingError) as err:
|
except (ValueError, ParsingError) as err:
|
||||||
message = f"{cmd_type} failed to execute due to some syntax error."
|
message = f"{cmd_type} failed to execute due to some syntax error."
|
||||||
logger.warning(message, exc_info=True)
|
logger.warning(message, exc_info=True)
|
||||||
|
@ -115,6 +125,8 @@ class EPPLibWrapper:
|
||||||
raise RegistryError(response.msg, code=response.code)
|
raise RegistryError(response.msg, code=response.code)
|
||||||
else:
|
else:
|
||||||
return response
|
return response
|
||||||
|
finally:
|
||||||
|
timeout.close()
|
||||||
|
|
||||||
def send(self, command, *, cleaned=False):
|
def send(self, command, *, cleaned=False):
|
||||||
"""Login, send the command, then close the connection. Tries 3 times."""
|
"""Login, send the command, then close the connection. Tries 3 times."""
|
||||||
|
@ -155,7 +167,6 @@ class EPPLibWrapper:
|
||||||
If an instance of the pool already exists,
|
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."""
|
It is generally recommended to keep this enabled."""
|
||||||
|
|
||||||
# Since we reuse the same creds for each pool, we can test on
|
# Since we reuse the same creds for each pool, we can test on
|
||||||
# one socket, and if successful, then we know we can connect.
|
# one socket, and if successful, then we know we can connect.
|
||||||
if settings.DEBUG or self._test_registry_connection_success():
|
if settings.DEBUG or self._test_registry_connection_success():
|
||||||
|
@ -179,6 +190,7 @@ class EPPLibWrapper:
|
||||||
client=self._client, login=self._login, options=self.pool_options
|
client=self._client, login=self._login, options=self.pool_options
|
||||||
)
|
)
|
||||||
self.pool_status.pool_running = True
|
self.pool_status.pool_running = True
|
||||||
|
self.pool_status.pool_hanging = False
|
||||||
|
|
||||||
logger.info("Connection pool started")
|
logger.info("Connection pool started")
|
||||||
|
|
||||||
|
|
0
src/epplibwrapper/tests/__init__.py
Normal file
0
src/epplibwrapper/tests/__init__.py
Normal file
132
src/epplibwrapper/tests/test_pool.py
Normal file
132
src/epplibwrapper/tests/test_pool.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
from unittest import skip
|
||||||
|
from unittest.mock import patch
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from django.test import Client
|
||||||
|
from epplibwrapper.client import EPPLibWrapper
|
||||||
|
from registrar.tests.common import MockEppLib
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
try:
|
||||||
|
from epplib.client import Client
|
||||||
|
from epplib import commands
|
||||||
|
from epplib.exceptions import TransportError
|
||||||
|
from epplib.transport import SocketTransport
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("djangooidc.views.CLIENT", autospec=True)
|
||||||
|
class TestConnectionPool(MockEppLib):
|
||||||
|
"""Tests for our connection pooling behaviour"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Background:
|
||||||
|
Given the registrant is logged in
|
||||||
|
And the registrant is the admin on a domain
|
||||||
|
"""
|
||||||
|
super().setUp()
|
||||||
|
self.pool_options = {
|
||||||
|
# Current pool size
|
||||||
|
"size": 1,
|
||||||
|
# Which errors the pool should look out for
|
||||||
|
"exc_classes": (
|
||||||
|
TransportError,
|
||||||
|
),
|
||||||
|
# Occasionally pings the registry to keep the connection alive.
|
||||||
|
# Value in seconds => (keepalive / size)
|
||||||
|
"keepalive": 60,
|
||||||
|
}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def user_info(*args):
|
||||||
|
return {
|
||||||
|
"sub": "TEST",
|
||||||
|
"email": "test@example.com",
|
||||||
|
"first_name": "Testy",
|
||||||
|
"last_name": "Tester",
|
||||||
|
"phone": "814564000",
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_pool_created_successfully(self, mock_client):
|
||||||
|
# setup
|
||||||
|
session = self.client.session
|
||||||
|
session["state"] = "TEST" # nosec B105
|
||||||
|
session.save()
|
||||||
|
# mock
|
||||||
|
mock_client.callback.side_effect = self.user_info
|
||||||
|
|
||||||
|
client = EPPLibWrapper()
|
||||||
|
pool = client._pool
|
||||||
|
|
||||||
|
# These are defined outside of the pool,
|
||||||
|
# so we can reimplement how this is being done
|
||||||
|
# in client.py. They should remain unchanged,
|
||||||
|
# and if they aren't, something went wrong.
|
||||||
|
expected_login = commands.Login(
|
||||||
|
cl_id='nothing',
|
||||||
|
password='nothing',
|
||||||
|
obj_uris=[
|
||||||
|
'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=[]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Key/cert will generate a new file everytime.
|
||||||
|
# This should never be null, so we can check for that.
|
||||||
|
try:
|
||||||
|
expected_client = Client(
|
||||||
|
SocketTransport(
|
||||||
|
settings.SECRET_REGISTRY_HOSTNAME,
|
||||||
|
cert_file=pool._client.transport.cert_file,
|
||||||
|
key_file=pool._client.transport.key_file,
|
||||||
|
password=settings.SECRET_REGISTRY_KEY_PASSPHRASE,
|
||||||
|
)
|
||||||
|
).__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.
|
||||||
|
actual_client = pool._client.__dict__
|
||||||
|
actual_client["transport"] = actual_client["transport"].__dict__
|
||||||
|
expected_client["transport"] = expected_client["transport"].__dict__
|
||||||
|
|
||||||
|
# Ensure that we're getting the credentials we expect
|
||||||
|
self.assertEqual(pool._login, expected_login)
|
||||||
|
self.assertEqual(actual_client, expected_client)
|
||||||
|
|
||||||
|
# Check that options are set correctly
|
||||||
|
self.assertEqual(pool.size, self.pool_options["size"])
|
||||||
|
self.assertEqual(pool.keepalive, self.pool_options["keepalive"])
|
||||||
|
self.assertEqual(pool.exc_classes, self.pool_options["exc_classes"])
|
||||||
|
|
||||||
|
# Check that it is running
|
||||||
|
self.assertEqual(client.pool_status.connection_success, True)
|
||||||
|
self.assertEqual(client.pool_status.pool_running, True)
|
||||||
|
|
||||||
|
@skip("not implemented yet")
|
||||||
|
def test_pool_timesout(self):
|
||||||
|
"""The pool timesout and restarts"""
|
||||||
|
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
|
||||||
|
|
|
@ -548,6 +548,10 @@ EPP_CONNECTION_POOL_SIZE = 1
|
||||||
# Calculated as POOL_KEEP_ALIVE / EPP_CONNECTION_POOL_SIZE
|
# Calculated as POOL_KEEP_ALIVE / EPP_CONNECTION_POOL_SIZE
|
||||||
POOL_KEEP_ALIVE = 60 # Ping every 20 seconds
|
POOL_KEEP_ALIVE = 60 # Ping every 20 seconds
|
||||||
|
|
||||||
|
# Determines how long we try to keep a pool alive for,
|
||||||
|
# before restarting it.
|
||||||
|
POOL_TIMEOUT = 60
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
# region: Security and Privacy----------------------------------------------###
|
# region: Security and Privacy----------------------------------------------###
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue