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
19 import cwltool.process
20 import cwltool.secrets
21 import cwltool.load_tool
22 from cwltool.update import INTERNAL_VERSION
23 from schema_salad.ref_resolver import Loader
24 from schema_salad.sourceline import cmap
27 from unittest import mock
29 from .matcher import JsonDiffMatcher, StripYAMLComments
30 from .mock_discovery import get_rootDesc
32 if not os.getenv('ARVADOS_DEBUG'):
33 logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
34 logging.getLogger('arvados.arv-run').setLevel(logging.WARN)
36 class CollectionMock(object):
37 def __init__(self, vwdmock, *args, **kwargs):
38 self.vwdmock = vwdmock
41 def open(self, *args, **kwargs):
43 return self.vwdmock.open(*args, **kwargs)
45 def copy(self, *args, **kwargs):
47 self.vwdmock.copy(*args, **kwargs)
49 def save_new(self, *args, **kwargs):
55 def portable_data_hash(self):
57 return arvados.config.EMPTY_BLOCK_LOCATOR
59 return "99999999999999999999999999999996+99"
62 class TestContainer(unittest.TestCase):
65 cwltool.process._names = set()
66 #arv_docker_clear_cache()
69 root_logger = logging.getLogger('')
71 # Remove existing RuntimeStatusLoggingHandlers if they exist
72 handlers = [h for h in root_logger.handlers if not isinstance(h, arvados_cwl.executor.RuntimeStatusLoggingHandler)]
73 root_logger.handlers = handlers
75 def helper(self, runner, enable_reuse=True):
76 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
78 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
79 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
80 fs_access = mock.MagicMock()
81 fs_access.exists.return_value = True
83 loadingContext = arvados_cwl.context.ArvLoadingContext(
84 {"avsc_names": avsc_names,
86 "make_fs_access": make_fs_access,
87 "construct_tool_object": runner.arv_make_tool,
88 "fetcher_constructor": functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=fs_access),
90 "metadata": cmap({"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"}),
91 "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__
93 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
94 {"work_api": "containers",
96 "name": "test_run_"+str(enable_reuse),
97 "make_fs_access": make_fs_access,
100 "enable_reuse": enable_reuse,
102 "project_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
103 "workflow_eval_lock": threading.Condition(threading.RLock())
106 if isinstance(runner, mock.MagicMock):
107 def make_tool(toolpath_object, loadingContext):
108 return arvados_cwl.ArvadosCommandTool(runner, toolpath_object, loadingContext)
109 runner.arv_make_tool.side_effect = make_tool
111 return loadingContext, runtimeContext
113 # Helper function to set up the ArvCwlExecutor to use the containers api
114 # and test that the RuntimeStatusLoggingHandler is set up correctly
115 def setup_and_test_container_executor_and_logging(self, gcc_mock) :
116 api = mock.MagicMock()
117 api._rootDesc = copy.deepcopy(get_rootDesc())
119 # Make sure ArvCwlExecutor thinks it's running inside a container so it
120 # adds the logging handler that will call runtime_status_update() mock
121 self.assertFalse(gcc_mock.called)
122 runner = arvados_cwl.ArvCwlExecutor(api)
123 self.assertEqual(runner.work_api, 'containers')
124 root_logger = logging.getLogger('')
125 handlerClasses = [h.__class__ for h in root_logger.handlers]
126 self.assertTrue(arvados_cwl.RuntimeStatusLoggingHandler in handlerClasses)
129 # The test passes no builder.resources
130 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
131 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
132 def test_run(self, keepdocker):
133 for enable_reuse in (True, False):
134 #arv_docker_clear_cache()
136 runner = mock.MagicMock()
137 runner.ignore_docker_for_reuse = False
138 runner.intermediate_output_ttl = 0
139 runner.secret_store = cwltool.secrets.SecretStore()
140 runner.api._rootDesc = {"revision": "20210628"}
141 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
143 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
144 runner.api.collections().get().execute.return_value = {
145 "portable_data_hash": "99999999999999999999999999999993+99"}
151 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
153 "class": "CommandLineTool",
157 loadingContext, runtimeContext = self.helper(runner, enable_reuse)
159 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
160 arvtool.formatgraph = None
162 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
163 j.run(runtimeContext)
164 runner.api.container_requests().create.assert_called_with(
165 body=JsonDiffMatcher({
167 'HOME': '/var/spool/cwl',
170 'name': 'test_run_'+str(enable_reuse),
171 'runtime_constraints': {
175 'use_existing': enable_reuse,
178 '/tmp': {'kind': 'tmp',
179 "capacity": 1073741824
181 '/var/spool/cwl': {'kind': 'tmp',
182 "capacity": 1073741824 }
184 'state': 'Committed',
185 'output_name': 'Output from step test_run_'+str(enable_reuse),
186 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
187 'output_path': '/var/spool/cwl',
189 'container_image': '99999999999999999999999999999993+99',
190 'command': ['ls', '/var/spool/cwl'],
191 'cwd': '/var/spool/cwl',
192 'scheduling_parameters': {},
193 'properties': {'cwl_input': {}},
195 'output_storage_classes': ["default"]
198 # The test passes some fields in builder.resources
199 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
200 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
201 def test_resource_requirements(self, keepdocker):
202 arvados_cwl.add_arv_hints()
203 runner = mock.MagicMock()
204 runner.ignore_docker_for_reuse = False
205 runner.intermediate_output_ttl = 3600
206 runner.secret_store = cwltool.secrets.SecretStore()
207 runner.api._rootDesc = {"revision": "20210628"}
208 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
210 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
211 runner.api.collections().get().execute.return_value = {
212 "portable_data_hash": "99999999999999999999999999999993+99"}
218 "class": "ResourceRequirement",
224 "class": "http://arvados.org/cwl#RuntimeConstraints",
227 "class": "http://arvados.org/cwl#APIRequirement",
229 "class": "http://arvados.org/cwl#PartitionRequirement",
232 "class": "http://arvados.org/cwl#IntermediateOutput",
235 "class": "WorkReuse",
240 "class": "CommandLineTool",
244 loadingContext, runtimeContext = self.helper(runner)
245 runtimeContext.name = "test_resource_requirements"
247 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
248 arvtool.formatgraph = None
249 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
250 j.run(runtimeContext)
252 call_args, call_kwargs = runner.api.container_requests().create.call_args
254 call_body_expected = {
256 'HOME': '/var/spool/cwl',
259 'name': 'test_resource_requirements',
260 'runtime_constraints': {
263 'keep_cache_ram': 536870912,
266 'use_existing': False,
269 '/tmp': {'kind': 'tmp',
270 "capacity": 4194304000 },
271 '/var/spool/cwl': {'kind': 'tmp',
272 "capacity": 5242880000 }
274 'state': 'Committed',
275 'output_name': 'Output from step test_resource_requirements',
276 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
277 'output_path': '/var/spool/cwl',
279 'container_image': '99999999999999999999999999999993+99',
281 'cwd': '/var/spool/cwl',
282 'scheduling_parameters': {
283 'partitions': ['blurb']
285 'properties': {'cwl_input': {}},
287 'output_storage_classes': ["default"]
290 call_body = call_kwargs.get('body', None)
291 self.assertNotEqual(None, call_body)
292 for key in call_body:
293 self.assertEqual(call_body_expected.get(key), call_body.get(key))
296 # The test passes some fields in builder.resources
297 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
298 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
299 @mock.patch("arvados.collection.Collection")
300 def test_initial_work_dir(self, collection_mock, keepdocker):
301 runner = mock.MagicMock()
302 runner.ignore_docker_for_reuse = False
303 runner.intermediate_output_ttl = 0
304 runner.secret_store = cwltool.secrets.SecretStore()
305 runner.api._rootDesc = {"revision": "20210628"}
306 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
308 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
309 runner.api.collections().get().execute.return_value = {
310 "portable_data_hash": "99999999999999999999999999999993+99"}
312 sourcemock = mock.MagicMock()
313 def get_collection_mock(p):
315 return (sourcemock, p.split("/", 1)[1])
317 return (sourcemock, "")
318 runner.fs_access.get_collection.side_effect = get_collection_mock
320 vwdmock = mock.MagicMock()
321 collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs)
327 "class": "InitialWorkDirRequirement",
331 "location": "keep:99999999999999999999999999999995+99/bar"
334 "class": "Directory",
336 "location": "keep:99999999999999999999999999999995+99"
340 "basename": "filename",
341 "location": "keep:99999999999999999999999999999995+99/baz/filename"
344 "class": "Directory",
345 "basename": "subdir",
346 "location": "keep:99999999999999999999999999999995+99/subdir"
350 "class": "CommandLineTool",
351 "cwlVersion": "v1.2",
355 loadingContext, runtimeContext = self.helper(runner)
356 runtimeContext.name = "test_initial_work_dir"
358 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
360 arvtool.formatgraph = None
361 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
362 j.run(runtimeContext)
364 call_args, call_kwargs = runner.api.container_requests().create.call_args
366 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
367 vwdmock.copy.assert_has_calls([mock.call('.', 'foo2', source_collection=sourcemock)])
368 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
369 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
371 call_body_expected = {
373 'HOME': '/var/spool/cwl',
376 'name': 'test_initial_work_dir',
377 'runtime_constraints': {
381 'use_existing': True,
384 '/tmp': {'kind': 'tmp',
385 "capacity": 1073741824 },
386 '/var/spool/cwl': {'kind': 'tmp',
387 "capacity": 1073741824 },
388 '/var/spool/cwl/foo': {
389 'kind': 'collection',
391 'portable_data_hash': '99999999999999999999999999999996+99'
393 '/var/spool/cwl/foo2': {
394 'kind': 'collection',
396 'portable_data_hash': '99999999999999999999999999999996+99'
398 '/var/spool/cwl/filename': {
399 'kind': 'collection',
401 'portable_data_hash': '99999999999999999999999999999996+99'
403 '/var/spool/cwl/subdir': {
404 'kind': 'collection',
406 'portable_data_hash': '99999999999999999999999999999996+99'
409 'state': 'Committed',
410 'output_name': 'Output from step test_initial_work_dir',
411 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
412 'output_path': '/var/spool/cwl',
414 'container_image': '99999999999999999999999999999993+99',
416 'cwd': '/var/spool/cwl',
417 'scheduling_parameters': {
419 'properties': {'cwl_input': {}},
421 'output_storage_classes': ["default"]
424 call_body = call_kwargs.get('body', None)
425 self.assertNotEqual(None, call_body)
426 for key in call_body:
427 self.assertEqual(call_body_expected.get(key), call_body.get(key))
430 # Test redirecting stdin/stdout/stderr
431 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
432 def test_redirects(self, keepdocker):
433 runner = mock.MagicMock()
434 runner.ignore_docker_for_reuse = False
435 runner.intermediate_output_ttl = 0
436 runner.secret_store = cwltool.secrets.SecretStore()
437 runner.api._rootDesc = {"revision": "20210628"}
438 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
440 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
441 runner.api.collections().get().execute.return_value = {
442 "portable_data_hash": "99999999999999999999999999999993+99"}
444 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema(INTERNAL_VERSION)
450 "stdout": "stdout.txt",
451 "stderr": "stderr.txt",
452 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
453 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
455 "class": "CommandLineTool",
459 loadingContext, runtimeContext = self.helper(runner)
460 runtimeContext.name = "test_run_redirect"
462 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
463 arvtool.formatgraph = None
464 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
465 j.run(runtimeContext)
466 runner.api.container_requests().create.assert_called_with(
467 body=JsonDiffMatcher({
469 'HOME': '/var/spool/cwl',
472 'name': 'test_run_redirect',
473 'runtime_constraints': {
477 'use_existing': True,
480 '/tmp': {'kind': 'tmp',
481 "capacity": 1073741824 },
482 '/var/spool/cwl': {'kind': 'tmp',
483 "capacity": 1073741824 },
486 "path": "/var/spool/cwl/stderr.txt"
489 "kind": "collection",
491 "portable_data_hash": "99999999999999999999999999999996+99"
495 "path": "/var/spool/cwl/stdout.txt"
498 'state': 'Committed',
499 "output_name": "Output from step test_run_redirect",
500 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
501 'output_path': '/var/spool/cwl',
503 'container_image': '99999999999999999999999999999993+99',
504 'command': ['ls', '/var/spool/cwl'],
505 'cwd': '/var/spool/cwl',
506 'scheduling_parameters': {},
507 'properties': {'cwl_input': {}},
509 'output_storage_classes': ["default"]
512 @mock.patch("arvados.collection.Collection")
513 def test_done(self, col):
514 api = mock.MagicMock()
516 runner = mock.MagicMock()
518 runner.num_retries = 0
519 runner.ignore_docker_for_reuse = False
520 runner.intermediate_output_ttl = 0
521 runner.secret_store = cwltool.secrets.SecretStore()
523 runner.api.container_requests().get().execute.return_value = {"container_uuid":"zzzzz-xvhdp-zzzzzzzzzzzzzzz"}
525 runner.api.containers().get().execute.return_value = {"state":"Complete",
529 # Need to noop-out the close method otherwise it gets
530 # discarded when closed and we can't call getvalue() to check
532 class NoopCloseStringIO(io.StringIO):
536 usage_report = NoopCloseStringIO()
537 def colreader_action(name, mode):
538 nonlocal usage_report
539 if name == "node.json":
540 return io.StringIO("""{
541 "ProviderType": "c5.large",
544 "IncludedScratch": 8000000000000,
547 "Preemptible": false,
550 "HardwareCapability": "",
554 if name == 'crunchstat.txt':
555 return open("tests/container_request_9tee4-xvhdp-kk0ja1cl8b2kr1y-arv-mount.txt", "rt")
556 if name == 'arv-mount.txt':
557 return open("tests/container_request_9tee4-xvhdp-kk0ja1cl8b2kr1y-crunchstat.txt", "rt")
558 if name == 'usage_report.html':
562 col().open.side_effect = colreader_action
563 col().__iter__.return_value = ['node.json', 'crunchstat.txt', 'arv-mount.txt']
565 loadingContext, runtimeContext = self.helper(runner)
567 arvjob = arvados_cwl.ArvadosContainer(runner,
575 arvjob.output_callback = mock.MagicMock()
576 arvjob.collect_outputs = mock.MagicMock()
577 arvjob.successCodes = [0]
578 arvjob.outdir = "/var/spool/cwl"
579 arvjob.output_ttl = 3600
580 arvjob.uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzz1"
582 arvjob.collect_outputs.return_value = {"out": "stuff"}
586 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
587 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
588 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
589 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
590 "modified_at": "2017-05-26T12:01:22Z",
595 self.assertFalse(api.collections().create.called)
596 self.assertFalse(runner.runtime_status_error.called)
598 # Assert that something was written to the usage report
599 self.assertTrue(len(usage_report.getvalue()) > 0)
601 arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
602 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
603 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
605 runner.api.container_requests().update.assert_called_with(uuid="zzzzz-xvhdp-zzzzzzzzzzzzzz1",
606 body={'container_request': {'properties': {'cwl_output': {'out': 'stuff'}}}})
609 # Test to make sure we dont call runtime_status_update if we already did
610 # some where higher up in the call stack
611 @mock.patch("arvados_cwl.util.get_current_container")
612 def test_recursive_runtime_status_update(self, gcc_mock):
613 self.setup_and_test_container_executor_and_logging(gcc_mock)
614 root_logger = logging.getLogger('')
616 # get_current_container is invoked when we call runtime_status_update
617 # so try and log again!
618 gcc_mock.side_effect = lambda *args: root_logger.error("Second Error")
620 root_logger.error("First Error")
622 self.fail("RuntimeStatusLoggingHandler should not be called recursively")
625 # Test to make sure that an exception raised from
626 # get_current_container doesn't cause the logger to raise an
628 @mock.patch("arvados_cwl.util.get_current_container")
629 def test_runtime_status_get_current_container_exception(self, gcc_mock):
630 self.setup_and_test_container_executor_and_logging(gcc_mock)
631 root_logger = logging.getLogger('')
633 # get_current_container is invoked when we call
634 # runtime_status_update, it is going to also raise an
636 gcc_mock.side_effect = Exception("Second Error")
638 root_logger.error("First Error")
640 self.fail("Exception in logger should not propagate")
641 self.assertTrue(gcc_mock.called)
643 @mock.patch("arvados_cwl.ArvCwlExecutor.runtime_status_update")
644 @mock.patch("arvados_cwl.util.get_current_container")
645 @mock.patch("arvados.collection.CollectionReader")
646 @mock.patch("arvados.collection.Collection")
647 def test_child_failure(self, col, reader, gcc_mock, rts_mock):
648 runner = self.setup_and_test_container_executor_and_logging(gcc_mock)
650 gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"}
651 self.assertTrue(gcc_mock.called)
653 runner.num_retries = 0
654 runner.ignore_docker_for_reuse = False
655 runner.intermediate_output_ttl = 0
656 runner.secret_store = cwltool.secrets.SecretStore()
657 runner.label = mock.MagicMock()
658 runner.label.return_value = '[container testjob]'
660 runner.api.containers().get().execute.return_value = {
667 col().open.return_value = []
669 loadingContext, runtimeContext = self.helper(runner)
671 arvjob = arvados_cwl.ArvadosContainer(runner,
679 arvjob.output_callback = mock.MagicMock()
680 arvjob.collect_outputs = mock.MagicMock()
681 arvjob.successCodes = [0]
682 arvjob.outdir = "/var/spool/cwl"
683 arvjob.output_ttl = 3600
684 arvjob.collect_outputs.return_value = {"out": "stuff"}
688 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
689 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
690 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
691 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
692 "modified_at": "2017-05-26T12:01:22Z",
696 rts_mock.assert_has_calls([
698 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
699 ' ** log is empty **'
702 'arvados.cwl-runner: [container testjob] unable to generate resource usage report'
704 arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
706 # The test passes no builder.resources
707 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
708 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
709 def test_mounts(self, keepdocker):
710 runner = mock.MagicMock()
711 runner.ignore_docker_for_reuse = False
712 runner.intermediate_output_ttl = 0
713 runner.secret_store = cwltool.secrets.SecretStore()
714 runner.api._rootDesc = {"revision": "20210628"}
715 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
717 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
718 runner.api.collections().get().execute.return_value = {
719 "portable_data_hash": "99999999999999999999999999999994+99",
720 "manifest_text": ". 99999999999999999999999999999994+99 0:0:file1 0:0:file2"}
722 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
731 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
733 "class": "CommandLineTool",
737 loadingContext, runtimeContext = self.helper(runner)
738 runtimeContext.name = "test_run_mounts"
740 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
741 arvtool.formatgraph = None
744 "class": "Directory",
745 "location": "keep:99999999999999999999999999999994+44",
746 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
750 "location": "keep:99999999999999999999999999999994+44/file1",
754 "location": "keep:99999999999999999999999999999994+44/file2",
759 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
760 j.run(runtimeContext)
761 runner.api.container_requests().create.assert_called_with(
762 body=JsonDiffMatcher({
764 'HOME': '/var/spool/cwl',
767 'name': 'test_run_mounts',
768 'runtime_constraints': {
772 'use_existing': True,
775 "/keep/99999999999999999999999999999994+44": {
776 "kind": "collection",
777 "portable_data_hash": "99999999999999999999999999999994+44",
778 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz"
780 '/tmp': {'kind': 'tmp',
781 "capacity": 1073741824 },
782 '/var/spool/cwl': {'kind': 'tmp',
783 "capacity": 1073741824 }
785 'state': 'Committed',
786 'output_name': 'Output from step test_run_mounts',
787 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
788 'output_path': '/var/spool/cwl',
790 'container_image': '99999999999999999999999999999994+99',
791 'command': ['ls', '/var/spool/cwl'],
792 'cwd': '/var/spool/cwl',
793 'scheduling_parameters': {},
794 'properties': {'cwl_input': {
796 "basename": "99999999999999999999999999999994+44",
797 "class": "Directory",
799 "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
804 "dirname": "/keep/99999999999999999999999999999994+44",
805 "location": "keep:99999999999999999999999999999994+44/file1",
808 "path": "/keep/99999999999999999999999999999994+44/file1",
814 "dirname": "/keep/99999999999999999999999999999994+44",
815 "location": "keep:99999999999999999999999999999994+44/file2",
818 "path": "/keep/99999999999999999999999999999994+44/file2",
822 "location": "keep:99999999999999999999999999999994+44",
823 "path": "/keep/99999999999999999999999999999994+44"
827 'output_storage_classes': ["default"]
830 # The test passes no builder.resources
831 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
832 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
833 def test_secrets(self, keepdocker):
834 arvados_cwl.add_arv_hints()
835 runner = mock.MagicMock()
836 runner.ignore_docker_for_reuse = False
837 runner.intermediate_output_ttl = 0
838 runner.secret_store = cwltool.secrets.SecretStore()
839 runner.api._rootDesc = {"revision": "20210628"}
840 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
842 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
843 runner.api.collections().get().execute.return_value = {
844 "portable_data_hash": "99999999999999999999999999999993+99"}
846 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.1")
848 tool = cmap({"arguments": ["md5sum", "example.conf"],
849 "class": "CommandLineTool",
850 "cwlVersion": "v1.2",
853 "class": "http://commonwl.org/cwltool#Secrets",
862 "id": "#secret_job.cwl/pw",
870 "class": "InitialWorkDirRequirement",
873 "entry": "username: user\npassword: $(inputs.pw)\n",
874 "entryname": "example.conf"
880 loadingContext, runtimeContext = self.helper(runner)
881 runtimeContext.name = "test_secrets"
883 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
884 arvtool.formatgraph = None
886 job_order = {"pw": "blorp"}
887 runner.secret_store.store(["pw"], job_order)
889 for j in arvtool.job(job_order, mock.MagicMock(), runtimeContext):
890 j.run(runtimeContext)
891 runner.api.container_requests().create.assert_called_with(
892 body=JsonDiffMatcher({
894 'HOME': '/var/spool/cwl',
897 'name': 'test_secrets',
898 'runtime_constraints': {
902 'use_existing': True,
905 '/tmp': {'kind': 'tmp',
906 "capacity": 1073741824
908 '/var/spool/cwl': {'kind': 'tmp',
909 "capacity": 1073741824 }
911 'state': 'Committed',
912 'output_name': 'Output from step test_secrets',
913 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
914 'output_path': '/var/spool/cwl',
916 'container_image': '99999999999999999999999999999993+99',
917 'command': ['md5sum', 'example.conf'],
918 'cwd': '/var/spool/cwl',
919 'scheduling_parameters': {},
920 'properties': {'cwl_input': job_order},
922 "/var/spool/cwl/example.conf": {
923 "content": "username: user\npassword: blorp\n",
927 'output_storage_classes': ["default"]
930 # The test passes no builder.resources
931 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
932 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
933 def test_timelimit(self, keepdocker):
934 runner = mock.MagicMock()
935 runner.ignore_docker_for_reuse = False
936 runner.intermediate_output_ttl = 0
937 runner.secret_store = cwltool.secrets.SecretStore()
938 runner.api._rootDesc = {"revision": "20210628"}
939 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
941 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
942 runner.api.collections().get().execute.return_value = {
943 "portable_data_hash": "99999999999999999999999999999993+99"}
949 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
951 "cwlVersion": "v1.2",
952 "class": "CommandLineTool",
955 "class": "ToolTimeLimit",
961 loadingContext, runtimeContext = self.helper(runner)
962 runtimeContext.name = "test_timelimit"
964 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
965 arvtool.formatgraph = None
967 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
968 j.run(runtimeContext)
970 _, kwargs = runner.api.container_requests().create.call_args
971 self.assertEqual(42, kwargs['body']['scheduling_parameters'].get('max_run_time'))
974 # The test passes no builder.resources
975 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
976 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
977 def test_setting_storage_class(self, keepdocker):
978 #arv_docker_clear_cache()
980 runner = mock.MagicMock()
981 runner.ignore_docker_for_reuse = False
982 runner.intermediate_output_ttl = 0
983 runner.secret_store = cwltool.secrets.SecretStore()
984 runner.api._rootDesc = {"revision": "20210628"}
985 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
987 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
988 runner.api.collections().get().execute.return_value = {
989 "portable_data_hash": "99999999999999999999999999999993+99"}
995 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
997 "cwlVersion": "v1.2",
998 "class": "CommandLineTool",
1001 "class": "http://arvados.org/cwl#OutputStorageClass",
1002 "finalStorageClass": ["baz_sc", "qux_sc"],
1003 "intermediateStorageClass": ["foo_sc", "bar_sc"]
1008 loadingContext, runtimeContext = self.helper(runner, True)
1010 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1011 arvtool.formatgraph = None
1013 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1014 j.run(runtimeContext)
1015 runner.api.container_requests().create.assert_called_with(
1016 body=JsonDiffMatcher({
1018 'HOME': '/var/spool/cwl',
1021 'name': 'test_run_True',
1022 'runtime_constraints': {
1026 'use_existing': True,
1029 '/tmp': {'kind': 'tmp',
1030 "capacity": 1073741824
1032 '/var/spool/cwl': {'kind': 'tmp',
1033 "capacity": 1073741824 }
1035 'state': 'Committed',
1036 'output_name': 'Output from step test_run_True',
1037 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1038 'output_path': '/var/spool/cwl',
1040 'container_image': '99999999999999999999999999999993+99',
1041 'command': ['ls', '/var/spool/cwl'],
1042 'cwd': '/var/spool/cwl',
1043 'scheduling_parameters': {},
1044 'properties': {'cwl_input': {}},
1045 'secret_mounts': {},
1046 'output_storage_classes': ["foo_sc", "bar_sc"]
1050 # The test passes no builder.resources
1051 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1052 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1053 def test_setting_process_properties(self, keepdocker):
1054 #arv_docker_clear_cache()
1056 runner = mock.MagicMock()
1057 runner.ignore_docker_for_reuse = False
1058 runner.intermediate_output_ttl = 0
1059 runner.secret_store = cwltool.secrets.SecretStore()
1060 runner.api._rootDesc = {"revision": "20210628"}
1061 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1063 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1064 runner.api.collections().get().execute.return_value = {
1065 "portable_data_hash": "99999999999999999999999999999993+99"}
1069 {"id": "x", "type": "string"}],
1071 "baseCommand": "ls",
1072 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1074 "class": "CommandLineTool",
1075 "cwlVersion": "v1.2",
1078 "class": "http://arvados.org/cwl#ProcessProperties",
1079 "processProperties": [
1080 {"propertyName": "foo",
1081 "propertyValue": "bar"},
1082 {"propertyName": "baz",
1083 "propertyValue": "$(inputs.x)"},
1084 {"propertyName": "quux",
1095 loadingContext, runtimeContext = self.helper(runner, True)
1097 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1098 arvtool.formatgraph = None
1100 for j in arvtool.job({"x": "blorp"}, mock.MagicMock(), runtimeContext):
1101 j.run(runtimeContext)
1102 runner.api.container_requests().create.assert_called_with(
1103 body=JsonDiffMatcher({
1105 'HOME': '/var/spool/cwl',
1108 'name': 'test_run_True',
1109 'runtime_constraints': {
1113 'use_existing': True,
1116 '/tmp': {'kind': 'tmp',
1117 "capacity": 1073741824
1119 '/var/spool/cwl': {'kind': 'tmp',
1120 "capacity": 1073741824 }
1122 'state': 'Committed',
1123 'output_name': 'Output from step test_run_True',
1124 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1125 'output_path': '/var/spool/cwl',
1127 'container_image': '99999999999999999999999999999993+99',
1128 'command': ['ls', '/var/spool/cwl'],
1129 'cwd': '/var/spool/cwl',
1130 'scheduling_parameters': {},
1133 "cwl_input": {"x": "blorp"},
1140 'secret_mounts': {},
1141 'output_storage_classes': ["default"]
1145 # The test passes no builder.resources
1146 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1147 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1148 def test_cuda_requirement(self, keepdocker):
1149 arvados_cwl.add_arv_hints()
1150 #arv_docker_clear_cache()
1152 runner = mock.MagicMock()
1153 runner.ignore_docker_for_reuse = False
1154 runner.intermediate_output_ttl = 0
1155 runner.secret_store = cwltool.secrets.SecretStore()
1156 runner.api._rootDesc = {"revision": "20210628"}
1157 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1159 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1160 runner.api.collections().get().execute.return_value = {
1161 "portable_data_hash": "99999999999999999999999999999993+99"}
1164 "class": "http://commonwl.org/cwltool#CUDARequirement",
1165 "cudaVersionMin": "11.0",
1166 "cudaComputeCapability": "9.0",
1168 "class": "http://commonwl.org/cwltool#CUDARequirement",
1169 "cudaVersionMin": "11.0",
1170 "cudaComputeCapability": "9.0",
1171 "cudaDeviceCountMin": 2
1173 "class": "http://commonwl.org/cwltool#CUDARequirement",
1174 "cudaVersionMin": "11.0",
1175 "cudaComputeCapability": ["4.0", "5.0"],
1176 "cudaDeviceCountMin": 2
1181 'driver_version': "11.0",
1182 'hardware_capability': "9.0"
1185 'driver_version': "11.0",
1186 'hardware_capability': "9.0"
1189 'driver_version': "11.0",
1190 'hardware_capability': "4.0"
1193 for test_case in range(0, len(test_cwl_req)):
1198 "baseCommand": "nvidia-smi",
1201 "cwlVersion": "v1.2",
1202 "class": "CommandLineTool",
1203 "requirements": [test_cwl_req[test_case]]
1206 loadingContext, runtimeContext = self.helper(runner, True)
1208 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1209 arvtool.formatgraph = None
1211 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1212 j.run(runtimeContext)
1213 runner.api.container_requests().create.assert_called_with(
1214 body=JsonDiffMatcher({
1216 'HOME': '/var/spool/cwl',
1219 'name': 'test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1220 'runtime_constraints': {
1223 'cuda': test_arv_req[test_case]
1225 'use_existing': True,
1228 '/tmp': {'kind': 'tmp',
1229 "capacity": 1073741824
1231 '/var/spool/cwl': {'kind': 'tmp',
1232 "capacity": 1073741824 }
1234 'state': 'Committed',
1235 'output_name': 'Output from step test_run_True' + ("" if test_case == 0 else "_"+str(test_case+1)),
1236 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1237 'output_path': '/var/spool/cwl',
1239 'container_image': '99999999999999999999999999999993+99',
1240 'command': ['nvidia-smi'],
1241 'cwd': '/var/spool/cwl',
1242 'scheduling_parameters': {},
1243 'properties': {'cwl_input': {}},
1244 'secret_mounts': {},
1245 'output_storage_classes': ["default"]
1249 # The test passes no builder.resources
1250 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1251 @mock.patch("arvados_cwl.arvdocker.determine_image_id")
1252 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1253 def test_match_local_docker(self, keepdocker, determine_image_id):
1254 arvados_cwl.add_arv_hints()
1256 runner = mock.MagicMock()
1257 runner.ignore_docker_for_reuse = False
1258 runner.intermediate_output_ttl = 0
1259 runner.secret_store = cwltool.secrets.SecretStore()
1260 runner.api._rootDesc = {"revision": "20210628"}
1261 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1263 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
1264 ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
1265 determine_image_id.side_effect = lambda x: "123"
1267 ex = mock.MagicMock()
1268 lookup = {"zzzzz-4zz18-zzzzzzzzzzzzzz4": {"portable_data_hash": "99999999999999999999999999999994+99"},
1269 "zzzzz-4zz18-zzzzzzzzzzzzzz3": {"portable_data_hash": "99999999999999999999999999999993+99"}}
1270 ex.execute.return_value = lookup[uuid]
1272 runner.api.collections().get.side_effect = execute
1277 "baseCommand": "echo",
1280 "cwlVersion": "v1.0",
1281 "class": "org.w3id.cwl.cwl.CommandLineTool"
1284 loadingContext, runtimeContext = self.helper(runner, True)
1286 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, loadingContext)
1287 arvtool.formatgraph = None
1289 container_request = {
1291 'HOME': '/var/spool/cwl',
1294 'name': 'test_run_True',
1295 'runtime_constraints': {
1299 'use_existing': True,
1302 '/tmp': {'kind': 'tmp',
1303 "capacity": 1073741824
1305 '/var/spool/cwl': {'kind': 'tmp',
1306 "capacity": 1073741824 }
1308 'state': 'Committed',
1309 'output_name': 'Output from step test_run_True',
1310 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1311 'output_path': '/var/spool/cwl',
1313 'container_image': '99999999999999999999999999999994+99',
1314 'command': ['echo'],
1315 'cwd': '/var/spool/cwl',
1316 'scheduling_parameters': {},
1317 'properties': {'cwl_input': {}},
1318 'secret_mounts': {},
1319 'output_storage_classes': ["default"]
1322 runtimeContext.match_local_docker = False
1323 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1324 j.run(runtimeContext)
1325 runner.api.container_requests().create.assert_called_with(
1326 body=JsonDiffMatcher(container_request))
1328 runtimeContext.cached_docker_lookups.clear()
1329 runtimeContext.match_local_docker = True
1330 container_request['container_image'] = '99999999999999999999999999999993+99'
1331 container_request['name'] = 'test_run_True_2'
1332 container_request['output_name'] = 'Output from step test_run_True_2'
1333 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1334 j.run(runtimeContext)
1335 runner.api.container_requests().create.assert_called_with(
1336 body=JsonDiffMatcher(container_request))
1339 # The test passes no builder.resources
1340 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1341 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1342 def test_run_preemptible_hint(self, keepdocker):
1343 arvados_cwl.add_arv_hints()
1344 for enable_preemptible in (None, True, False):
1345 for preemptible_hint in (None, True, False):
1346 #arv_docker_clear_cache()
1348 runner = mock.MagicMock()
1349 runner.ignore_docker_for_reuse = False
1350 runner.intermediate_output_ttl = 0
1351 runner.secret_store = cwltool.secrets.SecretStore()
1352 runner.api._rootDesc = {"revision": "20210628"}
1353 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1355 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1356 runner.api.collections().get().execute.return_value = {
1357 "portable_data_hash": "99999999999999999999999999999993+99"}
1359 if preemptible_hint is not None:
1361 "class": "http://arvados.org/cwl#UsePreemptible",
1362 "usePreemptible": preemptible_hint
1370 "baseCommand": "ls",
1371 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1373 "class": "CommandLineTool",
1374 "cwlVersion": "v1.2",
1378 loadingContext, runtimeContext = self.helper(runner)
1380 runtimeContext.name = 'test_run_enable_preemptible_'+str(enable_preemptible)+str(preemptible_hint)
1381 runtimeContext.enable_preemptible = enable_preemptible
1383 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1384 arvtool.formatgraph = None
1386 # Test the interactions between --enable/disable-preemptible
1387 # and UsePreemptible hint
1389 if enable_preemptible is None:
1390 if preemptible_hint is None:
1393 sched = {'preemptible': preemptible_hint}
1395 if preemptible_hint is None:
1396 sched = {'preemptible': enable_preemptible}
1398 sched = {'preemptible': enable_preemptible and preemptible_hint}
1400 for j in arvtool.job({}, mock.MagicMock(), runtimeContext):
1401 j.run(runtimeContext)
1402 runner.api.container_requests().create.assert_called_with(
1403 body=JsonDiffMatcher({
1405 'HOME': '/var/spool/cwl',
1408 'name': runtimeContext.name,
1409 'runtime_constraints': {
1413 'use_existing': True,
1416 '/tmp': {'kind': 'tmp',
1417 "capacity": 1073741824
1419 '/var/spool/cwl': {'kind': 'tmp',
1420 "capacity": 1073741824 }
1422 'state': 'Committed',
1423 'output_name': 'Output from step '+runtimeContext.name,
1424 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
1425 'output_path': '/var/spool/cwl',
1427 'container_image': '99999999999999999999999999999993+99',
1428 'command': ['ls', '/var/spool/cwl'],
1429 'cwd': '/var/spool/cwl',
1430 'scheduling_parameters': sched,
1431 'properties': {'cwl_input': {}},
1432 'secret_mounts': {},
1433 'output_storage_classes': ["default"]
1437 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
1438 def test_output_properties(self, keepdocker):
1439 arvados_cwl.add_arv_hints()
1440 for rev in ["20210628", "20220510"]:
1441 runner = mock.MagicMock()
1442 runner.ignore_docker_for_reuse = False
1443 runner.intermediate_output_ttl = 0
1444 runner.secret_store = cwltool.secrets.SecretStore()
1445 runner.api._rootDesc = {"revision": rev}
1446 runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1448 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
1449 runner.api.collections().get().execute.return_value = {
1450 "portable_data_hash": "99999999999999999999999999999993+99"}
1458 "baseCommand": "ls",
1459 "arguments": [{"valueFrom": "$(runtime.outdir)"}],
1461 "cwlVersion": "v1.2",
1462 "class": "CommandLineTool",
1465 "class": "http://arvados.org/cwl#OutputCollectionProperties",
1466 "outputProperties": {
1468 "baz": "$(inputs.inp)"
1474 loadingContext, runtimeContext = self.helper(runner)
1475 runtimeContext.name = "test_timelimit"
1477 arvtool = cwltool.load_tool.load_tool(tool, loadingContext)
1478 arvtool.formatgraph = None
1480 for j in arvtool.job({"inp": "quux"}, mock.MagicMock(), runtimeContext):
1481 j.run(runtimeContext)
1483 _, kwargs = runner.api.container_requests().create.call_args
1484 if rev == "20220510":
1485 self.assertEqual({"foo": "bar", "baz": "quux"}, kwargs['body'].get('output_properties'))
1487 self.assertEqual(None, kwargs['body'].get('output_properties'))
1490 class TestWorkflow(unittest.TestCase):
1492 cwltool.process._names = set()
1493 #arv_docker_clear_cache()
1495 def helper(self, runner, enable_reuse=True):
1496 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
1498 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
1499 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
1501 document_loader.fetcher_constructor = functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=make_fs_access(""))
1502 document_loader.fetcher = document_loader.fetcher_constructor(document_loader.cache, document_loader.session)
1503 document_loader.fetch_text = document_loader.fetcher.fetch_text
1504 document_loader.check_exists = document_loader.fetcher.check_exists
1506 loadingContext = arvados_cwl.context.ArvLoadingContext(
1507 {"avsc_names": avsc_names,
1509 "make_fs_access": make_fs_access,
1510 "loader": document_loader,
1511 "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
1512 "construct_tool_object": runner.arv_make_tool,
1513 "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__})
1514 runtimeContext = arvados_cwl.context.ArvRuntimeContext(
1515 {"work_api": "containers",
1517 "name": "test_run_wf_"+str(enable_reuse),
1518 "make_fs_access": make_fs_access,
1520 "enable_reuse": enable_reuse,
1523 return loadingContext, runtimeContext
1525 # The test passes no builder.resources
1526 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1527 @mock.patch("arvados.collection.CollectionReader")
1528 @mock.patch("arvados.collection.Collection")
1529 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1530 def test_run(self, list_images_in_arv, mockcollection, mockcollectionreader):
1531 arvados_cwl.add_arv_hints()
1533 api = mock.MagicMock()
1534 api._rootDesc = get_rootDesc()
1535 api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1537 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1538 self.assertEqual(runner.work_api, 'containers')
1540 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1541 runner.api.collections().get().execute.return_value = {"portable_data_hash": "99999999999999999999999999999993+99"}
1542 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1543 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1545 runner.api.containers().current().execute.return_value = {}
1547 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1548 runner.ignore_docker_for_reuse = False
1549 runner.num_retries = 0
1550 runner.secret_store = cwltool.secrets.SecretStore()
1552 loadingContext, runtimeContext = self.helper(runner)
1553 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1555 mockcollectionreader().exists.return_value = True
1557 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/scatter2.cwl")
1558 metadata["cwlVersion"] = tool["cwlVersion"]
1560 mockc = mock.MagicMock()
1561 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mockc, *args, **kwargs)
1562 mockcollectionreader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "token.txt")
1564 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1565 arvtool.formatgraph = None
1566 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1568 next(it).run(runtimeContext)
1569 next(it).run(runtimeContext)
1571 with open("tests/wf/scatter2_subwf.cwl") as f:
1572 subwf = StripYAMLComments(f.read()).rstrip()
1574 runner.api.container_requests().create.assert_called_with(
1575 body=JsonDiffMatcher({
1580 "--preserve-entire-environment",
1584 "container_image": "99999999999999999999999999999993+99",
1585 "cwd": "/var/spool/cwl",
1587 "HOME": "/var/spool/cwl",
1591 "/keep/99999999999999999999999999999999+118": {
1592 "kind": "collection",
1593 "portable_data_hash": "99999999999999999999999999999999+118"
1596 "capacity": 1073741824,
1600 "capacity": 1073741824,
1603 "/var/spool/cwl/cwl.input.yml": {
1604 "kind": "collection",
1605 "path": "cwl.input.yml",
1606 "portable_data_hash": "99999999999999999999999999999996+99"
1608 "/var/spool/cwl/workflow.cwl": {
1609 "kind": "collection",
1610 "path": "workflow.cwl",
1611 "portable_data_hash": "99999999999999999999999999999996+99"
1615 "path": "/var/spool/cwl/cwl.output.json"
1618 "name": "scatterstep",
1619 "output_name": "Output from step scatterstep",
1620 "output_path": "/var/spool/cwl",
1623 "properties": {'cwl_input': {
1625 "basename": "token.txt",
1627 "dirname": "/keep/99999999999999999999999999999999+118",
1628 "location": "keep:99999999999999999999999999999999+118/token.txt",
1630 "nameroot": "token",
1631 "path": "/keep/99999999999999999999999999999999+118/token.txt",
1636 "runtime_constraints": {
1640 "scheduling_parameters": {},
1641 "secret_mounts": {},
1642 "state": "Committed",
1643 "use_existing": True,
1644 'output_storage_classes': ["default"]
1646 mockc.open().__enter__().write.assert_has_calls([mock.call(subwf)])
1647 mockc.open().__enter__().write.assert_has_calls([mock.call(
1650 "basename": "token.txt",
1652 "location": "/keep/99999999999999999999999999999999+118/token.txt",
1658 # The test passes no builder.resources
1659 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
1660 @mock.patch("arvados.collection.CollectionReader")
1661 @mock.patch("arvados.collection.Collection")
1662 @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
1663 def test_overall_resource_singlecontainer(self, list_images_in_arv, mockcollection, mockcollectionreader):
1664 arvados_cwl.add_arv_hints()
1666 api = mock.MagicMock()
1667 api._rootDesc = get_rootDesc()
1668 api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
1670 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1671 self.assertEqual(runner.work_api, 'containers')
1673 list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
1674 runner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1675 "portable_data_hash": "99999999999999999999999999999993+99"}
1676 runner.api.collections().list().execute.return_value = {"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
1677 "portable_data_hash": "99999999999999999999999999999993+99"}]}
1679 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
1680 runner.ignore_docker_for_reuse = False
1681 runner.num_retries = 0
1682 runner.secret_store = cwltool.secrets.SecretStore()
1684 loadingContext, runtimeContext = self.helper(runner)
1685 runner.fs_access = runtimeContext.make_fs_access(runtimeContext.basedir)
1686 loadingContext.do_update = True
1687 tool, metadata = loadingContext.loader.resolve_ref("tests/wf/echo-wf.cwl")
1689 mockcollection.side_effect = lambda *args, **kwargs: CollectionMock(mock.MagicMock(), *args, **kwargs)
1691 arvtool = arvados_cwl.ArvadosWorkflow(runner, tool, loadingContext)
1692 arvtool.formatgraph = None
1693 it = arvtool.job({}, mock.MagicMock(), runtimeContext)
1695 next(it).run(runtimeContext)
1696 next(it).run(runtimeContext)
1698 with open("tests/wf/echo-subwf.cwl") as f:
1699 subwf = StripYAMLComments(f.read())
1701 runner.api.container_requests().create.assert_called_with(
1702 body=JsonDiffMatcher({
1704 'environment': {'HOME': '/var/spool/cwl', 'TMPDIR': '/tmp'},
1705 'scheduling_parameters': {},
1706 'name': u'echo-subwf',
1707 'secret_mounts': {},
1708 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
1709 'properties': {'cwl_input': {}},
1712 '/var/spool/cwl/cwl.input.yml': {
1713 'portable_data_hash': '99999999999999999999999999999996+99',
1714 'kind': 'collection',
1715 'path': 'cwl.input.yml'
1717 '/var/spool/cwl/workflow.cwl': {
1718 'portable_data_hash': '99999999999999999999999999999996+99',
1719 'kind': 'collection',
1720 'path': 'workflow.cwl'
1723 'path': '/var/spool/cwl/cwl.output.json',
1728 'capacity': 1073741824
1729 }, '/var/spool/cwl': {
1731 'capacity': 3221225472
1734 'state': 'Committed',
1735 'output_path': '/var/spool/cwl',
1736 'container_image': '99999999999999999999999999999993+99',
1741 u'--preserve-entire-environment',
1745 'use_existing': True,
1746 'output_name': u'Output from step echo-subwf',
1747 'cwd': '/var/spool/cwl',
1748 'output_storage_classes': ["default"]
1751 def test_default_work_api(self):
1752 arvados_cwl.add_arv_hints()
1754 api = mock.MagicMock()
1755 api._rootDesc = copy.deepcopy(get_rootDesc())
1756 runner = arvados_cwl.executor.ArvCwlExecutor(api)
1757 self.assertEqual(runner.work_api, 'containers')