diff env/lib/python3.7/site-packages/planemo/galaxy/config.py @ 5:9b1c78e6ba9c draft default tip

"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author shellac
date Mon, 01 Jun 2020 08:59:25 -0400
parents 79f47841a781
children
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/planemo/galaxy/config.py	Thu May 14 16:47:39 2020 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1270 +0,0 @@
-"""Abstractions for setting up a Galaxy instance."""
-from __future__ import absolute_import
-from __future__ import print_function
-
-import abc
-import contextlib
-import os
-import random
-import shutil
-from string import Template
-from tempfile import mkdtemp
-
-from galaxy.containers.docker_model import DockerVolume
-from galaxy.tool_util.deps import docker_util
-from galaxy.tool_util.deps.commands import argv_to_str
-from pkg_resources import parse_version
-from six import (
-    add_metaclass,
-    iteritems
-)
-from six.moves import shlex_quote
-
-from planemo import git
-from planemo.config import OptionSource
-from planemo.deps import ensure_dependency_resolvers_conf_configured
-from planemo.docker import docker_host_args
-from planemo.io import (
-    communicate,
-    kill_pid_file,
-    shell,
-    shell_join,
-    untar_to,
-    wait_on,
-    warn,
-    write_file,
-)
-from planemo.mulled import build_involucro_context
-from planemo.shed import tool_shed_url
-from planemo.virtualenv import DEFAULT_PYTHON_VERSION
-from .api import (
-    DEFAULT_MASTER_API_KEY,
-    gi,
-    user_api_key,
-)
-from .distro_tools import (
-    DISTRO_TOOLS_ID_TO_PATH
-)
-from .run import (
-    setup_common_startup_args,
-    setup_venv,
-)
-from .workflows import (
-    find_tool_ids,
-    import_workflow,
-    install_shed_repos,
-)
-
-
-NO_TEST_DATA_MESSAGE = (
-    "planemo couldn't find a target test-data directory, you should likely "
-    "create a test-data directory or pass an explicit path using --test_data."
-)
-
-WEB_SERVER_CONFIG_TEMPLATE = """
-[server:${server_name}]
-use = egg:Paste#http
-port = ${port}
-host = ${host}
-use_threadpool = True
-threadpool_kill_thread_limit = 10800
-[app:main]
-paste.app_factory = galaxy.web.buildapp:app_factory
-"""
-
-TOOL_CONF_TEMPLATE = """<toolbox>
-  <tool file="data_source/upload.xml" />
-  ${tool_definition}
-</toolbox>
-"""
-
-SHED_TOOL_CONF_TEMPLATE = """<?xml version="1.0"?>
-<toolbox tool_path="${shed_tool_path}">
-</toolbox>
-"""
-
-SHED_DATA_MANAGER_CONF_TEMPLATE = """<?xml version="1.0"?>
-<data_managers>
-</data_managers>
-"""
-
-EMPTY_JOB_METRICS_TEMPLATE = """<?xml version="1.0"?>
-<job_metrics>
-</job_metrics>
-"""
-
-TOOL_SHEDS_CONF = """<tool_sheds>
-  <tool_shed name="Target Shed" url="${shed_target_url}" />
-</tool_sheds>
-"""
-
-JOB_CONFIG_LOCAL = """<job_conf>
-    <plugins>
-        <plugin id="planemo_runner" type="runner" load="galaxy.jobs.runners.local:LocalJobRunner" workers="4"/>
-    </plugins>
-    <handlers>
-        <handler id="main"/>
-    </handlers>
-    <destinations default="planemo_dest">
-        <destination id="planemo_dest" runner="planemo_runner">
-            <param id="require_container">${require_container}</param>
-            <param id="docker_enabled">${docker_enable}</param>
-            <param id="docker_sudo">${docker_sudo}</param>
-            <param id="docker_sudo_cmd">${docker_sudo_cmd}</param>
-            <param id="docker_cmd">${docker_cmd}</param>
-            ${docker_host_param}
-        </destination>
-        <destination id="upload_dest" runner="planemo_runner">
-            <param id="docker_enable">false</param>
-        </destination>
-    </destinations>
-    <tools>
-        <tool id="upload1" destination="upload_dest" />
-    </tools>
-</job_conf>
-"""
-
-LOGGING_TEMPLATE = """
-## Configure Python loggers.
-[loggers]
-keys = root,paste,displayapperrors,galaxydeps,galaxymasterapikey,galaxy
-
-[handlers]
-keys = console
-
-[formatters]
-keys = generic
-
-[logger_root]
-level = WARN
-handlers = console
-
-[logger_paste]
-level = WARN
-handlers = console
-qualname = paste
-propagate = 0
-
-[logger_galaxydeps]
-level = DEBUG
-handlers = console
-qualname = galaxy.tools.deps
-propagate = 0
-
-[logger_galaxymasterapikey]
-level = WARN
-handlers = console
-qualname = galaxy.web.framework.webapp
-propagate = 0
-
-[logger_displayapperrors]
-level = ERROR
-handlers =
-qualname = galaxy.datatypes.display_applications.application
-propagate = 0
-
-[logger_galaxy]
-level = ${log_level}
-handlers = console
-qualname = galaxy
-propagate = 0
-
-[handler_console]
-class = StreamHandler
-args = (sys.stderr,)
-level = DEBUG
-formatter = generic
-
-[formatter_generic]
-format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s
-"""
-
-
-EMPTY_TOOL_CONF_TEMPLATE = """<toolbox></toolbox>"""
-
-DEFAULT_GALAXY_BRANCH = "master"
-DEFAULT_GALAXY_SOURCE = "https://github.com/galaxyproject/galaxy"
-CWL_GALAXY_SOURCE = "https://github.com/common-workflow-language/galaxy"
-
-DATABASE_LOCATION_TEMPLATE = "sqlite:///%s?isolation_level=IMMEDIATE"
-
-COMMAND_STARTUP_COMMAND = "./scripts/common_startup.sh ${COMMON_STARTUP_ARGS}"
-
-CLEANUP_IGNORE_ERRORS = True
-DEFAULT_GALAXY_BRAND = 'Configured by Planemo'
-
-
-@contextlib.contextmanager
-def galaxy_config(ctx, runnables, **kwds):
-    """Set up a ``GalaxyConfig`` in an auto-cleaned context."""
-    c = local_galaxy_config
-    if kwds.get("dockerize", False):
-        c = docker_galaxy_config
-    elif kwds.get("external", False):
-        c = external_galaxy_config
-
-    with c(ctx, runnables, **kwds) as config:
-        yield config
-
-
-def simple_docker_volume(path):
-    path = os.path.abspath(path)
-    return DockerVolume("%s:%s:rw" % (path, path))
-
-
-@contextlib.contextmanager
-def docker_galaxy_config(ctx, runnables, for_tests=False, **kwds):
-    """Set up a ``GalaxyConfig`` for Docker container."""
-    test_data_dir = _find_test_data(runnables, **kwds)
-
-    with _config_directory(ctx, **kwds) as config_directory:
-        def config_join(*args):
-            return os.path.join(config_directory, *args)
-
-        ensure_dependency_resolvers_conf_configured(ctx, kwds, os.path.join(config_directory, "resolvers_conf.xml"))
-        _handle_job_metrics(config_directory, kwds)
-
-        shed_tool_conf = "config/shed_tool_conf.xml"
-        all_tool_paths = _all_tool_paths(runnables, **kwds)
-
-        tool_directories = set([])  # Things to mount...
-        for tool_path in all_tool_paths:
-            directory = os.path.dirname(os.path.normpath(tool_path))
-            if os.path.exists(directory):
-                tool_directories.add(directory)
-
-        # TODO: remap these.
-        tool_volumes = []
-        for tool_directory in tool_directories:
-            volume = simple_docker_volume(tool_directory)
-            tool_volumes.append(volume)
-
-        empty_tool_conf = config_join("empty_tool_conf.xml")
-
-        tool_conf = config_join("tool_conf.xml")
-
-        shed_tool_path = kwds.get("shed_tool_path") or config_join("shed_tools")
-        _ensure_directory(shed_tool_path)
-
-        sheds_config_path = _configure_sheds_config_file(
-            ctx, config_directory, **kwds
-        )
-        port = _get_port(kwds)
-        properties = _shared_galaxy_properties(config_directory, kwds, for_tests=for_tests)
-        _handle_container_resolution(ctx, kwds, properties)
-        master_api_key = _get_master_api_key(kwds)
-
-        template_args = dict(
-            shed_tool_path=shed_tool_path,
-            tool_conf=tool_conf,
-        )
-        tool_config_file = "%s,%s" % (tool_conf, shed_tool_conf)
-
-        _write_tool_conf(ctx, all_tool_paths, tool_conf)
-        write_file(empty_tool_conf, EMPTY_TOOL_CONF_TEMPLATE)
-
-        properties.update(dict(
-            tool_config_file=tool_config_file,
-            tool_sheds_config_file=sheds_config_path,
-            migrated_tools_config=empty_tool_conf,
-        ))
-
-        server_name = "planemo%d" % random.randint(0, 100000)
-
-        # Value substitutions in Galaxy properties - for consistency with
-        # non-Dockerized version.
-        template_args = dict(
-        )
-        env = _build_env_for_galaxy(properties, template_args)
-        env["NONUSE"] = "nodejs,proftp,reports"
-        if ctx.verbose:
-            env["GALAXY_LOGGING"] = "full"
-
-        # TODO: setup FTP upload dir and disable FTP server in container.
-        _build_test_env(properties, env)
-
-        docker_target_kwds = docker_host_args(**kwds)
-        volumes = tool_volumes + [simple_docker_volume(config_directory)]
-        export_directory = kwds.get("export_directory", None)
-        if export_directory is not None:
-            volumes.append(DockerVolume("%s:/export:rw" % export_directory))
-
-        # TODO: Allow this to real Docker volumes and allow multiple.
-        extra_volume = kwds.get("docker_extra_volume")
-        if extra_volume:
-            volumes.append(simple_docker_volume(extra_volume))
-        yield DockerGalaxyConfig(
-            ctx,
-            config_directory,
-            env,
-            test_data_dir,
-            port,
-            server_name,
-            master_api_key,
-            runnables,
-            docker_target_kwds=docker_target_kwds,
-            volumes=volumes,
-            export_directory=export_directory,
-            kwds=kwds,
-        )
-
-
-@contextlib.contextmanager
-def local_galaxy_config(ctx, runnables, for_tests=False, **kwds):
-    """Set up a ``GalaxyConfig`` in an auto-cleaned context."""
-    test_data_dir = _find_test_data(runnables, **kwds)
-    tool_data_table = _find_tool_data_table(
-        runnables,
-        test_data_dir=test_data_dir,
-        **kwds
-    )
-    data_manager_config_paths = [r.data_manager_conf_path for r in runnables if r.data_manager_conf_path]
-    galaxy_root = _find_galaxy_root(ctx, **kwds)
-    install_galaxy = kwds.get("install_galaxy", False)
-    if galaxy_root is not None:
-        if os.path.isdir(galaxy_root) and not os.listdir(galaxy_root):
-            os.rmdir(galaxy_root)
-        if os.path.isdir(galaxy_root) and install_galaxy:
-            raise Exception("%s is an existing non-empty directory, cannot install Galaxy again" % galaxy_root)
-
-    # Duplicate block in docker variant above.
-    if kwds.get("mulled_containers", False) and not kwds.get("docker", False):
-        if ctx.get_option_source("docker") != OptionSource.cli:
-            kwds["docker"] = True
-        else:
-            raise Exception("Specified no docker and mulled containers together.")
-
-    with _config_directory(ctx, **kwds) as config_directory:
-        def config_join(*args):
-            return os.path.join(config_directory, *args)
-
-        install_env = {}
-        if kwds.get('galaxy_skip_client_build', True):
-            install_env['GALAXY_SKIP_CLIENT_BUILD'] = '1'
-        if galaxy_root is None:
-            galaxy_root = config_join("galaxy-dev")
-        if not os.path.isdir(galaxy_root):
-            _build_eggs_cache(ctx, install_env, kwds)
-            _install_galaxy(ctx, galaxy_root, install_env, kwds)
-
-        if parse_version(kwds.get('galaxy_python_version') or DEFAULT_PYTHON_VERSION) >= parse_version('3'):
-            # on python 3 we use gunicorn,
-            # which requires 'main' as server name
-            server_name = 'main'
-        else:
-            server_name = "planemo%d" % random.randint(0, 100000)
-        # Once we don't have to support earlier than 18.01 - try putting these files
-        # somewhere better than with Galaxy.
-        log_file = "%s.log" % server_name
-        pid_file = "%s.pid" % server_name
-        ensure_dependency_resolvers_conf_configured(ctx, kwds, os.path.join(config_directory, "resolvers_conf.xml"))
-        _handle_job_config_file(config_directory, server_name, kwds)
-        _handle_job_metrics(config_directory, kwds)
-        file_path = kwds.get("file_path") or config_join("files")
-        _ensure_directory(file_path)
-
-        tool_dependency_dir = kwds.get("tool_dependency_dir") or config_join("deps")
-        _ensure_directory(tool_dependency_dir)
-
-        shed_tool_conf = kwds.get("shed_tool_conf") or config_join("shed_tools_conf.xml")
-        all_tool_paths = _all_tool_paths(runnables, **kwds)
-        empty_tool_conf = config_join("empty_tool_conf.xml")
-
-        tool_conf = config_join("tool_conf.xml")
-
-        shed_data_manager_config_file = config_join("shed_data_manager_conf.xml")
-
-        shed_tool_path = kwds.get("shed_tool_path") or config_join("shed_tools")
-        _ensure_directory(shed_tool_path)
-
-        sheds_config_path = _configure_sheds_config_file(
-            ctx, config_directory, **kwds
-        )
-
-        database_location = config_join("galaxy.sqlite")
-        master_api_key = _get_master_api_key(kwds)
-        dependency_dir = os.path.join(config_directory, "deps")
-        _ensure_directory(shed_tool_path)
-        port = _get_port(kwds)
-        template_args = dict(
-            port=port,
-            host=kwds.get("host", "127.0.0.1"),
-            server_name=server_name,
-            temp_directory=config_directory,
-            shed_tool_path=shed_tool_path,
-            database_location=database_location,
-            tool_conf=tool_conf,
-            debug=kwds.get("debug", "true"),
-            id_secret=kwds.get("id_secret", "test_secret"),
-            log_level="DEBUG" if ctx.verbose else "INFO",
-        )
-        tool_config_file = "%s,%s" % (tool_conf, shed_tool_conf)
-        # Setup both galaxy_email and older test user test@bx.psu.edu
-        # as admins for command_line, etc...
-        properties = _shared_galaxy_properties(config_directory, kwds, for_tests=for_tests)
-        properties.update(dict(
-            server_name="main",
-            ftp_upload_dir_template="${ftp_upload_dir}",
-            ftp_upload_purge="False",
-            ftp_upload_dir=test_data_dir or os.path.abspath('.'),
-            ftp_upload_site="Test Data",
-            check_upload_content="False",
-            tool_dependency_dir=dependency_dir,
-            file_path=file_path,
-            new_file_path="${temp_directory}/tmp",
-            tool_config_file=tool_config_file,
-            tool_sheds_config_file=sheds_config_path,
-            manage_dependency_relationships="False",
-            job_working_directory="${temp_directory}/job_working_directory",
-            template_cache_path="${temp_directory}/compiled_templates",
-            citation_cache_type="file",
-            citation_cache_data_dir="${temp_directory}/citations/data",
-            citation_cache_lock_dir="${temp_directory}/citations/lock",
-            database_auto_migrate="True",
-            enable_beta_tool_formats="True",
-            id_secret="${id_secret}",
-            log_level="${log_level}",
-            debug="${debug}",
-            watch_tools="auto",
-            default_job_shell="/bin/bash",  # For conda dependency resolution
-            tool_data_table_config_path=tool_data_table,
-            data_manager_config_file=",".join(data_manager_config_paths) or None,  # without 'or None' may raise IOError in galaxy (see #946)
-            integrated_tool_panel_config=("${temp_directory}/"
-                                          "integrated_tool_panel_conf.xml"),
-            migrated_tools_config=empty_tool_conf,
-            test_data_dir=test_data_dir,  # TODO: make gx respect this
-            shed_data_manager_config_file=shed_data_manager_config_file,
-        ))
-        _handle_container_resolution(ctx, kwds, properties)
-        write_file(config_join("logging.ini"), _sub(LOGGING_TEMPLATE, template_args))
-        properties["database_connection"] = _database_connection(database_location, **kwds)
-
-        _handle_kwd_overrides(properties, kwds)
-
-        # TODO: consider following property
-        # watch_tool = False
-        # datatypes_config_file = config/datatypes_conf.xml
-        # welcome_url = /static/welcome.html
-        # logo_url = /
-        # sanitize_all_html = True
-        # serve_xss_vulnerable_mimetypes = False
-        # track_jobs_in_database = None
-        # outputs_to_working_directory = False
-        # retry_job_output_collection = 0
-
-        env = _build_env_for_galaxy(properties, template_args)
-        env.update(install_env)
-        _build_test_env(properties, env)
-        env['GALAXY_TEST_SHED_TOOL_CONF'] = shed_tool_conf
-        env['GALAXY_TEST_DBURI'] = properties["database_connection"]
-
-        env["GALAXY_TEST_UPLOAD_ASYNC"] = "false"
-        env["GALAXY_TEST_LOGGING_CONFIG"] = config_join("logging.ini")
-        env["GALAXY_DEVELOPMENT_ENVIRONMENT"] = "1"
-        # Following are needed in 18.01 to prevent Galaxy from changing log and pid.
-        # https://github.com/galaxyproject/planemo/issues/788
-        env["GALAXY_LOG"] = log_file
-        env["GALAXY_PID"] = pid_file
-        web_config = _sub(WEB_SERVER_CONFIG_TEMPLATE, template_args)
-        write_file(config_join("galaxy.ini"), web_config)
-        _write_tool_conf(ctx, all_tool_paths, tool_conf)
-        write_file(empty_tool_conf, EMPTY_TOOL_CONF_TEMPLATE)
-
-        shed_tool_conf_contents = _sub(SHED_TOOL_CONF_TEMPLATE, template_args)
-        # Write a new shed_tool_conf.xml if needed.
-        write_file(shed_tool_conf, shed_tool_conf_contents, force=False)
-
-        write_file(shed_data_manager_config_file, SHED_DATA_MANAGER_CONF_TEMPLATE)
-
-        yield LocalGalaxyConfig(
-            ctx,
-            config_directory,
-            env,
-            test_data_dir,
-            port,
-            server_name,
-            master_api_key,
-            runnables,
-            galaxy_root,
-            kwds,
-        )
-
-
-def _all_tool_paths(runnables, **kwds):
-    tool_paths = [r.path for r in runnables if r.has_tools and not r.data_manager_conf_path]
-    all_tool_paths = list(tool_paths) + list(kwds.get("extra_tools", []))
-    for runnable in runnables:
-        if runnable.type.name == "galaxy_workflow":
-            tool_ids = find_tool_ids(runnable.path)
-            for tool_id in tool_ids:
-                if tool_id in DISTRO_TOOLS_ID_TO_PATH:
-                    all_tool_paths.append(DISTRO_TOOLS_ID_TO_PATH[tool_id])
-
-    return all_tool_paths
-
-
-def _shared_galaxy_properties(config_directory, kwds, for_tests):
-    """Setup properties useful for local and Docker Galaxy instances.
-
-    Most things related to paths, etc... are very different between Galaxy
-    modalities and many taken care of internally to the container in that mode.
-    But this method sets up API stuff, tool, and job stuff that can be shared.
-    """
-    master_api_key = _get_master_api_key(kwds)
-    user_email = _user_email(kwds)
-    properties = {
-        'master_api_key': master_api_key,
-        'admin_users': "%s,test@bx.psu.edu" % user_email,
-        'expose_dataset_path': "True",
-        'cleanup_job': 'never',
-        'collect_outputs_from': "job_working_directory",
-        'allow_path_paste': "True",
-        'check_migrate_tools': "False",
-        'use_cached_dependency_manager': str(kwds.get("conda_auto_install", False)),
-        'brand': kwds.get("galaxy_brand", DEFAULT_GALAXY_BRAND),
-        'strict_cwl_validation': str(not kwds.get("non_strict_cwl", False)),
-    }
-    if kwds.get("galaxy_single_user", True):
-        properties['single_user'] = user_email
-
-    if for_tests:
-        empty_dir = os.path.join(config_directory, "empty")
-        _ensure_directory(empty_dir)
-        properties["tour_config_dir"] = empty_dir
-        properties["interactive_environment_plugins_directory"] = empty_dir
-        properties["visualization_plugins_directory"] = empty_dir
-    return properties
-
-
-@contextlib.contextmanager
-def external_galaxy_config(ctx, runnables, for_tests=False, **kwds):
-    yield BaseGalaxyConfig(
-        ctx=ctx,
-        galaxy_url=kwds.get("galaxy_url", None),
-        master_api_key=_get_master_api_key(kwds),
-        user_api_key=kwds.get("galaxy_user_key", None),
-        runnables=runnables,
-        kwds=kwds
-    )
-
-
-def _get_master_api_key(kwds):
-    master_api_key = kwds.get("galaxy_admin_key") or DEFAULT_MASTER_API_KEY
-    return master_api_key
-
-
-def _get_port(kwds):
-    port = int(kwds.get("port", 9090))
-    return port
-
-
-def _user_email(kwds):
-    user_email = kwds.get("galaxy_email")
-    return user_email
-
-
-@contextlib.contextmanager
-def _config_directory(ctx, **kwds):
-    config_directory = kwds.get("config_directory", None)
-    created_config_directory = False
-    if not config_directory:
-        created_config_directory = True
-        config_directory = os.path.realpath(mkdtemp())
-        ctx.vlog("Created directory for Galaxy configuration [%s]" % config_directory)
-    try:
-        yield config_directory
-    finally:
-        cleanup = not kwds.get("no_cleanup", False)
-        if created_config_directory and cleanup:
-            shutil.rmtree(config_directory)
-
-
-@add_metaclass(abc.ABCMeta)
-class GalaxyInterface(object):
-    """Abstraction around a Galaxy instance.
-
-    Description of a Galaxy instance and how to interact with it - this could
-    potentially be a remote, already running instance or an instance Planemo manages
-    to execute some task(s).
-    """
-
-    @abc.abstractproperty
-    def gi(self):
-        """Return an admin bioblend Galaxy instance for API interactions."""
-
-    @abc.abstractproperty
-    def user_gi(self):
-        """Return a user-backed bioblend Galaxy instance for API interactions."""
-
-    @abc.abstractmethod
-    def install_repo(self, *args, **kwds):
-        """Install specified tool shed repository."""
-
-    @abc.abstractproperty
-    def tool_shed_client(self):
-        """Return a admin bioblend tool shed client."""
-
-    @abc.abstractmethod
-    def wait_for_all_installed(self):
-        """Wait for all queued up repositories installs to complete."""
-
-    @abc.abstractmethod
-    def install_workflows(self):
-        """Install all workflows configured with these planemo arguments."""
-
-    @abc.abstractmethod
-    def workflow_id(self, path):
-        """Get installed workflow API ID for input path."""
-
-
-@add_metaclass(abc.ABCMeta)
-class GalaxyConfig(GalaxyInterface):
-    """Specialization of GalaxyInterface for Galaxy instances Planemo manages itself.
-
-    This assumes more than an API connection is available - Planemo needs to be able to
-    start and stop the Galaxy instance, recover logs, etc... There are currently two
-    implementations - a locally executed Galaxy and one running inside a Docker containe
-    """
-
-    @abc.abstractproperty
-    def kill(self):
-        """Stop the running instance."""
-
-    @abc.abstractmethod
-    def startup_command(self, ctx, **kwds):
-        """Return a shell command used to startup this instance.
-
-        Among other common planmo kwds, this should respect the
-        ``daemon`` keyword.
-        """
-
-    @abc.abstractproperty
-    def log_contents(self):
-        """Retrieve text of log for running Galaxy instance."""
-
-    @abc.abstractmethod
-    def cleanup(self):
-        """Cleanup allocated resources to run this instance."""
-
-    @abc.abstractproperty
-    def use_path_paste(self):
-        """Use path paste to upload data."""
-
-
-class BaseGalaxyConfig(GalaxyInterface):
-
-    def __init__(
-        self,
-        ctx,
-        galaxy_url,
-        master_api_key,
-        user_api_key,
-        runnables,
-        kwds,
-    ):
-        self._ctx = ctx
-        self.galaxy_url = galaxy_url
-        self.master_api_key = master_api_key
-        self._user_api_key = user_api_key
-        self.runnables = runnables
-        self._kwds = kwds
-        self._workflow_ids = {}
-
-    @property
-    def gi(self):
-        assert self.galaxy_url
-        return gi(url=self.galaxy_url, key=self.master_api_key)
-
-    @property
-    def user_gi(self):
-        user_api_key = self.user_api_key
-        assert user_api_key
-        return self._gi_for_key(user_api_key)
-
-    @property
-    def user_api_key(self):
-        # TODO: thread-safe
-        if self._user_api_key is None:
-            # TODO: respect --galaxy_email - seems like a real bug
-            self._user_api_key = user_api_key(self.gi)
-
-        return self._user_api_key
-
-    def _gi_for_key(self, key):
-        assert self.galaxy_url
-        return gi(url=self.galaxy_url, key=key)
-
-    def install_repo(self, *args, **kwds):
-        self.tool_shed_client.install_repository_revision(
-            *args, **kwds
-        )
-
-    @property
-    def tool_shed_client(self):
-        return self.gi.toolShed
-
-    def wait_for_all_installed(self):
-        def status_ready(repo):
-            status = repo["status"]
-            if status in ["Installing", "New"]:
-                return None
-            if status == "Installed":
-                return True
-            raise Exception("Error installing repo status is %s" % status)
-
-        def ready():
-            repos = self.tool_shed_client.get_repositories()
-            ready = all(map(status_ready, repos))
-            return ready or None
-
-        wait_on(ready, "galaxy tool installation", timeout=60 * 60 * 1)
-
-    def install_workflows(self):
-        for runnable in self.runnables:
-            if runnable.type.name in ["galaxy_workflow", "cwl_workflow"]:
-                self._install_workflow(runnable)
-
-    def _install_workflow(self, runnable):
-        if self._kwds["shed_install"]:
-            install_shed_repos(runnable, self.gi, self._kwds.get("ignore_dependency_problems", False))
-
-        default_from_path = self._kwds.get("workflows_from_path", False)
-        # TODO: Allow serialization so this doesn't need to assume a
-        # shared filesystem with Galaxy server.
-        from_path = default_from_path or (runnable.type.name == "cwl_workflow")
-        workflow = import_workflow(
-            runnable.path, admin_gi=self.gi, user_gi=self.user_gi, from_path=from_path
-        )
-        self._workflow_ids[runnable.path] = workflow["id"]
-
-    def workflow_id(self, path):
-        return self._workflow_ids[path]
-
-    @property
-    def use_path_paste(self):
-        option = self._kwds.get("paste_test_data_paths")
-        if option is None:
-            return self.default_use_path_paste
-        else:
-            return option
-
-    @property
-    def default_use_path_paste(self):
-        return False
-
-
-class BaseManagedGalaxyConfig(BaseGalaxyConfig):
-
-    def __init__(
-        self,
-        ctx,
-        config_directory,
-        env,
-        test_data_dir,
-        port,
-        server_name,
-        master_api_key,
-        runnables,
-        kwds,
-    ):
-        galaxy_url = "http://localhost:%d" % port
-        super(BaseManagedGalaxyConfig, self).__init__(
-            ctx=ctx,
-            galaxy_url=galaxy_url,
-            master_api_key=master_api_key,
-            user_api_key=None,
-            runnables=runnables,
-            kwds=kwds
-        )
-        self.config_directory = config_directory
-        self.env = env
-        self.test_data_dir = test_data_dir
-        self.port = port
-        self.server_name = server_name
-
-
-class DockerGalaxyConfig(BaseManagedGalaxyConfig):
-    """A :class:`GalaxyConfig` description of a Dockerized Galaxy instance."""
-
-    def __init__(
-        self,
-        ctx,
-        config_directory,
-        env,
-        test_data_dir,
-        port,
-        server_name,
-        master_api_key,
-        runnables,
-        docker_target_kwds,
-        volumes,
-        export_directory,
-        kwds,
-    ):
-        super(DockerGalaxyConfig, self).__init__(
-            ctx,
-            config_directory,
-            env,
-            test_data_dir,
-            port,
-            server_name,
-            master_api_key,
-            runnables,
-            kwds,
-        )
-        self.docker_target_kwds = docker_target_kwds
-        self.volumes = volumes
-        self.export_directory = export_directory
-
-    def kill(self):
-        """Kill planemo container..."""
-        kill_command = docker_util.kill_command(
-            self.server_name,
-            **self.docker_target_kwds
-        )
-        return shell(kill_command)
-
-    def startup_command(self, ctx, **kwds):
-        """Return a shell command used to startup this instance.
-
-        Among other common planmo kwds, this should respect the
-        ``daemon`` keyword.
-        """
-        daemon = kwds.get("daemon", False)
-        daemon_str = "" if not daemon else " -d"
-        docker_run_extras = "-p %s:80%s" % (self.port, daemon_str)
-        env_directives = ["%s='%s'" % item for item in self.env.items()]
-        image = kwds.get("docker_galaxy_image", "bgruening/galaxy-stable")
-        run_command = docker_util.build_docker_run_command(
-            "", image,
-            interactive=False,
-            env_directives=env_directives,
-            working_directory=None,
-            name=self.server_name,
-            run_extra_arguments=docker_run_extras,
-            set_user=False,
-            volumes=self.volumes,
-            **self.docker_target_kwds
-        )
-        chmod_command = [
-            "chmod",
-            "-R",
-            "o+rwx",
-            self.config_directory,
-        ]
-        if self.export_directory:
-            chmod_command.append(self.export_directory)
-
-        return shell_join(
-            argv_to_str(chmod_command),
-            run_command,
-        )
-
-    @property
-    def log_contents(self):
-        logs_command = docker_util.logs_command(
-            self.server_name,
-            **self.docker_target_kwds
-        )
-        output, _ = communicate(
-            logs_command
-        )
-        return output
-
-    def cleanup(self):
-        shutil.rmtree(self.config_directory, CLEANUP_IGNORE_ERRORS)
-
-
-class LocalGalaxyConfig(BaseManagedGalaxyConfig):
-    """A local, non-containerized implementation of :class:`GalaxyConfig`."""
-
-    def __init__(
-        self,
-        ctx,
-        config_directory,
-        env,
-        test_data_dir,
-        port,
-        server_name,
-        master_api_key,
-        runnables,
-        galaxy_root,
-        kwds,
-    ):
-        super(LocalGalaxyConfig, self).__init__(
-            ctx,
-            config_directory,
-            env,
-            test_data_dir,
-            port,
-            server_name,
-            master_api_key,
-            runnables,
-            kwds,
-        )
-        self.galaxy_root = galaxy_root
-
-    def kill(self):
-        kill_pid_file(self.pid_file)
-
-    def startup_command(self, ctx, **kwds):
-        """Return a shell command used to startup this instance.
-
-        Among other common planemo kwds, this should respect the
-        ``daemon`` keyword.
-        """
-        daemon = kwds.get("daemon", False)
-        # TODO: Allow running dockerized Galaxy here instead.
-        setup_venv_command = setup_venv(ctx, kwds)
-        run_script = "%s $COMMON_STARTUP_ARGS" % shlex_quote(os.path.join(self.galaxy_root, "run.sh"))
-        if daemon:
-            run_script += " --daemon"
-            self.env["GALAXY_RUN_ALL"] = "1"
-        else:
-            run_script += " --server-name %s" % shlex_quote(self.server_name)
-        server_ini = os.path.join(self.config_directory, "galaxy.ini")
-        self.env["GALAXY_CONFIG_FILE"] = server_ini
-        if parse_version(kwds.get('galaxy_python_version') or DEFAULT_PYTHON_VERSION) >= parse_version('3'):
-            # We need to start under gunicorn
-            self.env['APP_WEBSERVER'] = 'gunicorn'
-            self.env['GUNICORN_CMD_ARGS'] = "--bind={host}:{port} --name={server_name}".format(
-                host=kwds.get('host', '127.0.0.1'),
-                port=kwds['port'],
-                server_name=self.server_name,
-            )
-        cd_to_galaxy_command = ['cd', self.galaxy_root]
-        return shell_join(
-            cd_to_galaxy_command,
-            setup_venv_command,
-            setup_common_startup_args(),
-            run_script,
-        )
-
-    @property
-    def log_file(self):
-        """Log file used when planemo serves this Galaxy instance."""
-        file_name = "%s.log" % self.server_name
-        return os.path.join(self.galaxy_root, file_name)
-
-    @property
-    def pid_file(self):
-        pid_file_name = "%s.pid" % self.server_name
-        return os.path.join(self.galaxy_root, pid_file_name)
-
-    @property
-    def log_contents(self):
-        if not os.path.exists(self.log_file):
-            return ""
-        with open(self.log_file, "r") as f:
-            return f.read()
-
-    def cleanup(self):
-        shutil.rmtree(self.config_directory, CLEANUP_IGNORE_ERRORS)
-
-    @property
-    def default_use_path_paste(self):
-        # If Planemo started a local, native Galaxy instance assume files URLs can be
-        # pasted.
-        return True
-
-
-def _database_connection(database_location, **kwds):
-    default_connection = DATABASE_LOCATION_TEMPLATE % database_location
-    database_connection = kwds.get("database_connection") or default_connection
-    return database_connection
-
-
-def _find_galaxy_root(ctx, **kwds):
-    root_prop = "galaxy_root"
-    cwl = kwds.get("cwl", False)
-    if cwl:
-        root_prop = "cwl_galaxy_root"
-    galaxy_root = kwds.get(root_prop, None)
-    if galaxy_root:
-        return galaxy_root
-    else:
-        par_dir = os.getcwd()
-        while True:
-            run = os.path.join(par_dir, "run.sh")
-            config = os.path.join(par_dir, "config")
-            if os.path.isfile(run) and os.path.isdir(config):
-                return par_dir
-            new_par_dir = os.path.dirname(par_dir)
-            if new_par_dir == par_dir:
-                break
-            par_dir = new_par_dir
-    return None
-
-
-def _find_test_data(runnables, **kwds):
-    test_data_search_path = "."
-    runnables = [r for r in runnables if r.has_tools]
-    if len(runnables) > 0:
-        test_data_search_path = runnables[0].test_data_search_path
-
-    # Find test data directory associated with path.
-    test_data = kwds.get("test_data", None)
-    if test_data:
-        return os.path.abspath(test_data)
-    else:
-        test_data = _search_tool_path_for(test_data_search_path, "test-data")
-        if test_data:
-            return test_data
-    warn(NO_TEST_DATA_MESSAGE)
-    return None
-
-
-def _find_tool_data_table(runnables, test_data_dir, **kwds):
-    tool_data_search_path = "."
-    runnables = [r for r in runnables if r.has_tools]
-    if len(runnables) > 0:
-        tool_data_search_path = runnables[0].tool_data_search_path
-
-    tool_data_table = kwds.get("tool_data_table", None)
-    if tool_data_table:
-        return os.path.abspath(tool_data_table)
-    else:
-        extra_paths = [test_data_dir] if test_data_dir else []
-        return _search_tool_path_for(
-            tool_data_search_path,
-            "tool_data_table_conf.xml.test",
-            extra_paths,
-        ) or _search_tool_path_for(  # if all else fails just use sample
-            tool_data_search_path,
-            "tool_data_table_conf.xml.sample"
-        )
-
-
-def _search_tool_path_for(path, target, extra_paths=[]):
-    """Check for presence of a target in different artifact directories."""
-    if not os.path.isdir(path):
-        tool_dir = os.path.dirname(path)
-    else:
-        tool_dir = path
-    possible_dirs = [tool_dir, "."] + extra_paths
-    for possible_dir in possible_dirs:
-        possible_path = os.path.join(possible_dir, target)
-        if os.path.exists(possible_path):
-            return os.path.abspath(possible_path)
-    return None
-
-
-def _configure_sheds_config_file(ctx, config_directory, **kwds):
-    if "shed_target" not in kwds:
-        kwds = kwds.copy()
-        kwds["shed_target"] = "toolshed"
-    shed_target_url = tool_shed_url(ctx, **kwds)
-    contents = _sub(TOOL_SHEDS_CONF, {"shed_target_url": shed_target_url})
-    tool_sheds_conf = os.path.join(config_directory, "tool_sheds_conf.xml")
-    write_file(tool_sheds_conf, contents)
-    return tool_sheds_conf
-
-
-def _tool_conf_entry_for(tool_paths):
-    tool_definitions = ""
-    for tool_path in tool_paths:
-        if os.path.isdir(tool_path):
-            tool_definitions += '''<tool_dir dir="%s" />''' % tool_path
-        else:
-            tool_definitions += '''<tool file="%s" />''' % tool_path
-    return tool_definitions
-
-
-def _install_galaxy(ctx, galaxy_root, env, kwds):
-    if not kwds.get("no_cache_galaxy", False):
-        _install_galaxy_via_git(ctx, galaxy_root, env, kwds)
-    else:
-        _install_galaxy_via_download(ctx, galaxy_root, env, kwds)
-
-
-def _install_galaxy_via_download(ctx, galaxy_root, env, kwds):
-    branch = _galaxy_branch(kwds)
-    untar_to("https://codeload.github.com/galaxyproject/galaxy/tar.gz/" + branch, tar_args=['-xvzf', '-', 'galaxy-' + branch], dest_dir=galaxy_root)
-    _install_with_command(ctx, galaxy_root, env, kwds)
-
-
-def _install_galaxy_via_git(ctx, galaxy_root, env, kwds):
-    gx_repo = _ensure_galaxy_repository_available(ctx, kwds)
-    branch = _galaxy_branch(kwds)
-    command = git.command_clone(ctx, gx_repo, galaxy_root, branch=branch)
-    shell(command, env=env)
-    _install_with_command(ctx, galaxy_root, env, kwds)
-
-
-def _build_eggs_cache(ctx, env, kwds):
-    if kwds.get("no_cache_galaxy", False):
-        return None
-    workspace = ctx.workspace
-    eggs_path = os.path.join(workspace, "gx_eggs")
-    if not os.path.exists(eggs_path):
-        os.makedirs(eggs_path)
-    env["GALAXY_EGGS_PATH"] = eggs_path
-
-
-def _galaxy_branch(kwds):
-    branch = kwds.get("galaxy_branch", None)
-    if branch is None:
-        cwl = kwds.get("cwl", False)
-        branch = "cwl-1.0" if cwl else None
-    if branch is None:
-        branch = DEFAULT_GALAXY_BRANCH
-
-    return branch
-
-
-def _galaxy_source(kwds):
-    source = kwds.get("galaxy_source", None)
-    if source is None:
-        cwl = kwds.get("cwl", False)
-        source = CWL_GALAXY_SOURCE if cwl else None
-    if source is None:
-        source = DEFAULT_GALAXY_SOURCE
-
-    return source
-
-
-def _install_with_command(ctx, galaxy_root, env, kwds):
-    setup_venv_command = setup_venv(ctx, kwds)
-    env['__PYVENV_LAUNCHER__'] = ''
-    install_cmd = shell_join(
-        setup_venv_command,
-        setup_common_startup_args(),
-        COMMAND_STARTUP_COMMAND,
-    )
-    shell(install_cmd, cwd=galaxy_root, env=env)
-
-
-def _ensure_galaxy_repository_available(ctx, kwds):
-    workspace = ctx.workspace
-    cwl = kwds.get("cwl", False)
-    galaxy_source = kwds.get('galaxy_source')
-    if galaxy_source and galaxy_source != DEFAULT_GALAXY_SOURCE:
-        sanitized_repo_name = "".join(c if c.isalnum() else '_' for c in kwds['galaxy_source']).rstrip()[:255]
-        gx_repo = os.path.join(workspace, "gx_repo_%s" % sanitized_repo_name)
-    else:
-        gx_repo = os.path.join(workspace, "gx_repo")
-    if cwl:
-        gx_repo += "_cwl"
-    if os.path.exists(gx_repo):
-        # Convert the git repository from bare to mirror, if needed
-        shell(['git', '--git-dir', gx_repo, 'config', 'remote.origin.fetch', '+refs/*:refs/*'])
-        shell(['git', '--git-dir', gx_repo, 'config', 'remote.origin.mirror', 'true'])
-        # Attempt remote update - but don't fail if not interweb, etc...
-        shell("git --git-dir %s remote update >/dev/null 2>&1" % gx_repo)
-    else:
-        remote_repo = _galaxy_source(kwds)
-        command = git.command_clone(ctx, remote_repo, gx_repo, mirror=True)
-        shell(command)
-    return gx_repo
-
-
-def _build_env_for_galaxy(properties, template_args):
-    env = {}
-    for key, value in iteritems(properties):
-        if value is not None:  # Do not override None with empty string
-            var = "GALAXY_CONFIG_OVERRIDE_%s" % key.upper()
-            value = _sub(value, template_args)
-            env[var] = value
-    return env
-
-
-def _build_test_env(properties, env):
-    # Keeping these environment variables around for a little while but
-    # many are probably not needed as of the following commit.
-    # https://bitbucket.org/galaxy/galaxy-central/commits/d7dd1f9
-    test_property_variants = {
-        'GALAXY_TEST_JOB_CONFIG_FILE': 'job_config_file',
-        'GALAXY_TEST_MIGRATED_TOOL_CONF': 'migrated_tools_config',
-        'GALAXY_TEST_TOOL_CONF': 'tool_config_file',
-        'GALAXY_TEST_FILE_DIR': 'test_data_dir',
-        'GALAXY_TOOL_DEPENDENCY_DIR': 'tool_dependency_dir',
-        # Next line would be required for tool shed tests.
-        # 'GALAXY_TEST_TOOL_DEPENDENCY_DIR': 'tool_dependency_dir',
-    }
-    for test_key, gx_key in test_property_variants.items():
-        value = properties.get(gx_key, None)
-        if value is not None:
-            env[test_key] = value
-
-
-def _handle_job_config_file(config_directory, server_name, kwds):
-    job_config_file = kwds.get("job_config_file", None)
-    if not job_config_file:
-        template_str = JOB_CONFIG_LOCAL
-        job_config_file = os.path.join(
-            config_directory,
-            "job_conf.xml",
-        )
-        docker_enable = str(kwds.get("docker", False))
-        docker_host = str(kwds.get("docker_host", docker_util.DEFAULT_HOST))
-        docker_host_param = ""
-        if docker_host:
-            docker_host_param = """<param id="docker_host">%s</param>""" % docker_host
-
-        conf_contents = Template(template_str).safe_substitute({
-            "server_name": server_name,
-            "docker_enable": docker_enable,
-            "require_container": "false",
-            "docker_sudo": str(kwds.get("docker_sudo", False)),
-            "docker_sudo_cmd": str(kwds.get("docker_sudo_cmd", docker_util.DEFAULT_SUDO_COMMAND)),
-            "docker_cmd": str(kwds.get("docker_cmd", docker_util.DEFAULT_DOCKER_COMMAND)),
-            "docker_host": docker_host_param,
-        })
-        write_file(job_config_file, conf_contents)
-    kwds["job_config_file"] = job_config_file
-
-
-def _write_tool_conf(ctx, tool_paths, tool_conf_path):
-    tool_definition = _tool_conf_entry_for(tool_paths)
-    tool_conf_template_kwds = dict(tool_definition=tool_definition)
-    tool_conf_contents = _sub(TOOL_CONF_TEMPLATE, tool_conf_template_kwds)
-    write_file(tool_conf_path, tool_conf_contents)
-    ctx.vlog(
-        "Writing tool_conf to path %s with contents [%s]",
-        tool_conf_path,
-        tool_conf_contents,
-    )
-
-
-def _handle_container_resolution(ctx, kwds, galaxy_properties):
-    if kwds.get("mulled_containers", False):
-        galaxy_properties["enable_beta_mulled_containers"] = "True"
-        involucro_context = build_involucro_context(ctx, **kwds)
-        galaxy_properties["involucro_auto_init"] = "False"  # Use planemo's
-        galaxy_properties["involucro_path"] = involucro_context.involucro_bin
-
-
-def _handle_job_metrics(config_directory, kwds):
-    metrics_conf = os.path.join(config_directory, "job_metrics_conf.xml")
-    with open(metrics_conf, "w") as fh:
-        fh.write(EMPTY_JOB_METRICS_TEMPLATE)
-    kwds["job_metrics_config_file"] = metrics_conf
-
-
-def _handle_kwd_overrides(properties, kwds):
-    kwds_gx_properties = [
-        'job_config_file',
-        'job_metrics_config_file',
-        'dependency_resolvers_config_file',
-    ]
-    for prop in kwds_gx_properties:
-        val = kwds.get(prop, None)
-        if val:
-            properties[prop] = val
-
-
-def _sub(template, args):
-    if template is None:
-        return ''
-    return Template(template).safe_substitute(args)
-
-
-def _ensure_directory(path):
-    if path is not None and not os.path.exists(path):
-        os.makedirs(path)
-
-
-__all__ = (
-    "DATABASE_LOCATION_TEMPLATE",
-    "galaxy_config",
-)