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': {},
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']
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': {
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': {},
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
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"
550 self.assertFalse(api.collections().create.called)
551 self.assertFalse(runner.runtime_status_error.called)
553 arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
554 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
555 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
557 # Test to make sure we dont call runtime_status_update if we already did
558 # some where higher up in the call stack
559 @mock.patch("arvados_cwl.util.get_current_container")
560 def test_recursive_runtime_status_update(self, gcc_mock):
561 self.setup_and_test_container_executor_and_logging(gcc_mock)
562 root_logger = logging.getLogger('')
564 # get_current_container is invoked when we call runtime_status_update
565 # so try and log again!
566 gcc_mock.side_effect = lambda *args: root_logger.error("Second Error")
568 root_logger.error("First Error")
570 self.fail("RuntimeStatusLoggingHandler should not be called recursively")
573 # Test to make sure that an exception raised from
574 # get_current_container doesn't cause the logger to raise an
576 @mock.patch("arvados_cwl.util.get_current_container")
577 def test_runtime_status_get_current_container_exception(self, gcc_mock):
578 self.setup_and_test_container_executor_and_logging(gcc_mock)
579 root_logger = logging.getLogger('')
581 # get_current_container is invoked when we call
582 # runtime_status_update, it is going to also raise an
584 gcc_mock.side_effect = Exception("Second Error")
586 root_logger.error("First Error")
588 self.fail("Exception in logger should not propagate")
589 self.assertTrue(gcc_mock.called)
591 @mock.patch("arvados_cwl.ArvCwlExecutor.runtime_status_update")
592 @mock.patch("arvados_cwl.util.get_current_container")
593 @mock.patch("arvados.collection.CollectionReader")
594 @mock.patch("arvados.collection.Collection")
595 def test_child_failure(self, col, reader, gcc_mock, rts_mock):
596 runner = self.setup_and_test_container_executor_and_logging(gcc_mock)
598 gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"}
599 self.assertTrue(gcc_mock.called)
601 runner.num_retries = 0
602 runner.ignore_docker_for_reuse = False
603 runner.intermediate_output_ttl = 0
604 runner.secret_store = cwltool.secrets.SecretStore()
605 runner.label = mock.MagicMock()
606 runner.label.return_value = '[container testjob]'
608 runner.api.containers().get().execute.return_value = {
615 col().open.return_value = []
617 loadingContext, runtimeContext = self.helper(runner)
619 arvjob = arvados_cwl.ArvadosContainer(runner,
627 arvjob.output_callback = mock.MagicMock()
628 arvjob.collect_outputs = mock.MagicMock()
629 arvjob.successCodes = [0]
630 arvjob.outdir = "/var/spool/cwl"
631 arvjob.output_ttl = 3600
632 arvjob.collect_outputs.return_value = {"out": "stuff"}
636 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
637 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
638 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
639 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
640 "modified_at": "2017-05-26T12:01:22Z"
643 rts_mock.assert_called_with(
645 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
646 ' ** log is empty **'
648 arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
650 # The test passes no builder.resources
651 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
652 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
653 def test_mounts(self, keepdocker):
654 runner = mock.MagicMock()
655 runner.ignore_docker_for_reuse = False
656 runner.intermediate_output_ttl = 0
657 runner.secret_store = cwltool.secrets.SecretStore()
658 runner.api._rootDesc = {"revision": "20210628"}
660 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
661 runner.api.collections().get().execute.return_value = {
662 "portable_data_hash": "99999999999999999999999999999994+99",
663 "manifest_text": ". 99999999999999999999999999999994+99 0:0:file1 0:0:file2"}
665 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
674 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
676 "class": "CommandLineTool",
680 loadingContext, runtimeContext = self.helper(runner)
681 runtimeContext.name = "test_run_mounts"
683 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
684 arvtool.formatgraph = None
687 "class": "Directory",
688 "location": "keep:99999999999999999999999999999994+44",
689 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
693 "location": "keep:99999999999999999999999999999994+44/file1",
697 "location": "keep:99999999999999999999999999999994+44/file2",
702 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
703 j.run(runtimeContext)
704 runner.api.container_requests().create.assert_called_with(
705 body=JsonDiffMatcher({
707 'HOME': '/var/spool/cwl',
710 'name': 'test_run_mounts',
711 'runtime_constraints': {
715 'use_existing': True,
718 "/keep/99999999999999999999999999999994+44": {
719 "kind": "collection",
720 "portable_data_hash": "99999999999999999999999999999994+44",
721 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz"
723 '/tmp': {'kind': 'tmp',
724 "capacity": 1073741824 },
725 '/var/spool/cwl': {'kind': 'tmp',
726 "capacity": 1073741824 }
728 'state': 'Committed',
729 'output_name': 'Output from step test_run_mounts',
730 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
731 'output_path': '/var/spool/cwl',
733 'container_image': '99999999999999999999999999999994+99',
734 'command': ['ls', '/var/spool/cwl'],
735 'cwd': '/var/spool/cwl',
736 'scheduling_parameters': {},
739 'output_storage_classes': ["default"]
742 # The test passes no builder.resources
743 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
744 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
745 def test_secrets(self, keepdocker):
746 arvados_cwl.add_arv_hints()
747 runner = mock.MagicMock()
748 runner.ignore_docker_for_reuse = False
749 runner.intermediate_output_ttl = 0
750 runner.secret_store = cwltool.secrets.SecretStore()
751 runner.api._rootDesc = {"revision": "20210628"}
753 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
754 runner.api.collections().get().execute.return_value = {
755 "portable_data_hash": "99999999999999999999999999999993+99"}
757 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
759 tool = cmap({"arguments": ["md5sum", "example.conf"],
760 "class": "CommandLineTool",
761 "cwlVersion": "v1.2",
764 "class": "http://commonwl.org/cwltool#Secrets",
773 "id": "#secret_job.cwl/pw",
781 "class": "InitialWorkDirRequirement",
784 "entry": "username: user\npassword: $(inputs.pw)\n",
785 "entryname": "example.conf"
791 loadingContext, runtimeContext = self.helper(runner)
792 runtimeContext.name = "test_secrets"
794 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
795 arvtool.formatgraph = None
797 job_order = {"pw": "blorp"}
798 runner.secret_store.store(["pw"], job_order)
800 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
801 j.run(runtimeContext)
802 runner.api.container_requests().create.assert_called_with(
803 body=JsonDiffMatcher({
805 'HOME': '/var/spool/cwl',
808 'name': 'test_secrets',
809 'runtime_constraints': {
813 'use_existing': True,
816 '/tmp': {'kind': 'tmp',
817 "capacity": 1073741824
819 '/var/spool/cwl': {'kind': 'tmp',
820 "capacity": 1073741824 }
822 'state': 'Committed',
823 'output_name': 'Output from step test_secrets',
824 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
825 'output_path': '/var/spool/cwl',
827 'container_image': '99999999999999999999999999999993+99',
828 'command': ['md5sum', 'example.conf'],
829 'cwd': '/var/spool/cwl',
830 'scheduling_parameters': {},
833 "/var/spool/cwl/example.conf": {
834 "content": "username: user\npassword: blorp\n",
838 'output_storage_classes': ["default"]
841 # The test passes no builder.resources
842 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
843 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
844 def test_timelimit(self, keepdocker):
845 runner = mock.MagicMock()
846 runner.ignore_docker_for_reuse = False
847 runner.intermediate_output_ttl = 0
848 runner.secret_store = cwltool.secrets.SecretStore()
849 runner.api._rootDesc = {"revision": "20210628"}
851 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
852 runner.api.collections().get().execute.return_value = {
853 "portable_data_hash": "99999999999999999999999999999993+99"}
859 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
861 "cwlVersion": "v1.2",
862 "class": "CommandLineTool",
865 "class": "ToolTimeLimit",
871 loadingContext, runtimeContext = self.helper(runner)
872 runtimeContext.name = "test_timelimit"
874 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
875 arvtool.formatgraph = None
877 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
878 j.run(runtimeContext)
880 _, kwargs = runner.api.container_requests().create.call_args
881 self.assertEqual(42, kwargs['body']['scheduling_parameters'].get('max_run_time'))
884 # The test passes no builder.resources
885 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
886 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
887 def test_setting_storage_class(self, keepdocker):
888 arv_docker_clear_cache()
890 runner = mock.MagicMock()
891 runner.ignore_docker_for_reuse = False
892 runner.intermediate_output_ttl = 0
893 runner.secret_store = cwltool.secrets.SecretStore()
894 runner.api._rootDesc = {"revision": "20210628"}
896 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
897 runner.api.collections().get().execute.return_value = {
898 "portable_data_hash": "99999999999999999999999999999993+99"}
904 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
906 "cwlVersion": "v1.2",
907 "class": "CommandLineTool",
910 "class": "http://arvados.org/cwl#OutputStorageClass",
911 "finalStorageClass": ["baz_sc", "qux_sc"],
912 "intermediateStorageClass": ["foo_sc", "bar_sc"]
917 loadingContext, runtimeContext = self.helper(runner, True)
919 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
920 arvtool.formatgraph = None
922 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
923 j.run(runtimeContext)
924 runner.api.container_requests().create.assert_called_with(
925 body=JsonDiffMatcher({
927 'HOME': '/var/spool/cwl',
930 'name': 'test_run_True',
931 'runtime_constraints': {
935 'use_existing': True,
938 '/tmp': {'kind': 'tmp',
939 "capacity": 1073741824
941 '/var/spool/cwl': {'kind': 'tmp',
942 "capacity": 1073741824 }
944 'state': 'Committed',
945 'output_name': 'Output from step test_run_True',
946 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
947 'output_path': '/var/spool/cwl',
949 'container_image': '99999999999999999999999999999993+99',
950 'command': ['ls', '/var/spool/cwl'],
951 'cwd': '/var/spool/cwl',
952 'scheduling_parameters': {},
955 'output_storage_classes': ["foo_sc", "bar_sc"]
959 # The test passes no builder.resources
960 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
961 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
962 def test_setting_process_properties(self, keepdocker):
963 arv_docker_clear_cache()
965 runner = mock.MagicMock()
966 runner.ignore_docker_for_reuse = False
967 runner.intermediate_output_ttl = 0
968 runner.secret_store = cwltool.secrets.SecretStore()
969 runner.api._rootDesc = {"revision": "20210628"}
971 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
972 runner.api.collections().get().execute.return_value = {
973 "portable_data_hash": "99999999999999999999999999999993+99"}
977 {"id": "x", "type": "string"}],
980 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
982 "class": "CommandLineTool",
983 "cwlVersion": "v1.2",
986 "class": "http://arvados.org/cwl#ProcessProperties",
987 "processProperties": [
988 {"propertyName": "foo",
989 "propertyValue": "bar"},
990 {"propertyName": "baz",
991 "propertyValue": "$(inputs.x)"},
992 {"propertyName": "quux",
1003 loadingContext, runtimeContext = self.helper(runner, True)
1005 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1006 arvtool.formatgraph = None
1008 for j in arvtool.job({"x": "blorp"}, mock.MagicMock(), runtimeContext):
1009 j.run(runtimeContext)
1010 runner.api.container_requests().create.assert_called_with(
1011 body=JsonDiffMatcher({
1013 'HOME': '/var/spool/cwl',
1016 'name': 'test_run_True',
1017 'runtime_constraints': {
1021 'use_existing': True,
1024 '/tmp': {'kind': 'tmp',
1025 "capacity": 1073741824
1027 '/var/spool/cwl': {'kind': 'tmp',
1028 "capacity": 1073741824 }
1030 'state': 'Committed',
1031 'output_name': 'Output from step test_run_True',
1032 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1033 'output_path': '/var/spool/cwl',
1035 'container_image': '99999999999999999999999999999993+99',
1036 'command': ['ls', '/var/spool/cwl'],
1037 'cwd': '/var/spool/cwl',
1038 'scheduling_parameters': {},
1047 'secret_mounts': {},
1048 'output_storage_classes': ["default"]
1052 # The test passes no builder.resources
1053 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1054 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1055 def test_cuda_requirement(self, keepdocker):
1056 arvados_cwl.add_arv_hints()
1057 arv_docker_clear_cache()
1059 runner = mock.MagicMock()
1060 runner.ignore_docker_for_reuse = False
1061 runner.intermediate_output_ttl = 0
1062 runner.secret_store = cwltool.secrets.SecretStore()
1063 runner.api._rootDesc = {"revision": "20210628"}
1065 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1066 runner.api.collections().get().execute.return_value = {
1067 "portable_data_hash": "99999999999999999999999999999993+99"}
1070 "class": "http://commonwl.org/cwltool#CUDARequirement",
1071 "cudaVersionMin": "11.0",
1072 "cudaComputeCapability": "9.0",
1074 "class": "http://commonwl.org/cwltool#CUDARequirement",
1075 "cudaVersionMin": "11.0",
1076 "cudaComputeCapability": "9.0",
1077 "cudaDeviceCountMin": 2
1079 "class": "http://commonwl.org/cwltool#CUDARequirement",
1080 "cudaVersionMin": "11.0",
1081 "cudaComputeCapability": ["4.0", "5.0"],
1082 "cudaDeviceCountMin": 2
1087 'driver_version': "11.0",
1088 'hardware_capability': "9.0"
1091 'driver_version': "11.0",
1092 'hardware_capability': "9.0"
1095 'driver_version': "11.0",
1096 'hardware_capability': "4.0"
1099 for test_case in range(0, len(test_cwl_req)):
1104 "baseCommand": "nvidia-smi",
1107 "cwlVersion": "v1.2",
1108 "class": "CommandLineTool",
1109 "requirements": [test_cwl_req[test_case]]
1112 loadingContext, runtimeContext = self.helper(runner, True)
1114 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1115 arvtool.formatgraph = None
1117 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1118 j.run(runtimeContext)
1119 runner.api.container_requests().create.assert_called_with(
1120 body=JsonDiffMatcher({
1122 'HOME': '/var/spool/cwl',
1125 'name': 'test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1126 'runtime_constraints': {
1129 'cuda': test_arv_req[test_case]
1131 'use_existing': True,
1134 '/tmp': {'kind': 'tmp',
1135 "capacity": 1073741824
1137 '/var/spool/cwl': {'kind': 'tmp',
1138 "capacity": 1073741824 }
1140 'state': 'Committed',
1141 'output_name': 'Output from step test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1142 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1143 'output_path': '/var/spool/cwl',
1145 'container_image': '99999999999999999999999999999993+99',
1146 'command': ['nvidia-smi'],
1147 'cwd': '/var/spool/cwl',
1148 'scheduling_parameters': {},
1150 'secret_mounts': {},
1151 'output_storage_classes': ["default"]
1155 # The test passes no builder.resources
1156 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1157 @mock.patch("arvados_cwl.arvdocker.determine_image_id")
1158 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1159 def test_match_local_docker(self, keepdocker, determine_image_id):
1160 arvados_cwl.add_arv_hints()
1161 arv_docker_clear_cache()
1163 runner = mock.MagicMock()
1164 runner.ignore_docker_for_reuse = False
1165 runner.intermediate_output_ttl = 0
1166 runner.secret_store = cwltool.secrets.SecretStore()
1167 runner.api._rootDesc = {"revision": "20210628"}
1169 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
1170 ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
1171 determine_image_id.side_effect = lambda x: "123"
1173 ex = mock.MagicMock()
1174 lookup = {"zzzzz-4zz18-zzzzzzzzzzzzzz4": {"portable_data_hash": "99999999999999999999999999999994+99"},
1175 "zzzzz-4zz18-zzzzzzzzzzzzzz3": {"portable_data_hash": "99999999999999999999999999999993+99"}}
1176 ex.execute.return_value = lookup[uuid]
1178 runner.api.collections().get.side_effect = execute
1183 "baseCommand": "echo",
1186 "cwlVersion": "v1.0",
1187 "class": "org.w3id.cwl.cwl.CommandLineTool"
1190 loadingContext, runtimeContext = self.helper(runner, True)
1192 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, loadingContext)
1193 arvtool.formatgraph = None
1195 container_request = {
1197 'HOME': '/var/spool/cwl',
1200 'name': 'test_run_True',
1201 'runtime_constraints': {
1205 'use_existing': True,
1208 '/tmp': {'kind': 'tmp',
1209 "capacity": 1073741824
1211 '/var/spool/cwl': {'kind': 'tmp',
1212 "capacity": 1073741824 }
1214 'state': 'Committed',
1215 'output_name': 'Output from step test_run_True',
1216 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1217 'output_path': '/var/spool/cwl',
1219 'container_image': '99999999999999999999999999999994+99',
1220 'command': ['echo'],
1221 'cwd': '/var/spool/cwl',
1222 'scheduling_parameters': {},
1224 'secret_mounts': {},
1225 'output_storage_classes': ["default"]
1228 runtimeContext.match_local_docker = False
1229 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1230 j.run(runtimeContext)
1231 runner.api.container_requests().create.assert_called_with(
1232 body=JsonDiffMatcher(container_request))
1234 arv_docker_clear_cache()
1235 runtimeContext.match_local_docker = True
1236 container_request['container_image'] = '99999999999999999999999999999993+99'
1237 container_request['name'] = 'test_run_True_2'
1238 container_request['output_name'] = 'Output from step test_run_True_2'
1239 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1240 j.run(runtimeContext)
1241 runner.api.container_requests().create.assert_called_with(
1242 body=JsonDiffMatcher(container_request))
1245 # The test passes no builder.resources
1246 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1247 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1248 def test_run_preemptible_hint(self, keepdocker):
1249 arvados_cwl.add_arv_hints()
1250 for enable_preemptible in (None, True, False):
1251 for preemptible_hint in (None, True, False):
1252 arv_docker_clear_cache()
1254 runner = mock.MagicMock()
1255 runner.ignore_docker_for_reuse = False
1256 runner.intermediate_output_ttl = 0
1257 runner.secret_store = cwltool.secrets.SecretStore()
1258 runner.api._rootDesc = {"revision": "20210628"}
1260 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1261 runner.api.collections().get().execute.return_value = {
1262 "portable_data_hash": "99999999999999999999999999999993+99"}
1264 if preemptible_hint is not None:
1266 "class": "http://arvados.org/cwl#UsePreemptible",
1267 "usePreemptible": preemptible_hint
1275 "baseCommand": "ls",
1276 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1278 "class": "CommandLineTool",
1279 "cwlVersion": "v1.2",
1283 loadingContext, runtimeContext = self.helper(runner)
1285 runtimeContext.name = 'test_run_enable_preemptible_'+str(enable_preemptible)+str(preemptible_hint)
1286 runtimeContext.enable_preemptible = enable_preemptible
1288 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1289 arvtool.formatgraph = None
1291 # Test the interactions between --enable/disable-preemptible
1292 # and UsePreemptible hint
1294 if enable_preemptible is None:
1295 if preemptible_hint is None:
1298 sched = {'preemptible': preemptible_hint}
1300 if preemptible_hint is None:
1301 sched = {'preemptible': enable_preemptible}
1303 sched = {'preemptible': enable_preemptible and preemptible_hint}
1305 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1306 j.run(runtimeContext)
1307 runner.api.container_requests().create.assert_called_with(
1308 body=JsonDiffMatcher({
1310 'HOME': '/var/spool/cwl',
1313 'name': runtimeContext.name,
1314 'runtime_constraints': {
1318 'use_existing': True,
1321 '/tmp': {'kind': 'tmp',
1322 "capacity": 1073741824
1324 '/var/spool/cwl': {'kind': 'tmp',
1325 "capacity": 1073741824 }
1327 'state': 'Committed',
1328 'output_name': 'Output from step '+runtimeContext.name,
1329 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1330 'output_path': '/var/spool/cwl',
1332 'container_image': '99999999999999999999999999999993+99',
1333 'command': ['ls', '/var/spool/cwl'],
1334 'cwd': '/var/spool/cwl',
1335 'scheduling_parameters': sched,
1337 'secret_mounts': {},
1338 'output_storage_classes': ["default"]
1342 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1343 def test_output_properties(self, keepdocker):
1344 arvados_cwl.add_arv_hints()
1345 for rev in ["20210628", "20220510"]:
1346 runner = mock.MagicMock()
1347 runner.ignore_docker_for_reuse = False
1348 runner.intermediate_output_ttl = 0
1349 runner.secret_store = cwltool.secrets.SecretStore()
1350 runner.api._rootDesc = {"revision": rev}
1352 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1353 runner.api.collections().get().execute.return_value = {
1354 "portable_data_hash": "99999999999999999999999999999993+99"}
1362 "baseCommand": "ls",
1363 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1365 "cwlVersion": "v1.2",
1366 "class": "CommandLineTool",
1369 "class": "http://arvados.org/cwl#OutputCollectionProperties",
1370 "outputProperties": {
1372 "baz": "$(inputs.inp)"
1378 loadingContext, runtimeContext = self.helper(runner)
1379 runtimeContext.name = "test_timelimit"
1381 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1382 arvtool.formatgraph = None
1384 for j in arvtool.job({"inp": "quux"}, mock.MagicMock(), runtimeContext):
1385 j.run(runtimeContext)
1387 _, kwargs = runner.api.container_requests().create.call_args
1388 if rev == "20220510":
1389 self.assertEqual({"foo": "bar", "baz": "quux"}, kwargs['body'].get('output_properties'))
1391 self.assertEqual(None, kwargs['body'].get('output_properties'))
1394 class TestWorkflow(unittest.TestCase):
1396 cwltool.process._names = set()
1397 arv_docker_clear_cache()
1399 def helper(self, runner, enable_reuse=True):
1400 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
1402 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
1403 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
1405 document_loader.fetcher_constructor = functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=make_fs_access(""))
1406 document_loader.fetcher = document_loader.fetcher_constructor(document_loader.cache, document_loader.session)
1407 document_loader.fetch_text = document_loader.fetcher.fetch_text
1408 document_loader.check_exists = document_loader.fetcher.check_exists
1410 loadingContext = arvados_cwl.context.ArvLoadingContext(
1411 {"avsc_names": avsc_names,
1413 "make_fs_access": make_fs_access,
1414 "loader": document_loader,
1415 "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
1416 "construct_tool_object": runner.arv_make_tool})
1417 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
1418 {"work_api": "containers",
1420 "name": "test_run_wf_"+str(enable_reuse),
1421 "make_fs_access": make_fs_access,
1423 "enable_reuse": enable_reuse,
1426 return loadingContext, runtimeContext
1428 # The test passes no builder.resources
1429 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1430 @mock.patch("arvados.collection.CollectionReader")
1431 @mock.patch("arvados.collection.Collection")
1432 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1433 def test_run(self, list_images_in_arv, mockcollection, mockcollectionreader):
1434 arvados_cwl.add_arv_hints()
1436 api = mock.MagicMock()
1437 api._rootDesc = get_rootDesc()
1439 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1440 self.assertEqual(runner.work_api, 'containers')
1442 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1443 runner.api.collections().get().execute.return_value = {"portable_data_hash": "99999999999999999999999999999993+99"}
1444 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1445 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1447 runner.api.containers().current().execute.return_value = {}
1449 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1450 runner.ignore_docker_for_reuse = False
1451 runner.num_retries = 0
1452 runner.secret_store = cwltool.secrets.SecretStore()
1454 loadingContext, runtimeContext = self.helper(runner)
1455 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1457 mockcollectionreader().exists.return_value = True
1459 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
1460 metadata["cwlVersion"] = tool["cwlVersion"]
1462 mockc = mock.MagicMock()
1463 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
1464 mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
1466 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1467 arvtool.formatgraph = None
1468 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1470 next(it).run(runtimeContext)
1471 next(it).run(runtimeContext)
1473 with open("tests/wf/scatter2_subwf.cwl") as f:
1474 subwf = StripYAMLComments(f.read()).rstrip()
1476 runner.api.container_requests().create.assert_called_with(
1477 body=JsonDiffMatcher({
1482 "--preserve-entire-environment",
1486 "container_image": "99999999999999999999999999999993+99",
1487 "cwd": "/var/spool/cwl",
1489 "HOME": "/var/spool/cwl",
1493 "/keep/99999999999999999999999999999999+118": {
1494 "kind": "collection",
1495 "portable_data_hash": "99999999999999999999999999999999+118"
1498 "capacity": 1073741824,
1502 "capacity": 1073741824,
1505 "/var/spool/cwl/cwl.input.yml": {
1506 "kind": "collection",
1507 "path": "cwl.input.yml",
1508 "portable_data_hash": "99999999999999999999999999999996+99"
1510 "/var/spool/cwl/workflow.cwl": {
1511 "kind": "collection",
1512 "path": "workflow.cwl",
1513 "portable_data_hash": "99999999999999999999999999999996+99"
1517 "path": "/var/spool/cwl/cwl.output.json"
1520 "name": "scatterstep",
1521 "output_name": "Output from step scatterstep",
1522 "output_path": "/var/spool/cwl",
1526 "runtime_constraints": {
1530 "scheduling_parameters": {},
1531 "secret_mounts": {},
1532 "state": "Committed",
1533 "use_existing": True,
1534 'output_storage_classes': ["default"]
1536 mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
1537 mockc.open().__enter__().write.assert_has_calls([mock.call(
1540 "basename": "token.txt",
1542 "location": "/keep/99999999999999999999999999999999+118/token.txt",
1548 # The test passes no builder.resources
1549 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1550 @mock.patch("arvados.collection.CollectionReader")
1551 @mock.patch("arvados.collection.Collection")
1552 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1553 def test_overall_resource_singlecontainer(self, list_images_in_arv, mockcollection, mockcollectionreader):
1554 arvados_cwl.add_arv_hints()
1556 api = mock.MagicMock()
1557 api._rootDesc = get_rootDesc()
1559 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1560 self.assertEqual(runner.work_api, 'containers')
1562 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1563 runner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1564 "portable_data_hash": "99999999999999999999999999999993+99"}
1565 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1566 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1568 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1569 runner.ignore_docker_for_reuse = False
1570 runner.num_retries = 0
1571 runner.secret_store = cwltool.secrets.SecretStore()
1573 loadingContext, runtimeContext = self.helper(runner)
1574 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1575 loadingContext.do_update = True
1576 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
1578 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
1580 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1581 arvtool.formatgraph = None
1582 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1584 next(it).run(runtimeContext)
1585 next(it).run(runtimeContext)
1587 with open("tests/wf/echo-subwf.cwl") as f:
1588 subwf = StripYAMLComments(f.read())
1590 runner.api.container_requests().create.assert_called_with(
1591 body=JsonDiffMatcher({
1593 'environment': {'HOME': '/var/spool/cwl', 'TMPDIR': '/tmp'},
1594 'scheduling_parameters': {},
1595 'name': u'echo-subwf',
1596 'secret_mounts': {},
1597 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
1601 '/var/spool/cwl/cwl.input.yml': {
1602 'portable_data_hash': '99999999999999999999999999999996+99',
1603 'kind': 'collection',
1604 'path': 'cwl.input.yml'
1606 '/var/spool/cwl/workflow.cwl': {
1607 'portable_data_hash': '99999999999999999999999999999996+99',
1608 'kind': 'collection',
1609 'path': 'workflow.cwl'
1612 'path': '/var/spool/cwl/cwl.output.json',
1617 'capacity': 1073741824
1618 }, '/var/spool/cwl': {
1620 'capacity': 3221225472
1623 'state': 'Committed',
1624 'output_path': '/var/spool/cwl',
1625 'container_image': '99999999999999999999999999999993+99',
1630 u'--preserve-entire-environment',
1634 'use_existing': True,
1635 'output_name': u'Output from step echo-subwf',
1636 'cwd': '/var/spool/cwl',
1637 'output_storage_classes': ["default"]
1640 def test_default_work_api(self):
1641 arvados_cwl.add_arv_hints()
1643 api = mock.MagicMock()
1644 api._rootDesc = copy.deepcopy(get_rootDesc())
1645 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1646 self.assertEqual(runner.work_api, 'containers')