Skip to content
This repository was archived by the owner on Sep 26, 2022. It is now read-only.

Pick WSGI server to run the API #196

Merged
merged 5 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions teos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

DATA_DIR = os.path.expanduser("~/.teos/")
CONF_FILE_NAME = "teos.conf"

# Default conf fields
DEFAULT_CONF = {
"API_BIND": {"value": "localhost", "type": str},
"API_PORT": {"value": 9814, "type": int},
Expand All @@ -26,8 +24,11 @@
"MIN_TO_SELF_DELAY": {"value": 20, "type": int},
"LOCATOR_CACHE_SIZE": {"value": 6, "type": int},
"OVERWRITE_KEY": {"value": False, "type": bool},
"WSGI": {"value": "gunicorn", "type": str},
"LOG_FILE": {"value": "teos.log", "type": str, "path": True},
"TEOS_SECRET_KEY": {"value": "teos_sk.der", "type": str, "path": True},
"APPOINTMENTS_DB_PATH": {"value": "appointments", "type": str, "path": True},
"USERS_DB_PATH": {"value": "users", "type": str, "path": True},
}

# Default conf fields
32 changes: 22 additions & 10 deletions teos/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import grpc
import multiprocessing as mpp
from google.protobuf import json_format
from flask import Flask, request, jsonify

Expand Down Expand Up @@ -56,27 +57,38 @@ def get_request_data_json(request):
raise InvalidParameter("Request is not json encoded")


def serve(internal_api_endpoint, min_to_self_delay, log_file):
def serve(internal_api_endpoint, endpoint, min_to_self_delay, log_file, auto_run=False):
"""
Starts the API.

This method is handled by an external WSGI server, such as gunicorn.

Notice since this is run via terminal (or via subprocess.Popen) all arguments are strings.
This method can be handled either form an external WSGI (like gunicorn) or by the Flask development server.

Args:
internal_api_endpoint (:obj:`str`): Endpoint where the internal api is running (host:port).
min_to_self_delay (:obj:`str`): The minimum to_self_delay accepted by the Inspector.
log_file (:obj:`str`): The file_path where to store logs.
internal_api_endpoint (:obj:`str`): endpoint where the internal api is running (host:port).
endpoint (:obj:`str`): endpoint where the http api will be running (host:port).
min_to_self_delay (:obj:`str`): the minimum to_self_delay accepted by the Inspector.
log_file (:obj:`str`): the file_path where to store logs.
auto_run (:obj:`bool`): whether the server should be started by this process. False if run with an external
WSGI. True is run by Flask.

Returns:
The application object needed by the WSGI server to run.
The application object needed by the WSGI server to run if ``auto_run`` if ``False``, ``None`` otherwise.
"""

setup_logging(log_file)
# For Python 3.7- and 3.8+ compatibility. Processes for MacOS are created as fork up to 3.8 by default, then it got
# changed to spawn.
if mpp.get_start_method() == "spawn":
setup_logging(log_file)
inspector = Inspector(int(min_to_self_delay))
api = API(inspector, internal_api_endpoint)
return api.app

api.logger.info(f"Initialized. Serving at {endpoint}")

if auto_run:
host, port = endpoint.split(":")
api.app.run(host=host, port=port)
else:
return api.app


class API:
Expand Down
8 changes: 1 addition & 7 deletions teos/cli/teos_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@
from common.exceptions import InvalidParameter

from teos import DEFAULT_CONF, DATA_DIR, CONF_FILE_NAME
from teos.cli.help import (
show_usage,
help_get_all_appointments,
help_get_tower_info,
help_get_users,
help_get_user,
)
from teos.cli.help import show_usage, help_get_all_appointments, help_get_tower_info, help_get_users, help_get_user
from teos.protobuf.tower_services_pb2_grpc import TowerServicesStub
from teos.protobuf.user_pb2 import GetUserRequest

Expand Down
3 changes: 3 additions & 0 deletions teos/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ def show_usage():
"\n\nGLOBAL OPTIONS (all modifiable in conf file):"
"\n\t--apibind \t\tAddress that teos API will bind to. Defaults to 'localhost'."
"\n\t--apiport \t\tPort that teos API will bind to. Defaults to '9814'."
"\n\t--rpcbind \t\tAddress that teos RPC server will bind to. Defaults to 'localhost'."
"\n\t--rpcport \t\tPort that teos RPC server will bind to. Defaults to '8814'."
"\n\t--btcnetwork \t\tNetwork bitcoind is connected to. Either mainnet, testnet or regtest. Defaults to "
"'mainnet'."
"\n\t--btcrpcuser \t\tbitcoind rpcuser. Defaults to 'user'."
Expand All @@ -14,6 +16,7 @@ def show_usage():
"\n\t--btcfeedconnect \tbitcoind zmq hostname (for blocks). Defaults to 'localhost'."
"\n\t--btcfeedport \t\tbitcoind zmq port (for blocks). Defaults to '28332'."
"\n\t--datadir \t\tSpecify data directory. Defaults to '~\\.teos'."
"\n\t--wsgi \t\tThe WSGI server used to run the API. Either 'gunicorn' or 'flask'. Defaults to 'gunicorn'."
"\n\t-d, --daemon \t\tRun in background as a daemon."
"\n\t--overwritekey \t\tOverwrites the tower secret key. THIS IS IRREVERSIBLE AND WILL CHANGE YOUR TOWER ID."
"\n\t-h, --help \t\tShows this message."
Expand Down
6 changes: 3 additions & 3 deletions teos/protobuf/appointment_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion teos/protobuf/tower_services_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions teos/protobuf/user_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 52 additions & 31 deletions teos/teosd.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from common.cryptographer import Cryptographer
from common.tools import setup_data_folder

