Skip to content

Commit 08cef54

Browse files
committed
Mulled support.
Implement option to build mulled containers for tools: ``` planemo mull [/path/to/tools]* ``` Implement option to force tool serving and execution to occur in mulled containers. ``` planemo test --mulled_containers ``` This includes fixes for support or tools in Docker, something that has been around since the early days but seemingly broken the whole time. xref #583
1 parent 2e4e5fc commit 08cef54

File tree

6 files changed

+143
-4
lines changed

6 files changed

+143
-4
lines changed

planemo/commands/cmd_mull.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Module describing the planemo ``mull`` command."""
2+
import click
3+
4+
from galaxy.tools.deps.mulled.mulled_build import mull_targets
5+
from galaxy.tools.deps.mulled.util import build_target
6+
7+
from planemo import options
8+
from planemo.cli import command_function
9+
from planemo.conda import collect_conda_target_lists
10+
from planemo.mulled import build_mull_target_kwds
11+
12+
13+
@click.command('mull')
14+
@options.optional_tools_arg(multiple=True)
15+
@options.recursive_option()
16+
@command_function
17+
def cli(ctx, paths, **kwds):
18+
"""Build containers for specified tools.
19+
20+
Supplied tools will be inspected for referenced requirement packages. For
21+
each combination of requirements a "mulled" container will be built. Galaxy
22+
can automatically discover this container and subsequently use it to run
23+
or test the tool.
24+
25+
For this to work, the tool's requirements will need to be present in a known
26+
Conda channel such as bioconda (https://github.com/bioconda/bioconda-recipes).
27+
This can be verified by running ``planemo lint --conda_requirements`` on the
28+
target tool(s).
29+
"""
30+
for conda_targets in collect_conda_target_lists(ctx, paths):
31+
mulled_targets = map(lambda c: build_target(c.package, c.version), conda_targets)
32+
mull_target_kwds = build_mull_target_kwds(ctx, **kwds)
33+
mull_targets(mulled_targets, command="build", **mull_target_kwds)

planemo/conda.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ def collect_conda_targets(ctx, paths, found_tool_callback=None, conda_context=No
4444
return conda_targets
4545

4646

47+
def collect_conda_target_lists(ctx, paths, found_tool_callback=None):
48+
"""Load CondaTarget lists from supplied artifact sources.
49+
50+
If a tool contains more than one requirement, the requirements will all
51+
appear together as one list element of the output list.
52+
"""
53+
conda_target_lists = set([])
54+
for (tool_path, tool_source) in yield_tool_sources_on_paths(ctx, paths):
55+
if found_tool_callback:
56+
found_tool_callback(tool_path)
57+
conda_target_lists.add(frozenset(tool_source_conda_targets(tool_source)))
58+
return conda_target_lists
59+
60+
4761
def tool_source_conda_targets(tool_source):
4862
"""Load CondaTarget object from supplied abstract tool source."""
4963
requirements, _ = tool_source.parse_requirements_and_containers()
@@ -53,5 +67,6 @@ def tool_source_conda_targets(tool_source):
5367
__all__ = [
5468
"build_conda_context",
5569
"collect_conda_targets",
70+
"collect_conda_target_lists",
5671
"tool_source_conda_targets",
5772
]

planemo/galaxy/config.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from planemo import git
2424
from planemo.conda import build_conda_context
25+
from planemo.config import OptionSource
2526
from planemo.docker import docker_host_args
2627
from planemo.io import (
2728
communicate,
@@ -32,6 +33,7 @@
3233
warn,
3334
write_file,
3435
)
36+
from planemo.mulled import build_involucro_context
3537
from planemo.shed import tool_shed_url
3638

3739
from .api import (
@@ -118,13 +120,20 @@
118120
</handlers>
119121
<destinations default="planemo_dest">
120122
<destination id="planemo_dest" runner="planemo_runner">
121-
<param id="docker_enable">${docker_enable}</param>
123+
<param id="require_container">${require_container}</param>
124+
<param id="docker_enabled">${docker_enable}</param>
122125
<param id="docker_sudo">${docker_sudo}</param>
123126
<param id="docker_sudo_cmd">${docker_sudo_cmd}</param>
124127
<param id="docker_cmd">${docker_cmd}</param>
125-
<param id="docker_host">${docker_host}</param>
128+
${docker_host_param}
129+
</destination>
130+
<destination id="upload_dest" runner="planemo_runner">
131+
<param id="docker_enable">false</param>
126132
</destination>
127133
</destinations>
134+
<tools>
135+
<tool id="upload1" destination="upload_dest" />
136+
</tools>
128137
</job_conf>
129138
"""
130139

@@ -211,6 +220,7 @@ def config_join(*args):
211220
)
212221
port = _get_port(kwds)
213222
properties = _shared_galaxy_properties(kwds)
223+
_handle_container_resolution(ctx, kwds, properties)
214224
master_api_key = _get_master_api_key(kwds)
215225

216226
template_args = dict(
@@ -276,6 +286,14 @@ def local_galaxy_config(ctx, runnables, for_tests=False, **kwds):
276286
)
277287
galaxy_root = _check_galaxy(ctx, **kwds)
278288
install_galaxy = galaxy_root is None
289+
290+
# Duplicate block in docker variant above.
291+
if kwds.get("mulled_containers", False) and not kwds.get("docker", False):
292+
if ctx.get_option_source("docker") != OptionSource.cli:
293+
kwds["docker"] = True
294+
else:
295+
raise Exception("Specified no docker and mulled containers together.")
296+
279297
with _config_directory(ctx, **kwds) as config_directory:
280298
def config_join(*args):
281299
return os.path.join(config_directory, *args)
@@ -374,6 +392,7 @@ def config_join(*args):
374392
migrated_tools_config=empty_tool_conf,
375393
test_data_dir=test_data_dir, # TODO: make gx respect this
376394
))
395+
_handle_container_resolution(ctx, kwds, properties)
377396
if not for_tests:
378397
properties["database_connection"] = _database_connection(database_location, **kwds)
379398

