From 5d8cda6233ed48028f58238a69993a7123afe11a Mon Sep 17 00:00:00 2001 From: John Chilton <jmchilton@gmail.com> Date: Thu, 5 Mar 2015 23:09:45 -0500 Subject: [PATCH] Fill out pulsar-config script. - Add many new options. - Add tests. --- pulsar/scripts/config.py | 263 +++++++++++++++++++++++++++--------- test/scripts_config_test.py | 75 ++++++++++ 2 files changed, 273 insertions(+), 65 deletions(-) create mode 100644 test/scripts_config_test.py diff --git a/pulsar/scripts/config.py b/pulsar/scripts/config.py index 293758a4..66710b67 100644 --- a/pulsar/scripts/config.py +++ b/pulsar/scripts/config.py @@ -2,6 +2,7 @@ from __future__ import print_function import os +import string import sys from pulsar.main import ( @@ -11,42 +12,66 @@ from pulsar.main import ( ) DESCRIPTION = "Initialize a directory with a minimal pulsar config." +HELP_DIRECTORY = "Directory containing the configuration files for Pulsar." +HELP_MQ = ("Write configuration files for message queue server deployment " + "instead of more traditional RESTful web based pulsar.") +HELP_SUPERVISOR = ("Write a supervisord configuration file for " + "managing pulsar out as well.") +HELP_FORCE = "Overwrite existing files if they already exist." +HELP_WSGI_SERVER = ("Web server stack used to host Pulsar wsgi application.") +HELP_LIBDRMAA = ("Configure Pulsar to submit jobs to a cluster via DRMAA by " + "supplying the path to a libdrmaa .so file using this argument.") +HELP_INSTALL = ("Install optional dependencies required by specified configuration " + "(e.g. drmaa, supervisor, uwsgi, etc...).") +HELP_HOST = ("Host to bind Pulsar to - defaults to localhost. Set to 0.0.0.0 " + "to listen on all interfaces.") +HELP_PORT = ("Port to bind Pulsar to (ignored if --mq is specified).") -def main(): - arg_parser = ArgumentParser(description=DESCRIPTION) - arg_parser.add_argument("--directory", default=".") - arg_parser.add_argument("--mq", - action="store_true", - default=False, - help=("Write configuration files for message queue " - "instead of web based pulsar.")) - arg_parser.add_argument("--force", - action="store_true", - default=False, - help="Overwrite existing files.") - args = arg_parser.parse_args() - directory = args.directory - force = args.force - directory = os.path.abspath(directory) +LOGGING_CONFIG_SECTIONS = """## Configure Python loggers. +[loggers] +keys = root,pulsar - if not os.path.exists(directory): - os.makedirs(directory) +[handlers] +keys = console - yaml_file = os.path.join(directory, DEFAULT_APP_YAML) - check_file(yaml_file, force) - if args.mq: - open(yaml_file, "w").write("""--- -message_queue_url: "amqp://guest:guest@localhost:5672//" +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_pulsar] +level = DEBUG +handlers = console +qualname = pulsar +propagate = 0 + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = DEBUG +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +""" + +SUPERVISOR_CONFIG_TEMPLATE = string.Template("""[program:pulsar] +user = ${user} +directory = ${directory} +command = pulsar --mode '${mode}' --config '${directory}' +redirect_stderr = true +autorestart = true """) - else: - open(yaml_file, "w").write("""---""") - ini_file = os.path.join(directory, DEFAULT_INI) - if not args.mq: - check_file(ini_file, force) - open(ini_file, "w").write("""[server:main] +SERVER_CONFIG_TEMPLATE = string.Template("""[server:main] use = egg:Paste#http +port = ${port} +host = ${host} +## pem file to use to enable SSL. +# ssl_pem = host.pem [app:main] paste.app_factory = pulsar.web.wsgi:app_factory @@ -55,8 +80,8 @@ app_config = %(here)s/app.yml ## Configure uWSGI (if used). [uwsgi] master = True -paste-logger = True -socket = 127.0.0.1:3031 +paste-logger = ${use_logging} +socket = ${host}:3031 processes = 1 enable-threads = True @@ -74,45 +99,17 @@ use_sockets = True numprocesses = 1 [socket:web] -host = localhost -port = 8913 +host = ${host} +port = ${port} -## Configure Python loggers. -[loggers] -keys = root,pulsar - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[logger_pulsar] -level = DEBUG -handlers = console -qualname = pulsar -propagate = 0 - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = DEBUG -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +${logging_sections} """) - local_env_file = os.path.join(directory, "local_env.sh") - open(local_env_file, "w").write("""## Place local configuration variables used by Pulsar and run.sh in here. For example +LOCAL_ENV_TEMPLATE = string.Template("""## Place local configuration variables used by Pulsar and run.sh in here. For example ## If using the drmaa queue manager, you will need to set the DRMAA_LIBRARY_PATH variable, ## you may also need to update LD_LIBRARY_PATH for underlying library as well. -#export DRMAA_LIBRARY_PATH=/path/to/libdrmaa.so +$libdrmaa_line ## If you wish to use a variety of Galaxy tools that depend on galaxy.eggs being defined, @@ -121,7 +118,143 @@ format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s """) -def check_file(path, force): +def main(argv=None): + if argv is None: + argv = sys.argv + dependencies = [] + arg_parser = ArgumentParser(description=DESCRIPTION) + arg_parser.add_argument("--directory", + default=".", + help=HELP_DIRECTORY) + arg_parser.add_argument("--mq", + action="store_true", + default=False, + help=HELP_MQ) + arg_parser.add_argument("--no_logging", + dest="logging", + action="store_false", + default=True, + help=HELP_MQ) + arg_parser.add_argument("--supervisor", + action="store_true", + default=False, + help=HELP_SUPERVISOR) + arg_parser.add_argument("--wsgi_server", + choices=["paste", "uwsgi"], + default=None, + help=HELP_WSGI_SERVER) + arg_parser.add_argument("--libdrmaa_path", + help=HELP_LIBDRMAA) + arg_parser.add_argument("--host", + default="localhost", + help=HELP_HOST) + arg_parser.add_argument("--port", + default="8913", + help=HELP_PORT) + arg_parser.add_argument("--install", + help=HELP_INSTALL) + arg_parser.add_argument("--force", + action="store_true", + default=False, + help=HELP_FORCE) + args = arg_parser.parse_args(argv) + directory = args.directory + directory = os.path.abspath(directory) + + mode = _determine_mode(args) + if mode == "uwsgi": + dependencies.append("uwsgi") + + if not os.path.exists(directory): + os.makedirs(directory) + + _handle_app_yaml(args, directory) + _handle_server_ini(args, directory) + _handle_local_env(args, directory, dependencies) + _handle_supervisor(args, mode, directory, dependencies) + _handle_install(args, dependencies) + + +def _determine_mode(args): + if args.wsgi_server: + mode = args.wsgi_server + elif args.mq: + mode = "webless" + else: + mode = "paster" + return mode + + +def _handle_server_ini(args, directory): + force = args.force + ini_file = os.path.join(directory, DEFAULT_INI) + if not args.mq: + _check_file(ini_file, force) + config_dict = dict( + port=args.port, + host=args.host, + ) + if args.logging: + config_dict["logging_sections"] = LOGGING_CONFIG_SECTIONS + config_dict["use_logging"] = "true" + else: + config_dict["logging_sections"] = "" + config_dict["use_logging"] = "false" + + server_config = SERVER_CONFIG_TEMPLATE.safe_substitute( + **config_dict + ) + open(ini_file, "w").write(server_config) + + +def _handle_app_yaml(args, directory): + force = args.force + yaml_file = os.path.join(directory, DEFAULT_APP_YAML) + _check_file(yaml_file, force) + contents = "---\n" + if args.mq: + contents += 'message_queue_url: "amqp://guest:guest@localhost:5672//"\n' + else: + if args.libdrmaa_path: + contents += 'manager:\n type: queued_drmaa\n' + open(yaml_file, "w").write(contents) + + +def _handle_local_env(args, directory, dependencies): + local_env_file = os.path.join(directory, "local_env.sh") + if args.libdrmaa_path: + libdrmaa_line = 'export DRMAA_LIBRARY_PATH=%s' % args.libdrmaa_path + os.environ["DRMAA_LIBRARY_PATH"] = args.libdrmaa_path + dependencies.append("drmaa") + else: + libdrmaa_line = '#export DRMAA_LIBRARY_PATH=/path/to/libdrmaa.so' + + local_env_contents = LOCAL_ENV_TEMPLATE.safe_substitute( + libdrmaa_line=libdrmaa_line, + ) + open(local_env_file, "w").write(local_env_contents) + + +def _handle_supervisor(args, mode, directory, dependencies): + if args.supervisor: + template = SUPERVISOR_CONFIG_TEMPLATE + config = template.safe_substitute( + user=os.environ["USER"], + directory=directory, + mode=mode, + ) + conf_path = os.path.join(directory, "supervisor.conf") + open(conf_path, "w").write(config) + dependencies.append("supervisor") + + +def _handle_install(args, dependencies): + if args.install and dependencies: + import pip + pip.main("install", *dependencies) + + +def _check_file(path, force): if os.path.exists(path) and not force: print("File %s exists, exiting." % path, file=sys.stderr) sys.exit(1) diff --git a/test/scripts_config_test.py b/test/scripts_config_test.py new file mode 100644 index 00000000..33036e80 --- /dev/null +++ b/test/scripts_config_test.py @@ -0,0 +1,75 @@ +import collections +import os +import subprocess +import yaml + +from six.moves import configparser + +from pulsar.scripts.config import main + +from test_utils import temp_directory + + +def test_default_web_config(): + with temp_directory() as project_dir: + main(["--directory", project_dir]) + project = _check_project_directory(project_dir) + assert project.ini_config is not None + + local_env = os.path.join(project_dir, "local_env.sh") + assert os.path.exists(local_env) + exit_code = subprocess.check_call(['/bin/bash', '-c', '. %s' % local_env]) + assert exit_code == 0 + + +def test_mq_config(): + with temp_directory() as project_dir: + main(["--directory", project_dir, "--mq"]) + project = _check_project_directory(project_dir) + assert project.ini_config is None + assert "message_queue_url" in project.app_config + + +def test_with_supervisor(): + with temp_directory() as project_dir: + main(["--directory", project_dir, "--supervisor"]) + project = _check_project_directory(project_dir) + assert project.ini_config is not None + + supervisor_conf = os.path.join(project_dir, "supervisor.conf") + assert os.path.exists(supervisor_conf) + + +def test_libdrmaa_config(): + with temp_directory() as project_dir: + main(["--directory", project_dir, "--libdrmaa_path", "/path/to/test/libdrmaa.so"]) + + local_env = os.path.join(project_dir, "local_env.sh") + assert os.path.exists(local_env) + exit_code = subprocess.check_call(['/bin/bash', '-c', '. %s' % local_env]) + assert exit_code == 0 + + +def _check_project_directory(project_dir): + def path_if_exists(name): + path = os.path.join(project_dir, name) + if os.path.exists(path): + return path + else: + return None + + app_config = None + app_config_path = path_if_exists("app.yml") + if app_config_path: + app_config = yaml.load(open(app_config_path, "r")) + assert isinstance(app_config, dict) or (app_config is None) + + ini_config = None + ini_path = path_if_exists("server.ini") + if ini_path: + ini_config = configparser.ConfigParser() + ini_config.read([ini_path]) + + return Project(ini_config, app_config) + +Project = collections.namedtuple('Project', ['ini_config', 'app_config']) -- GitLab