import teos.api as api
import teos.rpc as rpc
from teos.help import show_usage
from teos.watcher import Watcher
Expand Down Expand Up @@ -198,16 +199,30 @@ def main(config):
# FIXME: 92-block-data-during-bootstrap-db
chain_monitor.monitor_chain()

# Start the API (using gunicorn) and the RPC server
# FIXME: We may like to add workers depending on a config value
subprocess.Popen(
[
"gunicorn",
f"--bind={config.get('API_BIND')}:{config.get('API_PORT')}",
f"teos.api:serve(internal_api_endpoint='{INTERNAL_API_ENDPOINT}', "
f"min_to_self_delay='{config.get('MIN_TO_SELF_DELAY')}', log_file='{config.get('LOG_FILE')}')",
]
)
# Start the API and the RPC server
api_endpoint = f"{config.get('API_BIND')}:{config.get('API_PORT')}"
if config.get("WSGI") == "gunicorn":
# FIXME: We may like to add workers depending on a config value
subprocess.Popen(
[
"gunicorn",
f"--bind={api_endpoint}",
f"teos.api:serve(internal_api_endpoint='{INTERNAL_API_ENDPOINT}', "
f"endpoint='{api_endpoint}', min_to_self_delay='{config.get('MIN_TO_SELF_DELAY')}', "
f"log_file='{config.get('LOG_FILE')}')",
]
)
else:
Process(
target=api.serve,
kwargs={
"internal_api_endpoint": INTERNAL_API_ENDPOINT,
"endpoint": api_endpoint,
"min_to_self_delay": config.get("MIN_TO_SELF_DELAY"),
"log_file": config.get("LOG_FILE"),
"auto_run": True,
},
).start()

Process(
target=rpc.serve,
Expand All @@ -227,28 +242,29 @@ def main(config):
command_line_conf = {}
data_dir = DATA_DIR

opts, _ = getopt(
argv[1:],
"hd",
[
"apibind=",
"apiport=",
"rpcbind=",
"rpcport=",
"btcnetwork=",
"btcrpcuser=",
"btcrpcpassword=",
"btcrpcconnect=",
"btcrpcport=",
"btcfeedconnect=",
"btcfeedport=",
"datadir=",
"wsgi=",
"daemon",
"overwritekey",
"help",
],
)
try:
opts, _ = getopt(
argv[1:],
"hd",
[
"apibind=",
"apiport=",
"rpcbind=",
"rpcport=",
"btcnetwork=",
"btcrpcuser=",
"btcrpcpassword=",
"btcrpcconnect=",
"btcrpcport=",
"btcfeedconnect=",
"btcfeedport=",
"datadir=",
"daemon",
"overwritekey",
"help",
],
)
for opt, arg in opts:
if opt in ["--apibind"]:
command_line_conf["API_BIND"] = arg
Expand Down Expand Up @@ -286,6 +302,11 @@ def main(config):
exit("btcfeedport must be an integer")
if opt in ["--datadir"]:
data_dir = os.path.expanduser(arg)
if opt in ["--wsgi"]:
if arg in ["gunicorn", "flask"]:
command_line_conf["WSGI"] = arg
else:
exit("wsgi must be either gunicorn or flask")
if opt in ["-d", "--daemon"]:
command_line_conf["DAEMON"] = True
if opt in ["--overwritekey"]:
Expand Down
10 changes: 1 addition & 9 deletions test/common/unit/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,7 @@ def test_setup_data_folder():


def test_intify_unchanged():
test_cases = [
0,
1.1,
True,
False,
"yo",
{},
[],
]
test_cases = [0, 1.1, True, False, "yo", {}, []]
for x in test_cases:
assert intify(x) == x

Expand Down
3 changes: 3 additions & 0 deletions test/teos/e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def teosd():
yield teosd_process, teos_id

shutil.rmtree(".teos")
teosd_process.terminate()


def run_teosd():
Expand All @@ -30,6 +31,8 @@ def run_teosd():

teos_id = Cryptographer.get_compressed_pk(teos_sk.public_key)

# Set teos to run with flask (should be good enough for testing).
config["WSGI"] = "flask"
teosd_process = Process(target=main, kwargs={"config": config})
teosd_process.start()

Expand Down