diff --git a/planemo/bioblend.py b/planemo/bioblend.py index 6955f2ea4..0c65575c4 100644 --- a/planemo/bioblend.py +++ b/planemo/bioblend.py @@ -12,6 +12,6 @@ "bioblend`") -def ensure_module(mod): +def ensure_module(): if toolshed is None: raise Exception(BIOBLEND_UNAVAILABLE) diff --git a/planemo/galaxy/api.py b/planemo/galaxy/api.py new file mode 100644 index 000000000..6d7d918e7 --- /dev/null +++ b/planemo/galaxy/api.py @@ -0,0 +1,39 @@ +"""A high-level interface to local Galaxy instances using bioblend.""" +from planemo.bioblend import ensure_module +from planemo.bioblend import galaxy + +DEFAULT_MASTER_API_KEY = "test_key" + + +def gi(port, key=None): + """Return a bioblend ``GalaxyInstance`` for Galaxy on this port.""" + ensure_module() + if key is None: + key = DEFAULT_MASTER_API_KEY + return galaxy.GalaxyInstance( + url="http://localhost:%d" % int(port), + key=key + ) + + +def user_api_key(admin_gi): + """Use an admin authenticated account to generate a user API key.""" + ensure_module() + # TODO: thread-safe + users = admin_gi.users + # Allow override with --user_api_key. + user_response = users.create_local_user( + "planemo", + "planemo@galaxyproject.org", + "planemo", + ) + user_id = user_response["id"] + + return users.create_user_apikey(user_id) + + +__all__ = [ + "DEFAULT_MASTER_API_KEY", + "gi", + "user_api_key", +] diff --git a/planemo/galaxy/config.py b/planemo/galaxy/config.py index 50c71db1f..db6c7db93 100644 --- a/planemo/galaxy/config.py +++ b/planemo/galaxy/config.py @@ -18,6 +18,11 @@ setup_venv, DOWNLOAD_GALAXY, ) +from .api import ( + DEFAULT_MASTER_API_KEY, + gi, + user_api_key, +) from planemo.conda import build_conda_context from planemo.io import warn from planemo.io import shell @@ -27,10 +32,6 @@ from planemo.io import wait_on from planemo import git from planemo.shed import tool_shed_url -from planemo.bioblend import ( - galaxy, - ensure_module, -) NO_TEST_DATA_MESSAGE = ( @@ -159,7 +160,7 @@ def config_join(*args): sheds_config_path = _configure_sheds_config_file( ctx, config_directory, **kwds ) - master_api_key = kwds.get("master_api_key", "test_key") + master_api_key = kwds.get("master_api_key", DEFAULT_MASTER_API_KEY) dependency_dir = os.path.join(config_directory, "deps") preseeded_database = attempt_database_preseed( galaxy_root, @@ -254,8 +255,11 @@ def config_join(*args): shed_tool_conf_contents = _sub(SHED_TOOL_CONF_TEMPLATE, template_args) write_file(shed_tool_conf, shed_tool_conf_contents) + pid_file = kwds.get("pid_file") or config_join("galaxy.pid") + yield GalaxyConfig( galaxy_root, + pid_file, config_directory, env, test_data_dir, @@ -274,6 +278,7 @@ class GalaxyConfig(object): def __init__( self, galaxy_root, + pid_file, config_directory, env, test_data_dir, @@ -282,6 +287,7 @@ def __init__( master_api_key, ): self.galaxy_root = galaxy_root + self._pid_file = pid_file self.config_directory = config_directory self.env = env self.test_data_dir = test_data_dir @@ -298,38 +304,22 @@ def kill(self): @property def pid_file(self): - return os.path.join(self.galaxy_root, "%s.pid" % self.server_name) + return self._pid_file @property def gi(self): - ensure_module(galaxy) - return galaxy.GalaxyInstance( - url="http://localhost:%d" % int(self.port), - key=self.master_api_key - ) + return gi(self.port, self.master_api_key) @property def user_gi(self): # TODO: thread-safe if self._user_api_key is None: - users = self.gi.users - # Allow override with --user_api_key. - user_response = users.create_local_user( - "planemo", - "planemo@galaxyproject.org", - "planemo", - ) - user_id = user_response["id"] + self._user_api_key = user_api_key(self.gi) - self._user_api_key = users.create_user_apikey(user_id) return self._gi_for_key(self._user_api_key) def _gi_for_key(self, key): - ensure_module(galaxy) - return galaxy.GalaxyInstance( - url="http://localhost:%d" % self.port, - key=key - ) + return gi(self.port, key) def install_repo(self, *args, **kwds): self.tool_shed_client.install_repository_revision( diff --git a/planemo/galaxy/serve.py b/planemo/galaxy/serve.py index f7e1cdad7..78e81c646 100644 --- a/planemo/galaxy/serve.py +++ b/planemo/galaxy/serve.py @@ -19,12 +19,13 @@ def serve(ctx, paths, **kwds): kwds["no_cleanup"] = True with galaxy_config(ctx, paths, **kwds) as config: + pid_file = config.pid_file # TODO: Allow running dockerized Galaxy here instead. setup_venv_command = setup_venv(ctx, kwds) run_script = os.path.join(config.galaxy_root, "run.sh") run_script += " $COMMON_STARTUP_ARGS" if daemon: - run_script += " --daemon --wait" + run_script += " --pid-file '%s' --daemon" % pid_file config.env["GALAXY_RUN_ALL"] = "1" else: run_script += " --server-name '%s' --reload" % config.server_name diff --git a/planemo/options.py b/planemo/options.py index cf58ca72e..5445e9f31 100644 --- a/planemo/options.py +++ b/planemo/options.py @@ -674,10 +674,28 @@ def galaxy_target_options(): ) +def pid_file_option(): + return planemo_option( + "--pid_file", + default=None, + help="Location of pid file is executed with --daemon." + ) + + +def daemon_option(): + return planemo_option( + "--daemon", + is_flag=True, + help="Serve Galaxy process as a daemon." + ) + + def galaxy_serve_options(): return _compose( galaxy_run_options(), galaxy_config_options(), + daemon_option(), + pid_file_option(), ) diff --git a/planemo/shed/interface.py b/planemo/shed/interface.py index 0d7880035..00c163a62 100644 --- a/planemo/shed/interface.py +++ b/planemo/shed/interface.py @@ -15,7 +15,7 @@ def tool_shed_instance(url, key, email, password): - ensure_module(toolshed) + ensure_module() tsi = toolshed.ToolShedInstance( url=url, key=key, diff --git a/tests/test_cmd_serve.py b/tests/test_cmd_serve.py index 2cff125be..ad0ff2349 100644 --- a/tests/test_cmd_serve.py +++ b/tests/test_cmd_serve.py @@ -3,6 +3,8 @@ import time import threading +from planemo.galaxy import api + from .test_utils import ( CliTestCase, skip_if_environ, @@ -10,6 +12,8 @@ ) from . import network_util +TEST_HISTORY_NAME = "Cool History 42" + class ServeTestCase(CliTestCase): @@ -17,13 +21,31 @@ class ServeTestCase(CliTestCase): def test_serve(self): port = network_util.get_free_port() serve = functools.partial(self._run, port) - t = threading.Thread(target=serve) + self._launch_thread_and_wait(serve, port) + + @skip_if_environ("PLANEMO_SKIP_GALAXY_TESTS") + def test_serve_daemon(self): + port = network_util.get_free_port() + pid_file = os.path.join(self._home, "test.pid") + extra_args = ["--daemon", "--pid_file", pid_file] + serve = functools.partial(self._run, port, extra_args) + self._launch_thread_and_wait(serve, port) + time.sleep(.1) + assert network_util.wait_net_service("127.0.0.1", port) + admin_gi = api.gi(port) + user_api_key = api.user_api_key(admin_gi) + user_gi = api.gi(port, user_api_key) + assert len(user_gi.histories.get_histories(name=TEST_HISTORY_NAME)) == 0 + user_gi.histories.create_history(TEST_HISTORY_NAME) + + def _launch_thread_and_wait(self, func, port): + t = threading.Thread(target=func) t.daemon = True t.start() time.sleep(15) assert network_util.wait_net_service("127.0.0.1", port) - def _run(self, port): + def _run(self, port, serve_args=[]): cat_path = os.path.join(TEST_REPOS_DIR, "single_tool", "cat.xml") test_cmd = [ "serve", @@ -32,4 +54,5 @@ def _run(self, port): str(port), cat_path, ] + test_cmd.extend(serve_args) self._check_exit_code(test_cmd)