1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
5 from builtins import str
6 from builtins import object
9 import arvados_cwl.context
10 import arvados_cwl.util
11 #from arvados_cwl.arvdocker import arv_docker_clear_cache
20 import cwltool.process
21 import cwltool.secrets
22 import cwltool.load_tool
23 from cwltool.update import INTERNAL_VERSION
24 from schema_salad.ref_resolver import Loader
25 from schema_salad.sourceline import cmap
27 from .matcher import JsonDiffMatcher, StripYAMLComments
28 from .mock_discovery import get_rootDesc
30 if not os.getenv('ARVADOS_DEBUG'):
31 logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
32 logging.getLogger('arvados.arv-run').setLevel(logging.WARN)
34 class CollectionMock(object):
35 def __init__(self, vwdmock, *args, **kwargs):
36 self.vwdmock = vwdmock
39 def open(self, *args, **kwargs):
41 return self.vwdmock.open(*args, **kwargs)
43 def copy(self, *args, **kwargs):
45 self.vwdmock.copy(*args, **kwargs)
47 def save_new(self, *args, **kwargs):
53 def portable_data_hash(self):
55 return arvados.config.EMPTY_BLOCK_LOCATOR
57 return "99999999999999999999999999999996+99"
60 class TestContainer(unittest.TestCase):
63 cwltool.process._names = set()
64 #arv_docker_clear_cache()
67 root_logger = logging.getLogger('')
69 # Remove existing RuntimeStatusLoggingHandlers if they exist
70 handlers = [h for h in root_logger.handlers if not isinstance(h, arvados_cwl.executor.RuntimeStatusLoggingHandler)]
71 root_logger.handlers = handlers
73 def helper(self, runner, enable_reuse=True):
74 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
76 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
77 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
78 fs_access = mock.MagicMock()
79 fs_access.exists.return_value = True
81 loadingContext = arvados_cwl.context.ArvLoadingContext(
82 {"avsc_names": avsc_names,
84 "make_fs_access": make_fs_access,
85 "construct_tool_object": runner.arv_make_tool,
86 "fetcher_constructor": functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=fs_access),
88 "metadata": cmap({"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"}),
89 "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__
91 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
92 {"work_api": "containers",
94 "name": "test_run_"+str(enable_reuse),
95 "make_fs_access": make_fs_access,
98 "enable_reuse": enable_reuse,
100 "project_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
101 "workflow_eval_lock": threading.Condition(threading.RLock())
104 if isinstance(runner, mock.MagicMock):
105 def make_tool(toolpath_object, loadingContext):
106 return arvados_cwl.ArvadosCommandTool(runner, toolpath_object, loadingContext)
107 runner.arv_make_tool.side_effect = make_tool
109 return loadingContext, runtimeContext
111 # Helper function to set up the ArvCwlExecutor to use the containers api
112 # and test that the RuntimeStatusLoggingHandler is set up correctly
113 def setup_and_test_container_executor_and_logging(self, gcc_mock) :
114 api = mock.MagicMock()
115 api._rootDesc = copy.deepcopy(get_rootDesc())
117 # Make sure ArvCwlExecutor thinks it's running inside a container so it
118 # adds the logging handler that will call runtime_status_update() mock
119 self.assertFalse(gcc_mock.called)
120 runner = arvados_cwl.ArvCwlExecutor(api)
121 self.assertEqual(runner.work_api, 'containers')
122 root_logger = logging.getLogger('')
123 handlerClasses = [h.__class__ for h in root_logger.handlers]
124 self.assertTrue(arvados_cwl.RuntimeStatusLoggingHandler in handlerClasses)
127 # The test passes no builder.resources
128 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
129 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
130 def test_run(self, keepdocker):
131 for enable_reuse in (True, False):
132 #arv_docker_clear_cache()
134 runner = mock.MagicMock()
135 runner.ignore_docker_for_reuse = False
136 runner.intermediate_output_ttl = 0
137 runner.secret_store = cwltool.secrets.SecretStore()
138 runner.api._rootDesc = {"revision": "20210628"}
139 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
141 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
142 runner.api.collections().get().execute.return_value = {
143 "portable_data_hash": "99999999999999999999999999999993+99"}
149 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
151 "class": "CommandLineTool",
155 loadingContext, runtimeContext = self.helper(runner, enable_reuse)
157 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
158 arvtool.formatgraph = None
160 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
161 j.run(runtimeContext)
162 runner.api.container_requests().create.assert_called_with(
163 body=JsonDiffMatcher({
165 'HOME': '/var/spool/cwl',
168 'name': 'test_run_'+str(enable_reuse),
169 'runtime_constraints': {
173 'use_existing': enable_reuse,
176 '/tmp': {'kind': 'tmp',
177 "capacity": 1073741824
179 '/var/spool/cwl': {'kind': 'tmp',
180 "capacity": 1073741824 }
182 'state': 'Committed',
183 'output_name': 'Output from step test_run_'+str(enable_reuse),
184 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
185 'output_path': '/var/spool/cwl',
187 'container_image': '99999999999999999999999999999993+99',
188 'command': ['ls', '/var/spool/cwl'],
189 'cwd': '/var/spool/cwl',
190 'scheduling_parameters': {},
191 'properties': {'cwl_input': {}},
193 'output_storage_classes': ["default"]
196 # The test passes some fields in builder.resources
197 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
198 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
199 def test_resource_requirements(self, keepdocker):
200 arvados_cwl.add_arv_hints()
201 runner = mock.MagicMock()
202 runner.ignore_docker_for_reuse = False
203 runner.intermediate_output_ttl = 3600
204 runner.secret_store = cwltool.secrets.SecretStore()
205 runner.api._rootDesc = {"revision": "20210628"}
206 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
208 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
209 runner.api.collections().get().execute.return_value = {
210 "portable_data_hash": "99999999999999999999999999999993+99"}
216 "class": "ResourceRequirement",
222 "class": "http://arvados.org/cwl#RuntimeConstraints",
225 "class": "http://arvados.org/cwl#APIRequirement",
227 "class": "http://arvados.org/cwl#PartitionRequirement",
230 "class": "http://arvados.org/cwl#IntermediateOutput",
233 "class": "WorkReuse",
238 "class": "CommandLineTool",
242 loadingContext, runtimeContext = self.helper(runner)
243 runtimeContext.name = "test_resource_requirements"
245 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
246 arvtool.formatgraph = None
247 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
248 j.run(runtimeContext)
250 call_args, call_kwargs = runner.api.container_requests().create.call_args
252 call_body_expected = {
254 'HOME': '/var/spool/cwl',
257 'name': 'test_resource_requirements',
258 'runtime_constraints': {
261 'keep_cache_ram': 536870912,
264 'use_existing': False,
267 '/tmp': {'kind': 'tmp',
268 "capacity": 4194304000 },
269 '/var/spool/cwl': {'kind': 'tmp',
270 "capacity": 5242880000 }
272 'state': 'Committed',
273 'output_name': 'Output from step test_resource_requirements',
274 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
275 'output_path': '/var/spool/cwl',
277 'container_image': '99999999999999999999999999999993+99',
279 'cwd': '/var/spool/cwl',
280 'scheduling_parameters': {
281 'partitions': ['blurb']
283 'properties': {'cwl_input': {}},
285 'output_storage_classes': ["default"]
288 call_body = call_kwargs.get('body', None)
289 self.assertNotEqual(None, call_body)
290 for key in call_body:
291 self.assertEqual(call_body_expected.get(key), call_body.get(key))
294 # The test passes some fields in builder.resources
295 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
296 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
297 @mock.patch("arvados.collection.Collection")
298 def test_initial_work_dir(self, collection_mock, keepdocker):
299 runner = mock.MagicMock()
300 runner.ignore_docker_for_reuse = False
301 runner.intermediate_output_ttl = 0
302 runner.secret_store = cwltool.secrets.SecretStore()
303 runner.api._rootDesc = {"revision": "20210628"}
304 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
306 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
307 runner.api.collections().get().execute.return_value = {
308 "portable_data_hash": "99999999999999999999999999999993+99"}
310 sourcemock = mock.MagicMock()
311 def get_collection_mock(p):
313 return (sourcemock, p.split("/", 1)[1])
315 return (sourcemock, "")
316 runner.fs_access.get_collection.side_effect = get_collection_mock
318 vwdmock = mock.MagicMock()
319 collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs)
325 "class": "InitialWorkDirRequirement",
329 "location": "keep:99999999999999999999999999999995+99/bar"
332 "class": "Directory",
334 "location": "keep:99999999999999999999999999999995+99"
338 "basename": "filename",
339 "location": "keep:99999999999999999999999999999995+99/baz/filename"
342 "class": "Directory",
343 "basename": "subdir",
344 "location": "keep:99999999999999999999999999999995+99/subdir"
348 "class": "CommandLineTool",
349 "cwlVersion": "v1.2",
353 loadingContext, runtimeContext = self.helper(runner)
354 runtimeContext.name = "test_initial_work_dir"
356 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
358 arvtool.formatgraph = None
359 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
360 j.run(runtimeContext)
362 call_args, call_kwargs = runner.api.container_requests().create.call_args
364 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
365 vwdmock.copy.assert_has_calls([mock.call('.', 'foo2', source_collection=sourcemock)])
366 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
367 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
369 call_body_expected = {
371 'HOME': '/var/spool/cwl',
374 'name': 'test_initial_work_dir',
375 'runtime_constraints': {
379 'use_existing': True,
382 '/tmp': {'kind': 'tmp',
383 "capacity": 1073741824 },
384 '/var/spool/cwl': {'kind': 'tmp',
385 "capacity": 1073741824 },
386 '/var/spool/cwl/foo': {
387 'kind': 'collection',
389 'portable_data_hash': '99999999999999999999999999999996+99'
391 '/var/spool/cwl/foo2': {
392 'kind': 'collection',
394 'portable_data_hash': '99999999999999999999999999999996+99'
396 '/var/spool/cwl/filename': {
397 'kind': 'collection',
399 'portable_data_hash': '99999999999999999999999999999996+99'
401 '/var/spool/cwl/subdir': {
402 'kind': 'collection',
404 'portable_data_hash': '99999999999999999999999999999996+99'
407 'state': 'Committed',
408 'output_name': 'Output from step test_initial_work_dir',
409 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
410 'output_path': '/var/spool/cwl',
412 'container_image': '99999999999999999999999999999993+99',
414 'cwd': '/var/spool/cwl',
415 'scheduling_parameters': {
417 'properties': {'cwl_input': {}},
419 'output_storage_classes': ["default"]
422 call_body = call_kwargs.get('body', None)
423 self.assertNotEqual(None, call_body)
424 for key in call_body:
425 self.assertEqual(call_body_expected.get(key), call_body.get(key))
428 # Test redirecting stdin/stdout/stderr
429 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
430 def test_redirects(self, keepdocker):
431 runner = mock.MagicMock()
432 runner.ignore_docker_for_reuse = False
433 runner.intermediate_output_ttl = 0
434 runner.secret_store = cwltool.secrets.SecretStore()
435 runner.api._rootDesc = {"revision": "20210628"}
436 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
438 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
439 runner.api.collections().get().execute.return_value = {
440 "portable_data_hash": "99999999999999999999999999999993+99"}
442 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
448 "stdout": "stdout.txt",
449 "stderr": "stderr.txt",
450 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
451 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
453 "class": "CommandLineTool",
457 loadingContext, runtimeContext = self.helper(runner)
458 runtimeContext.name = "test_run_redirect"
460 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
461 arvtool.formatgraph = None
462 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
463 j.run(runtimeContext)
464 runner.api.container_requests().create.assert_called_with(
465 body=JsonDiffMatcher({
467 'HOME': '/var/spool/cwl',
470 'name': 'test_run_redirect',
471 'runtime_constraints': {
475 'use_existing': True,
478 '/tmp': {'kind': 'tmp',
479 "capacity": 1073741824 },
480 '/var/spool/cwl': {'kind': 'tmp',
481 "capacity": 1073741824 },
484 "path": "/var/spool/cwl/stderr.txt"
487 "kind": "collection",
489 "portable_data_hash": "99999999999999999999999999999996+99"
493 "path": "/var/spool/cwl/stdout.txt"
496 'state': 'Committed',
497 "output_name": "Output from step test_run_redirect",
498 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
499 'output_path': '/var/spool/cwl',
501 'container_image': '99999999999999999999999999999993+99',
502 'command': ['ls', '/var/spool/cwl'],
503 'cwd': '/var/spool/cwl',
504 'scheduling_parameters': {},
505 'properties': {'cwl_input': {}},
507 'output_storage_classes': ["default"]
510 @mock.patch("arvados.collection.Collection")
511 def test_done(self, col):
512 api = mock.MagicMock()
514 runner = mock.MagicMock()
516 runner.num_retries = 0
517 runner.ignore_docker_for_reuse = False
518 runner.intermediate_output_ttl = 0
519 runner.secret_store = cwltool.secrets.SecretStore()
521 runner.api.containers().get().execute.return_value = {"state":"Complete",
525 col().open.return_value = []
527 loadingContext, runtimeContext = self.helper(runner)
529 arvjob = arvados_cwl.ArvadosContainer(runner,
537 arvjob.output_callback = mock.MagicMock()
538 arvjob.collect_outputs = mock.MagicMock()
539 arvjob.successCodes = [0]
540 arvjob.outdir = "/var/spool/cwl"
541 arvjob.output_ttl = 3600
542 arvjob.uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzz1"
544 arvjob.collect_outputs.return_value = {"out": "stuff"}
548 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
549 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
550 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
551 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
552 "modified_at": "2017-05-26T12:01:22Z",
556 self.assertFalse(api.collections().create.called)
557 self.assertFalse(runner.runtime_status_error.called)
559 arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
560 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
561 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
563 runner.api.container_requests().update.assert_called_with(uuid="zzzzz-xvhdp-zzzzzzzzzzzzzz1",
564 body={'container_request': {'properties': {'cwl_output': {'out': 'stuff'}}}})
567 # Test to make sure we dont call runtime_status_update if we already did
568 # some where higher up in the call stack
569 @mock.patch("arvados_cwl.util.get_current_container")
570 def test_recursive_runtime_status_update(self, gcc_mock):
571 self.setup_and_test_container_executor_and_logging(gcc_mock)
572 root_logger = logging.getLogger('')
574 # get_current_container is invoked when we call runtime_status_update
575 # so try and log again!
576 gcc_mock.side_effect = lambda *args: root_logger.error("Second Error")
578 root_logger.error("First Error")
580 self.fail("RuntimeStatusLoggingHandler should not be called recursively")
583 # Test to make sure that an exception raised from
584 # get_current_container doesn't cause the logger to raise an
586 @mock.patch("arvados_cwl.util.get_current_container")
587 def test_runtime_status_get_current_container_exception(self, gcc_mock):
588 self.setup_and_test_container_executor_and_logging(gcc_mock)
589 root_logger = logging.getLogger('')
591 # get_current_container is invoked when we call
592 # runtime_status_update, it is going to also raise an
594 gcc_mock.side_effect = Exception("Second Error")
596 root_logger.error("First Error")
598 self.fail("Exception in logger should not propagate")
599 self.assertTrue(gcc_mock.called)
601 @mock.patch("arvados_cwl.ArvCwlExecutor.runtime_status_update")
602 @mock.patch("arvados_cwl.util.get_current_container")
603 @mock.patch("arvados.collection.CollectionReader")
604 @mock.patch("arvados.collection.Collection")
605 def test_child_failure(self, col, reader, gcc_mock, rts_mock):
606 runner = self.setup_and_test_container_executor_and_logging(gcc_mock)
608 gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"}
609 self.assertTrue(gcc_mock.called)
611 runner.num_retries = 0
612 runner.ignore_docker_for_reuse = False
613 runner.intermediate_output_ttl = 0
614 runner.secret_store = cwltool.secrets.SecretStore()
615 runner.label = mock.MagicMock()
616 runner.label.return_value = '[container testjob]'
618 runner.api.containers().get().execute.return_value = {
625 col().open.return_value = []
627 loadingContext, runtimeContext = self.helper(runner)
629 arvjob = arvados_cwl.ArvadosContainer(runner,
637 arvjob.output_callback = mock.MagicMock()
638 arvjob.collect_outputs = mock.MagicMock()
639 arvjob.successCodes = [0]
640 arvjob.outdir = "/var/spool/cwl"
641 arvjob.output_ttl = 3600
642 arvjob.collect_outputs.return_value = {"out": "stuff"}
646 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
647 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
648 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
649 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
650 "modified_at": "2017-05-26T12:01:22Z",
654 rts_mock.assert_called_with(
656 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
657 ' ** log is empty **'
659 arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
661 # The test passes no builder.resources
662 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
663 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
664 def test_mounts(self, keepdocker):
665 runner = mock.MagicMock()
666 runner.ignore_docker_for_reuse = False
667 runner.intermediate_output_ttl = 0
668 runner.secret_store = cwltool.secrets.SecretStore()
669 runner.api._rootDesc = {"revision": "20210628"}
670 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
672 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
673 runner.api.collections().get().execute.return_value = {
674 "portable_data_hash": "99999999999999999999999999999994+99",
675 "manifest_text": ". 99999999999999999999999999999994+99 0:0:file1 0:0:file2"}
677 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
686 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
688 "class": "CommandLineTool",
692 loadingContext, runtimeContext = self.helper(runner)
693 runtimeContext.name = "test_run_mounts"
695 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
696 arvtool.formatgraph = None
699 "class": "Directory",
700 "location": "keep:99999999999999999999999999999994+44",
701 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
705 "location": "keep:99999999999999999999999999999994+44/file1",
709 "location": "keep:99999999999999999999999999999994+44/file2",
714 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
715 j.run(runtimeContext)
716 runner.api.container_requests().create.assert_called_with(
717 body=JsonDiffMatcher({
719 'HOME': '/var/spool/cwl',
722 'name': 'test_run_mounts',
723 'runtime_constraints': {
727 'use_existing': True,
730 "/keep/99999999999999999999999999999994+44": {
731 "kind": "collection",
732 "portable_data_hash": "99999999999999999999999999999994+44",
733 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz"
735 '/tmp': {'kind': 'tmp',
736 "capacity": 1073741824 },
737 '/var/spool/cwl': {'kind': 'tmp',
738 "capacity": 1073741824 }
740 'state': 'Committed',
741 'output_name': 'Output from step test_run_mounts',
742 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
743 'output_path': '/var/spool/cwl',
745 'container_image': '99999999999999999999999999999994+99',
746 'command': ['ls', '/var/spool/cwl'],
747 'cwd': '/var/spool/cwl',
748 'scheduling_parameters': {},
749 'properties': {'cwl_input': {
751 "basename": "99999999999999999999999999999994+44",
752 "class": "Directory",
754 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
759 "dirname": "/keep/99999999999999999999999999999994+44",
760 "location": "keep:99999999999999999999999999999994+44/file1",
763 "path": "/keep/99999999999999999999999999999994+44/file1",
769 "dirname": "/keep/99999999999999999999999999999994+44",
770 "location": "keep:99999999999999999999999999999994+44/file2",
773 "path": "/keep/99999999999999999999999999999994+44/file2",
777 "location": "keep:99999999999999999999999999999994+44",
778 "path": "/keep/99999999999999999999999999999994+44"
782 'output_storage_classes': ["default"]
785 # The test passes no builder.resources
786 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
787 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
788 def test_secrets(self, keepdocker):
789 arvados_cwl.add_arv_hints()
790 runner = mock.MagicMock()
791 runner.ignore_docker_for_reuse = False
792 runner.intermediate_output_ttl = 0
793 runner.secret_store = cwltool.secrets.SecretStore()
794 runner.api._rootDesc = {"revision": "20210628"}
795 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
797 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
798 runner.api.collections().get().execute.return_value = {
799 "portable_data_hash": "99999999999999999999999999999993+99"}
801 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
803 tool = cmap({"arguments": ["md5sum", "example.conf"],
804 "class": "CommandLineTool",
805 "cwlVersion": "v1.2",
808 "class": "http://commonwl.org/cwltool#Secrets",
817 "id": "#secret_job.cwl/pw",
825 "class": "InitialWorkDirRequirement",
828 "entry": "username: user\npassword: $(inputs.pw)\n",
829 "entryname": "example.conf"
835 loadingContext, runtimeContext = self.helper(runner)
836 runtimeContext.name = "test_secrets"
838 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
839 arvtool.formatgraph = None
841 job_order = {"pw": "blorp"}
842 runner.secret_store.store(["pw"], job_order)
844 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
845 j.run(runtimeContext)
846 runner.api.container_requests().create.assert_called_with(
847 body=JsonDiffMatcher({
849 'HOME': '/var/spool/cwl',
852 'name': 'test_secrets',
853 'runtime_constraints': {
857 'use_existing': True,
860 '/tmp': {'kind': 'tmp',
861 "capacity": 1073741824
863 '/var/spool/cwl': {'kind': 'tmp',
864 "capacity": 1073741824 }
866 'state': 'Committed',
867 'output_name': 'Output from step test_secrets',
868 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
869 'output_path': '/var/spool/cwl',
871 'container_image': '99999999999999999999999999999993+99',
872 'command': ['md5sum', 'example.conf'],
873 'cwd': '/var/spool/cwl',
874 'scheduling_parameters': {},
875 'properties': {'cwl_input': job_order},
877 "/var/spool/cwl/example.conf": {
878 "content": "username: user\npassword: blorp\n",
882 'output_storage_classes': ["default"]
885 # The test passes no builder.resources
886 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
887 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
888 def test_timelimit(self, keepdocker):
889 runner = mock.MagicMock()
890 runner.ignore_docker_for_reuse = False
891 runner.intermediate_output_ttl = 0
892 runner.secret_store = cwltool.secrets.SecretStore()
893 runner.api._rootDesc = {"revision": "20210628"}
894 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
896 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
897 runner.api.collections().get().execute.return_value = {
898 "portable_data_hash": "99999999999999999999999999999993+99"}
904 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
906 "cwlVersion": "v1.2",
907 "class": "CommandLineTool",
910 "class": "ToolTimeLimit",
916 loadingContext, runtimeContext = self.helper(runner)
917 runtimeContext.name = "test_timelimit"
919 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
920 arvtool.formatgraph = None
922 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
923 j.run(runtimeContext)
925 _, kwargs = runner.api.container_requests().create.call_args
926 self.assertEqual(42, kwargs['body']['scheduling_parameters'].get('max_run_time'))
929 # The test passes no builder.resources
930 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
931 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
932 def test_setting_storage_class(self, keepdocker):
933 #arv_docker_clear_cache()
935 runner = mock.MagicMock()
936 runner.ignore_docker_for_reuse = False
937 runner.intermediate_output_ttl = 0
938 runner.secret_store = cwltool.secrets.SecretStore()
939 runner.api._rootDesc = {"revision": "20210628"}
940 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
942 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
943 runner.api.collections().get().execute.return_value = {
944 "portable_data_hash": "99999999999999999999999999999993+99"}
950 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
952 "cwlVersion": "v1.2",
953 "class": "CommandLineTool",
956 "class": "http://arvados.org/cwl#OutputStorageClass",
957 "finalStorageClass": ["baz_sc", "qux_sc"],
958 "intermediateStorageClass": ["foo_sc", "bar_sc"]
963 loadingContext, runtimeContext = self.helper(runner, True)
965 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
966 arvtool.formatgraph = None
968 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
969 j.run(runtimeContext)
970 runner.api.container_requests().create.assert_called_with(
971 body=JsonDiffMatcher({
973 'HOME': '/var/spool/cwl',
976 'name': 'test_run_True',
977 'runtime_constraints': {
981 'use_existing': True,
984 '/tmp': {'kind': 'tmp',
985 "capacity": 1073741824
987 '/var/spool/cwl': {'kind': 'tmp',
988 "capacity": 1073741824 }
990 'state': 'Committed',
991 'output_name': 'Output from step test_run_True',
992 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
993 'output_path': '/var/spool/cwl',
995 'container_image': '99999999999999999999999999999993+99',
996 'command': ['ls', '/var/spool/cwl'],
997 'cwd': '/var/spool/cwl',
998 'scheduling_parameters': {},
999 'properties': {'cwl_input': {}},
1000 'secret_mounts': {},
1001 'output_storage_classes': ["foo_sc", "bar_sc"]
1005 # The test passes no builder.resources
1006 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1007 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1008 def test_setting_process_properties(self, keepdocker):
1009 #arv_docker_clear_cache()
1011 runner = mock.MagicMock()
1012 runner.ignore_docker_for_reuse = False
1013 runner.intermediate_output_ttl = 0
1014 runner.secret_store = cwltool.secrets.SecretStore()
1015 runner.api._rootDesc = {"revision": "20210628"}
1016 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1018 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1019 runner.api.collections().get().execute.return_value = {
1020 "portable_data_hash": "99999999999999999999999999999993+99"}
1024 {"id": "x", "type": "string"}],
1026 "baseCommand": "ls",
1027 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1029 "class": "CommandLineTool",
1030 "cwlVersion": "v1.2",
1033 "class": "http://arvados.org/cwl#ProcessProperties",
1034 "processProperties": [
1035 {"propertyName": "foo",
1036 "propertyValue": "bar"},
1037 {"propertyName": "baz",
1038 "propertyValue": "$(inputs.x)"},
1039 {"propertyName": "quux",
1050 loadingContext, runtimeContext = self.helper(runner, True)
1052 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1053 arvtool.formatgraph = None
1055 for j in arvtool.job({"x": "blorp"}, mock.MagicMock(), runtimeContext):
1056 j.run(runtimeContext)
1057 runner.api.container_requests().create.assert_called_with(
1058 body=JsonDiffMatcher({
1060 'HOME': '/var/spool/cwl',
1063 'name': 'test_run_True',
1064 'runtime_constraints': {
1068 'use_existing': True,
1071 '/tmp': {'kind': 'tmp',
1072 "capacity": 1073741824
1074 '/var/spool/cwl': {'kind': 'tmp',
1075 "capacity": 1073741824 }
1077 'state': 'Committed',
1078 'output_name': 'Output from step test_run_True',
1079 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1080 'output_path': '/var/spool/cwl',
1082 'container_image': '99999999999999999999999999999993+99',
1083 'command': ['ls', '/var/spool/cwl'],
1084 'cwd': '/var/spool/cwl',
1085 'scheduling_parameters': {},
1088 "cwl_input": {"x": "blorp"},
1095 'secret_mounts': {},
1096 'output_storage_classes': ["default"]
1100 # The test passes no builder.resources
1101 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1102 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1103 def test_cuda_requirement(self, keepdocker):
1104 arvados_cwl.add_arv_hints()
1105 #arv_docker_clear_cache()
1107 runner = mock.MagicMock()
1108 runner.ignore_docker_for_reuse = False
1109 runner.intermediate_output_ttl = 0
1110 runner.secret_store = cwltool.secrets.SecretStore()
1111 runner.api._rootDesc = {"revision": "20210628"}
1112 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1114 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1115 runner.api.collections().get().execute.return_value = {
1116 "portable_data_hash": "99999999999999999999999999999993+99"}
1119 "class": "http://commonwl.org/cwltool#CUDARequirement",
1120 "cudaVersionMin": "11.0",
1121 "cudaComputeCapability": "9.0",
1123 "class": "http://commonwl.org/cwltool#CUDARequirement",
1124 "cudaVersionMin": "11.0",
1125 "cudaComputeCapability": "9.0",
1126 "cudaDeviceCountMin": 2
1128 "class": "http://commonwl.org/cwltool#CUDARequirement",
1129 "cudaVersionMin": "11.0",
1130 "cudaComputeCapability": ["4.0", "5.0"],
1131 "cudaDeviceCountMin": 2
1136 'driver_version': "11.0",
1137 'hardware_capability': "9.0"
1140 'driver_version': "11.0",
1141 'hardware_capability': "9.0"
1144 'driver_version': "11.0",
1145 'hardware_capability': "4.0"
1148 for test_case in range(0, len(test_cwl_req)):
1153 "baseCommand": "nvidia-smi",
1156 "cwlVersion": "v1.2",
1157 "class": "CommandLineTool",
1158 "requirements": [test_cwl_req[test_case]]
1161 loadingContext, runtimeContext = self.helper(runner, True)
1163 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1164 arvtool.formatgraph = None
1166 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1167 j.run(runtimeContext)
1168 runner.api.container_requests().create.assert_called_with(
1169 body=JsonDiffMatcher({
1171 'HOME': '/var/spool/cwl',
1174 'name': 'test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1175 'runtime_constraints': {
1178 'cuda': test_arv_req[test_case]
1180 'use_existing': True,
1183 '/tmp': {'kind': 'tmp',
1184 "capacity": 1073741824
1186 '/var/spool/cwl': {'kind': 'tmp',
1187 "capacity": 1073741824 }
1189 'state': 'Committed',
1190 'output_name': 'Output from step test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1191 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1192 'output_path': '/var/spool/cwl',
1194 'container_image': '99999999999999999999999999999993+99',
1195 'command': ['nvidia-smi'],
1196 'cwd': '/var/spool/cwl',
1197 'scheduling_parameters': {},
1198 'properties': {'cwl_input': {}},
1199 'secret_mounts': {},
1200 'output_storage_classes': ["default"]
1204 # The test passes no builder.resources
1205 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1206 @mock.patch("arvados_cwl.arvdocker.determine_image_id")
1207 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1208 def test_match_local_docker(self, keepdocker, determine_image_id):
1209 arvados_cwl.add_arv_hints()
1211 runner = mock.MagicMock()
1212 runner.ignore_docker_for_reuse = False
1213 runner.intermediate_output_ttl = 0
1214 runner.secret_store = cwltool.secrets.SecretStore()
1215 runner.api._rootDesc = {"revision": "20210628"}
1216 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1218 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
1219 ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
1220 determine_image_id.side_effect = lambda x: "123"
1222 ex = mock.MagicMock()
1223 lookup = {"zzzzz-4zz18-zzzzzzzzzzzzzz4": {"portable_data_hash": "99999999999999999999999999999994+99"},
1224 "zzzzz-4zz18-zzzzzzzzzzzzzz3": {"portable_data_hash": "99999999999999999999999999999993+99"}}
1225 ex.execute.return_value = lookup[uuid]
1227 runner.api.collections().get.side_effect = execute
1232 "baseCommand": "echo",
1235 "cwlVersion": "v1.0",
1236 "class": "org.w3id.cwl.cwl.CommandLineTool"
1239 loadingContext, runtimeContext = self.helper(runner, True)
1241 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, loadingContext)
1242 arvtool.formatgraph = None
1244 container_request = {
1246 'HOME': '/var/spool/cwl',
1249 'name': 'test_run_True',
1250 'runtime_constraints': {
1254 'use_existing': True,
1257 '/tmp': {'kind': 'tmp',
1258 "capacity": 1073741824
1260 '/var/spool/cwl': {'kind': 'tmp',
1261 "capacity": 1073741824 }
1263 'state': 'Committed',
1264 'output_name': 'Output from step test_run_True',
1265 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1266 'output_path': '/var/spool/cwl',
1268 'container_image': '99999999999999999999999999999994+99',
1269 'command': ['echo'],
1270 'cwd': '/var/spool/cwl',
1271 'scheduling_parameters': {},
1272 'properties': {'cwl_input': {}},
1273 'secret_mounts': {},
1274 'output_storage_classes': ["default"]
1277 runtimeContext.match_local_docker = False
1278 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1279 j.run(runtimeContext)
1280 runner.api.container_requests().create.assert_called_with(
1281 body=JsonDiffMatcher(container_request))
1283 runtimeContext.cached_docker_lookups.clear()
1284 runtimeContext.match_local_docker = True
1285 container_request['container_image'] = '99999999999999999999999999999993+99'
1286 container_request['name'] = 'test_run_True_2'
1287 container_request['output_name'] = 'Output from step test_run_True_2'
1288 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1289 j.run(runtimeContext)
1290 runner.api.container_requests().create.assert_called_with(
1291 body=JsonDiffMatcher(container_request))
1294 # The test passes no builder.resources
1295 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1296 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1297 def test_run_preemptible_hint(self, keepdocker):
1298 arvados_cwl.add_arv_hints()
1299 for enable_preemptible in (None, True, False):
1300 for preemptible_hint in (None, True, False):
1301 #arv_docker_clear_cache()
1303 runner = mock.MagicMock()
1304 runner.ignore_docker_for_reuse = False
1305 runner.intermediate_output_ttl = 0
1306 runner.secret_store = cwltool.secrets.SecretStore()
1307 runner.api._rootDesc = {"revision": "20210628"}
1308 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1310 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1311 runner.api.collections().get().execute.return_value = {
1312 "portable_data_hash": "99999999999999999999999999999993+99"}
1314 if preemptible_hint is not None:
1316 "class": "http://arvados.org/cwl#UsePreemptible",
1317 "usePreemptible": preemptible_hint
1325 "baseCommand": "ls",
1326 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1328 "class": "CommandLineTool",
1329 "cwlVersion": "v1.2",
1333 loadingContext, runtimeContext = self.helper(runner)
1335 runtimeContext.name = 'test_run_enable_preemptible_'+str(enable_preemptible)+str(preemptible_hint)
1336 runtimeContext.enable_preemptible = enable_preemptible
1338 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1339 arvtool.formatgraph = None
1341 # Test the interactions between --enable/disable-preemptible
1342 # and UsePreemptible hint
1344 if enable_preemptible is None:
1345 if preemptible_hint is None:
1348 sched = {'preemptible': preemptible_hint}
1350 if preemptible_hint is None:
1351 sched = {'preemptible': enable_preemptible}
1353 sched = {'preemptible': enable_preemptible and preemptible_hint}
1355 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1356 j.run(runtimeContext)
1357 runner.api.container_requests().create.assert_called_with(
1358 body=JsonDiffMatcher({
1360 'HOME': '/var/spool/cwl',
1363 'name': runtimeContext.name,
1364 'runtime_constraints': {
1368 'use_existing': True,
1371 '/tmp': {'kind': 'tmp',
1372 "capacity": 1073741824
1374 '/var/spool/cwl': {'kind': 'tmp',
1375 "capacity": 1073741824 }
1377 'state': 'Committed',
1378 'output_name': 'Output from step '+runtimeContext.name,
1379 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1380 'output_path': '/var/spool/cwl',
1382 'container_image': '99999999999999999999999999999993+99',
1383 'command': ['ls', '/var/spool/cwl'],
1384 'cwd': '/var/spool/cwl',
1385 'scheduling_parameters': sched,
1386 'properties': {'cwl_input': {}},
1387 'secret_mounts': {},
1388 'output_storage_classes': ["default"]
1392 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1393 def test_output_properties(self, keepdocker):
1394 arvados_cwl.add_arv_hints()
1395 for rev in ["20210628", "20220510"]:
1396 runner = mock.MagicMock()
1397 runner.ignore_docker_for_reuse = False
1398 runner.intermediate_output_ttl = 0
1399 runner.secret_store = cwltool.secrets.SecretStore()
1400 runner.api._rootDesc = {"revision": rev}
1401 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1403 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1404 runner.api.collections().get().execute.return_value = {
1405 "portable_data_hash": "99999999999999999999999999999993+99"}
1413 "baseCommand": "ls",
1414 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1416 "cwlVersion": "v1.2",
1417 "class": "CommandLineTool",
1420 "class": "http://arvados.org/cwl#OutputCollectionProperties",
1421 "outputProperties": {
1423 "baz": "$(inputs.inp)"
1429 loadingContext, runtimeContext = self.helper(runner)
1430 runtimeContext.name = "test_timelimit"
1432 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1433 arvtool.formatgraph = None
1435 for j in arvtool.job({"inp": "quux"}, mock.MagicMock(), runtimeContext):
1436 j.run(runtimeContext)
1438 _, kwargs = runner.api.container_requests().create.call_args
1439 if rev == "20220510":
1440 self.assertEqual({"foo": "bar", "baz": "quux"}, kwargs['body'].get('output_properties'))
1442 self.assertEqual(None, kwargs['body'].get('output_properties'))
1445 class TestWorkflow(unittest.TestCase):
1447 cwltool.process._names = set()
1448 #arv_docker_clear_cache()
1450 def helper(self, runner, enable_reuse=True):
1451 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
1453 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
1454 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
1456 document_loader.fetcher_constructor = functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=make_fs_access(""))
1457 document_loader.fetcher = document_loader.fetcher_constructor(document_loader.cache, document_loader.session)
1458 document_loader.fetch_text = document_loader.fetcher.fetch_text
1459 document_loader.check_exists = document_loader.fetcher.check_exists
1461 loadingContext = arvados_cwl.context.ArvLoadingContext(
1462 {"avsc_names": avsc_names,
1464 "make_fs_access": make_fs_access,
1465 "loader": document_loader,
1466 "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
1467 "construct_tool_object": runner.arv_make_tool,
1468 "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__})
1469 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
1470 {"work_api": "containers",
1472 "name": "test_run_wf_"+str(enable_reuse),
1473 "make_fs_access": make_fs_access,
1475 "enable_reuse": enable_reuse,
1478 return loadingContext, runtimeContext
1480 # The test passes no builder.resources
1481 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1482 @mock.patch("arvados.collection.CollectionReader")
1483 @mock.patch("arvados.collection.Collection")
1484 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1485 def test_run(self, list_images_in_arv, mockcollection, mockcollectionreader):
1486 arvados_cwl.add_arv_hints()
1488 api = mock.MagicMock()
1489 api._rootDesc = get_rootDesc()
1490 api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1492 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1493 self.assertEqual(runner.work_api, 'containers')
1495 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1496 runner.api.collections().get().execute.return_value = {"portable_data_hash": "99999999999999999999999999999993+99"}
1497 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1498 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1500 runner.api.containers().current().execute.return_value = {}
1502 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1503 runner.ignore_docker_for_reuse = False
1504 runner.num_retries = 0
1505 runner.secret_store = cwltool.secrets.SecretStore()
1507 loadingContext, runtimeContext = self.helper(runner)
1508 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1510 mockcollectionreader().exists.return_value = True
1512 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
1513 metadata["cwlVersion"] = tool["cwlVersion"]
1515 mockc = mock.MagicMock()
1516 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
1517 mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
1519 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1520 arvtool.formatgraph = None
1521 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1523 next(it).run(runtimeContext)
1524 next(it).run(runtimeContext)
1526 with open("tests/wf/scatter2_subwf.cwl") as f:
1527 subwf = StripYAMLComments(f.read()).rstrip()
1529 runner.api.container_requests().create.assert_called_with(
1530 body=JsonDiffMatcher({
1535 "--preserve-entire-environment",
1539 "container_image": "99999999999999999999999999999993+99",
1540 "cwd": "/var/spool/cwl",
1542 "HOME": "/var/spool/cwl",
1546 "/keep/99999999999999999999999999999999+118": {
1547 "kind": "collection",
1548 "portable_data_hash": "99999999999999999999999999999999+118"
1551 "capacity": 1073741824,
1555 "capacity": 1073741824,
1558 "/var/spool/cwl/cwl.input.yml": {
1559 "kind": "collection",
1560 "path": "cwl.input.yml",
1561 "portable_data_hash": "99999999999999999999999999999996+99"
1563 "/var/spool/cwl/workflow.cwl": {
1564 "kind": "collection",
1565 "path": "workflow.cwl",
1566 "portable_data_hash": "99999999999999999999999999999996+99"
1570 "path": "/var/spool/cwl/cwl.output.json"
1573 "name": "scatterstep",
1574 "output_name": "Output from step scatterstep",
1575 "output_path": "/var/spool/cwl",
1578 "properties": {'cwl_input': {
1580 "basename": "token.txt",
1582 "dirname": "/keep/99999999999999999999999999999999+118",
1583 "location": "keep:99999999999999999999999999999999+118/token.txt",
1585 "nameroot": "token",
1586 "path": "/keep/99999999999999999999999999999999+118/token.txt",
1591 "runtime_constraints": {
1595 "scheduling_parameters": {},
1596 "secret_mounts": {},
1597 "state": "Committed",
1598 "use_existing": True,
1599 'output_storage_classes': ["default"]
1601 mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
1602 mockc.open().__enter__().write.assert_has_calls([mock.call(
1605 "basename": "token.txt",
1607 "location": "/keep/99999999999999999999999999999999+118/token.txt",
1613 # The test passes no builder.resources
1614 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1615 @mock.patch("arvados.collection.CollectionReader")
1616 @mock.patch("arvados.collection.Collection")
1617 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1618 def test_overall_resource_singlecontainer(self, list_images_in_arv, mockcollection, mockcollectionreader):
1619 arvados_cwl.add_arv_hints()
1621 api = mock.MagicMock()
1622 api._rootDesc = get_rootDesc()
1623 api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1625 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1626 self.assertEqual(runner.work_api, 'containers')
1628 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1629 runner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1630 "portable_data_hash": "99999999999999999999999999999993+99"}
1631 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1632 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1634 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1635 runner.ignore_docker_for_reuse = False
1636 runner.num_retries = 0
1637 runner.secret_store = cwltool.secrets.SecretStore()
1639 loadingContext, runtimeContext = self.helper(runner)
1640 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1641 loadingContext.do_update = True
1642 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
1644 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
1646 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1647 arvtool.formatgraph = None
1648 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1650 next(it).run(runtimeContext)
1651 next(it).run(runtimeContext)
1653 with open("tests/wf/echo-subwf.cwl") as f:
1654 subwf = StripYAMLComments(f.read())
1656 runner.api.container_requests().create.assert_called_with(
1657 body=JsonDiffMatcher({
1659 'environment': {'HOME': '/var/spool/cwl', 'TMPDIR': '/tmp'},
1660 'scheduling_parameters': {},
1661 'name': u'echo-subwf',
1662 'secret_mounts': {},
1663 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
1664 'properties': {'cwl_input': {}},
1667 '/var/spool/cwl/cwl.input.yml': {
1668 'portable_data_hash': '99999999999999999999999999999996+99',
1669 'kind': 'collection',
1670 'path': 'cwl.input.yml'
1672 '/var/spool/cwl/workflow.cwl': {
1673 'portable_data_hash': '99999999999999999999999999999996+99',
1674 'kind': 'collection',
1675 'path': 'workflow.cwl'
1678 'path': '/var/spool/cwl/cwl.output.json',
1683 'capacity': 1073741824
1684 }, '/var/spool/cwl': {
1686 'capacity': 3221225472
1689 'state': 'Committed',
1690 'output_path': '/var/spool/cwl',
1691 'container_image': '99999999999999999999999999999993+99',
1696 u'--preserve-entire-environment',
1700 'use_existing': True,
1701 'output_name': u'Output from step echo-subwf',
1702 'cwd': '/var/spool/cwl',
1703 'output_storage_classes': ["default"]
1706 def test_default_work_api(self):
1707 arvados_cwl.add_arv_hints()
1709 api = mock.MagicMock()
1710 api._rootDesc = copy.deepcopy(get_rootDesc())
1711 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1712 self.assertEqual(runner.work_api, 'containers')