2017-05-23 03:59:55 +01:00
|
|
|
import time
|
|
|
|
import json
|
|
|
|
import shlex
|
|
|
|
import subprocess
|
|
|
|
import urllib.request
|
2017-06-22 21:26:50 +01:00
|
|
|
from unittest import mock
|
2018-10-09 03:40:36 +01:00
|
|
|
from sanic.worker import GunicornWorker
|
|
|
|
from sanic.app import Sanic
|
2017-06-22 21:26:50 +01:00
|
|
|
import asyncio
|
2017-05-23 03:59:55 +01:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
2018-12-30 11:18:06 +00:00
|
|
|
@pytest.fixture(scope="module")
|
2017-05-23 03:59:55 +01:00
|
|
|
def gunicorn_worker():
|
2018-09-29 18:54:47 +01:00
|
|
|
command = (
|
2018-12-30 11:18:06 +00:00
|
|
|
"gunicorn "
|
|
|
|
"--bind 127.0.0.1:1337 "
|
|
|
|
"--worker-class sanic.worker.GunicornWorker "
|
|
|
|
"examples.simple_server:app"
|
2018-09-29 18:54:47 +01:00
|
|
|
)
|
2017-05-23 03:59:55 +01:00
|
|
|
worker = subprocess.Popen(shlex.split(command))
|
2017-06-11 17:06:48 +01:00
|
|
|
time.sleep(3)
|
2017-05-23 03:59:55 +01:00
|
|
|
yield
|
|
|
|
worker.kill()
|
|
|
|
|
|
|
|
|
2019-02-06 18:29:33 +00:00
|
|
|
@pytest.fixture(scope="module")
|
2018-12-30 18:37:30 +00:00
|
|
|
def gunicorn_worker_with_access_logs():
|
2018-12-29 22:56:01 +00:00
|
|
|
command = (
|
2019-02-06 18:29:33 +00:00
|
|
|
"gunicorn "
|
|
|
|
"--bind 127.0.0.1:1338 "
|
|
|
|
"--worker-class sanic.worker.GunicornWorker "
|
|
|
|
"examples.simple_server:app"
|
2018-12-29 22:56:01 +00:00
|
|
|
)
|
|
|
|
worker = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
|
|
|
time.sleep(2)
|
|
|
|
return worker
|
|
|
|
|
|
|
|
|
2019-02-06 18:29:33 +00:00
|
|
|
@pytest.fixture(scope="module")
|
2018-12-30 18:37:30 +00:00
|
|
|
def gunicorn_worker_with_env_var():
|
2018-12-29 22:56:01 +00:00
|
|
|
command = (
|
2018-12-30 18:37:30 +00:00
|
|
|
'env SANIC_ACCESS_LOG="False" '
|
2019-02-06 18:29:33 +00:00
|
|
|
"gunicorn "
|
|
|
|
"--bind 127.0.0.1:1339 "
|
|
|
|
"--worker-class sanic.worker.GunicornWorker "
|
|
|
|
"--log-level info "
|
|
|
|
"examples.simple_server:app"
|
2018-12-29 22:56:01 +00:00
|
|
|
)
|
|
|
|
worker = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
|
|
|
time.sleep(2)
|
|
|
|
return worker
|
|
|
|
|
|
|
|
|
2017-05-23 03:59:55 +01:00
|
|
|
def test_gunicorn_worker(gunicorn_worker):
|
2018-12-30 11:18:06 +00:00
|
|
|
with urllib.request.urlopen("http://localhost:1337/") as f:
|
2017-05-23 03:59:55 +01:00
|
|
|
res = json.loads(f.read(100).decode())
|
2018-12-30 11:18:06 +00:00
|
|
|
assert res["test"]
|
2017-06-22 21:26:50 +01:00
|
|
|
|
|
|
|
|
2018-12-30 18:37:30 +00:00
|
|
|
def test_gunicorn_worker_no_logs(gunicorn_worker_with_env_var):
|
2018-12-29 22:56:01 +00:00
|
|
|
"""
|
2018-12-30 18:37:30 +00:00
|
|
|
if SANIC_ACCESS_LOG was set to False do not show access logs
|
2018-12-29 22:56:01 +00:00
|
|
|
"""
|
2019-02-06 18:29:33 +00:00
|
|
|
with urllib.request.urlopen("http://localhost:1339/") as _:
|
2018-12-30 18:37:30 +00:00
|
|
|
gunicorn_worker_with_env_var.kill()
|
|
|
|
assert not gunicorn_worker_with_env_var.stdout.read()
|
2018-12-29 22:56:01 +00:00
|
|
|
|
|
|
|
|
2018-12-30 18:37:30 +00:00
|
|
|
def test_gunicorn_worker_with_logs(gunicorn_worker_with_access_logs):
|
2018-12-29 22:56:01 +00:00
|
|
|
"""
|
2018-12-30 18:37:30 +00:00
|
|
|
default - show access logs
|
2018-12-29 22:56:01 +00:00
|
|
|
"""
|
2019-02-06 18:29:33 +00:00
|
|
|
with urllib.request.urlopen("http://localhost:1338/") as _:
|
2018-12-30 18:37:30 +00:00
|
|
|
gunicorn_worker_with_access_logs.kill()
|
2019-02-06 18:29:33 +00:00
|
|
|
assert (
|
|
|
|
b"(sanic.access)[INFO][127.0.0.1"
|
|
|
|
in gunicorn_worker_with_access_logs.stdout.read()
|
|
|
|
)
|
2018-12-29 22:56:01 +00:00
|
|
|
|
|
|
|
|
2017-06-22 21:26:50 +01:00
|
|
|
class GunicornTestWorker(GunicornWorker):
|
|
|
|
def __init__(self):
|
|
|
|
self.app = mock.Mock()
|
|
|
|
self.app.callable = Sanic("test_gunicorn_worker")
|
|
|
|
self.servers = {}
|
|
|
|
self.exit_code = 0
|
|
|
|
self.cfg = mock.Mock()
|
|
|
|
self.notify = mock.Mock()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def worker():
|
|
|
|
return GunicornTestWorker()
|
|
|
|
|
|
|
|
|
|
|
|
def test_worker_init_process(worker):
|
2018-12-30 11:18:06 +00:00
|
|
|
with mock.patch("sanic.worker.asyncio") as mock_asyncio:
|
2017-06-22 21:26:50 +01:00
|
|
|
try:
|
|
|
|
worker.init_process()
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
assert mock_asyncio.get_event_loop.return_value.close.called
|
|
|
|
assert mock_asyncio.new_event_loop.called
|
|
|
|
assert mock_asyncio.set_event_loop.called
|
|
|
|
|
|
|
|
|
|
|
|
def test_worker_init_signals(worker):
|
|
|
|
worker.loop = mock.Mock()
|
|
|
|
worker.init_signals()
|
|
|
|
assert worker.loop.add_signal_handler.called
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_abort(worker):
|
2018-12-30 11:18:06 +00:00
|
|
|
with mock.patch("sanic.worker.sys") as mock_sys:
|
2017-06-22 21:26:50 +01:00
|
|
|
worker.handle_abort(object(), object())
|
|
|
|
assert not worker.alive
|
|
|
|
assert worker.exit_code == 1
|
|
|
|
mock_sys.exit.assert_called_with(1)
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_quit(worker):
|
|
|
|
worker.handle_quit(object(), object())
|
|
|
|
assert not worker.alive
|
|
|
|
assert worker.exit_code == 0
|
|
|
|
|
|
|
|
|
|
|
|
def test_run_max_requests_exceeded(worker):
|
|
|
|
loop = asyncio.new_event_loop()
|
|
|
|
worker.ppid = 1
|
|
|
|
worker.alive = True
|
|
|
|
sock = mock.Mock()
|
2018-12-30 11:18:06 +00:00
|
|
|
sock.cfg_addr = ("localhost", 8080)
|
2017-06-22 21:26:50 +01:00
|
|
|
worker.sockets = [sock]
|
|
|
|
worker.wsgi = mock.Mock()
|
|
|
|
worker.connections = set()
|
|
|
|
worker.log = mock.Mock()
|
|
|
|
worker.loop = loop
|
|
|
|
worker.servers = {
|
|
|
|
"server1": {"requests_count": 14},
|
|
|
|
"server2": {"requests_count": 15},
|
|
|
|
}
|
|
|
|
worker.max_requests = 10
|
|
|
|
worker._run = mock.Mock(wraps=asyncio.coroutine(lambda *a, **kw: None))
|
|
|
|
|
|
|
|
# exceeding request count
|
|
|
|
_runner = asyncio.ensure_future(worker._check_alive(), loop=loop)
|
|
|
|
loop.run_until_complete(_runner)
|
|
|
|
|
2018-10-09 03:40:36 +01:00
|
|
|
assert not worker.alive
|
2017-06-22 21:26:50 +01:00
|
|
|
worker.notify.assert_called_with()
|
2018-12-30 11:18:06 +00:00
|
|
|
worker.log.info.assert_called_with(
|
|
|
|
"Max requests exceeded, shutting " "down: %s", worker
|
|
|
|
)
|
2017-06-25 08:51:59 +01:00
|
|
|
|
2018-10-09 03:40:36 +01:00
|
|
|
|
2017-06-25 08:51:59 +01:00
|
|
|
def test_worker_close(worker):
|
2017-06-25 09:03:28 +01:00
|
|
|
loop = asyncio.new_event_loop()
|
|
|
|
asyncio.sleep = mock.Mock(wraps=asyncio.coroutine(lambda *a, **kw: None))
|
2017-06-25 08:51:59 +01:00
|
|
|
worker.ppid = 1
|
|
|
|
worker.pid = 2
|
|
|
|
worker.cfg.graceful_timeout = 1.0
|
|
|
|
worker.signal = mock.Mock()
|
|
|
|
worker.signal.stopped = False
|
|
|
|
worker.wsgi = mock.Mock()
|
|
|
|
conn = mock.Mock()
|
|
|
|
conn.websocket = mock.Mock()
|
|
|
|
conn.websocket.close_connection = mock.Mock(
|
2018-10-09 03:40:36 +01:00
|
|
|
wraps=asyncio.coroutine(lambda *a, **kw: None)
|
|
|
|
)
|
2017-06-25 08:51:59 +01:00
|
|
|
worker.connections = set([conn])
|
|
|
|
worker.log = mock.Mock()
|
|
|
|
worker.loop = loop
|
|
|
|
server = mock.Mock()
|
|
|
|
server.close = mock.Mock(wraps=lambda *a, **kw: None)
|
2018-12-30 11:18:06 +00:00
|
|
|
server.wait_closed = mock.Mock(
|
|
|
|
wraps=asyncio.coroutine(lambda *a, **kw: None)
|
|
|
|
)
|
|
|
|
worker.servers = {server: {"requests_count": 14}}
|
2017-06-25 08:51:59 +01:00
|
|
|
worker.max_requests = 10
|
|
|
|
|
|
|
|
# close worker
|
|
|
|
_close = asyncio.ensure_future(worker.close(), loop=loop)
|
|
|
|
loop.run_until_complete(_close)
|
|
|
|
|
2018-10-09 03:40:36 +01:00
|
|
|
assert worker.signal.stopped
|
|
|
|
assert conn.websocket.close_connection.called
|
2017-06-25 08:51:59 +01:00
|
|
|
assert len(worker.servers) == 0
|