Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/galaxy/tool_util/deps/containers.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:18:57 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d30785e31577 |
|---|---|
| 1 import collections | |
| 2 import logging | |
| 3 import os | |
| 4 | |
| 5 from galaxy.util import ( | |
| 6 asbool, | |
| 7 plugin_config | |
| 8 ) | |
| 9 from .container_classes import ( | |
| 10 CONTAINER_CLASSES, | |
| 11 DOCKER_CONTAINER_TYPE, | |
| 12 NULL_CONTAINER, | |
| 13 SINGULARITY_CONTAINER_TYPE, | |
| 14 ) | |
| 15 from .container_resolvers import ResolutionCache | |
| 16 from .container_resolvers.explicit import ( | |
| 17 ExplicitContainerResolver, | |
| 18 ExplicitSingularityContainerResolver, | |
| 19 ) | |
| 20 from .container_resolvers.mulled import ( | |
| 21 BuildMulledDockerContainerResolver, | |
| 22 BuildMulledSingularityContainerResolver, | |
| 23 CachedMulledDockerContainerResolver, | |
| 24 CachedMulledSingularityContainerResolver, | |
| 25 MulledDockerContainerResolver, | |
| 26 MulledSingularityContainerResolver, | |
| 27 ) | |
| 28 from .requirements import ( | |
| 29 ContainerDescription, | |
| 30 ) | |
| 31 | |
| 32 log = logging.getLogger(__name__) | |
| 33 | |
| 34 | |
| 35 DEFAULT_CONTAINER_TYPE = DOCKER_CONTAINER_TYPE | |
| 36 ALL_CONTAINER_TYPES = [DOCKER_CONTAINER_TYPE, SINGULARITY_CONTAINER_TYPE] | |
| 37 | |
| 38 ResolvedContainerDescription = collections.namedtuple('ResolvedContainerDescription', ['container_resolver', 'container_description']) | |
| 39 | |
| 40 | |
| 41 class ContainerFinder(object): | |
| 42 | |
| 43 def __init__(self, app_info, mulled_resolution_cache=None): | |
| 44 self.app_info = app_info | |
| 45 self.container_registry = ContainerRegistry(app_info, mulled_resolution_cache=mulled_resolution_cache) | |
| 46 | |
| 47 def _enabled_container_types(self, destination_info): | |
| 48 return [t for t in ALL_CONTAINER_TYPES if self.__container_type_enabled(t, destination_info)] | |
| 49 | |
| 50 def find_best_container_description(self, enabled_container_types, tool_info, **kwds): | |
| 51 """Regardless of destination properties - find best container for tool. | |
| 52 | |
| 53 Given container types and container.ToolInfo description of the tool.""" | |
| 54 return self.container_registry.find_best_container_description(enabled_container_types, tool_info, **kwds) | |
| 55 | |
| 56 def resolve(self, enabled_container_types, tool_info, **kwds): | |
| 57 """Regardless of destination properties - find ResolvedContainerDescription for tool.""" | |
| 58 return self.container_registry.resolve(enabled_container_types, tool_info, **kwds) | |
| 59 | |
| 60 def find_container(self, tool_info, destination_info, job_info): | |
| 61 enabled_container_types = self._enabled_container_types(destination_info) | |
| 62 | |
| 63 # Short-cut everything else and just skip checks if no container type is enabled. | |
| 64 if not enabled_container_types: | |
| 65 return NULL_CONTAINER | |
| 66 | |
| 67 def __destination_container(container_description=None, container_id=None, container_type=None): | |
| 68 if container_description: | |
| 69 container_id = container_description.identifier | |
| 70 container_type = container_description.type | |
| 71 container = self.__destination_container( | |
| 72 container_id, | |
| 73 container_type, | |
| 74 tool_info, | |
| 75 destination_info, | |
| 76 job_info, | |
| 77 container_description, | |
| 78 ) | |
| 79 return container | |
| 80 | |
| 81 def container_from_description_from_dicts(destination_container_dicts): | |
| 82 for destination_container_dict in destination_container_dicts: | |
| 83 container_description = ContainerDescription.from_dict(destination_container_dict) | |
| 84 if container_description: | |
| 85 container = __destination_container(container_description) | |
| 86 if container: | |
| 87 return container | |
| 88 | |
| 89 if "container_override" in destination_info: | |
| 90 container = container_from_description_from_dicts(destination_info["container_override"]) | |
| 91 if container: | |
| 92 return container | |
| 93 | |
| 94 # If destination forcing Galaxy to use a particular container do it, | |
| 95 # this is likely kind of a corner case. For instance if deployers | |
| 96 # do not trust the containers annotated in tools. | |
| 97 for container_type in CONTAINER_CLASSES.keys(): | |
| 98 container_id = self.__overridden_container_id(container_type, destination_info) | |
| 99 if container_id: | |
| 100 container = __destination_container(container_type=container_type, container_id=container_id) | |
| 101 if container: | |
| 102 return container | |
| 103 | |
| 104 # Otherwise lets see if we can find container for the tool. | |
| 105 container_description = self.find_best_container_description(enabled_container_types, tool_info) | |
| 106 container = __destination_container(container_description) | |
| 107 if container: | |
| 108 return container | |
| 109 | |
| 110 # If we still don't have a container, check to see if any container | |
| 111 # types define a default container id and use that. | |
| 112 if "container" in destination_info: | |
| 113 container = container_from_description_from_dicts(destination_info["container"]) | |
| 114 if container: | |
| 115 return container | |
| 116 | |
| 117 for container_type in CONTAINER_CLASSES.keys(): | |
| 118 container_id = self.__default_container_id(container_type, destination_info) | |
| 119 if container_id: | |
| 120 container = __destination_container(container_type=container_type, container_id=container_id) | |
| 121 if container: | |
| 122 return container | |
| 123 | |
| 124 return NULL_CONTAINER | |
| 125 | |
| 126 def resolution_cache(self): | |
| 127 cache = ResolutionCache() | |
| 128 if self.container_registry.mulled_resolution_cache is not None: | |
| 129 cache.mulled_resolution_cache = self.container_registry.mulled_resolution_cache | |
| 130 return cache | |
| 131 | |
| 132 def __overridden_container_id(self, container_type, destination_info): | |
| 133 if not self.__container_type_enabled(container_type, destination_info): | |
| 134 return None | |
| 135 if "%s_container_id_override" % container_type in destination_info: | |
| 136 return destination_info.get("%s_container_id_override" % container_type) | |
| 137 if "%s_image_override" % container_type in destination_info: | |
| 138 return self.__build_container_id_from_parts(container_type, destination_info, mode="override") | |
| 139 | |
| 140 def __build_container_id_from_parts(self, container_type, destination_info, mode): | |
| 141 repo = "" | |
| 142 owner = "" | |
| 143 repo_key = "%s_repo_%s" % (container_type, mode) | |
| 144 owner_key = "%s_owner_%s" % (container_type, mode) | |
| 145 if repo_key in destination_info: | |
| 146 repo = destination_info[repo_key] + "/" | |
| 147 if owner_key in destination_info: | |
| 148 owner = destination_info[owner_key] + "/" | |
| 149 cont_id = repo + owner + destination_info["%s_image_%s" % (container_type, mode)] | |
| 150 tag_key = "%s_tag_%s" % (container_type, mode) | |
| 151 if tag_key in destination_info: | |
| 152 cont_id += ":" + destination_info[tag_key] | |
| 153 return cont_id | |
| 154 | |
| 155 def __default_container_id(self, container_type, destination_info): | |
| 156 if not self.__container_type_enabled(container_type, destination_info): | |
| 157 return None | |
| 158 key = "%s_default_container_id" % container_type | |
| 159 # Also allow docker_image... | |
| 160 if key not in destination_info: | |
| 161 key = "%s_image" % container_type | |
| 162 if key in destination_info: | |
| 163 return destination_info.get(key) | |
| 164 elif "%s_image_default" % container_type in destination_info: | |
| 165 return self.__build_container_id_from_parts(container_type, destination_info, mode="default") | |
| 166 return None | |
| 167 | |
| 168 def __destination_container(self, container_id, container_type, tool_info, destination_info, job_info, container_description=None): | |
| 169 # TODO: ensure destination_info is dict-like | |
| 170 if not self.__container_type_enabled(container_type, destination_info): | |
| 171 return NULL_CONTAINER | |
| 172 | |
| 173 # TODO: Right now this assumes all containers available when a | |
| 174 # container type is - there should be more thought put into this. | |
| 175 # Checking which are available - settings policies for what can be | |
| 176 # auto-fetched, etc.... | |
| 177 return CONTAINER_CLASSES[container_type](container_id, self.app_info, tool_info, destination_info, job_info, container_description) | |
| 178 | |
| 179 def __container_type_enabled(self, container_type, destination_info): | |
| 180 return asbool(destination_info.get("%s_enabled" % container_type, False)) | |
| 181 | |
| 182 | |
| 183 class NullContainerFinder(object): | |
| 184 | |
| 185 def find_container(self, tool_info, destination_info, job_info): | |
| 186 return [] | |
| 187 | |
| 188 | |
| 189 class ContainerRegistry(object): | |
| 190 """Loop through enabled ContainerResolver plugins and find first match.""" | |
| 191 | |
| 192 def __init__(self, app_info, mulled_resolution_cache=None): | |
| 193 self.resolver_classes = self.__resolvers_dict() | |
| 194 self.enable_mulled_containers = app_info.enable_mulled_containers | |
| 195 self.app_info = app_info | |
| 196 self.container_resolvers = self.__build_container_resolvers(app_info) | |
| 197 self.mulled_resolution_cache = mulled_resolution_cache | |
| 198 | |
| 199 def __build_container_resolvers(self, app_info): | |
| 200 conf_file = getattr(app_info, 'containers_resolvers_config_file', None) | |
| 201 if not conf_file: | |
| 202 return self.__default_containers_resolvers() | |
| 203 if not os.path.exists(conf_file): | |
| 204 log.debug("Unable to find config file '%s'", conf_file) | |
| 205 return self.__default_containers_resolvers() | |
| 206 plugin_source = plugin_config.plugin_source_from_path(conf_file) | |
| 207 return self._parse_resolver_conf(plugin_source) | |
| 208 | |
| 209 def _parse_resolver_conf(self, plugin_source): | |
| 210 extra_kwds = { | |
| 211 'app_info': self.app_info | |
| 212 } | |
| 213 return plugin_config.load_plugins(self.resolver_classes, plugin_source, extra_kwds) | |
| 214 | |
| 215 def __default_containers_resolvers(self): | |
| 216 default_resolvers = [ | |
| 217 ExplicitContainerResolver(self.app_info), | |
| 218 ExplicitSingularityContainerResolver(self.app_info), | |
| 219 ] | |
| 220 if self.enable_mulled_containers: | |
| 221 default_resolvers.extend([ | |
| 222 CachedMulledDockerContainerResolver(self.app_info, namespace="biocontainers"), | |
| 223 CachedMulledDockerContainerResolver(self.app_info, namespace="local"), | |
| 224 CachedMulledSingularityContainerResolver(self.app_info, namespace="biocontainers"), | |
| 225 CachedMulledSingularityContainerResolver(self.app_info, namespace="local"), | |
| 226 MulledDockerContainerResolver(self.app_info, namespace="biocontainers"), | |
| 227 MulledSingularityContainerResolver(self.app_info, namespace="biocontainers"), | |
| 228 BuildMulledDockerContainerResolver(self.app_info), | |
| 229 BuildMulledSingularityContainerResolver(self.app_info), | |
| 230 ]) | |
| 231 return default_resolvers | |
| 232 | |
| 233 def __resolvers_dict(self): | |
| 234 import galaxy.tool_util.deps.container_resolvers | |
| 235 return plugin_config.plugins_dict(galaxy.tool_util.deps.container_resolvers, 'resolver_type') | |
| 236 | |
| 237 def find_best_container_description(self, enabled_container_types, tool_info, **kwds): | |
| 238 """Yield best container description of supplied types matching tool info.""" | |
| 239 try: | |
| 240 resolved_container_description = self.resolve(enabled_container_types, tool_info, **kwds) | |
| 241 except Exception: | |
| 242 log.exception("Could not get container description for tool '%s'", tool_info.tool_id) | |
| 243 return None | |
| 244 return None if resolved_container_description is None else resolved_container_description.container_description | |
| 245 | |
| 246 def resolve(self, enabled_container_types, tool_info, index=None, resolver_type=None, install=True, resolution_cache=None): | |
| 247 for i, container_resolver in enumerate(self.container_resolvers): | |
| 248 if index is not None and i != index: | |
| 249 continue | |
| 250 | |
| 251 if resolver_type is not None and resolver_type != container_resolver.resolver_type: | |
| 252 continue | |
| 253 | |
| 254 if hasattr(container_resolver, "container_type"): | |
| 255 if container_resolver.container_type not in enabled_container_types: | |
| 256 continue | |
| 257 | |
| 258 if not install and container_resolver.builds_on_resolution: | |
| 259 continue | |
| 260 | |
| 261 container_description = container_resolver.resolve(enabled_container_types, tool_info, resolution_cache=resolution_cache) | |
| 262 log.info("Checking with container resolver [%s] found description [%s]" % (container_resolver, container_description)) | |
| 263 if container_description: | |
| 264 assert container_description.type in enabled_container_types | |
| 265 return ResolvedContainerDescription(container_resolver, container_description) | |
| 266 | |
| 267 return None |
