mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Support setting per-job-environment container resolvers using the container_resolvers key in the environment config.
This commit is contained in:
@@ -1208,8 +1208,8 @@ class ConfiguresGalaxyMixin:
|
||||
for enabled_container_type in self.container_finder._enabled_container_types(destination.params):
|
||||
container_types_to_destinations[enabled_container_type].append(destination)
|
||||
self.toolbox.dependency_manager.set_enabled_container_types(container_types_to_destinations)
|
||||
self.toolbox.dependency_manager.resolver_classes.update(self.container_finder.container_registry.resolver_classes)
|
||||
self.toolbox.dependency_manager.dependency_resolvers.extend(self.container_finder.container_registry.container_resolvers)
|
||||
self.toolbox.dependency_manager.resolver_classes.update(self.container_finder.default_container_registry.resolver_classes)
|
||||
self.toolbox.dependency_manager.dependency_resolvers.extend(self.container_finder.default_container_registry.container_resolvers)
|
||||
|
||||
def _configure_tool_data_tables(self, from_shed_config):
|
||||
from galaxy.tools.data import ToolDataTableManager
|
||||
|
||||
@@ -42,7 +42,9 @@ class ContainerFinder:
|
||||
|
||||
def __init__(self, app_info, mulled_resolution_cache=None):
|
||||
self.app_info = app_info
|
||||
self.container_registry = ContainerRegistry(app_info, mulled_resolution_cache=mulled_resolution_cache)
|
||||
self.mulled_resolution_cache = mulled_resolution_cache
|
||||
self.default_container_registry = ContainerRegistry(app_info, mulled_resolution_cache=mulled_resolution_cache)
|
||||
self.destination_container_registeries = {}
|
||||
|
||||
def _enabled_container_types(self, destination_info):
|
||||
return [t for t in ALL_CONTAINER_TYPES if self.__container_type_enabled(t, destination_info)]
|
||||
@@ -51,11 +53,26 @@ class ContainerFinder:
|
||||
"""Regardless of destination properties - find best container for tool.
|
||||
|
||||
Given container types and container.ToolInfo description of the tool."""
|
||||
return self.container_registry.find_best_container_description(enabled_container_types, tool_info, **kwds)
|
||||
return self.default_container_registry.find_best_container_description(enabled_container_types, tool_info, **kwds)
|
||||
|
||||
def resolve(self, enabled_container_types, tool_info, **kwds):
|
||||
"""Regardless of destination properties - find ResolvedContainerDescription for tool."""
|
||||
return self.container_registry.resolve(enabled_container_types, tool_info, **kwds)
|
||||
return self.default_container_registry.resolve(enabled_container_types, tool_info, **kwds)
|
||||
|
||||
def _container_registry_for_destination(self, destination_info):
|
||||
destination_id = destination_info.get("id") # Probably not the way to get the ID?
|
||||
destination_container_registry = None
|
||||
if destination_id and destination_id not in self.destination_container_registeries:
|
||||
if 'container_resolvers' in destination_info:
|
||||
destination_container_registry = ContainerRegistry(self.app_info, destination_info=destination_info, mulled_resolution_cache=self.mulled_resolution_cache)
|
||||
self.destination_container_registeries[destination_id] = destination_container_registry
|
||||
elif not destination_id and 'container_resolvers' in destination_info:
|
||||
destination_container_registry = ContainerRegistry(self.app_info, destination_info=destination_info, mulled_resolution_cache=self.mulled_resolution_cache)
|
||||
|
||||
if destination_container_registry is None and destination_id and destination_id in self.destination_container_registeries:
|
||||
destination_container_registry = self.destination_container_registeries[destination_id]
|
||||
|
||||
return destination_container_registry or self.default_container_registry
|
||||
|
||||
def find_container(self, tool_info, destination_info, job_info):
|
||||
enabled_container_types = self._enabled_container_types(destination_info)
|
||||
@@ -102,7 +119,8 @@ class ContainerFinder:
|
||||
return container
|
||||
|
||||
# Otherwise lets see if we can find container for the tool.
|
||||
container_description = self.find_best_container_description(enabled_container_types, tool_info)
|
||||
container_registry = self._container_registry_for_destination(destination_info)
|
||||
container_description = container_registry.find_best_container_description(enabled_container_types, tool_info)
|
||||
container = __destination_container(container_description)
|
||||
if container:
|
||||
return container
|
||||
@@ -124,7 +142,7 @@ class ContainerFinder:
|
||||
return NULL_CONTAINER
|
||||
|
||||
def resolution_cache(self):
|
||||
return self.container_registry.get_resolution_cache()
|
||||
return self.default_container_registry.get_resolution_cache()
|
||||
|
||||
def __overridden_container_id(self, container_type, destination_info):
|
||||
if not self.__container_type_enabled(container_type, destination_info):
|
||||
@@ -186,25 +204,33 @@ class NullContainerFinder:
|
||||
class ContainerRegistry:
|
||||
"""Loop through enabled ContainerResolver plugins and find first match."""
|
||||
|
||||
def __init__(self, app_info, mulled_resolution_cache=None):
|
||||
def __init__(self, app_info, destination_info=None, mulled_resolution_cache=None):
|
||||
self.resolver_classes = self.__resolvers_dict()
|
||||
self.enable_mulled_containers = app_info.enable_mulled_containers
|
||||
self.app_info = app_info
|
||||
self.container_resolvers = self.__build_container_resolvers(app_info)
|
||||
self.container_resolvers = self.__build_container_resolvers(app_info, destination_info)
|
||||
self.mulled_resolution_cache = mulled_resolution_cache
|
||||
|
||||
def __build_container_resolvers(self, app_info):
|
||||
conf_file = getattr(app_info, 'container_resolvers_config_file', None)
|
||||
conf_dict = getattr(app_info, 'container_resolvers_config_dict', None)
|
||||
def __build_container_resolvers(self, app_info, destination_info):
|
||||
app_conf_file = getattr(app_info, 'container_resolvers_config_file', None)
|
||||
app_conf_dict = getattr(app_info, 'container_resolvers_config_dict', None)
|
||||
|
||||
if destination_info is not None:
|
||||
conf_file = destination_info.get('container_resolvers_config_file', app_conf_file)
|
||||
conf_dict = destination_info.get('container_resolvers', app_conf_dict)
|
||||
else:
|
||||
conf_file = app_conf_file
|
||||
conf_dict = app_conf_dict
|
||||
|
||||
plugin_source = None
|
||||
if conf_file and not os.path.exists(conf_file):
|
||||
if conf_dict:
|
||||
log.debug("Loading container resolution config inline from Galaxy or job configuration file")
|
||||
plugin_source = plugin_config.plugin_source_from_dict(conf_dict)
|
||||
elif conf_file and not os.path.exists(conf_file):
|
||||
log.warning(f"Unable to find config file '{conf_file}'")
|
||||
elif conf_file:
|
||||
log.debug("Loading container resolution config from file '{conf_file}'")
|
||||
plugin_source = plugin_config.plugin_source_from_path(conf_file)
|
||||
elif conf_dict:
|
||||
log.debug("Loading container resolution config inline from Galaxy configuration file")
|
||||
plugin_source = plugin_config.plugin_source_from_dict(conf_dict)
|
||||
if plugin_source:
|
||||
return self._parse_resolver_conf(plugin_source)
|
||||
return self.__default_container_resolvers()
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<tool id="mulled_example_broken_no_requirements_fallback" name="mulled_example_broken_no_requirements_fallback" version="0.1.0">
|
||||
<!-- mulled_example_explicit without requirements... to test mapping with job conf inline container resolvers config -->
|
||||
<!--
|
||||
<requirements>
|
||||
<requirement type="package" version="0.7.15">bwa</requirement>
|
||||
<container type="docker">quay.io/biocontainers/bwa:0.7.15-\-0</container>
|
||||
</requirements>
|
||||
-->
|
||||
<stdio>
|
||||
<exit_code range="2:" />
|
||||
</stdio>
|
||||
<command><![CDATA[
|
||||
bwa > $output_1 2>&1
|
||||
]]></command>
|
||||
<inputs>
|
||||
</inputs>
|
||||
<outputs>
|
||||
<data name="output_1" />
|
||||
</outputs>
|
||||
<tests>
|
||||
</tests>
|
||||
<help><![CDATA[
|
||||
TODO: Fill in help.
|
||||
]]></help>
|
||||
</tool>
|
||||
@@ -243,6 +243,7 @@
|
||||
<tool file="mulled_example_explicit.xml" />
|
||||
<tool file="mulled_example_invalid_case.xml" />
|
||||
<tool file="mulled_example_broken_no_requirements.xml" />
|
||||
<tool file="mulled_example_broken_no_requirements_fallback.xml" />
|
||||
|
||||
<tool file="simple_constructs.yml" />
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<job_conf>
|
||||
<plugins>
|
||||
<plugin id="local" type="runner" load="galaxy.jobs.runners.local:LocalJobRunner" workers="1"/>
|
||||
</plugins>
|
||||
|
||||
<destinations default="local_docker">
|
||||
<destination id="local_docker" runner="local">
|
||||
<param id="docker_enabled">true</param>
|
||||
<param id="docker_sudo">false</param>
|
||||
</destination>
|
||||
|
||||
<destination id="local_upload" runner="local">
|
||||
</destination>
|
||||
|
||||
</destinations>
|
||||
|
||||
<tools>
|
||||
<tool id="upload1" destination="local_upload" />
|
||||
</tools>
|
||||
|
||||
</job_conf>
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
runners:
|
||||
local:
|
||||
load: galaxy.jobs.runners.local:LocalJobRunner
|
||||
workers: 1
|
||||
|
||||
execution:
|
||||
default: local_docker
|
||||
environments:
|
||||
local_docker:
|
||||
runner: local
|
||||
docker_enabled: true
|
||||
local_docker_inline_container_resolvers:
|
||||
runner: local
|
||||
docker_enabled: true
|
||||
container_resolvers:
|
||||
- type: fallback
|
||||
identifier: 'quay.io/biocontainers/bwa:0.7.15--0'
|
||||
local_upload:
|
||||
runner: local
|
||||
|
||||
tools:
|
||||
- id: upload1
|
||||
environment: local_upload
|
||||
- id: mulled_example_broken_no_requirements_fallback
|
||||
environment: local_docker_inline_container_resolvers
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<job_conf>
|
||||
<plugins>
|
||||
<plugin id="local" type="runner" load="galaxy.jobs.runners.local:LocalJobRunner" workers="1"/>
|
||||
</plugins>
|
||||
|
||||
<destinations default="local_singularity">
|
||||
<destination id="local_singularity" runner="local">
|
||||
<param id="singularity_enabled">true</param>
|
||||
</destination>
|
||||
|
||||
<destination id="local_upload" runner="local">
|
||||
</destination>
|
||||
|
||||
</destinations>
|
||||
|
||||
<tools>
|
||||
<tool class="local" destination="local_upload" />
|
||||
</tools>
|
||||
|
||||
</job_conf>
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
runners:
|
||||
local:
|
||||
load: galaxy.jobs.runners.local:LocalJobRunner
|
||||
workers: 1
|
||||
|
||||
execution:
|
||||
default: local_singularity
|
||||
environments:
|
||||
local_singularity:
|
||||
runner: local
|
||||
singularity_enabled: true
|
||||
local_upload:
|
||||
runner: local
|
||||
|
||||
tools:
|
||||
- id: upload1
|
||||
environment: local_upload
|
||||
@@ -12,8 +12,8 @@ from galaxy_test.driver import integration_util
|
||||
from .test_job_environments import RunsEnvironmentJobs
|
||||
|
||||
SCRIPT_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
|
||||
DOCKERIZED_JOB_CONFIG_FILE = os.path.join(SCRIPT_DIRECTORY, "dockerized_job_conf.xml")
|
||||
SINGULARITY_JOB_CONFIG_FILE = os.path.join(SCRIPT_DIRECTORY, "singularity_job_conf.xml")
|
||||
DOCKERIZED_JOB_CONFIG_FILE = os.path.join(SCRIPT_DIRECTORY, "dockerized_job_conf.yml")
|
||||
SINGULARITY_JOB_CONFIG_FILE = os.path.join(SCRIPT_DIRECTORY, "singularity_job_conf.yml")
|
||||
EXTENDED_TIMEOUT = 120
|
||||
|
||||
|
||||
@@ -202,6 +202,36 @@ class InlineContainerConfigurationTestCase(MappingContainerResolverTestCase):
|
||||
config["container_resolvers"] = container_resolvers_config
|
||||
|
||||
|
||||
class InlineJobEnvironmentContainerResolverTestCase(integration_util.IntegrationTestCase):
|
||||
|
||||
framework_tool_and_types = True
|
||||
container_type = 'docker'
|
||||
job_config_file = DOCKERIZED_JOB_CONFIG_FILE
|
||||
|
||||
@classmethod
|
||||
def handle_galaxy_config_kwds(cls, config):
|
||||
cls.jobs_directory = cls._test_driver.mkdtemp()
|
||||
config["jobs_directory"] = cls.jobs_directory
|
||||
config["job_config_file"] = cls.job_config_file
|
||||
disable_dependency_resolution(config)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
skip_if_container_type_unavailable(cls)
|
||||
super().setUpClass()
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.dataset_populator = DatasetPopulator(self.galaxy_interactor)
|
||||
self.history_id = self.dataset_populator.new_history()
|
||||
|
||||
def test_inline_environment_container_resolver_configuration(self):
|
||||
self.dataset_populator.run_tool("mulled_example_broken_no_requirements_fallback", {}, self.history_id)
|
||||
self.dataset_populator.wait_for_history(self.history_id, assert_ok=True)
|
||||
output = self.dataset_populator.get_history_dataset_content(self.history_id, timeout=EXTENDED_TIMEOUT)
|
||||
assert "0.7.15-r1140" in output
|
||||
|
||||
|
||||
# Singularity 2.4 in the official Vagrant issue has some problems running this test
|
||||
# case by default because subdirectories of /tmp don't bind correctly. Overridding
|
||||
# TMPDIR can fix this.
|
||||
|
||||
Reference in New Issue
Block a user