1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
6 import arvados_cwl.context
7 import arvados_cwl.util
8 #from arvados_cwl.arvdocker import arv_docker_clear_cache
16 import cwltool.process
17 import cwltool.secrets
18 import cwltool.load_tool
19 from cwltool.update import INTERNAL_VERSION
20 from schema_salad.ref_resolver import Loader
21 from schema_salad.sourceline import cmap
24 from unittest import mock
26 from .matcher import JsonDiffMatcher, StripYAMLComments
27 from .mock_discovery import get_rootDesc
29 if not os.getenv('ARVADOS_DEBUG'):
30 logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
31 logging.getLogger('arvados.arv-run').setLevel(logging.WARN)
33 class CollectionMock(object):
34 def __init__(self, vwdmock, *args, **kwargs):
35 self.vwdmock = vwdmock
38 def open(self, *args, **kwargs):
40 return self.vwdmock.open(*args, **kwargs)
42 def copy(self, *args, **kwargs):
44 self.vwdmock.copy(*args, **kwargs)
46 def save_new(self, *args, **kwargs):
52 def portable_data_hash(self):
54 return arvados.config.EMPTY_BLOCK_LOCATOR
56 return "99999999999999999999999999999996+99"
59 class TestContainer(unittest.TestCase):
62 cwltool.process._names = set()
63 #arv_docker_clear_cache()
66 root_logger = logging.getLogger('')
68 # Remove existing RuntimeStatusLoggingHandlers if they exist
69 handlers = [h for h in root_logger.handlers if not isinstance(h, arvados_cwl.executor.RuntimeStatusLoggingHandler)]
70 root_logger.handlers = handlers
72 def helper(self, runner, enable_reuse=True):
73 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
75 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
76 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
77 fs_access = mock.MagicMock()
78 fs_access.exists.return_value = True
80 loadingContext = arvados_cwl.context.ArvLoadingContext(
81 {"avsc_names": avsc_names,
83 "make_fs_access": make_fs_access,
84 "construct_tool_object": runner.arv_make_tool,
85 "fetcher_constructor": functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=fs_access),
87 "metadata": cmap({"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"}),
88 "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__
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"}
138 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
140 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
141 runner.api.collections().get().execute.return_value = {
142 "portable_data_hash": "99999999999999999999999999999993+99"}
148 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
150 "class": "CommandLineTool",
154 loadingContext, runtimeContext = self.helper(runner, enable_reuse)
156 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
157 arvtool.formatgraph = None
159 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
160 j.run(runtimeContext)
161 runner.api.container_requests().create.assert_called_with(
162 body=JsonDiffMatcher({
164 'HOME': '/var/spool/cwl',
167 'name': 'test_run_'+str(enable_reuse),
168 'runtime_constraints': {
172 'use_existing': enable_reuse,
175 '/tmp': {'kind': 'tmp',
176 "capacity": 1073741824
178 '/var/spool/cwl': {'kind': 'tmp',
179 "capacity": 1073741824 }
181 'state': 'Committed',
182 'output_name': 'Output from step test_run_'+str(enable_reuse),
183 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
184 'output_path': '/var/spool/cwl',
186 'container_image': '99999999999999999999999999999993+99',
187 'command': ['ls', '/var/spool/cwl'],
188 'cwd': '/var/spool/cwl',
189 'scheduling_parameters': {},
190 'properties': {'cwl_input': {}},
192 'output_storage_classes': ["default"]
195 # The test passes some fields in builder.resources
196 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
197 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
198 def test_resource_requirements(self, keepdocker):
199 arvados_cwl.add_arv_hints()
200 runner = mock.MagicMock()
201 runner.ignore_docker_for_reuse = False
202 runner.intermediate_output_ttl = 3600
203 runner.secret_store = cwltool.secrets.SecretStore()
204 runner.api._rootDesc = {"revision": "20210628"}
205 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
207 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
208 runner.api.collections().get().execute.return_value = {
209 "portable_data_hash": "99999999999999999999999999999993+99"}
215 "class": "ResourceRequirement",
221 "class": "http://arvados.org/cwl#RuntimeConstraints",
224 "class": "http://arvados.org/cwl#APIRequirement",
226 "class": "http://arvados.org/cwl#PartitionRequirement",
229 "class": "http://arvados.org/cwl#IntermediateOutput",
232 "class": "WorkReuse",
237 "class": "CommandLineTool",
241 loadingContext, runtimeContext = self.helper(runner)
242 runtimeContext.name = "test_resource_requirements"
244 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
245 arvtool.formatgraph = None
246 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
247 j.run(runtimeContext)
249 call_args, call_kwargs = runner.api.container_requests().create.call_args
251 call_body_expected = {
253 'HOME': '/var/spool/cwl',
256 'name': 'test_resource_requirements',
257 'runtime_constraints': {
260 'keep_cache_ram': 536870912,
263 'use_existing': False,
266 '/tmp': {'kind': 'tmp',
267 "capacity": 4194304000 },
268 '/var/spool/cwl': {'kind': 'tmp',
269 "capacity": 5242880000 }
271 'state': 'Committed',
272 'output_name': 'Output from step test_resource_requirements',
273 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
274 'output_path': '/var/spool/cwl',
276 'container_image': '99999999999999999999999999999993+99',
278 'cwd': '/var/spool/cwl',
279 'scheduling_parameters': {
280 'partitions': ['blurb']
282 'properties': {'cwl_input': {}},
284 'output_storage_classes': ["default"]
287 call_body = call_kwargs.get('body', None)
288 self.assertNotEqual(None, call_body)
289 for key in call_body:
290 self.assertEqual(call_body_expected.get(key), call_body.get(key))
293 # The test passes some fields in builder.resources
294 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
295 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
296 @mock.patch("arvados.collection.Collection")
297 def test_initial_work_dir(self, collection_mock, keepdocker):
298 runner = mock.MagicMock()
299 runner.ignore_docker_for_reuse = False
300 runner.intermediate_output_ttl = 0
301 runner.secret_store = cwltool.secrets.SecretStore()
302 runner.api._rootDesc = {"revision": "20210628"}
303 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
305 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
306 runner.api.collections().get().execute.return_value = {
307 "portable_data_hash": "99999999999999999999999999999993+99"}
309 sourcemock = mock.MagicMock()
310 def get_collection_mock(p):
312 return (sourcemock, p.split("/", 1)[1])
314 return (sourcemock, "")
315 runner.fs_access.get_collection.side_effect = get_collection_mock
317 vwdmock = mock.MagicMock()
318 collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs)
324 "class": "InitialWorkDirRequirement",
328 "location": "keep:99999999999999999999999999999995+99/bar"
331 "class": "Directory",
333 "location": "keep:99999999999999999999999999999995+99"
337 "basename": "filename",
338 "location": "keep:99999999999999999999999999999995+99/baz/filename"
341 "class": "Directory",
342 "basename": "subdir",
343 "location": "keep:99999999999999999999999999999995+99/subdir"
347 "class": "CommandLineTool",
348 "cwlVersion": "v1.2",
352 loadingContext, runtimeContext = self.helper(runner)
353 runtimeContext.name = "test_initial_work_dir"
355 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
357 arvtool.formatgraph = None
358 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
359 j.run(runtimeContext)
361 call_args, call_kwargs = runner.api.container_requests().create.call_args
363 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
364 vwdmock.copy.assert_has_calls([mock.call('.', 'foo2', source_collection=sourcemock)])
365 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
366 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
368 call_body_expected = {
370 'HOME': '/var/spool/cwl',
373 'name': 'test_initial_work_dir',
374 'runtime_constraints': {
378 'use_existing': True,
381 '/tmp': {'kind': 'tmp',
382 "capacity": 1073741824 },
383 '/var/spool/cwl': {'kind': 'tmp',
384 "capacity": 1073741824 },
385 '/var/spool/cwl/foo': {
386 'kind': 'collection',
388 'portable_data_hash': '99999999999999999999999999999996+99'
390 '/var/spool/cwl/foo2': {
391 'kind': 'collection',
393 'portable_data_hash': '99999999999999999999999999999996+99'
395 '/var/spool/cwl/filename': {
396 'kind': 'collection',
398 'portable_data_hash': '99999999999999999999999999999996+99'
400 '/var/spool/cwl/subdir': {
401 'kind': 'collection',
403 'portable_data_hash': '99999999999999999999999999999996+99'
406 'state': 'Committed',
407 'output_name': 'Output from step test_initial_work_dir',
408 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
409 'output_path': '/var/spool/cwl',
411 'container_image': '99999999999999999999999999999993+99',
413 'cwd': '/var/spool/cwl',
414 'scheduling_parameters': {
416 'properties': {'cwl_input': {}},
418 'output_storage_classes': ["default"]
421 call_body = call_kwargs.get('body', None)
422 self.assertNotEqual(None, call_body)
423 for key in call_body:
424 self.assertEqual(call_body_expected.get(key), call_body.get(key))
427 # Test redirecting stdin/stdout/stderr
428 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
429 def test_redirects(self, keepdocker):
430 runner = mock.MagicMock()
431 runner.ignore_docker_for_reuse = False
432 runner.intermediate_output_ttl = 0
433 runner.secret_store = cwltool.secrets.SecretStore()
434 runner.api._rootDesc = {"revision": "20210628"}
435 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
437 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
438 runner.api.collections().get().execute.return_value = {
439 "portable_data_hash": "99999999999999999999999999999993+99"}
441 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
447 "stdout": "stdout.txt",
448 "stderr": "stderr.txt",
449 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
450 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
452 "class": "CommandLineTool",
456 loadingContext, runtimeContext = self.helper(runner)
457 runtimeContext.name = "test_run_redirect"
459 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
460 arvtool.formatgraph = None
461 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
462 j.run(runtimeContext)
463 runner.api.container_requests().create.assert_called_with(
464 body=JsonDiffMatcher({
466 'HOME': '/var/spool/cwl',
469 'name': 'test_run_redirect',
470 'runtime_constraints': {
474 'use_existing': True,
477 '/tmp': {'kind': 'tmp',
478 "capacity": 1073741824 },
479 '/var/spool/cwl': {'kind': 'tmp',
480 "capacity": 1073741824 },
483 "path": "/var/spool/cwl/stderr.txt"
486 "kind": "collection",
488 "portable_data_hash": "99999999999999999999999999999996+99"
492 "path": "/var/spool/cwl/stdout.txt"
495 'state': 'Committed',
496 "output_name": "Output from step test_run_redirect",
497 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
498 'output_path': '/var/spool/cwl',
500 'container_image': '99999999999999999999999999999993+99',
501 'command': ['ls', '/var/spool/cwl'],
502 'cwd': '/var/spool/cwl',
503 'scheduling_parameters': {},
504 'properties': {'cwl_input': {}},
506 'output_storage_classes': ["default"]
509 @mock.patch("arvados.collection.Collection")
510 def test_done(self, col):
511 api = mock.MagicMock()
513 runner = mock.MagicMock()
515 runner.num_retries = 0
516 runner.ignore_docker_for_reuse = False
517 runner.intermediate_output_ttl = 0
518 runner.secret_store = cwltool.secrets.SecretStore()
520 runner.api.container_requests().get().execute.return_value = {"container_uuid":"zzzzz-xvhdp-zzzzzzzzzzzzzzz"}
522 runner.api.containers().get().execute.return_value = {"state":"Complete",
526 # Need to noop-out the close method otherwise it gets
527 # discarded when closed and we can't call getvalue() to check
529 class NoopCloseStringIO(io.StringIO):
533 usage_report = NoopCloseStringIO()
534 def colreader_action(name, mode):
535 nonlocal usage_report
536 if name == "node.json":
537 return io.StringIO("""{
538 "ProviderType": "c5.large",
541 "IncludedScratch": 8000000000000,
544 "Preemptible": false,
547 "HardwareCapability": "",
551 if name == 'crunchstat.txt':
552 return open("tests/container_request_9tee4-xvhdp-kk0ja1cl8b2kr1y-arv-mount.txt", "rt")
553 if name == 'arv-mount.txt':
554 return open("tests/container_request_9tee4-xvhdp-kk0ja1cl8b2kr1y-crunchstat.txt", "rt")
555 if name == 'usage_report.html':
559 col().open.side_effect = colreader_action
560 col().__iter__.return_value = ['node.json', 'crunchstat.txt', 'arv-mount.txt']
562 loadingContext, runtimeContext = self.helper(runner)
564 arvjob = arvados_cwl.ArvadosContainer(runner,
572 arvjob.output_callback = mock.MagicMock()
573 arvjob.collect_outputs = mock.MagicMock()
574 arvjob.successCodes = [0]
575 arvjob.outdir = "/var/spool/cwl"
576 arvjob.output_ttl = 3600
577 arvjob.uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzz1"
579 arvjob.collect_outputs.return_value = {"out": "stuff"}
583 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
584 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
585 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
586 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
587 "modified_at": "2017-05-26T12:01:22Z",
592 self.assertFalse(api.collections().create.called)
593 self.assertFalse(runner.runtime_status_error.called)
595 # Assert that something was written to the usage report
596 self.assertTrue(len(usage_report.getvalue()) > 0)
598 arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
599 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
600 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
602 runner.api.container_requests().update.assert_called_with(uuid="zzzzz-xvhdp-zzzzzzzzzzzzzz1",
603 body={'container_request': {'properties': {'cwl_output': {'out': 'stuff'}}}})
606 # Test to make sure we dont call runtime_status_update if we already did
607 # some where higher up in the call stack
608 @mock.patch("arvados_cwl.util.get_current_container")
609 def test_recursive_runtime_status_update(self, gcc_mock):
610 self.setup_and_test_container_executor_and_logging(gcc_mock)
611 root_logger = logging.getLogger('')
613 # get_current_container is invoked when we call runtime_status_update
614 # so try and log again!
615 gcc_mock.side_effect = lambda *args: root_logger.error("Second Error")
617 root_logger.error("First Error")
619 self.fail("RuntimeStatusLoggingHandler should not be called recursively")
622 # Test to make sure that an exception raised from
623 # get_current_container doesn't cause the logger to raise an
625 @mock.patch("arvados_cwl.util.get_current_container")
626 def test_runtime_status_get_current_container_exception(self, gcc_mock):
627 self.setup_and_test_container_executor_and_logging(gcc_mock)
628 root_logger = logging.getLogger('')
630 # get_current_container is invoked when we call
631 # runtime_status_update, it is going to also raise an
633 gcc_mock.side_effect = Exception("Second Error")
635 root_logger.error("First Error")
637 self.fail("Exception in logger should not propagate")
638 self.assertTrue(gcc_mock.called)
640 @mock.patch("arvados_cwl.ArvCwlExecutor.runtime_status_update")
641 @mock.patch("arvados_cwl.util.get_current_container")
642 @mock.patch("arvados.collection.CollectionReader")
643 @mock.patch("arvados.collection.Collection")
644 def test_child_failure(self, col, reader, gcc_mock, rts_mock):
645 runner = self.setup_and_test_container_executor_and_logging(gcc_mock)
647 gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"}
648 self.assertTrue(gcc_mock.called)
650 runner.num_retries = 0
651 runner.ignore_docker_for_reuse = False
652 runner.intermediate_output_ttl = 0
653 runner.secret_store = cwltool.secrets.SecretStore()
654 runner.label = mock.MagicMock()
655 runner.label.return_value = '[container testjob]'
657 runner.api.containers().get().execute.return_value = {
664 col().open.return_value = []
666 loadingContext, runtimeContext = self.helper(runner)
668 arvjob = arvados_cwl.ArvadosContainer(runner,
676 arvjob.output_callback = mock.MagicMock()
677 arvjob.collect_outputs = mock.MagicMock()
678 arvjob.successCodes = [0]
679 arvjob.outdir = "/var/spool/cwl"
680 arvjob.output_ttl = 3600
681 arvjob.collect_outputs.return_value = {"out": "stuff"}
685 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
686 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
687 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
688 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
689 "modified_at": "2017-05-26T12:01:22Z",
693 rts_mock.assert_has_calls([
695 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
696 ' ** log is empty **'
699 'arvados.cwl-runner: [container testjob] unable to generate resource usage report'
701 arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
703 # The test passes no builder.resources
704 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
705 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
706 def test_mounts(self, keepdocker):
707 runner = mock.MagicMock()
708 runner.ignore_docker_for_reuse = False
709 runner.intermediate_output_ttl = 0
710 runner.secret_store = cwltool.secrets.SecretStore()
711 runner.api._rootDesc = {"revision": "20210628"}
712 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
714 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
715 runner.api.collections().get().execute.return_value = {
716 "portable_data_hash": "99999999999999999999999999999994+99",
717 "manifest_text": ". 99999999999999999999999999999994+99 0:0:file1 0:0:file2"}
719 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
728 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
730 "class": "CommandLineTool",
734 loadingContext, runtimeContext = self.helper(runner)
735 runtimeContext.name = "test_run_mounts"
737 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
738 arvtool.formatgraph = None
741 "class": "Directory",
742 "location": "keep:99999999999999999999999999999994+44",
743 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
747 "location": "keep:99999999999999999999999999999994+44/file1",
751 "location": "keep:99999999999999999999999999999994+44/file2",
756 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
757 j.run(runtimeContext)
758 runner.api.container_requests().create.assert_called_with(
759 body=JsonDiffMatcher({
761 'HOME': '/var/spool/cwl',
764 'name': 'test_run_mounts',
765 'runtime_constraints': {
769 'use_existing': True,
772 "/keep/99999999999999999999999999999994+44": {
773 "kind": "collection",
774 "portable_data_hash": "99999999999999999999999999999994+44",
775 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz"
777 '/tmp': {'kind': 'tmp',
778 "capacity": 1073741824 },
779 '/var/spool/cwl': {'kind': 'tmp',
780 "capacity": 1073741824 }
782 'state': 'Committed',
783 'output_name': 'Output from step test_run_mounts',
784 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
785 'output_path': '/var/spool/cwl',
787 'container_image': '99999999999999999999999999999994+99',
788 'command': ['ls', '/var/spool/cwl'],
789 'cwd': '/var/spool/cwl',
790 'scheduling_parameters': {},
791 'properties': {'cwl_input': {
793 "basename": "99999999999999999999999999999994+44",
794 "class": "Directory",
796 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
801 "dirname": "/keep/99999999999999999999999999999994+44",
802 "location": "keep:99999999999999999999999999999994+44/file1",
805 "path": "/keep/99999999999999999999999999999994+44/file1",
811 "dirname": "/keep/99999999999999999999999999999994+44",
812 "location": "keep:99999999999999999999999999999994+44/file2",
815 "path": "/keep/99999999999999999999999999999994+44/file2",
819 "location": "keep:99999999999999999999999999999994+44",
820 "path": "/keep/99999999999999999999999999999994+44"
824 'output_storage_classes': ["default"]
827 # The test passes no builder.resources
828 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
829 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
830 def test_secrets(self, keepdocker):
831 arvados_cwl.add_arv_hints()
832 runner = mock.MagicMock()
833 runner.ignore_docker_for_reuse = False
834 runner.intermediate_output_ttl = 0
835 runner.secret_store = cwltool.secrets.SecretStore()
836 runner.api._rootDesc = {"revision": "20210628"}
837 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
839 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
840 runner.api.collections().get().execute.return_value = {
841 "portable_data_hash": "99999999999999999999999999999993+99"}
843 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
845 tool = cmap({"arguments": ["md5sum", "example.conf"],
846 "class": "CommandLineTool",
847 "cwlVersion": "v1.2",
850 "class": "http://commonwl.org/cwltool#Secrets",
859 "id": "#secret_job.cwl/pw",
867 "class": "InitialWorkDirRequirement",
870 "entry": "username: user\npassword: $(inputs.pw)\n",
871 "entryname": "example.conf"
877 loadingContext, runtimeContext = self.helper(runner)
878 runtimeContext.name = "test_secrets"
880 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
881 arvtool.formatgraph = None
883 job_order = {"pw": "blorp"}
884 runner.secret_store.store(["pw"], job_order)
886 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
887 j.run(runtimeContext)
888 runner.api.container_requests().create.assert_called_with(
889 body=JsonDiffMatcher({
891 'HOME': '/var/spool/cwl',
894 'name': 'test_secrets',
895 'runtime_constraints': {
899 'use_existing': True,
902 '/tmp': {'kind': 'tmp',
903 "capacity": 1073741824
905 '/var/spool/cwl': {'kind': 'tmp',
906 "capacity": 1073741824 }
908 'state': 'Committed',
909 'output_name': 'Output from step test_secrets',
910 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
911 'output_path': '/var/spool/cwl',
913 'container_image': '99999999999999999999999999999993+99',
914 'command': ['md5sum', 'example.conf'],
915 'cwd': '/var/spool/cwl',
916 'scheduling_parameters': {},
917 'properties': {'cwl_input': job_order},
919 "/var/spool/cwl/example.conf": {
920 "content": "username: user\npassword: blorp\n",
924 'output_storage_classes': ["default"]
927 # The test passes no builder.resources
928 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
929 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
930 def test_timelimit(self, keepdocker):
931 runner = mock.MagicMock()
932 runner.ignore_docker_for_reuse = False
933 runner.intermediate_output_ttl = 0
934 runner.secret_store = cwltool.secrets.SecretStore()
935 runner.api._rootDesc = {"revision": "20210628"}
936 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
938 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
939 runner.api.collections().get().execute.return_value = {
940 "portable_data_hash": "99999999999999999999999999999993+99"}
946 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
948 "cwlVersion": "v1.2",
949 "class": "CommandLineTool",
952 "class": "ToolTimeLimit",
958 loadingContext, runtimeContext = self.helper(runner)
959 runtimeContext.name = "test_timelimit"
961 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
962 arvtool.formatgraph = None
964 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
965 j.run(runtimeContext)
967 _, kwargs = runner.api.container_requests().create.call_args
968 self.assertEqual(42, kwargs['body']['scheduling_parameters'].get('max_run_time'))
971 # The test passes no builder.resources
972 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
973 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
974 def test_setting_storage_class(self, keepdocker):
975 #arv_docker_clear_cache()
977 runner = mock.MagicMock()
978 runner.ignore_docker_for_reuse = False
979 runner.intermediate_output_ttl = 0
980 runner.secret_store = cwltool.secrets.SecretStore()
981 runner.api._rootDesc = {"revision": "20210628"}
982 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
984 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
985 runner.api.collections().get().execute.return_value = {
986 "portable_data_hash": "99999999999999999999999999999993+99"}
992 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
994 "cwlVersion": "v1.2",
995 "class": "CommandLineTool",
998 "class": "http://arvados.org/cwl#OutputStorageClass",
999 "finalStorageClass": ["baz_sc", "qux_sc"],
1000 "intermediateStorageClass": ["foo_sc", "bar_sc"]
1005 loadingContext, runtimeContext = self.helper(runner, True)
1007 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1008 arvtool.formatgraph = None
1010 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1011 j.run(runtimeContext)
1012 runner.api.container_requests().create.assert_called_with(
1013 body=JsonDiffMatcher({
1015 'HOME': '/var/spool/cwl',
1018 'name': 'test_run_True',
1019 'runtime_constraints': {
1023 'use_existing': True,
1026 '/tmp': {'kind': 'tmp',
1027 "capacity": 1073741824
1029 '/var/spool/cwl': {'kind': 'tmp',
1030 "capacity": 1073741824 }
1032 'state': 'Committed',
1033 'output_name': 'Output from step test_run_True',
1034 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1035 'output_path': '/var/spool/cwl',
1037 'container_image': '99999999999999999999999999999993+99',
1038 'command': ['ls', '/var/spool/cwl'],
1039 'cwd': '/var/spool/cwl',
1040 'scheduling_parameters': {},
1041 'properties': {'cwl_input': {}},
1042 'secret_mounts': {},
1043 'output_storage_classes': ["foo_sc", "bar_sc"]
1047 # The test passes no builder.resources
1048 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1049 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1050 def test_setting_process_properties(self, keepdocker):
1051 #arv_docker_clear_cache()
1053 runner = mock.MagicMock()
1054 runner.ignore_docker_for_reuse = False
1055 runner.intermediate_output_ttl = 0
1056 runner.secret_store = cwltool.secrets.SecretStore()
1057 runner.api._rootDesc = {"revision": "20210628"}
1058 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1060 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1061 runner.api.collections().get().execute.return_value = {
1062 "portable_data_hash": "99999999999999999999999999999993+99"}
1066 {"id": "x", "type": "string"}],
1068 "baseCommand": "ls",
1069 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1071 "class": "CommandLineTool",
1072 "cwlVersion": "v1.2",
1075 "class": "http://arvados.org/cwl#ProcessProperties",
1076 "processProperties": [
1077 {"propertyName": "foo",
1078 "propertyValue": "bar"},
1079 {"propertyName": "baz",
1080 "propertyValue": "$(inputs.x)"},
1081 {"propertyName": "quux",
1092 loadingContext, runtimeContext = self.helper(runner, True)
1094 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1095 arvtool.formatgraph = None
1097 for j in arvtool.job({"x": "blorp"}, mock.MagicMock(), runtimeContext):
1098 j.run(runtimeContext)
1099 runner.api.container_requests().create.assert_called_with(
1100 body=JsonDiffMatcher({
1102 'HOME': '/var/spool/cwl',
1105 'name': 'test_run_True',
1106 'runtime_constraints': {
1110 'use_existing': True,
1113 '/tmp': {'kind': 'tmp',
1114 "capacity": 1073741824
1116 '/var/spool/cwl': {'kind': 'tmp',
1117 "capacity": 1073741824 }
1119 'state': 'Committed',
1120 'output_name': 'Output from step test_run_True',
1121 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1122 'output_path': '/var/spool/cwl',
1124 'container_image': '99999999999999999999999999999993+99',
1125 'command': ['ls', '/var/spool/cwl'],
1126 'cwd': '/var/spool/cwl',
1127 'scheduling_parameters': {},
1130 "cwl_input": {"x": "blorp"},
1137 'secret_mounts': {},
1138 'output_storage_classes': ["default"]
1142 # The test passes no builder.resources
1143 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1144 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1145 def test_cuda_requirement(self, keepdocker):
1146 arvados_cwl.add_arv_hints()
1147 #arv_docker_clear_cache()
1149 runner = mock.MagicMock()
1150 runner.ignore_docker_for_reuse = False
1151 runner.intermediate_output_ttl = 0
1152 runner.secret_store = cwltool.secrets.SecretStore()
1153 runner.api._rootDesc = {"revision": "20210628"}
1154 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1156 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1157 runner.api.collections().get().execute.return_value = {
1158 "portable_data_hash": "99999999999999999999999999999993+99"}
1161 "class": "http://commonwl.org/cwltool#CUDARequirement",
1162 "cudaVersionMin": "11.0",
1163 "cudaComputeCapability": "9.0",
1165 "class": "http://commonwl.org/cwltool#CUDARequirement",
1166 "cudaVersionMin": "11.0",
1167 "cudaComputeCapability": "9.0",
1168 "cudaDeviceCountMin": 2
1170 "class": "http://commonwl.org/cwltool#CUDARequirement",
1171 "cudaVersionMin": "11.0",
1172 "cudaComputeCapability": ["4.0", "5.0"],
1173 "cudaDeviceCountMin": 2
1178 'driver_version': "11.0",
1179 'hardware_capability': "9.0"
1182 'driver_version': "11.0",
1183 'hardware_capability': "9.0"
1186 'driver_version': "11.0",
1187 'hardware_capability': "4.0"
1190 for test_case in range(0, len(test_cwl_req)):
1195 "baseCommand": "nvidia-smi",
1198 "cwlVersion": "v1.2",
1199 "class": "CommandLineTool",
1200 "requirements": [test_cwl_req[test_case]]
1203 loadingContext, runtimeContext = self.helper(runner, True)
1205 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1206 arvtool.formatgraph = None
1208 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1209 j.run(runtimeContext)
1210 runner.api.container_requests().create.assert_called_with(
1211 body=JsonDiffMatcher({
1213 'HOME': '/var/spool/cwl',
1216 'name': 'test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1217 'runtime_constraints': {
1220 'cuda': test_arv_req[test_case]
1222 'use_existing': True,
1225 '/tmp': {'kind': 'tmp',
1226 "capacity": 1073741824
1228 '/var/spool/cwl': {'kind': 'tmp',
1229 "capacity": 1073741824 }
1231 'state': 'Committed',
1232 'output_name': 'Output from step test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1233 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1234 'output_path': '/var/spool/cwl',
1236 'container_image': '99999999999999999999999999999993+99',
1237 'command': ['nvidia-smi'],
1238 'cwd': '/var/spool/cwl',
1239 'scheduling_parameters': {},
1240 'properties': {'cwl_input': {}},
1241 'secret_mounts': {},
1242 'output_storage_classes': ["default"]
1246 # The test passes no builder.resources
1247 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1248 @mock.patch("arvados_cwl.arvdocker.determine_image_id")
1249 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1250 def test_match_local_docker(self, keepdocker, determine_image_id):
1251 arvados_cwl.add_arv_hints()
1253 runner = mock.MagicMock()
1254 runner.ignore_docker_for_reuse = False
1255 runner.intermediate_output_ttl = 0
1256 runner.secret_store = cwltool.secrets.SecretStore()
1257 runner.api._rootDesc = {"revision": "20210628"}
1258 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1260 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
1261 ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
1262 determine_image_id.side_effect = lambda x: "123"
1264 ex = mock.MagicMock()
1265 lookup = {"zzzzz-4zz18-zzzzzzzzzzzzzz4": {"portable_data_hash": "99999999999999999999999999999994+99"},
1266 "zzzzz-4zz18-zzzzzzzzzzzzzz3": {"portable_data_hash": "99999999999999999999999999999993+99"}}
1267 ex.execute.return_value = lookup[uuid]
1269 runner.api.collections().get.side_effect = execute
1274 "baseCommand": "echo",
1277 "cwlVersion": "v1.0",
1278 "class": "org.w3id.cwl.cwl.CommandLineTool"
1281 loadingContext, runtimeContext = self.helper(runner, True)
1283 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, loadingContext)
1284 arvtool.formatgraph = None
1286 container_request = {
1288 'HOME': '/var/spool/cwl',
1291 'name': 'test_run_True',
1292 'runtime_constraints': {
1296 'use_existing': True,
1299 '/tmp': {'kind': 'tmp',
1300 "capacity": 1073741824
1302 '/var/spool/cwl': {'kind': 'tmp',
1303 "capacity": 1073741824 }
1305 'state': 'Committed',
1306 'output_name': 'Output from step test_run_True',
1307 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1308 'output_path': '/var/spool/cwl',
1310 'container_image': '99999999999999999999999999999994+99',
1311 'command': ['echo'],
1312 'cwd': '/var/spool/cwl',
1313 'scheduling_parameters': {},
1314 'properties': {'cwl_input': {}},
1315 'secret_mounts': {},
1316 'output_storage_classes': ["default"]
1319 runtimeContext.match_local_docker = False
1320 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1321 j.run(runtimeContext)
1322 runner.api.container_requests().create.assert_called_with(
1323 body=JsonDiffMatcher(container_request))
1325 runtimeContext.cached_docker_lookups.clear()
1326 runtimeContext.match_local_docker = True
1327 container_request['container_image'] = '99999999999999999999999999999993+99'
1328 container_request['name'] = 'test_run_True_2'
1329 container_request['output_name'] = 'Output from step test_run_True_2'
1330 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1331 j.run(runtimeContext)
1332 runner.api.container_requests().create.assert_called_with(
1333 body=JsonDiffMatcher(container_request))
1336 # The test passes no builder.resources
1337 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1338 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1339 def test_run_preemptible_hint(self, keepdocker):
1340 arvados_cwl.add_arv_hints()
1341 for enable_preemptible in (None, True, False):
1342 for preemptible_hint in (None, True, False):
1343 #arv_docker_clear_cache()
1345 runner = mock.MagicMock()
1346 runner.ignore_docker_for_reuse = False
1347 runner.intermediate_output_ttl = 0
1348 runner.secret_store = cwltool.secrets.SecretStore()
1349 runner.api._rootDesc = {"revision": "20210628"}
1350 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1352 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1353 runner.api.collections().get().execute.return_value = {
1354 "portable_data_hash": "99999999999999999999999999999993+99"}
1356 if preemptible_hint is not None:
1358 "class": "http://arvados.org/cwl#UsePreemptible",
1359 "usePreemptible": preemptible_hint
1367 "baseCommand": "ls",
1368 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1370 "class": "CommandLineTool",
1371 "cwlVersion": "v1.2",
1375 loadingContext, runtimeContext = self.helper(runner)
1377 runtimeContext.name = 'test_run_enable_preemptible_'+str(enable_preemptible)+str(preemptible_hint)
1378 runtimeContext.enable_preemptible = enable_preemptible
1380 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1381 arvtool.formatgraph = None
1383 # Test the interactions between --enable/disable-preemptible
1384 # and UsePreemptible hint
1386 if enable_preemptible is None:
1387 if preemptible_hint is None:
1390 sched = {'preemptible': preemptible_hint}
1392 if preemptible_hint is None:
1393 sched = {'preemptible': enable_preemptible}
1395 sched = {'preemptible': enable_preemptible and preemptible_hint}
1397 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1398 j.run(runtimeContext)
1399 runner.api.container_requests().create.assert_called_with(
1400 body=JsonDiffMatcher({
1402 'HOME': '/var/spool/cwl',
1405 'name': runtimeContext.name,
1406 'runtime_constraints': {
1410 'use_existing': True,
1413 '/tmp': {'kind': 'tmp',
1414 "capacity": 1073741824
1416 '/var/spool/cwl': {'kind': 'tmp',
1417 "capacity": 1073741824 }
1419 'state': 'Committed',
1420 'output_name': 'Output from step '+runtimeContext.name,
1421 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1422 'output_path': '/var/spool/cwl',
1424 'container_image': '99999999999999999999999999999993+99',
1425 'command': ['ls', '/var/spool/cwl'],
1426 'cwd': '/var/spool/cwl',
1427 'scheduling_parameters': sched,
1428 'properties': {'cwl_input': {}},
1429 'secret_mounts': {},
1430 'output_storage_classes': ["default"]
1434 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1435 def test_output_properties(self, keepdocker):
1436 arvados_cwl.add_arv_hints()
1437 for rev in ["20210628", "20220510"]:
1438 runner = mock.MagicMock()
1439 runner.ignore_docker_for_reuse = False
1440 runner.intermediate_output_ttl = 0
1441 runner.secret_store = cwltool.secrets.SecretStore()
1442 runner.api._rootDesc = {"revision": rev}
1443 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1445 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1446 runner.api.collections().get().execute.return_value = {
1447 "portable_data_hash": "99999999999999999999999999999993+99"}
1455 "baseCommand": "ls",
1456 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1458 "cwlVersion": "v1.2",
1459 "class": "CommandLineTool",
1462 "class": "http://arvados.org/cwl#OutputCollectionProperties",
1463 "outputProperties": {
1465 "baz": "$(inputs.inp)"
1471 loadingContext, runtimeContext = self.helper(runner)
1472 runtimeContext.name = "test_timelimit"
1474 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1475 arvtool.formatgraph = None
1477 for j in arvtool.job({"inp": "quux"}, mock.MagicMock(), runtimeContext):
1478 j.run(runtimeContext)
1480 _, kwargs = runner.api.container_requests().create.call_args
1481 if rev == "20220510":
1482 self.assertEqual({"foo": "bar", "baz": "quux"}, kwargs['body'].get('output_properties'))
1484 self.assertEqual(None, kwargs['body'].get('output_properties'))
1487 class TestWorkflow(unittest.TestCase):
1489 cwltool.process._names = set()
1490 #arv_docker_clear_cache()
1492 def helper(self, runner, enable_reuse=True):
1493 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
1495 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
1496 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
1498 document_loader.fetcher_constructor = functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=make_fs_access(""))
1499 document_loader.fetcher = document_loader.fetcher_constructor(document_loader.cache, document_loader.session)
1500 document_loader.fetch_text = document_loader.fetcher.fetch_text
1501 document_loader.check_exists = document_loader.fetcher.check_exists
1503 loadingContext = arvados_cwl.context.ArvLoadingContext(
1504 {"avsc_names": avsc_names,
1506 "make_fs_access": make_fs_access,
1507 "loader": document_loader,
1508 "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
1509 "construct_tool_object": runner.arv_make_tool,
1510 "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__})
1511 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
1512 {"work_api": "containers",
1514 "name": "test_run_wf_"+str(enable_reuse),
1515 "make_fs_access": make_fs_access,
1517 "enable_reuse": enable_reuse,
1520 return loadingContext, runtimeContext
1522 # The test passes no builder.resources
1523 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1524 @mock.patch("arvados.collection.CollectionReader")
1525 @mock.patch("arvados.collection.Collection")
1526 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1527 def test_run(self, list_images_in_arv, mockcollection, mockcollectionreader):
1528 arvados_cwl.add_arv_hints()
1530 api = mock.MagicMock()
1531 api._rootDesc = get_rootDesc()
1532 api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1534 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1535 self.assertEqual(runner.work_api, 'containers')
1537 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1538 runner.api.collections().get().execute.return_value = {"portable_data_hash": "99999999999999999999999999999993+99"}
1539 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1540 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1542 runner.api.containers().current().execute.return_value = {}
1544 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1545 runner.ignore_docker_for_reuse = False
1546 runner.num_retries = 0
1547 runner.secret_store = cwltool.secrets.SecretStore()
1549 loadingContext, runtimeContext = self.helper(runner)
1550 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1552 mockcollectionreader().exists.return_value = True
1554 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
1555 metadata["cwlVersion"] = tool["cwlVersion"]
1557 mockc = mock.MagicMock()
1558 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
1559 mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
1561 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1562 arvtool.formatgraph = None
1563 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1565 next(it).run(runtimeContext)
1566 next(it).run(runtimeContext)
1568 with open("tests/wf/scatter2_subwf.cwl") as f:
1569 subwf = StripYAMLComments(f.read()).rstrip()
1571 runner.api.container_requests().create.assert_called_with(
1572 body=JsonDiffMatcher({
1577 "--preserve-entire-environment",
1581 "container_image": "99999999999999999999999999999993+99",
1582 "cwd": "/var/spool/cwl",
1584 "HOME": "/var/spool/cwl",
1588 "/keep/99999999999999999999999999999999+118": {
1589 "kind": "collection",
1590 "portable_data_hash": "99999999999999999999999999999999+118"
1593 "capacity": 1073741824,
1597 "capacity": 1073741824,
1600 "/var/spool/cwl/cwl.input.yml": {
1601 "kind": "collection",
1602 "path": "cwl.input.yml",
1603 "portable_data_hash": "99999999999999999999999999999996+99"
1605 "/var/spool/cwl/workflow.cwl": {
1606 "kind": "collection",
1607 "path": "workflow.cwl",
1608 "portable_data_hash": "99999999999999999999999999999996+99"
1612 "path": "/var/spool/cwl/cwl.output.json"
1615 "name": "scatterstep",
1616 "output_name": "Output from step scatterstep",
1617 "output_path": "/var/spool/cwl",
1620 "properties": {'cwl_input': {
1622 "basename": "token.txt",
1624 "dirname": "/keep/99999999999999999999999999999999+118",
1625 "location": "keep:99999999999999999999999999999999+118/token.txt",
1627 "nameroot": "token",
1628 "path": "/keep/99999999999999999999999999999999+118/token.txt",
1633 "runtime_constraints": {
1637 "scheduling_parameters": {},
1638 "secret_mounts": {},
1639 "state": "Committed",
1640 "use_existing": True,
1641 'output_storage_classes': ["default"]
1643 mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
1644 mockc.open().__enter__().write.assert_has_calls([mock.call(
1647 "basename": "token.txt",
1649 "location": "/keep/99999999999999999999999999999999+118/token.txt",
1655 # The test passes no builder.resources
1656 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1657 @mock.patch("arvados.collection.CollectionReader")
1658 @mock.patch("arvados.collection.Collection")
1659 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1660 def test_overall_resource_singlecontainer(self, list_images_in_arv, mockcollection, mockcollectionreader):
1661 arvados_cwl.add_arv_hints()
1663 api = mock.MagicMock()
1664 api._rootDesc = get_rootDesc()
1665 api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1667 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1668 self.assertEqual(runner.work_api, 'containers')
1670 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1671 runner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1672 "portable_data_hash": "99999999999999999999999999999993+99"}
1673 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1674 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1676 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1677 runner.ignore_docker_for_reuse = False
1678 runner.num_retries = 0
1679 runner.secret_store = cwltool.secrets.SecretStore()
1681 loadingContext, runtimeContext = self.helper(runner)
1682 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1683 loadingContext.do_update = True
1684 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
1686 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
1688 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1689 arvtool.formatgraph = None
1690 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1692 next(it).run(runtimeContext)
1693 next(it).run(runtimeContext)
1695 with open("tests/wf/echo-subwf.cwl") as f:
1696 subwf = StripYAMLComments(f.read())
1698 runner.api.container_requests().create.assert_called_with(
1699 body=JsonDiffMatcher({
1701 'environment': {'HOME': '/var/spool/cwl', 'TMPDIR': '/tmp'},
1702 'scheduling_parameters': {},
1703 'name': u'echo-subwf',
1704 'secret_mounts': {},
1705 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
1706 'properties': {'cwl_input': {}},
1709 '/var/spool/cwl/cwl.input.yml': {
1710 'portable_data_hash': '99999999999999999999999999999996+99',
1711 'kind': 'collection',
1712 'path': 'cwl.input.yml'
1714 '/var/spool/cwl/workflow.cwl': {
1715 'portable_data_hash': '99999999999999999999999999999996+99',
1716 'kind': 'collection',
1717 'path': 'workflow.cwl'
1720 'path': '/var/spool/cwl/cwl.output.json',
1725 'capacity': 1073741824
1726 }, '/var/spool/cwl': {
1728 'capacity': 3221225472
1731 'state': 'Committed',
1732 'output_path': '/var/spool/cwl',
1733 'container_image': '99999999999999999999999999999993+99',
1738 u'--preserve-entire-environment',
1742 'use_existing': True,
1743 'output_name': u'Output from step echo-subwf',
1744 'cwd': '/var/spool/cwl',
1745 'output_storage_classes': ["default"]
1748 def test_default_work_api(self):
1749 arvados_cwl.add_arv_hints()
1751 api = mock.MagicMock()
1752 api._rootDesc = copy.deepcopy(get_rootDesc())
1753 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1754 self.assertEqual(runner.work_api, 'containers')