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 |