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"}
204 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheDisk": 0}}
206 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
207 runner.api.collections().get().execute.return_value = {
208 "portable_data_hash": "99999999999999999999999999999993+99"}
214 "class": "ResourceRequirement",
220 "class": "http://arvados.org/cwl#RuntimeConstraints",
223 "class": "http://arvados.org/cwl#APIRequirement",
225 "class": "http://arvados.org/cwl#PartitionRequirement",
228 "class": "http://arvados.org/cwl#IntermediateOutput",
231 "class": "WorkReuse",
236 "class": "CommandLineTool",
240 loadingContext, runtimeContext = self.helper(runner)
241 runtimeContext.name = "test_resource_requirements"
243 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
244 arvtool.formatgraph = None
245 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
246 j.run(runtimeContext)
248 call_args, call_kwargs = runner.api.container_requests().create.call_args
250 call_body_expected = {
252 'HOME': '/var/spool/cwl',
255 'name': 'test_resource_requirements',
256 'runtime_constraints': {
259 'keep_cache_ram': 536870912,
262 'use_existing': False,
265 '/tmp': {'kind': 'tmp',
266 "capacity": 4194304000 },
267 '/var/spool/cwl': {'kind': 'tmp',
268 "capacity": 5242880000 }
270 'state': 'Committed',
271 'output_name': 'Output from step test_resource_requirements',
272 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
273 'output_path': '/var/spool/cwl',
275 'container_image': '99999999999999999999999999999993+99',
277 'cwd': '/var/spool/cwl',
278 'scheduling_parameters': {
279 'partitions': ['blurb']
281 'properties': {'cwl_input': {}},
283 'output_storage_classes': ["default"]
286 call_body = call_kwargs.get('body', None)
287 self.assertNotEqual(None, call_body)
288 for key in call_body:
289 self.assertEqual(call_body_expected.get(key), call_body.get(key))
292 # The test passes some fields in builder.resources
293 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
294 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
295 @mock.patch("arvados.collection.Collection")
296 def test_initial_work_dir(self, collection_mock, keepdocker):
297 runner = mock.MagicMock()
298 runner.ignore_docker_for_reuse = False
299 runner.intermediate_output_ttl = 0
300 runner.secret_store = cwltool.secrets.SecretStore()
301 runner.api._rootDesc = {"revision": "20210628"}
303 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
304 runner.api.collections().get().execute.return_value = {
305 "portable_data_hash": "99999999999999999999999999999993+99"}
307 sourcemock = mock.MagicMock()
308 def get_collection_mock(p):
310 return (sourcemock, p.split("/", 1)[1])
312 return (sourcemock, "")
313 runner.fs_access.get_collection.side_effect = get_collection_mock
315 vwdmock = mock.MagicMock()
316 collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs)
322 "class": "InitialWorkDirRequirement",
326 "location": "keep:99999999999999999999999999999995+99/bar"
329 "class": "Directory",
331 "location": "keep:99999999999999999999999999999995+99"
335 "basename": "filename",
336 "location": "keep:99999999999999999999999999999995+99/baz/filename"
339 "class": "Directory",
340 "basename": "subdir",
341 "location": "keep:99999999999999999999999999999995+99/subdir"
345 "class": "CommandLineTool",
346 "cwlVersion": "v1.2",
350 loadingContext, runtimeContext = self.helper(runner)
351 runtimeContext.name = "test_initial_work_dir"
353 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
355 arvtool.formatgraph = None
356 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
357 j.run(runtimeContext)
359 call_args, call_kwargs = runner.api.container_requests().create.call_args
361 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
362 vwdmock.copy.assert_has_calls([mock.call('.', 'foo2', source_collection=sourcemock)])
363 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
364 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
366 call_body_expected = {
368 'HOME': '/var/spool/cwl',
371 'name': 'test_initial_work_dir',
372 'runtime_constraints': {
376 'use_existing': True,
379 '/tmp': {'kind': 'tmp',
380 "capacity": 1073741824 },
381 '/var/spool/cwl': {'kind': 'tmp',
382 "capacity": 1073741824 },
383 '/var/spool/cwl/foo': {
384 'kind': 'collection',
386 'portable_data_hash': '99999999999999999999999999999996+99'
388 '/var/spool/cwl/foo2': {
389 'kind': 'collection',
391 'portable_data_hash': '99999999999999999999999999999996+99'
393 '/var/spool/cwl/filename': {
394 'kind': 'collection',
396 'portable_data_hash': '99999999999999999999999999999996+99'
398 '/var/spool/cwl/subdir': {
399 'kind': 'collection',
401 'portable_data_hash': '99999999999999999999999999999996+99'
404 'state': 'Committed',
405 'output_name': 'Output from step test_initial_work_dir',
406 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
407 'output_path': '/var/spool/cwl',
409 'container_image': '99999999999999999999999999999993+99',
411 'cwd': '/var/spool/cwl',
412 'scheduling_parameters': {
414 'properties': {'cwl_input': {}},
416 'output_storage_classes': ["default"]
419 call_body = call_kwargs.get('body', None)
420 self.assertNotEqual(None, call_body)
421 for key in call_body:
422 self.assertEqual(call_body_expected.get(key), call_body.get(key))
425 # Test redirecting stdin/stdout/stderr
426 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
427 def test_redirects(self, keepdocker):
428 runner = mock.MagicMock()
429 runner.ignore_docker_for_reuse = False
430 runner.intermediate_output_ttl = 0
431 runner.secret_store = cwltool.secrets.SecretStore()
432 runner.api._rootDesc = {"revision": "20210628"}
434 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
435 runner.api.collections().get().execute.return_value = {
436 "portable_data_hash": "99999999999999999999999999999993+99"}
438 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
444 "stdout": "stdout.txt",
445 "stderr": "stderr.txt",
446 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
447 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
449 "class": "CommandLineTool",
453 loadingContext, runtimeContext = self.helper(runner)
454 runtimeContext.name = "test_run_redirect"
456 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
457 arvtool.formatgraph = None
458 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
459 j.run(runtimeContext)
460 runner.api.container_requests().create.assert_called_with(
461 body=JsonDiffMatcher({
463 'HOME': '/var/spool/cwl',
466 'name': 'test_run_redirect',
467 'runtime_constraints': {
471 'use_existing': True,
474 '/tmp': {'kind': 'tmp',
475 "capacity": 1073741824 },
476 '/var/spool/cwl': {'kind': 'tmp',
477 "capacity": 1073741824 },
480 "path": "/var/spool/cwl/stderr.txt"
483 "kind": "collection",
485 "portable_data_hash": "99999999999999999999999999999996+99"
489 "path": "/var/spool/cwl/stdout.txt"
492 'state': 'Committed',
493 "output_name": "Output from step test_run_redirect",
494 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
495 'output_path': '/var/spool/cwl',
497 'container_image': '99999999999999999999999999999993+99',
498 'command': ['ls', '/var/spool/cwl'],
499 'cwd': '/var/spool/cwl',
500 'scheduling_parameters': {},
501 'properties': {'cwl_input': {}},
503 'output_storage_classes': ["default"]
506 @mock.patch("arvados.collection.Collection")
507 def test_done(self, col):
508 api = mock.MagicMock()
510 runner = mock.MagicMock()
512 runner.num_retries = 0
513 runner.ignore_docker_for_reuse = False
514 runner.intermediate_output_ttl = 0
515 runner.secret_store = cwltool.secrets.SecretStore()
517 runner.api.containers().get().execute.return_value = {"state":"Complete",
521 col().open.return_value = []
523 loadingContext, runtimeContext = self.helper(runner)
525 arvjob = arvados_cwl.ArvadosContainer(runner,
533 arvjob.output_callback = mock.MagicMock()
534 arvjob.collect_outputs = mock.MagicMock()
535 arvjob.successCodes = [0]
536 arvjob.outdir = "/var/spool/cwl"
537 arvjob.output_ttl = 3600
538 arvjob.uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzz1"
540 arvjob.collect_outputs.return_value = {"out": "stuff"}
544 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
545 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
546 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
547 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
548 "modified_at": "2017-05-26T12:01:22Z",
552 self.assertFalse(api.collections().create.called)
553 self.assertFalse(runner.runtime_status_error.called)
555 arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
556 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
557 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
559 runner.api.container_requests().update.assert_called_with(uuid="zzzzz-xvhdp-zzzzzzzzzzzzzz1",
560 body={'container_request': {'properties': {'cwl_output': {'out': 'stuff'}}}})
563 # Test to make sure we dont call runtime_status_update if we already did
564 # some where higher up in the call stack
565 @mock.patch("arvados_cwl.util.get_current_container")
566 def test_recursive_runtime_status_update(self, gcc_mock):
567 self.setup_and_test_container_executor_and_logging(gcc_mock)
568 root_logger = logging.getLogger('')
570 # get_current_container is invoked when we call runtime_status_update
571 # so try and log again!
572 gcc_mock.side_effect = lambda *args: root_logger.error("Second Error")
574 root_logger.error("First Error")
576 self.fail("RuntimeStatusLoggingHandler should not be called recursively")
579 # Test to make sure that an exception raised from
580 # get_current_container doesn't cause the logger to raise an
582 @mock.patch("arvados_cwl.util.get_current_container")
583 def test_runtime_status_get_current_container_exception(self, gcc_mock):
584 self.setup_and_test_container_executor_and_logging(gcc_mock)
585 root_logger = logging.getLogger('')
587 # get_current_container is invoked when we call
588 # runtime_status_update, it is going to also raise an
590 gcc_mock.side_effect = Exception("Second Error")
592 root_logger.error("First Error")
594 self.fail("Exception in logger should not propagate")
595 self.assertTrue(gcc_mock.called)
597 @mock.patch("arvados_cwl.ArvCwlExecutor.runtime_status_update")
598 @mock.patch("arvados_cwl.util.get_current_container")
599 @mock.patch("arvados.collection.CollectionReader")
600 @mock.patch("arvados.collection.Collection")
601 def test_child_failure(self, col, reader, gcc_mock, rts_mock):
602 runner = self.setup_and_test_container_executor_and_logging(gcc_mock)
604 gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"}
605 self.assertTrue(gcc_mock.called)
607 runner.num_retries = 0
608 runner.ignore_docker_for_reuse = False
609 runner.intermediate_output_ttl = 0
610 runner.secret_store = cwltool.secrets.SecretStore()
611 runner.label = mock.MagicMock()
612 runner.label.return_value = '[container testjob]'
614 runner.api.containers().get().execute.return_value = {
621 col().open.return_value = []
623 loadingContext, runtimeContext = self.helper(runner)
625 arvjob = arvados_cwl.ArvadosContainer(runner,
633 arvjob.output_callback = mock.MagicMock()
634 arvjob.collect_outputs = mock.MagicMock()
635 arvjob.successCodes = [0]
636 arvjob.outdir = "/var/spool/cwl"
637 arvjob.output_ttl = 3600
638 arvjob.collect_outputs.return_value = {"out": "stuff"}
642 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
643 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
644 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
645 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
646 "modified_at": "2017-05-26T12:01:22Z",
650 rts_mock.assert_called_with(
652 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
653 ' ** log is empty **'
655 arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
657 # The test passes no builder.resources
658 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
659 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
660 def test_mounts(self, keepdocker):
661 runner = mock.MagicMock()
662 runner.ignore_docker_for_reuse = False
663 runner.intermediate_output_ttl = 0
664 runner.secret_store = cwltool.secrets.SecretStore()
665 runner.api._rootDesc = {"revision": "20210628"}
667 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
668 runner.api.collections().get().execute.return_value = {
669 "portable_data_hash": "99999999999999999999999999999994+99",
670 "manifest_text": ". 99999999999999999999999999999994+99 0:0:file1 0:0:file2"}
672 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
681 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
683 "class": "CommandLineTool",
687 loadingContext, runtimeContext = self.helper(runner)
688 runtimeContext.name = "test_run_mounts"
690 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
691 arvtool.formatgraph = None
694 "class": "Directory",
695 "location": "keep:99999999999999999999999999999994+44",
696 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
700 "location": "keep:99999999999999999999999999999994+44/file1",
704 "location": "keep:99999999999999999999999999999994+44/file2",
709 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
710 j.run(runtimeContext)
711 runner.api.container_requests().create.assert_called_with(
712 body=JsonDiffMatcher({
714 'HOME': '/var/spool/cwl',
717 'name': 'test_run_mounts',
718 'runtime_constraints': {
722 'use_existing': True,
725 "/keep/99999999999999999999999999999994+44": {
726 "kind": "collection",
727 "portable_data_hash": "99999999999999999999999999999994+44",
728 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz"
730 '/tmp': {'kind': 'tmp',
731 "capacity": 1073741824 },
732 '/var/spool/cwl': {'kind': 'tmp',
733 "capacity": 1073741824 }
735 'state': 'Committed',
736 'output_name': 'Output from step test_run_mounts',
737 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
738 'output_path': '/var/spool/cwl',
740 'container_image': '99999999999999999999999999999994+99',
741 'command': ['ls', '/var/spool/cwl'],
742 'cwd': '/var/spool/cwl',
743 'scheduling_parameters': {},
744 'properties': {'cwl_input': {
746 "basename": "99999999999999999999999999999994+44",
747 "class": "Directory",
749 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
754 "dirname": "/keep/99999999999999999999999999999994+44",
755 "location": "keep:99999999999999999999999999999994+44/file1",
758 "path": "/keep/99999999999999999999999999999994+44/file1",
764 "dirname": "/keep/99999999999999999999999999999994+44",
765 "location": "keep:99999999999999999999999999999994+44/file2",
768 "path": "/keep/99999999999999999999999999999994+44/file2",
772 "location": "keep:99999999999999999999999999999994+44",
773 "path": "/keep/99999999999999999999999999999994+44"
777 'output_storage_classes': ["default"]
780 # The test passes no builder.resources
781 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
782 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
783 def test_secrets(self, keepdocker):
784 arvados_cwl.add_arv_hints()
785 runner = mock.MagicMock()
786 runner.ignore_docker_for_reuse = False
787 runner.intermediate_output_ttl = 0
788 runner.secret_store = cwltool.secrets.SecretStore()
789 runner.api._rootDesc = {"revision": "20210628"}
791 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
792 runner.api.collections().get().execute.return_value = {
793 "portable_data_hash": "99999999999999999999999999999993+99"}
795 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
797 tool = cmap({"arguments": ["md5sum", "example.conf"],
798 "class": "CommandLineTool",
799 "cwlVersion": "v1.2",
802 "class": "http://commonwl.org/cwltool#Secrets",
811 "id": "#secret_job.cwl/pw",
819 "class": "InitialWorkDirRequirement",
822 "entry": "username: user\npassword: $(inputs.pw)\n",
823 "entryname": "example.conf"
829 loadingContext, runtimeContext = self.helper(runner)
830 runtimeContext.name = "test_secrets"
832 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
833 arvtool.formatgraph = None
835 job_order = {"pw": "blorp"}
836 runner.secret_store.store(["pw"], job_order)
838 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
839 j.run(runtimeContext)
840 runner.api.container_requests().create.assert_called_with(
841 body=JsonDiffMatcher({
843 'HOME': '/var/spool/cwl',
846 'name': 'test_secrets',
847 'runtime_constraints': {
851 'use_existing': True,
854 '/tmp': {'kind': 'tmp',
855 "capacity": 1073741824
857 '/var/spool/cwl': {'kind': 'tmp',
858 "capacity": 1073741824 }
860 'state': 'Committed',
861 'output_name': 'Output from step test_secrets',
862 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
863 'output_path': '/var/spool/cwl',
865 'container_image': '99999999999999999999999999999993+99',
866 'command': ['md5sum', 'example.conf'],
867 'cwd': '/var/spool/cwl',
868 'scheduling_parameters': {},
869 'properties': {'cwl_input': job_order},
871 "/var/spool/cwl/example.conf": {
872 "content": "username: user\npassword: blorp\n",
876 'output_storage_classes': ["default"]
879 # The test passes no builder.resources
880 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
881 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
882 def test_timelimit(self, keepdocker):
883 runner = mock.MagicMock()
884 runner.ignore_docker_for_reuse = False
885 runner.intermediate_output_ttl = 0
886 runner.secret_store = cwltool.secrets.SecretStore()
887 runner.api._rootDesc = {"revision": "20210628"}
889 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
890 runner.api.collections().get().execute.return_value = {
891 "portable_data_hash": "99999999999999999999999999999993+99"}
897 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
899 "cwlVersion": "v1.2",
900 "class": "CommandLineTool",
903 "class": "ToolTimeLimit",
909 loadingContext, runtimeContext = self.helper(runner)
910 runtimeContext.name = "test_timelimit"
912 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
913 arvtool.formatgraph = None
915 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
916 j.run(runtimeContext)
918 _, kwargs = runner.api.container_requests().create.call_args
919 self.assertEqual(42, kwargs['body']['scheduling_parameters'].get('max_run_time'))
922 # The test passes no builder.resources
923 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
924 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
925 def test_setting_storage_class(self, keepdocker):
926 arv_docker_clear_cache()
928 runner = mock.MagicMock()
929 runner.ignore_docker_for_reuse = False
930 runner.intermediate_output_ttl = 0
931 runner.secret_store = cwltool.secrets.SecretStore()
932 runner.api._rootDesc = {"revision": "20210628"}
934 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
935 runner.api.collections().get().execute.return_value = {
936 "portable_data_hash": "99999999999999999999999999999993+99"}
942 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
944 "cwlVersion": "v1.2",
945 "class": "CommandLineTool",
948 "class": "http://arvados.org/cwl#OutputStorageClass",
949 "finalStorageClass": ["baz_sc", "qux_sc"],
950 "intermediateStorageClass": ["foo_sc", "bar_sc"]
955 loadingContext, runtimeContext = self.helper(runner, True)
957 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
958 arvtool.formatgraph = None
960 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
961 j.run(runtimeContext)
962 runner.api.container_requests().create.assert_called_with(
963 body=JsonDiffMatcher({
965 'HOME': '/var/spool/cwl',
968 'name': 'test_run_True',
969 'runtime_constraints': {
973 'use_existing': True,
976 '/tmp': {'kind': 'tmp',
977 "capacity": 1073741824
979 '/var/spool/cwl': {'kind': 'tmp',
980 "capacity": 1073741824 }
982 'state': 'Committed',
983 'output_name': 'Output from step test_run_True',
984 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
985 'output_path': '/var/spool/cwl',
987 'container_image': '99999999999999999999999999999993+99',
988 'command': ['ls', '/var/spool/cwl'],
989 'cwd': '/var/spool/cwl',
990 'scheduling_parameters': {},
991 'properties': {'cwl_input': {}},
993 'output_storage_classes': ["foo_sc", "bar_sc"]
997 # The test passes no builder.resources
998 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
999 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1000 def test_setting_process_properties(self, keepdocker):
1001 arv_docker_clear_cache()
1003 runner = mock.MagicMock()
1004 runner.ignore_docker_for_reuse = False
1005 runner.intermediate_output_ttl = 0
1006 runner.secret_store = cwltool.secrets.SecretStore()
1007 runner.api._rootDesc = {"revision": "20210628"}
1009 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1010 runner.api.collections().get().execute.return_value = {
1011 "portable_data_hash": "99999999999999999999999999999993+99"}
1015 {"id": "x", "type": "string"}],
1017 "baseCommand": "ls",
1018 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1020 "class": "CommandLineTool",
1021 "cwlVersion": "v1.2",
1024 "class": "http://arvados.org/cwl#ProcessProperties",
1025 "processProperties": [
1026 {"propertyName": "foo",
1027 "propertyValue": "bar"},
1028 {"propertyName": "baz",
1029 "propertyValue": "$(inputs.x)"},
1030 {"propertyName": "quux",
1041 loadingContext, runtimeContext = self.helper(runner, True)
1043 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1044 arvtool.formatgraph = None
1046 for j in arvtool.job({"x": "blorp"}, mock.MagicMock(), runtimeContext):
1047 j.run(runtimeContext)
1048 runner.api.container_requests().create.assert_called_with(
1049 body=JsonDiffMatcher({
1051 'HOME': '/var/spool/cwl',
1054 'name': 'test_run_True',
1055 'runtime_constraints': {
1059 'use_existing': True,
1062 '/tmp': {'kind': 'tmp',
1063 "capacity": 1073741824
1065 '/var/spool/cwl': {'kind': 'tmp',
1066 "capacity": 1073741824 }
1068 'state': 'Committed',
1069 'output_name': 'Output from step test_run_True',
1070 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1071 'output_path': '/var/spool/cwl',
1073 'container_image': '99999999999999999999999999999993+99',
1074 'command': ['ls', '/var/spool/cwl'],
1075 'cwd': '/var/spool/cwl',
1076 'scheduling_parameters': {},
1079 "cwl_input": {"x": "blorp"},
1086 'secret_mounts': {},
1087 'output_storage_classes': ["default"]
1091 # The test passes no builder.resources
1092 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1093 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1094 def test_cuda_requirement(self, keepdocker):
1095 arvados_cwl.add_arv_hints()
1096 arv_docker_clear_cache()
1098 runner = mock.MagicMock()
1099 runner.ignore_docker_for_reuse = False
1100 runner.intermediate_output_ttl = 0
1101 runner.secret_store = cwltool.secrets.SecretStore()
1102 runner.api._rootDesc = {"revision": "20210628"}
1104 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1105 runner.api.collections().get().execute.return_value = {
1106 "portable_data_hash": "99999999999999999999999999999993+99"}
1109 "class": "http://commonwl.org/cwltool#CUDARequirement",
1110 "cudaVersionMin": "11.0",
1111 "cudaComputeCapability": "9.0",
1113 "class": "http://commonwl.org/cwltool#CUDARequirement",
1114 "cudaVersionMin": "11.0",
1115 "cudaComputeCapability": "9.0",
1116 "cudaDeviceCountMin": 2
1118 "class": "http://commonwl.org/cwltool#CUDARequirement",
1119 "cudaVersionMin": "11.0",
1120 "cudaComputeCapability": ["4.0", "5.0"],
1121 "cudaDeviceCountMin": 2
1126 'driver_version': "11.0",
1127 'hardware_capability': "9.0"
1130 'driver_version': "11.0",
1131 'hardware_capability': "9.0"
1134 'driver_version': "11.0",
1135 'hardware_capability': "4.0"
1138 for test_case in range(0, len(test_cwl_req)):
1143 "baseCommand": "nvidia-smi",
1146 "cwlVersion": "v1.2",
1147 "class": "CommandLineTool",
1148 "requirements": [test_cwl_req[test_case]]
1151 loadingContext, runtimeContext = self.helper(runner, True)
1153 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1154 arvtool.formatgraph = None
1156 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1157 j.run(runtimeContext)
1158 runner.api.container_requests().create.assert_called_with(
1159 body=JsonDiffMatcher({
1161 'HOME': '/var/spool/cwl',
1164 'name': 'test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1165 'runtime_constraints': {
1168 'cuda': test_arv_req[test_case]
1170 'use_existing': True,
1173 '/tmp': {'kind': 'tmp',
1174 "capacity": 1073741824
1176 '/var/spool/cwl': {'kind': 'tmp',
1177 "capacity": 1073741824 }
1179 'state': 'Committed',
1180 'output_name': 'Output from step test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1181 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1182 'output_path': '/var/spool/cwl',
1184 'container_image': '99999999999999999999999999999993+99',
1185 'command': ['nvidia-smi'],
1186 'cwd': '/var/spool/cwl',
1187 'scheduling_parameters': {},
1188 'properties': {'cwl_input': {}},
1189 'secret_mounts': {},
1190 'output_storage_classes': ["default"]
1194 # The test passes no builder.resources
1195 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1196 @mock.patch("arvados_cwl.arvdocker.determine_image_id")
1197 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1198 def test_match_local_docker(self, keepdocker, determine_image_id):
1199 arvados_cwl.add_arv_hints()
1200 arv_docker_clear_cache()
1202 runner = mock.MagicMock()
1203 runner.ignore_docker_for_reuse = False
1204 runner.intermediate_output_ttl = 0
1205 runner.secret_store = cwltool.secrets.SecretStore()
1206 runner.api._rootDesc = {"revision": "20210628"}
1208 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
1209 ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
1210 determine_image_id.side_effect = lambda x: "123"
1212 ex = mock.MagicMock()
1213 lookup = {"zzzzz-4zz18-zzzzzzzzzzzzzz4": {"portable_data_hash": "99999999999999999999999999999994+99"},
1214 "zzzzz-4zz18-zzzzzzzzzzzzzz3": {"portable_data_hash": "99999999999999999999999999999993+99"}}
1215 ex.execute.return_value = lookup[uuid]
1217 runner.api.collections().get.side_effect = execute
1222 "baseCommand": "echo",
1225 "cwlVersion": "v1.0",
1226 "class": "org.w3id.cwl.cwl.CommandLineTool"
1229 loadingContext, runtimeContext = self.helper(runner, True)
1231 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, loadingContext)
1232 arvtool.formatgraph = None
1234 container_request = {
1236 'HOME': '/var/spool/cwl',
1239 'name': 'test_run_True',
1240 'runtime_constraints': {
1244 'use_existing': True,
1247 '/tmp': {'kind': 'tmp',
1248 "capacity": 1073741824
1250 '/var/spool/cwl': {'kind': 'tmp',
1251 "capacity": 1073741824 }
1253 'state': 'Committed',
1254 'output_name': 'Output from step test_run_True',
1255 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1256 'output_path': '/var/spool/cwl',
1258 'container_image': '99999999999999999999999999999994+99',
1259 'command': ['echo'],
1260 'cwd': '/var/spool/cwl',
1261 'scheduling_parameters': {},
1262 'properties': {'cwl_input': {}},
1263 'secret_mounts': {},
1264 'output_storage_classes': ["default"]
1267 runtimeContext.match_local_docker = False
1268 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1269 j.run(runtimeContext)
1270 runner.api.container_requests().create.assert_called_with(
1271 body=JsonDiffMatcher(container_request))
1273 arv_docker_clear_cache()
1274 runtimeContext.match_local_docker = True
1275 container_request['container_image'] = '99999999999999999999999999999993+99'
1276 container_request['name'] = 'test_run_True_2'
1277 container_request['output_name'] = 'Output from step test_run_True_2'
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))
1284 # The test passes no builder.resources
1285 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1286 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1287 def test_run_preemptible_hint(self, keepdocker):
1288 arvados_cwl.add_arv_hints()
1289 for enable_preemptible in (None, True, False):
1290 for preemptible_hint in (None, True, False):
1291 arv_docker_clear_cache()
1293 runner = mock.MagicMock()
1294 runner.ignore_docker_for_reuse = False
1295 runner.intermediate_output_ttl = 0
1296 runner.secret_store = cwltool.secrets.SecretStore()
1297 runner.api._rootDesc = {"revision": "20210628"}
1299 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1300 runner.api.collections().get().execute.return_value = {
1301 "portable_data_hash": "99999999999999999999999999999993+99"}
1303 if preemptible_hint is not None:
1305 "class": "http://arvados.org/cwl#UsePreemptible",
1306 "usePreemptible": preemptible_hint
1314 "baseCommand": "ls",
1315 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1317 "class": "CommandLineTool",
1318 "cwlVersion": "v1.2",
1322 loadingContext, runtimeContext = self.helper(runner)
1324 runtimeContext.name = 'test_run_enable_preemptible_'+str(enable_preemptible)+str(preemptible_hint)
1325 runtimeContext.enable_preemptible = enable_preemptible
1327 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1328 arvtool.formatgraph = None
1330 # Test the interactions between --enable/disable-preemptible
1331 # and UsePreemptible hint
1333 if enable_preemptible is None:
1334 if preemptible_hint is None:
1337 sched = {'preemptible': preemptible_hint}
1339 if preemptible_hint is None:
1340 sched = {'preemptible': enable_preemptible}
1342 sched = {'preemptible': enable_preemptible and preemptible_hint}
1344 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1345 j.run(runtimeContext)
1346 runner.api.container_requests().create.assert_called_with(
1347 body=JsonDiffMatcher({
1349 'HOME': '/var/spool/cwl',
1352 'name': runtimeContext.name,
1353 'runtime_constraints': {
1357 'use_existing': True,
1360 '/tmp': {'kind': 'tmp',
1361 "capacity": 1073741824
1363 '/var/spool/cwl': {'kind': 'tmp',
1364 "capacity": 1073741824 }
1366 'state': 'Committed',
1367 'output_name': 'Output from step '+runtimeContext.name,
1368 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1369 'output_path': '/var/spool/cwl',
1371 'container_image': '99999999999999999999999999999993+99',
1372 'command': ['ls', '/var/spool/cwl'],
1373 'cwd': '/var/spool/cwl',
1374 'scheduling_parameters': sched,
1375 'properties': {'cwl_input': {}},
1376 'secret_mounts': {},
1377 'output_storage_classes': ["default"]
1381 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1382 def test_output_properties(self, keepdocker):
1383 arvados_cwl.add_arv_hints()
1384 for rev in ["20210628", "20220510"]:
1385 runner = mock.MagicMock()
1386 runner.ignore_docker_for_reuse = False
1387 runner.intermediate_output_ttl = 0
1388 runner.secret_store = cwltool.secrets.SecretStore()
1389 runner.api._rootDesc = {"revision": rev}
1391 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1392 runner.api.collections().get().execute.return_value = {
1393 "portable_data_hash": "99999999999999999999999999999993+99"}
1401 "baseCommand": "ls",
1402 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1404 "cwlVersion": "v1.2",
1405 "class": "CommandLineTool",
1408 "class": "http://arvados.org/cwl#OutputCollectionProperties",
1409 "outputProperties": {
1411 "baz": "$(inputs.inp)"
1417 loadingContext, runtimeContext = self.helper(runner)
1418 runtimeContext.name = "test_timelimit"
1420 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1421 arvtool.formatgraph = None
1423 for j in arvtool.job({"inp": "quux"}, mock.MagicMock(), runtimeContext):
1424 j.run(runtimeContext)
1426 _, kwargs = runner.api.container_requests().create.call_args
1427 if rev == "20220510":
1428 self.assertEqual({"foo": "bar", "baz": "quux"}, kwargs['body'].get('output_properties'))
1430 self.assertEqual(None, kwargs['body'].get('output_properties'))
1433 class TestWorkflow(unittest.TestCase):
1435 cwltool.process._names = set()
1436 arv_docker_clear_cache()
1438 def helper(self, runner, enable_reuse=True):
1439 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
1441 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
1442 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
1444 document_loader.fetcher_constructor = functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=make_fs_access(""))
1445 document_loader.fetcher = document_loader.fetcher_constructor(document_loader.cache, document_loader.session)
1446 document_loader.fetch_text = document_loader.fetcher.fetch_text
1447 document_loader.check_exists = document_loader.fetcher.check_exists
1449 loadingContext = arvados_cwl.context.ArvLoadingContext(
1450 {"avsc_names": avsc_names,
1452 "make_fs_access": make_fs_access,
1453 "loader": document_loader,
1454 "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
1455 "construct_tool_object": runner.arv_make_tool})
1456 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
1457 {"work_api": "containers",
1459 "name": "test_run_wf_"+str(enable_reuse),
1460 "make_fs_access": make_fs_access,
1462 "enable_reuse": enable_reuse,
1465 return loadingContext, runtimeContext
1467 # The test passes no builder.resources
1468 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1469 @mock.patch("arvados.collection.CollectionReader")
1470 @mock.patch("arvados.collection.Collection")
1471 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1472 def test_run(self, list_images_in_arv, mockcollection, mockcollectionreader):
1473 arvados_cwl.add_arv_hints()
1475 api = mock.MagicMock()
1476 api._rootDesc = get_rootDesc()
1478 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1479 self.assertEqual(runner.work_api, 'containers')
1481 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1482 runner.api.collections().get().execute.return_value = {"portable_data_hash": "99999999999999999999999999999993+99"}
1483 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1484 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1486 runner.api.containers().current().execute.return_value = {}
1488 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1489 runner.ignore_docker_for_reuse = False
1490 runner.num_retries = 0
1491 runner.secret_store = cwltool.secrets.SecretStore()
1493 loadingContext, runtimeContext = self.helper(runner)
1494 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1496 mockcollectionreader().exists.return_value = True
1498 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
1499 metadata["cwlVersion"] = tool["cwlVersion"]
1501 mockc = mock.MagicMock()
1502 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
1503 mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
1505 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1506 arvtool.formatgraph = None
1507 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1509 next(it).run(runtimeContext)
1510 next(it).run(runtimeContext)
1512 with open("tests/wf/scatter2_subwf.cwl") as f:
1513 subwf = StripYAMLComments(f.read()).rstrip()
1515 runner.api.container_requests().create.assert_called_with(
1516 body=JsonDiffMatcher({
1521 "--preserve-entire-environment",
1525 "container_image": "99999999999999999999999999999993+99",
1526 "cwd": "/var/spool/cwl",
1528 "HOME": "/var/spool/cwl",
1532 "/keep/99999999999999999999999999999999+118": {
1533 "kind": "collection",
1534 "portable_data_hash": "99999999999999999999999999999999+118"
1537 "capacity": 1073741824,
1541 "capacity": 1073741824,
1544 "/var/spool/cwl/cwl.input.yml": {
1545 "kind": "collection",
1546 "path": "cwl.input.yml",
1547 "portable_data_hash": "99999999999999999999999999999996+99"
1549 "/var/spool/cwl/workflow.cwl": {
1550 "kind": "collection",
1551 "path": "workflow.cwl",
1552 "portable_data_hash": "99999999999999999999999999999996+99"
1556 "path": "/var/spool/cwl/cwl.output.json"
1559 "name": "scatterstep",
1560 "output_name": "Output from step scatterstep",
1561 "output_path": "/var/spool/cwl",
1564 "properties": {'cwl_input': {
1566 "basename": "token.txt",
1568 "dirname": "/keep/99999999999999999999999999999999+118",
1569 "location": "keep:99999999999999999999999999999999+118/token.txt",
1571 "nameroot": "token",
1572 "path": "/keep/99999999999999999999999999999999+118/token.txt",
1577 "runtime_constraints": {
1581 "scheduling_parameters": {},
1582 "secret_mounts": {},
1583 "state": "Committed",
1584 "use_existing": True,
1585 'output_storage_classes': ["default"]
1587 mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
1588 mockc.open().__enter__().write.assert_has_calls([mock.call(
1591 "basename": "token.txt",
1593 "location": "/keep/99999999999999999999999999999999+118/token.txt",
1599 # The test passes no builder.resources
1600 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1601 @mock.patch("arvados.collection.CollectionReader")
1602 @mock.patch("arvados.collection.Collection")
1603 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1604 def test_overall_resource_singlecontainer(self, list_images_in_arv, mockcollection, mockcollectionreader):
1605 arvados_cwl.add_arv_hints()
1607 api = mock.MagicMock()
1608 api._rootDesc = get_rootDesc()
1610 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1611 self.assertEqual(runner.work_api, 'containers')
1613 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1614 runner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1615 "portable_data_hash": "99999999999999999999999999999993+99"}
1616 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1617 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1619 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1620 runner.ignore_docker_for_reuse = False
1621 runner.num_retries = 0
1622 runner.secret_store = cwltool.secrets.SecretStore()
1624 loadingContext, runtimeContext = self.helper(runner)
1625 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1626 loadingContext.do_update = True
1627 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
1629 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
1631 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1632 arvtool.formatgraph = None
1633 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1635 next(it).run(runtimeContext)
1636 next(it).run(runtimeContext)
1638 with open("tests/wf/echo-subwf.cwl") as f:
1639 subwf = StripYAMLComments(f.read())
1641 runner.api.container_requests().create.assert_called_with(
1642 body=JsonDiffMatcher({
1644 'environment': {'HOME': '/var/spool/cwl', 'TMPDIR': '/tmp'},
1645 'scheduling_parameters': {},
1646 'name': u'echo-subwf',
1647 'secret_mounts': {},
1648 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
1649 'properties': {'cwl_input': {}},
1652 '/var/spool/cwl/cwl.input.yml': {
1653 'portable_data_hash': '99999999999999999999999999999996+99',
1654 'kind': 'collection',
1655 'path': 'cwl.input.yml'
1657 '/var/spool/cwl/workflow.cwl': {
1658 'portable_data_hash': '99999999999999999999999999999996+99',
1659 'kind': 'collection',
1660 'path': 'workflow.cwl'
1663 'path': '/var/spool/cwl/cwl.output.json',
1668 'capacity': 1073741824
1669 }, '/var/spool/cwl': {
1671 'capacity': 3221225472
1674 'state': 'Committed',
1675 'output_path': '/var/spool/cwl',
1676 'container_image': '99999999999999999999999999999993+99',
1681 u'--preserve-entire-environment',
1685 'use_existing': True,
1686 'output_name': u'Output from step echo-subwf',
1687 'cwd': '/var/spool/cwl',
1688 'output_storage_classes': ["default"]
1691 def test_default_work_api(self):
1692 arvados_cwl.add_arv_hints()
1694 api = mock.MagicMock()
1695 api._rootDesc = copy.deepcopy(get_rootDesc())
1696 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1697 self.assertEqual(runner.work_api, 'containers')