@@ -1106,13 +1125,20 @@ def _handle_job_config_file(config_directory, server_name, kwds):
11061125
config_directory,
11071126
"job_conf.xml",
11081127
)
1128+
docker_enable = str(kwds.get("docker", False))
1129+
docker_host = str(kwds.get("docker_host", docker_util.DEFAULT_HOST))
1130+
docker_host_param = ""
1131+
if docker_host:
1132+
docker_host_param = """<param id="docker_host">%s</param>""" % docker_host
1133+
11091134
conf_contents = Template(template_str).safe_substitute({
11101135
"server_name": server_name,
11111136
"docker_enable": str(kwds.get("docker", False)),
1137+
"require_container": docker_enable,
11121138
"docker_sudo": str(kwds.get("docker_sudo", False)),
11131139
"docker_sudo_cmd": str(kwds.get("docker_sudo_cmd", docker_util.DEFAULT_SUDO_COMMAND)),
11141140
"docker_cmd": str(kwds.get("docker_cmd", docker_util.DEFAULT_DOCKER_COMMAND)),
1115-
"docker_host": str(kwds.get("docker_host", docker_util.DEFAULT_HOST)),
1141+
"docker_host": docker_host_param,
11161142
})
11171143
write_file(job_config_file, conf_contents)
11181144
kwds["job_config_file"] = job_config_file
@@ -1184,6 +1210,14 @@ def add_attribute(key, value):
11841210
kwds["dependency_resolvers_config_file"] = resolvers_conf
11851211

11861212

1213+
def _handle_container_resolution(ctx, kwds, galaxy_properties):
1214+
if kwds.get("mulled_containers", False):
1215+
galaxy_properties["enable_beta_mulled_containers"] = "True"
1216+
involucro_context = build_involucro_context(ctx, **kwds)
1217+
galaxy_properties["involucro_auto_init"] = "False" # Use planemo's
1218+
galaxy_properties["involucro_path"] = involucro_context.involucro_bin
1219+
1220+
11871221
def _handle_job_metrics(config_directory, kwds):
11881222
metrics_conf = os.path.join(config_directory, "job_metrics_conf.xml")
11891223
open(metrics_conf, "w").write(EMPTY_JOB_METRICS_TEMPLATE)

planemo/mulled.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Planemo specific utilities for dealing with mulled containers.
2+
3+
The extend Galaxy/galaxy-lib's features with planemo specific idioms.
4+
"""
5+
from __future__ import absolute_import
6+
7+
import os
8+
9+
from galaxy.tools.deps.mulled.mulled_build import (
10+
DEFAULT_CHANNELS,
11+
ensure_installed,
12+
InvolucroContext,
13+
)
14+
15+
from planemo.io import shell
16+
17+
18+
def build_involucro_context(ctx, **kwds):
19+
"""Build a galaxy-lib CondaContext tailored to planemo use.
20+
21+
Using planemo's common command-line/global config options.
22+
"""
23+
involucro_path_default = os.path.join(ctx.workspace, "involucro")
24+
involucro_path = kwds.get("involucro_path", involucro_path_default)
25+
use_planemo_shell = kwds.get("use_planemo_shell_exec", True)
26+
shell_exec = shell if use_planemo_shell else None
27+
involucro_context = InvolucroContext(involucro_bin=involucro_path,
28+
shell_exec=shell_exec)
29+
if not ensure_installed(involucro_context, True):
30+
raise Exception("Failed to install involucro for Planemo.")
31+
return involucro_context
32+
33+
34+
def build_mull_target_kwds(ctx, **kwds):
35+
"""Adapt Planemo's CLI and workspace configuration to galaxy-lib's mulled_build options."""
36+
involucro_context = build_involucro_context(ctx, **kwds)
37+
channels = kwds.get("conda_ensure_channels", ",".join(DEFAULT_CHANNELS))
38+
39+
return {
40+
'involucro_context': involucro_context,
41+
'channels': channels.split(","),
42+
}
43+
44+
__all__ = [
45+
"build_involucro_context",
46+
]

planemo/options.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,14 @@ def job_config_option():
333333
)
334334

335335

336+
def mulled_containers_option():
337+
return planemo_option(
338+
"--mulled_containers",
339+
is_flag=True,
340+
help="Test tools against mulled containers (forces --docker).",
341+
)
342+
343+
336344
def install_galaxy_option():
337345
return planemo_option(
338346
"--install_galaxy",
@@ -430,6 +438,8 @@ def conda_debug_option():
430438

431439
def conda_ensure_channels_option():
432440
return planemo_option(
441+
"conda_ensure_channels",
442+
"--conda_channels",
433443
"--conda_ensure_channels",
434444
type=str,
435445
use_global_config=True,
@@ -878,6 +888,7 @@ def galaxy_target_options():
878888
no_cleanup_option(),
879889
galaxy_email_option(),
880890
galaxy_docker_options(),
891+
mulled_containers_option(),
881892
# Profile options...
882893
job_config_option(),
883894
tool_dependency_dir_option(),

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ virtualenv
1010
lxml
1111
gxformat2>=0.1.1
1212
ephemeris>=0.2.0
13-
galaxy-lib>=16.10.3
13+
galaxy-lib>=16.10.5
1414
html5lib>=0.9999999,!=0.99999999,!=0.999999999,!=1.0b10,!=1.0b09 ; python_version == '2.7'
1515
cwltool==1.0.20160726135535 ; python_version == '2.7'

0 commit comments

Comments
 (0)