Skip to content
This repository has been archived by the owner on Jan 24, 2018. It is now read-only.

Only use flask for debug #1607

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
27 changes: 27 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ Configuration
The GA4GH reference server `Configuration file`_. allows Flask and application
specific configuration values to be set.

-------------------
Starting the server
-------------------

The ``ga4gh_server`` command looks attempts to start a server for a given
configuration. Please see below for mandatory configuration settings, including
``DATA_SOURCE``.

.. argparse::
:module: ga4gh.server.cli.server
:func: getServerParser
:prog: ga4gh_server
:nodefault:

++++++++++++++++++++
Gunicorn WSGI Server
++++++++++++++++++++

Using the ``-g`` option allows one to run the server in an experimental
mode behind the http://gunicorn.org/ WSGI HTTP Server. This allows multiple
workers to spawn to handle simultaneous requests. For some purposes, this will
allow an implementor to avoid the need to configure an Apache or nginx process.

This feature is experimental. Please post your issues to https://github.com/ga4gh/schemas/issues .
Currently, the logging facility and TLS mode are known to not work under
gunicorn.

------------------
Configuration file
------------------
Expand Down
56 changes: 50 additions & 6 deletions ga4gh/server/cli/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,32 @@
from __future__ import unicode_literals

import requests
import multiprocessing
import gunicorn.app.base

import ga4gh.server.cli as cli
import ga4gh.server.frontend as frontend

import ga4gh.common.cli as common_cli


class StandaloneApplication(gunicorn.app.base.BaseApplication):
def __init__(self, app, options=None):
self.options = options or {}
self.application = app
super(StandaloneApplication, self).__init__()

def load_config(self):
config = dict(
[(key, value) for key, value in self.options.iteritems()
if key in self.cfg.settings and value is not None])
for key, value in config.iteritems():
self.cfg.set(key.lower(), value)

def load(self):
return self.application


def addServerOptions(parser):
parser.add_argument(
"--port", "-P", default=8000, type=int,
Expand All @@ -28,20 +47,42 @@ def addServerOptions(parser):
help="The configuration file to use")
parser.add_argument(
"--tls", "-t", action="store_true", default=False,
help="Start in TLS (https) mode.")
help="Start in TLS (https) mode (for Flask debug)")
parser.add_argument(
"--dont-use-reloader", default=False, action="store_true",
help="Don't use the flask reloader")
help="Don't use the Flask reloader (for Flask debug)")
parser.add_argument(
"--debug", "-d", action='store_true', default=False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the docs say the -g flag is to run gunicorn

Copy link
Member Author

@david4096 david4096 Mar 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I started by making running under gunicorn a flag, but I believe the default functionality when running ga4gh_server should be to run in a multiworker environment. The -d will run using the Flask debug server (werkzeug).

help="Runs the server using the gunicorn WSGI server.")
cli.addVersionArgument(parser)
cli.addDisableUrllibWarningsArgument(parser)


def runGunicornServer(parsedArgs):
options = {
'bind': '%s:%s' % (parsedArgs.host, parsedArgs.port),
'workers': number_of_workers(),
'accesslog': '-', # Puts the access log on stdout
'errorlog': '-' # Puts the error log on stdout
}
app = StandaloneApplication(frontend.app, options)
app.run()
return app


def getServerParser():
"""
Used by sphinx.argparse.
"""
parser = common_cli.createArgumentParser("GA4GH reference server")
addServerOptions(parser)
return parser


def number_of_workers():
return (multiprocessing.cpu_count() * 2) + 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably have a comment here justifying this formula

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.



def server_main(args=None):
parser = getServerParser()
parsedArgs = parser.parse_args(args)
Expand All @@ -52,7 +93,10 @@ def server_main(args=None):
sslContext = None
if parsedArgs.tls or ("OIDC_PROVIDER" in frontend.app.config):
sslContext = "adhoc"
frontend.app.run(
host=parsedArgs.host, port=parsedArgs.port,
use_reloader=not parsedArgs.dont_use_reloader,
ssl_context=sslContext)
if parsedArgs.debug:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why this is called debug ... seems like this should be run_guincorn or something...

frontend.app.run(host=parsedArgs.host,
port=parsedArgs.port,
use_reloader=not parsedArgs.dont_use_reloader,
ssl_context=sslContext)
else:
runGunicornServer(parsedArgs)
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
# these libraries are the set listed by pipdeptree -f -w
# that are dependencies of libraries listed in the next section

# Adding the constraints.txt allows you to choose a specific
# Adding the constraints.txt allows you to choose a specific
# way to resolve our internal dependencies. During development,
# the constraints file will point at the current master branch
# the constraints file will point at the current master branch
# of the respective module.
ga4gh-common
ga4gh-schemas
Expand All @@ -37,6 +37,7 @@ future==0.15.2
pyjwkest==1.0.1
PyJWT==1.4.2
peewee==2.8.5
gunicorn==19.7.0

### This section is for the actual libraries ###
# these libraries are imported in code that can be reached via
Expand Down
1 change: 1 addition & 0 deletions tests/end_to_end/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def getCmdLine(self):
python server_dev.py
--dont-use-reloader
--disable-urllib-warnings
-d
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best to use the non-shorthand version of the flag here, for clarity in reading this file

--host 0.0.0.0
--config TestConfig
--config-file {}
Expand Down
32 changes: 32 additions & 0 deletions tests/unit/test_cli_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Tests related to the server start script
"""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import unittest

import ga4gh.server.cli.server as server
import ga4gh.server.frontend as frontend


class TestExceptionHandler(unittest.TestCase):
"""
Test that the server script functions behave in expected ways.
"""
def testGetServerParser(self):
self.assertIsNotNone(server.getServerParser(),
"The server parser should be returned so "
"that we can create docs for sphinx")

def test_number_of_workers(self):
self.assertTrue(type(server.number_of_workers()) == int,
"The number of workers function should return an "
"integer.")

def testStandaloneApplicationInstance(self):
app = server.StandaloneApplication(frontend.app)
self.assertIsNotNone(app.run, "Ensures the class instantiates from "
"our WSGI app properly. The run "
"function will spawn many processes.")