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"})
90 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
91 {"work_api": "containers",
93 "name": "test_run_"+str(enable_reuse),
94 "make_fs_access": make_fs_access,
97 "enable_reuse": enable_reuse,
99 "project_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
100 "workflow_eval_lock": threading.Condition(threading.RLock())
103 if isinstance(runner, mock.MagicMock):
104 def make_tool(toolpath_object, loadingContext):
105 return arvados_cwl.ArvadosCommandTool(runner, toolpath_object, loadingContext)
106 runner.arv_make_tool.side_effect = make_tool
108 return loadingContext, runtimeContext
110 # Helper function to set up the ArvCwlExecutor to use the containers api
111 # and test that the RuntimeStatusLoggingHandler is set up correctly
112 def setup_and_test_container_executor_and_logging(self, gcc_mock) :
113 api = mock.MagicMock()
114 api._rootDesc = copy.deepcopy(get_rootDesc())
116 # Make sure ArvCwlExecutor thinks it's running inside a container so it
117 # adds the logging handler that will call runtime_status_update() mock
118 self.assertFalse(gcc_mock.called)
119 runner = arvados_cwl.ArvCwlExecutor(api)
120 self.assertEqual(runner.work_api, 'containers')
121 root_logger = logging.getLogger('')
122 handlerClasses = [h.__class__ for h in root_logger.handlers]
123 self.assertTrue(arvados_cwl.RuntimeStatusLoggingHandler in handlerClasses)
126 # The test passes no builder.resources
127 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
128 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
129 def test_run(self, keepdocker):
130 for enable_reuse in (True, False):
131 arv_docker_clear_cache()
133 runner = mock.MagicMock()
134 runner.ignore_docker_for_reuse = False
135 runner.intermediate_output_ttl = 0
136 runner.secret_store = cwltool.secrets.SecretStore()
137 runner.api._rootDesc = {"revision": "20210628"}
139 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
140 runner.api.collections().get().execute.return_value = {
141 "portable_data_hash": "99999999999999999999999999999993+99"}
147 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
149 "class": "CommandLineTool",
153 loadingContext, runtimeContext = self.helper(runner, enable_reuse)
155 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
156 arvtool.formatgraph = None
158 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
159 j.run(runtimeContext)
160 runner.api.container_requests().create.assert_called_with(
161 body=JsonDiffMatcher({
163 'HOME': '/var/spool/cwl',
166 'name': 'test_run_'+str(enable_reuse),
167 'runtime_constraints': {
171 'use_existing': enable_reuse,
174 '/tmp': {'kind': 'tmp',
175 "capacity": 1073741824
177 '/var/spool/cwl': {'kind': 'tmp',
178 "capacity": 1073741824 }
180 'state': 'Committed',
181 'output_name': 'Output from step test_run_'+str(enable_reuse),
182 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
183 'output_path': '/var/spool/cwl',
185 'container_image': '99999999999999999999999999999993+99',
186 'command': ['ls', '/var/spool/cwl'],
187 'cwd': '/var/spool/cwl',
188 'scheduling_parameters': {},
189 'properties': {'cwl_input': {}},
191 'output_storage_classes': ["default"]
194 # The test passes some fields in builder.resources
195 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
196 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
197 def test_resource_requirements(self, keepdocker):
198 arvados_cwl.add_arv_hints()
199 runner = mock.MagicMock()
200 runner.ignore_docker_for_reuse = False
201 runner.intermediate_output_ttl = 3600
202 runner.secret_store = cwltool.secrets.SecretStore()
203 runner.api._rootDesc = {"revision": "20210628"}
205 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
206 runner.api.collections().get().execute.return_value = {
207 "portable_data_hash": "99999999999999999999999999999993+99"}
213 "class": "ResourceRequirement",
219 "class": "http://arvados.org/cwl#RuntimeConstraints",
222 "class": "http://arvados.org/cwl#APIRequirement",
224 "class": "http://arvados.org/cwl#PartitionRequirement",
227 "class": "http://arvados.org/cwl#IntermediateOutput",
230 "class": "WorkReuse",
235 "class": "CommandLineTool",
239 loadingContext, runtimeContext = self.helper(runner)
240 runtimeContext.name = "test_resource_requirements"
242 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
243 arvtool.formatgraph = None
244 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
245 j.run(runtimeContext)
247 call_args, call_kwargs = runner.api.container_requests().create.call_args
249 call_body_expected = {
251 'HOME': '/var/spool/cwl',
254 'name': 'test_resource_requirements',
255 'runtime_constraints': {
258 'keep_cache_ram': 536870912,
261 'use_existing': False,
264 '/tmp': {'kind': 'tmp',
265 "capacity": 4194304000 },
266 '/var/spool/cwl': {'kind': 'tmp',
267 "capacity": 5242880000 }
269 'state': 'Committed',
270 'output_name': 'Output from step test_resource_requirements',
271 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
272 'output_path': '/var/spool/cwl',
274 'container_image': '99999999999999999999999999999993+99',
276 'cwd': '/var/spool/cwl',
277 'scheduling_parameters': {
278 'partitions': ['blurb']
280 'properties': {'cwl_input': {}},
282 'output_storage_classes': ["default"]
285 call_body = call_kwargs.get('body', None)
286 self.assertNotEqual(None, call_body)
287 for key in call_body:
288 self.assertEqual(call_body_expected.get(key), call_body.get(key))
291 # The test passes some fields in builder.resources
292 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
293 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
294 @mock.patch("arvados.collection.Collection")
295 def test_initial_work_dir(self, collection_mock, keepdocker):
296 runner = mock.MagicMock()
297 runner.ignore_docker_for_reuse = False
298 runner.intermediate_output_ttl = 0
299 runner.secret_store = cwltool.secrets.SecretStore()
300 runner.api._rootDesc = {"revision": "20210628"}
302 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
303 runner.api.collections().get().execute.return_value = {
304 "portable_data_hash": "99999999999999999999999999999993+99"}
306 sourcemock = mock.MagicMock()
307 def get_collection_mock(p):
309 return (sourcemock, p.split("/", 1)[1])
311 return (sourcemock, "")
312 runner.fs_access.get_collection.side_effect = get_collection_mock
314 vwdmock = mock.MagicMock()
315 collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs)
321 "class": "InitialWorkDirRequirement",
325 "location": "keep:99999999999999999999999999999995+99/bar"
328 "class": "Directory",
330 "location": "keep:99999999999999999999999999999995+99"
334 "basename": "filename",
335 "location": "keep:99999999999999999999999999999995+99/baz/filename"
338 "class": "Directory",
339 "basename": "subdir",
340 "location": "keep:99999999999999999999999999999995+99/subdir"
344 "class": "CommandLineTool",
345 "cwlVersion": "v1.2",
349 loadingContext, runtimeContext = self.helper(runner)
350 runtimeContext.name = "test_initial_work_dir"
352 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
354 arvtool.formatgraph = None
355 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
356 j.run(runtimeContext)
358 call_args, call_kwargs = runner.api.container_requests().create.call_args
360 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
361 vwdmock.copy.assert_has_calls([mock.call('.', 'foo2', source_collection=sourcemock)])
362 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
363 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
365 call_body_expected = {
367 'HOME': '/var/spool/cwl',
370 'name': 'test_initial_work_dir',
371 'runtime_constraints': {
375 'use_existing': True,
378 '/tmp': {'kind': 'tmp',
379 "capacity": 1073741824 },
380 '/var/spool/cwl': {'kind': 'tmp',
381 "capacity": 1073741824 },
382 '/var/spool/cwl/foo': {
383 'kind': 'collection',
385 'portable_data_hash': '99999999999999999999999999999996+99'
387 '/var/spool/cwl/foo2': {
388 'kind': 'collection',
390 'portable_data_hash': '99999999999999999999999999999996+99'
392 '/var/spool/cwl/filename': {
393 'kind': 'collection',
395 'portable_data_hash': '99999999999999999999999999999996+99'
397 '/var/spool/cwl/subdir': {
398 'kind': 'collection',
400 'portable_data_hash': '99999999999999999999999999999996+99'
403 'state': 'Committed',
404 'output_name': 'Output from step test_initial_work_dir',
405 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
406 'output_path': '/var/spool/cwl',
408 'container_image': '99999999999999999999999999999993+99',
410 'cwd': '/var/spool/cwl',
411 'scheduling_parameters': {
413 'properties': {'cwl_input': {}},
415 'output_storage_classes': ["default"]
418 call_body = call_kwargs.get('body', None)
419 self.assertNotEqual(None, call_body)
420 for key in call_body:
421 self.assertEqual(call_body_expected.get(key), call_body.get(key))
424 # Test redirecting stdin/stdout/stderr
425 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
426 def test_redirects(self, keepdocker):
427 runner = mock.MagicMock()
428 runner.ignore_docker_for_reuse = False
429 runner.intermediate_output_ttl = 0
430 runner.secret_store = cwltool.secrets.SecretStore()
431 runner.api._rootDesc = {"revision": "20210628"}
433 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
434 runner.api.collections().get().execute.return_value = {
435 "portable_data_hash": "99999999999999999999999999999993+99"}
437 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
443 "stdout": "stdout.txt",
444 "stderr": "stderr.txt",
445 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
446 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
448 "class": "CommandLineTool",
452 loadingContext, runtimeContext = self.helper(runner)
453 runtimeContext.name = "test_run_redirect"
455 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
456 arvtool.formatgraph = None
457 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
458 j.run(runtimeContext)
459 runner.api.container_requests().create.assert_called_with(
460 body=JsonDiffMatcher({
462 'HOME': '/var/spool/cwl',
465 'name': 'test_run_redirect',
466 'runtime_constraints': {
470 'use_existing': True,
473 '/tmp': {'kind': 'tmp',
474 "capacity": 1073741824 },
475 '/var/spool/cwl': {'kind': 'tmp',
476 "capacity": 1073741824 },
479 "path": "/var/spool/cwl/stderr.txt"
482 "kind": "collection",
484 "portable_data_hash": "99999999999999999999999999999996+99"
488 "path": "/var/spool/cwl/stdout.txt"
491 'state': 'Committed',
492 "output_name": "Output from step test_run_redirect",
493 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
494 'output_path': '/var/spool/cwl',
496 'container_image': '99999999999999999999999999999993+99',
497 'command': ['ls', '/var/spool/cwl'],
498 'cwd': '/var/spool/cwl',
499 'scheduling_parameters': {},
500 'properties': {'cwl_input': {}},
502 'output_storage_classes': ["default"]
505 @mock.patch("arvados.collection.Collection")
506 def test_done(self, col):
507 api = mock.MagicMock()
509 runner = mock.MagicMock()
511 runner.num_retries = 0
512 runner.ignore_docker_for_reuse = False
513 runner.intermediate_output_ttl = 0
514 runner.secret_store = cwltool.secrets.SecretStore()
516 runner.api.containers().get().execute.return_value = {"state":"Complete",
520 col().open.return_value = []
522 loadingContext, runtimeContext = self.helper(runner)
524 arvjob = arvados_cwl.ArvadosContainer(runner,
532 arvjob.output_callback = mock.MagicMock()
533 arvjob.collect_outputs = mock.MagicMock()
534 arvjob.successCodes = [0]
535 arvjob.outdir = "/var/spool/cwl"
536 arvjob.output_ttl = 3600
537 arvjob.uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzz1"
539 arvjob.collect_outputs.return_value = {"out": "stuff"}
543 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
544 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
545 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
546 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
547 "modified_at": "2017-05-26T12:01:22Z",
551 self.assertFalse(api.collections().create.called)
552 self.assertFalse(runner.runtime_status_error.called)
554 arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
555 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
556 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
558 runner.api.container_requests().update.assert_called_with(uuid="zzzzz-xvhdp-zzzzzzzzzzzzzz1",
559 body={'container_request': {'properties': {'cwl_output': {'out': 'stuff'}}}})
562 # Test to make sure we dont call runtime_status_update if we already did
563 # some where higher up in the call stack
564 @mock.patch("arvados_cwl.util.get_current_container")
565 def test_recursive_runtime_status_update(self, gcc_mock):
566 self.setup_and_test_container_executor_and_logging(gcc_mock)
567 root_logger = logging.getLogger('')
569 # get_current_container is invoked when we call runtime_status_update
570 # so try and log again!
571 gcc_mock.side_effect = lambda *args: root_logger.error("Second Error")
573 root_logger.error("First Error")
575 self.fail("RuntimeStatusLoggingHandler should not be called recursively")
578 # Test to make sure that an exception raised from
579 # get_current_container doesn't cause the logger to raise an
581 @mock.patch("arvados_cwl.util.get_current_container")
582 def test_runtime_status_get_current_container_exception(self, gcc_mock):
583 self.setup_and_test_container_executor_and_logging(gcc_mock)
584 root_logger = logging.getLogger('')
586 # get_current_container is invoked when we call
587 # runtime_status_update, it is going to also raise an
589 gcc_mock.side_effect = Exception("Second Error")
591 root_logger.error("First Error")
593 self.fail("Exception in logger should not propagate")
594 self.assertTrue(gcc_mock.called)
596 @mock.patch("arvados_cwl.ArvCwlExecutor.runtime_status_update")
597 @mock.patch("arvados_cwl.util.get_current_container")
598 @mock.patch("arvados.collection.CollectionReader")
599 @mock.patch("arvados.collection.Collection")
600 def test_child_failure(self, col, reader, gcc_mock, rts_mock):
601 runner = self.setup_and_test_container_executor_and_logging(gcc_mock)
603 gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"}
604 self.assertTrue(gcc_mock.called)
606 runner.num_retries = 0
607 runner.ignore_docker_for_reuse = False
608 runner.intermediate_output_ttl = 0
609 runner.secret_store = cwltool.secrets.SecretStore()
610 runner.label = mock.MagicMock()
611 runner.label.return_value = '[container testjob]'
613 runner.api.containers().get().execute.return_value = {
620 col().open.return_value = []
622 loadingContext, runtimeContext = self.helper(runner)
624 arvjob = arvados_cwl.ArvadosContainer(runner,
632 arvjob.output_callback = mock.MagicMock()
633 arvjob.collect_outputs = mock.MagicMock()
634 arvjob.successCodes = [0]
635 arvjob.outdir = "/var/spool/cwl"
636 arvjob.output_ttl = 3600
637 arvjob.collect_outputs.return_value = {"out": "stuff"}
641 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
642 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
643 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
644 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
645 "modified_at": "2017-05-26T12:01:22Z",
649 rts_mock.assert_called_with(
651 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
652 ' ** log is empty **'
654 arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
656 # The test passes no builder.resources
657 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
658 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
659 def test_mounts(self, keepdocker):
660 runner = mock.MagicMock()
661 runner.ignore_docker_for_reuse = False
662 runner.intermediate_output_ttl = 0
663 runner.secret_store = cwltool.secrets.SecretStore()
664 runner.api._rootDesc = {"revision": "20210628"}
666 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
667 runner.api.collections().get().execute.return_value = {
668 "portable_data_hash": "99999999999999999999999999999994+99",
669 "manifest_text": ". 99999999999999999999999999999994+99 0:0:file1 0:0:file2"}
671 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
680 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
682 "class": "CommandLineTool",
686 loadingContext, runtimeContext = self.helper(runner)
687 runtimeContext.name = "test_run_mounts"
689 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
690 arvtool.formatgraph = None
693 "class": "Directory",
694 "location": "keep:99999999999999999999999999999994+44",
695 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
699 "location": "keep:99999999999999999999999999999994+44/file1",
703 "location": "keep:99999999999999999999999999999994+44/file2",
708 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
709 j.run(runtimeContext)
710 runner.api.container_requests().create.assert_called_with(
711 body=JsonDiffMatcher({
713 'HOME': '/var/spool/cwl',
716 'name': 'test_run_mounts',
717 'runtime_constraints': {
721 'use_existing': True,
724 "/keep/99999999999999999999999999999994+44": {
725 "kind": "collection",
726 "portable_data_hash": "99999999999999999999999999999994+44",
727 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz"
729 '/tmp': {'kind': 'tmp',
730 "capacity": 1073741824 },
731 '/var/spool/cwl': {'kind': 'tmp',
732 "capacity": 1073741824 }
734 'state': 'Committed',
735 'output_name': 'Output from step test_run_mounts',
736 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
737 'output_path': '/var/spool/cwl',
739 'container_image': '99999999999999999999999999999994+99',
740 'command': ['ls', '/var/spool/cwl'],
741 'cwd': '/var/spool/cwl',
742 'scheduling_parameters': {},
743 'properties': {'cwl_input': {
745 "basename": "99999999999999999999999999999994+44",
746 "class": "Directory",
748 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
753 "dirname": "/keep/99999999999999999999999999999994+44",
754 "location": "keep:99999999999999999999999999999994+44/file1",
757 "path": "/keep/99999999999999999999999999999994+44/file1",
763 "dirname": "/keep/99999999999999999999999999999994+44",
764 "location": "keep:99999999999999999999999999999994+44/file2",
767 "path": "/keep/99999999999999999999999999999994+44/file2",
771 "location": "keep:99999999999999999999999999999994+44",
772 "path": "/keep/99999999999999999999999999999994+44"
776 'output_storage_classes': ["default"]
779 # The test passes no builder.resources
780 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
781 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
782 def test_secrets(self, keepdocker):
783 arvados_cwl.add_arv_hints()
784 runner = mock.MagicMock()
785 runner.ignore_docker_for_reuse = False
786 runner.intermediate_output_ttl = 0
787 runner.secret_store = cwltool.secrets.SecretStore()
788 runner.api._rootDesc = {"revision": "20210628"}
790 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
791 runner.api.collections().get().execute.return_value = {
792 "portable_data_hash": "99999999999999999999999999999993+99"}
794 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
796 tool = cmap({"arguments": ["md5sum", "example.conf"],
797 "class": "CommandLineTool",
798 "cwlVersion": "v1.2",
801 "class": "http://commonwl.org/cwltool#Secrets",
810 "id": "#secret_job.cwl/pw",
818 "class": "InitialWorkDirRequirement",
821 "entry": "username: user\npassword: $(inputs.pw)\n",
822 "entryname": "example.conf"
828 loadingContext, runtimeContext = self.helper(runner)
829 runtimeContext.name = "test_secrets"
831 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
832 arvtool.formatgraph = None
834 job_order = {"pw": "blorp"}
835 runner.secret_store.store(["pw"], job_order)
837 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
838 j.run(runtimeContext)
839 runner.api.container_requests().create.assert_called_with(
840 body=JsonDiffMatcher({
842 'HOME': '/var/spool/cwl',
845 'name': 'test_secrets',
846 'runtime_constraints': {
850 'use_existing': True,
853 '/tmp': {'kind': 'tmp',
854 "capacity": 1073741824
856 '/var/spool/cwl': {'kind': 'tmp',
857 "capacity": 1073741824 }
859 'state': 'Committed',
860 'output_name': 'Output from step test_secrets',
861 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
862 'output_path': '/var/spool/cwl',
864 'container_image': '99999999999999999999999999999993+99',
865 'command': ['md5sum', 'example.conf'],
866 'cwd': '/var/spool/cwl',
867 'scheduling_parameters': {},
868 'properties': {'cwl_input': job_order},
870 "/var/spool/cwl/example.conf": {
871 "content": "username: user\npassword: blorp\n",
875 'output_storage_classes': ["default"]
878 # The test passes no builder.resources
879 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
880 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
881 def test_timelimit(self, keepdocker):
882 runner = mock.MagicMock()
883 runner.ignore_docker_for_reuse = False
884 runner.intermediate_output_ttl = 0
885 runner.secret_store = cwltool.secrets.SecretStore()
886 runner.api._rootDesc = {"revision": "20210628"}
888 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
889 runner.api.collections().get().execute.return_value = {
890 "portable_data_hash": "99999999999999999999999999999993+99"}
896 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
898 "cwlVersion": "v1.2",
899 "class": "CommandLineTool",
902 "class": "ToolTimeLimit",
908 loadingContext, runtimeContext = self.helper(runner)
909 runtimeContext.name = "test_timelimit"
911 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
912 arvtool.formatgraph = None
914 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
915 j.run(runtimeContext)
917 _, kwargs = runner.api.container_requests().create.call_args
918 self.assertEqual(42, kwargs['body']['scheduling_parameters'].get('max_run_time'))
921 # The test passes no builder.resources
922 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
923 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
924 def test_setting_storage_class(self, keepdocker):
925 arv_docker_clear_cache()
927 runner = mock.MagicMock()
928 runner.ignore_docker_for_reuse = False
929 runner.intermediate_output_ttl = 0
930 runner.secret_store = cwltool.secrets.SecretStore()
931 runner.api._rootDesc = {"revision": "20210628"}
933 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
934 runner.api.collections().get().execute.return_value = {
935 "portable_data_hash": "99999999999999999999999999999993+99"}
941 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
943 "cwlVersion": "v1.2",
944 "class": "CommandLineTool",
947 "class": "http://arvados.org/cwl#OutputStorageClass",
948 "finalStorageClass": ["baz_sc", "qux_sc"],
949 "intermediateStorageClass": ["foo_sc", "bar_sc"]
954 loadingContext, runtimeContext = self.helper(runner, True)
956 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
957 arvtool.formatgraph = None
959 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
960 j.run(runtimeContext)
961 runner.api.container_requests().create.assert_called_with(
962 body=JsonDiffMatcher({
964 'HOME': '/var/spool/cwl',
967 'name': 'test_run_True',
968 'runtime_constraints': {
972 'use_existing': True,
975 '/tmp': {'kind': 'tmp',
976 "capacity": 1073741824
978 '/var/spool/cwl': {'kind': 'tmp',
979 "capacity": 1073741824 }
981 'state': 'Committed',
982 'output_name': 'Output from step test_run_True',
983 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
984 'output_path': '/var/spool/cwl',
986 'container_image': '99999999999999999999999999999993+99',
987 'command': ['ls', '/var/spool/cwl'],
988 'cwd': '/var/spool/cwl',
989 'scheduling_parameters': {},
990 'properties': {'cwl_input': {}},
992 'output_storage_classes': ["foo_sc", "bar_sc"]
996 # The test passes no builder.resources
997 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
998 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
999 def test_setting_process_properties(self, keepdocker):
1000 arv_docker_clear_cache()
1002 runner = mock.MagicMock()
1003 runner.ignore_docker_for_reuse = False
1004 runner.intermediate_output_ttl = 0
1005 runner.secret_store = cwltool.secrets.SecretStore()
1006 runner.api._rootDesc = {"revision": "20210628"}
1008 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1009 runner.api.collections().get().execute.return_value = {
1010 "portable_data_hash": "99999999999999999999999999999993+99"}
1014 {"id": "x", "type": "string"}],
1016 "baseCommand": "ls",
1017 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1019 "class": "CommandLineTool",
1020 "cwlVersion": "v1.2",
1023 "class": "http://arvados.org/cwl#ProcessProperties",
1024 "processProperties": [
1025 {"propertyName": "foo",
1026 "propertyValue": "bar"},
1027 {"propertyName": "baz",
1028 "propertyValue": "$(inputs.x)"},
1029 {"propertyName": "quux",
1040 loadingContext, runtimeContext = self.helper(runner, True)
1042 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1043 arvtool.formatgraph = None
1045 for j in arvtool.job({"x": "blorp"}, mock.MagicMock(), runtimeContext):
1046 j.run(runtimeContext)
1047 runner.api.container_requests().create.assert_called_with(
1048 body=JsonDiffMatcher({
1050 'HOME': '/var/spool/cwl',
1053 'name': 'test_run_True',
1054 'runtime_constraints': {
1058 'use_existing': True,
1061 '/tmp': {'kind': 'tmp',
1062 "capacity": 1073741824
1064 '/var/spool/cwl': {'kind': 'tmp',
1065 "capacity": 1073741824 }
1067 'state': 'Committed',
1068 'output_name': 'Output from step test_run_True',
1069 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1070 'output_path': '/var/spool/cwl',
1072 'container_image': '99999999999999999999999999999993+99',
1073 'command': ['ls', '/var/spool/cwl'],
1074 'cwd': '/var/spool/cwl',
1075 'scheduling_parameters': {},
1078 "cwl_input": {"x": "blorp"},
1085 'secret_mounts': {},
1086 'output_storage_classes': ["default"]
1090 # The test passes no builder.resources
1091 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1092 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1093 def test_cuda_requirement(self, keepdocker):
1094 arvados_cwl.add_arv_hints()
1095 arv_docker_clear_cache()
1097 runner = mock.MagicMock()
1098 runner.ignore_docker_for_reuse = False
1099 runner.intermediate_output_ttl = 0
1100 runner.secret_store = cwltool.secrets.SecretStore()
1101 runner.api._rootDesc = {"revision": "20210628"}
1103 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1104 runner.api.collections().get().execute.return_value = {
1105 "portable_data_hash": "99999999999999999999999999999993+99"}
1108 "class": "http://commonwl.org/cwltool#CUDARequirement",
1109 "cudaVersionMin": "11.0",
1110 "cudaComputeCapability": "9.0",
1112 "class": "http://commonwl.org/cwltool#CUDARequirement",
1113 "cudaVersionMin": "11.0",
1114 "cudaComputeCapability": "9.0",
1115 "cudaDeviceCountMin": 2
1117 "class": "http://commonwl.org/cwltool#CUDARequirement",
1118 "cudaVersionMin": "11.0",
1119 "cudaComputeCapability": ["4.0", "5.0"],
1120 "cudaDeviceCountMin": 2
1125 'driver_version': "11.0",
1126 'hardware_capability': "9.0"
1129 'driver_version': "11.0",
1130 'hardware_capability': "9.0"
1133 'driver_version': "11.0",
1134 'hardware_capability': "4.0"
1137 for test_case in range(0, len(test_cwl_req)):
1142 "baseCommand": "nvidia-smi",
1145 "cwlVersion": "v1.2",
1146 "class": "CommandLineTool",
1147 "requirements": [test_cwl_req[test_case]]
1150 loadingContext, runtimeContext = self.helper(runner, True)
1152 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1153 arvtool.formatgraph = None
1155 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1156 j.run(runtimeContext)
1157 runner.api.container_requests().create.assert_called_with(
1158 body=JsonDiffMatcher({
1160 'HOME': '/var/spool/cwl',
1163 'name': 'test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1164 'runtime_constraints': {
1167 'cuda': test_arv_req[test_case]
1169 'use_existing': True,
1172 '/tmp': {'kind': 'tmp',
1173 "capacity": 1073741824
1175 '/var/spool/cwl': {'kind': 'tmp',
1176 "capacity": 1073741824 }
1178 'state': 'Committed',
1179 'output_name': 'Output from step test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1180 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1181 'output_path': '/var/spool/cwl',
1183 'container_image': '99999999999999999999999999999993+99',
1184 'command': ['nvidia-smi'],
1185 'cwd': '/var/spool/cwl',
1186 'scheduling_parameters': {},
1187 'properties': {'cwl_input': {}},
1188 'secret_mounts': {},
1189 'output_storage_classes': ["default"]
1193 # The test passes no builder.resources
1194 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1195 @mock.patch("arvados_cwl.arvdocker.determine_image_id")
1196 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1197 def test_match_local_docker(self, keepdocker, determine_image_id):
1198 arvados_cwl.add_arv_hints()
1199 arv_docker_clear_cache()
1201 runner = mock.MagicMock()
1202 runner.ignore_docker_for_reuse = False
1203 runner.intermediate_output_ttl = 0
1204 runner.secret_store = cwltool.secrets.SecretStore()
1205 runner.api._rootDesc = {"revision": "20210628"}
1207 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
1208 ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
1209 determine_image_id.side_effect = lambda x: "123"
1211 ex = mock.MagicMock()
1212 lookup = {"zzzzz-4zz18-zzzzzzzzzzzzzz4": {"portable_data_hash": "99999999999999999999999999999994+99"},
1213 "zzzzz-4zz18-zzzzzzzzzzzzzz3": {"portable_data_hash": "99999999999999999999999999999993+99"}}
1214 ex.execute.return_value = lookup[uuid]
1216 runner.api.collections().get.side_effect = execute
1221 "baseCommand": "echo",
1224 "cwlVersion": "v1.0",
1225 "class": "org.w3id.cwl.cwl.CommandLineTool"
1228 loadingContext, runtimeContext = self.helper(runner, True)
1230 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, loadingContext)
1231 arvtool.formatgraph = None
1233 container_request = {
1235 'HOME': '/var/spool/cwl',
1238 'name': 'test_run_True',
1239 'runtime_constraints': {
1243 'use_existing': True,
1246 '/tmp': {'kind': 'tmp',
1247 "capacity": 1073741824
1249 '/var/spool/cwl': {'kind': 'tmp',
1250 "capacity": 1073741824 }
1252 'state': 'Committed',
1253 'output_name': 'Output from step test_run_True',
1254 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1255 'output_path': '/var/spool/cwl',
1257 'container_image': '99999999999999999999999999999994+99',
1258 'command': ['echo'],
1259 'cwd': '/var/spool/cwl',
1260 'scheduling_parameters': {},
1261 'properties': {'cwl_input': {}},
1262 'secret_mounts': {},
1263 'output_storage_classes': ["default"]
1266 runtimeContext.match_local_docker = False
1267 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1268 j.run(runtimeContext)
1269 runner.api.container_requests().create.assert_called_with(
1270 body=JsonDiffMatcher(container_request))
1272 arv_docker_clear_cache()
1273 runtimeContext.match_local_docker = True
1274 container_request['container_image'] = '99999999999999999999999999999993+99'
1275 container_request['name'] = 'test_run_True_2'
1276 container_request['output_name'] = 'Output from step test_run_True_2'
1277 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1278 j.run(runtimeContext)
1279 runner.api.container_requests().create.assert_called_with(
1280 body=JsonDiffMatcher(container_request))
1283 # The test passes no builder.resources
1284 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1285 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1286 def test_run_preemptible_hint(self, keepdocker):
1287 arvados_cwl.add_arv_hints()
1288 for enable_preemptible in (None, True, False):
1289 for preemptible_hint in (None, True, False):
1290 arv_docker_clear_cache()
1292 runner = mock.MagicMock()
1293 runner.ignore_docker_for_reuse = False
1294 runner.intermediate_output_ttl = 0
1295 runner.secret_store = cwltool.secrets.SecretStore()
1296 runner.api._rootDesc = {"revision": "20210628"}
1298 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1299 runner.api.collections().get().execute.return_value = {
1300 "portable_data_hash": "99999999999999999999999999999993+99"}
1302 if preemptible_hint is not None:
1304 "class": "http://arvados.org/cwl#UsePreemptible",
1305 "usePreemptible": preemptible_hint
1313 "baseCommand": "ls",
1314 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1316 "class": "CommandLineTool",
1317 "cwlVersion": "v1.2",
1321 loadingContext, runtimeContext = self.helper(runner)
1323 runtimeContext.name = 'test_run_enable_preemptible_'+str(enable_preemptible)+str(preemptible_hint)
1324 runtimeContext.enable_preemptible = enable_preemptible
1326 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1327 arvtool.formatgraph = None
1329 # Test the interactions between --enable/disable-preemptible
1330 # and UsePreemptible hint
1332 if enable_preemptible is None:
1333 if preemptible_hint is None:
1336 sched = {'preemptible': preemptible_hint}
1338 if preemptible_hint is None:
1339 sched = {'preemptible': enable_preemptible}
1341 sched = {'preemptible': enable_preemptible and preemptible_hint}
1343 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1344 j.run(runtimeContext)
1345 runner.api.container_requests().create.assert_called_with(
1346 body=JsonDiffMatcher({
1348 'HOME': '/var/spool/cwl',
1351 'name': runtimeContext.name,
1352 'runtime_constraints': {
1356 'use_existing': True,
1359 '/tmp': {'kind': 'tmp',
1360 "capacity": 1073741824
1362 '/var/spool/cwl': {'kind': 'tmp',
1363 "capacity": 1073741824 }
1365 'state': 'Committed',
1366 'output_name': 'Output from step '+runtimeContext.name,
1367 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1368 'output_path': '/var/spool/cwl',
1370 'container_image': '99999999999999999999999999999993+99',
1371 'command': ['ls', '/var/spool/cwl'],
1372 'cwd': '/var/spool/cwl',
1373 'scheduling_parameters': sched,
1374 'properties': {'cwl_input': {}},
1375 'secret_mounts': {},
1376 'output_storage_classes': ["default"]
1380 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1381 def test_output_properties(self, keepdocker):
1382 arvados_cwl.add_arv_hints()
1383 for rev in ["20210628", "20220510"]:
1384 runner = mock.MagicMock()
1385 runner.ignore_docker_for_reuse = False
1386 runner.intermediate_output_ttl = 0
1387 runner.secret_store = cwltool.secrets.SecretStore()
1388 runner.api._rootDesc = {"revision": rev}
1390 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1391 runner.api.collections().get().execute.return_value = {
1392 "portable_data_hash": "99999999999999999999999999999993+99"}
1400 "baseCommand": "ls",
1401 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1403 "cwlVersion": "v1.2",
1404 "class": "CommandLineTool",
1407 "class": "http://arvados.org/cwl#OutputCollectionProperties",
1408 "outputProperties": {
1410 "baz": "$(inputs.inp)"
1416 loadingContext, runtimeContext = self.helper(runner)
1417 runtimeContext.name = "test_timelimit"
1419 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1420 arvtool.formatgraph = None
1422 for j in arvtool.job({"inp": "quux"}, mock.MagicMock(), runtimeContext):
1423 j.run(runtimeContext)
1425 _, kwargs = runner.api.container_requests().create.call_args
1426 if rev == "20220510":
1427 self.assertEqual({"foo": "bar", "baz": "quux"}, kwargs['body'].get('output_properties'))
1429 self.assertEqual(None, kwargs['body'].get('output_properties'))
1432 class TestWorkflow(unittest.TestCase):
1434 cwltool.process._names = set()
1435 arv_docker_clear_cache()
1437 def helper(self, runner, enable_reuse=True):
1438 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
1440 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
1441 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
1443 document_loader.fetcher_constructor = functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=make_fs_access(""))
1444 document_loader.fetcher = document_loader.fetcher_constructor(document_loader.cache, document_loader.session)
1445 document_loader.fetch_text = document_loader.fetcher.fetch_text
1446 document_loader.check_exists = document_loader.fetcher.check_exists
1448 loadingContext = arvados_cwl.context.ArvLoadingContext(
1449 {"avsc_names": avsc_names,
1451 "make_fs_access": make_fs_access,
1452 "loader": document_loader,
1453 "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
1454 "construct_tool_object": runner.arv_make_tool})
1455 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
1456 {"work_api": "containers",
1458 "name": "test_run_wf_"+str(enable_reuse),
1459 "make_fs_access": make_fs_access,
1461 "enable_reuse": enable_reuse,
1464 return loadingContext, runtimeContext
1466 # The test passes no builder.resources
1467 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1468 @mock.patch("arvados.collection.CollectionReader")
1469 @mock.patch("arvados.collection.Collection")
1470 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1471 def test_run(self, list_images_in_arv, mockcollection, mockcollectionreader):
1472 arvados_cwl.add_arv_hints()
1474 api = mock.MagicMock()
1475 api._rootDesc = get_rootDesc()
1477 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1478 self.assertEqual(runner.work_api, 'containers')
1480 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1481 runner.api.collections().get().execute.return_value = {"portable_data_hash": "99999999999999999999999999999993+99"}
1482 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1483 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1485 runner.api.containers().current().execute.return_value = {}
1487 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1488 runner.ignore_docker_for_reuse = False
1489 runner.num_retries = 0
1490 runner.secret_store = cwltool.secrets.SecretStore()
1492 loadingContext, runtimeContext = self.helper(runner)
1493 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1495 mockcollectionreader().exists.return_value = True
1497 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
1498 metadata["cwlVersion"] = tool["cwlVersion"]
1500 mockc = mock.MagicMock()
1501 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
1502 mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
1504 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1505 arvtool.formatgraph = None
1506 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1508 next(it).run(runtimeContext)
1509 next(it).run(runtimeContext)
1511 with open("tests/wf/scatter2_subwf.cwl") as f:
1512 subwf = StripYAMLComments(f.read()).rstrip()
1514 runner.api.container_requests().create.assert_called_with(
1515 body=JsonDiffMatcher({
1520 "--preserve-entire-environment",
1524 "container_image": "99999999999999999999999999999993+99",
1525 "cwd": "/var/spool/cwl",
1527 "HOME": "/var/spool/cwl",
1531 "/keep/99999999999999999999999999999999+118": {
1532 "kind": "collection",
1533 "portable_data_hash": "99999999999999999999999999999999+118"
1536 "capacity": 1073741824,
1540 "capacity": 1073741824,
1543 "/var/spool/cwl/cwl.input.yml": {
1544 "kind": "collection",
1545 "path": "cwl.input.yml",
1546 "portable_data_hash": "99999999999999999999999999999996+99"
1548 "/var/spool/cwl/workflow.cwl": {
1549 "kind": "collection",
1550 "path": "workflow.cwl",
1551 "portable_data_hash": "99999999999999999999999999999996+99"
1555 "path": "/var/spool/cwl/cwl.output.json"
1558 "name": "scatterstep",
1559 "output_name": "Output from step scatterstep",
1560 "output_path": "/var/spool/cwl",
1563 "properties": {'cwl_input': {
1565 "basename": "token.txt",
1567 "dirname": "/keep/99999999999999999999999999999999+118",
1568 "location": "keep:99999999999999999999999999999999+118/token.txt",
1570 "nameroot": "token",
1571 "path": "/keep/99999999999999999999999999999999+118/token.txt",
1576 "runtime_constraints": {
1580 "scheduling_parameters": {},
1581 "secret_mounts": {},
1582 "state": "Committed",
1583 "use_existing": True,
1584 'output_storage_classes': ["default"]
1586 mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
1587 mockc.open().__enter__().write.assert_has_calls([mock.call(
1590 "basename": "token.txt",
1592 "location": "/keep/99999999999999999999999999999999+118/token.txt",
1598 # The test passes no builder.resources
1599 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1600 @mock.patch("arvados.collection.CollectionReader")
1601 @mock.patch("arvados.collection.Collection")
1602 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1603 def test_overall_resource_singlecontainer(self, list_images_in_arv, mockcollection, mockcollectionreader):
1604 arvados_cwl.add_arv_hints()
1606 api = mock.MagicMock()
1607 api._rootDesc = get_rootDesc()
1609 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1610 self.assertEqual(runner.work_api, 'containers')
1612 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1613 runner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1614 "portable_data_hash": "99999999999999999999999999999993+99"}
1615 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1616 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1618 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1619 runner.ignore_docker_for_reuse = False
1620 runner.num_retries = 0
1621 runner.secret_store = cwltool.secrets.SecretStore()
1623 loadingContext, runtimeContext = self.helper(runner)
1624 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1625 loadingContext.do_update = True
1626 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
1628 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
1630 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1631 arvtool.formatgraph = None
1632 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1634 next(it).run(runtimeContext)
1635 next(it).run(runtimeContext)
1637 with open("tests/wf/echo-subwf.cwl") as f:
1638 subwf = StripYAMLComments(f.read())
1640 runner.api.container_requests().create.assert_called_with(
1641 body=JsonDiffMatcher({
1643 'environment': {'HOME': '/var/spool/cwl', 'TMPDIR': '/tmp'},
1644 'scheduling_parameters': {},
1645 'name': u'echo-subwf',
1646 'secret_mounts': {},
1647 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
1648 'properties': {'cwl_input': {}},
1651 '/var/spool/cwl/cwl.input.yml': {
1652 'portable_data_hash': '99999999999999999999999999999996+99',
1653 'kind': 'collection',
1654 'path': 'cwl.input.yml'
1656 '/var/spool/cwl/workflow.cwl': {
1657 'portable_data_hash': '99999999999999999999999999999996+99',
1658 'kind': 'collection',
1659 'path': 'workflow.cwl'
1662 'path': '/var/spool/cwl/cwl.output.json',
1667 'capacity': 1073741824
1668 }, '/var/spool/cwl': {
1670 'capacity': 3221225472
1673 'state': 'Committed',
1674 'output_path': '/var/spool/cwl',
1675 'container_image': '99999999999999999999999999999993+99',
1680 u'--preserve-entire-environment',
1684 'use_existing': True,
1685 'output_name': u'Output from step echo-subwf',
1686 'cwd': '/var/spool/cwl',
1687 'output_storage_classes': ["default"]
1690 def test_default_work_api(self):
1691 arvados_cwl.add_arv_hints()
1693 api = mock.MagicMock()
1694 api._rootDesc = copy.deepcopy(get_rootDesc())
1695 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1696 self.assertEqual(runner.work_api, 'containers')