2 from arvados_cwl.arvdocker import arv_docker_clear_cache
9 from schema_salad.ref_resolver import Loader
10 from schema_salad.sourceline import cmap
12 from .matcher import JsonDiffMatcher
14 if not os.getenv('ARVADOS_DEBUG'):
15 logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
16 logging.getLogger('arvados.arv-run').setLevel(logging.WARN)
19 class TestContainer(unittest.TestCase):
21 # The test passes no builder.resources
22 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
23 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
24 def test_run(self, keepdocker):
25 for enable_reuse in (True, False):
26 arv_docker_clear_cache()
28 runner = mock.MagicMock()
29 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
30 runner.ignore_docker_for_reuse = False
31 runner.intermediate_output_ttl = 0
33 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
34 runner.api.collections().get().execute.return_value = {
35 "portable_data_hash": "99999999999999999999999999999993+99"}
37 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
43 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
45 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
46 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
47 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
48 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
49 arvtool.formatgraph = None
50 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run_"+str(enable_reuse),
51 make_fs_access=make_fs_access, tmpdir="/tmp"):
52 j.run(enable_reuse=enable_reuse)
53 runner.api.container_requests().create.assert_called_with(
54 body=JsonDiffMatcher({
56 'HOME': '/var/spool/cwl',
59 'name': 'test_run_'+str(enable_reuse),
60 'runtime_constraints': {
64 'use_existing': enable_reuse,
67 '/tmp': {'kind': 'tmp',
68 "capacity": 1073741824
70 '/var/spool/cwl': {'kind': 'tmp',
71 "capacity": 1073741824 }
74 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
75 'output_path': '/var/spool/cwl',
77 'container_image': 'arvados/jobs',
78 'command': ['ls', '/var/spool/cwl'],
79 'cwd': '/var/spool/cwl',
80 'scheduling_parameters': {},
84 # The test passes some fields in builder.resources
85 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
86 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
87 def test_resource_requirements(self, keepdocker):
88 arv_docker_clear_cache()
89 runner = mock.MagicMock()
90 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
91 runner.ignore_docker_for_reuse = False
92 runner.intermediate_output_ttl = 3600
93 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
95 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
96 runner.api.collections().get().execute.return_value = {
97 "portable_data_hash": "99999999999999999999999999999993+99"}
103 "class": "ResourceRequirement",
109 "class": "http://arvados.org/cwl#RuntimeConstraints",
112 "class": "http://arvados.org/cwl#APIRequirement",
114 "class": "http://arvados.org/cwl#PartitionRequirement",
117 "class": "http://arvados.org/cwl#IntermediateOutput",
120 "class": "http://arvados.org/cwl#ReuseRequirement",
125 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
126 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
127 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
128 avsc_names=avsc_names, make_fs_access=make_fs_access,
130 arvtool.formatgraph = None
131 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_resource_requirements",
132 make_fs_access=make_fs_access, tmpdir="/tmp"):
133 j.run(enable_reuse=True)
135 call_args, call_kwargs = runner.api.container_requests().create.call_args
137 call_body_expected = {
139 'HOME': '/var/spool/cwl',
142 'name': 'test_resource_requirements',
143 'runtime_constraints': {
146 'keep_cache_ram': 536870912,
149 'use_existing': False,
152 '/tmp': {'kind': 'tmp',
153 "capacity": 4194304000 },
154 '/var/spool/cwl': {'kind': 'tmp',
155 "capacity": 5242880000 }
157 'state': 'Committed',
158 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
159 'output_path': '/var/spool/cwl',
161 'container_image': 'arvados/jobs',
163 'cwd': '/var/spool/cwl',
164 'scheduling_parameters': {
165 'partitions': ['blurb']
170 call_body = call_kwargs.get('body', None)
171 self.assertNotEqual(None, call_body)
172 for key in call_body:
173 self.assertEqual(call_body_expected.get(key), call_body.get(key))
176 # The test passes some fields in builder.resources
177 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
178 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
179 @mock.patch("arvados.collection.Collection")
180 def test_initial_work_dir(self, collection_mock, keepdocker):
181 arv_docker_clear_cache()
182 runner = mock.MagicMock()
183 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
184 runner.ignore_docker_for_reuse = False
185 runner.intermediate_output_ttl = 0
186 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
188 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
189 runner.api.collections().get().execute.return_value = {
190 "portable_data_hash": "99999999999999999999999999999993+99"}
192 sourcemock = mock.MagicMock()
193 def get_collection_mock(p):
195 return (sourcemock, p.split("/", 1)[1])
197 return (sourcemock, "")
198 runner.fs_access.get_collection.side_effect = get_collection_mock
200 vwdmock = mock.MagicMock()
201 collection_mock.return_value = vwdmock
202 vwdmock.portable_data_hash.return_value = "99999999999999999999999999999996+99"
208 "class": "InitialWorkDirRequirement",
212 "location": "keep:99999999999999999999999999999995+99/bar"
215 "class": "Directory",
217 "location": "keep:99999999999999999999999999999995+99"
221 "basename": "filename",
222 "location": "keep:99999999999999999999999999999995+99/baz/filename"
225 "class": "Directory",
226 "basename": "subdir",
227 "location": "keep:99999999999999999999999999999995+99/subdir"
232 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
233 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
234 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
235 avsc_names=avsc_names, make_fs_access=make_fs_access,
237 arvtool.formatgraph = None
238 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_initial_work_dir",
239 make_fs_access=make_fs_access, tmpdir="/tmp"):
242 call_args, call_kwargs = runner.api.container_requests().create.call_args
244 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
245 vwdmock.copy.assert_has_calls([mock.call('', 'foo2', source_collection=sourcemock)])
246 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
247 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
249 call_body_expected = {
251 'HOME': '/var/spool/cwl',
254 'name': 'test_initial_work_dir',
255 'runtime_constraints': {
259 'use_existing': True,
262 '/tmp': {'kind': 'tmp',
263 "capacity": 1073741824 },
264 '/var/spool/cwl': {'kind': 'tmp',
265 "capacity": 1073741824 },
266 '/var/spool/cwl/foo': {
267 'kind': 'collection',
269 'portable_data_hash': '99999999999999999999999999999996+99'
271 '/var/spool/cwl/foo2': {
272 'kind': 'collection',
274 'portable_data_hash': '99999999999999999999999999999996+99'
276 '/var/spool/cwl/filename': {
277 'kind': 'collection',
279 'portable_data_hash': '99999999999999999999999999999996+99'
281 '/var/spool/cwl/subdir': {
282 'kind': 'collection',
284 'portable_data_hash': '99999999999999999999999999999996+99'
287 'state': 'Committed',
288 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
289 'output_path': '/var/spool/cwl',
291 'container_image': 'arvados/jobs',
293 'cwd': '/var/spool/cwl',
294 'scheduling_parameters': {
299 call_body = call_kwargs.get('body', None)
300 self.assertNotEqual(None, call_body)
301 for key in call_body:
302 self.assertEqual(call_body_expected.get(key), call_body.get(key))
305 # Test redirecting stdin/stdout/stderr
306 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
307 def test_redirects(self, keepdocker):
308 arv_docker_clear_cache()
310 runner = mock.MagicMock()
311 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
312 runner.ignore_docker_for_reuse = False
313 runner.intermediate_output_ttl = 0
315 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
316 runner.api.collections().get().execute.return_value = {
317 "portable_data_hash": "99999999999999999999999999999993+99"}
319 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
325 "stdout": "stdout.txt",
326 "stderr": "stderr.txt",
327 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
328 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
330 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
331 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
332 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
333 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
334 arvtool.formatgraph = None
335 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run_redirect",
336 make_fs_access=make_fs_access, tmpdir="/tmp"):
338 runner.api.container_requests().create.assert_called_with(
339 body=JsonDiffMatcher({
341 'HOME': '/var/spool/cwl',
344 'name': 'test_run_redirect',
345 'runtime_constraints': {
349 'use_existing': True,
352 '/tmp': {'kind': 'tmp',
353 "capacity": 1073741824 },
354 '/var/spool/cwl': {'kind': 'tmp',
355 "capacity": 1073741824 },
358 "path": "/var/spool/cwl/stderr.txt"
361 "kind": "collection",
363 "portable_data_hash": "99999999999999999999999999999996+99"
367 "path": "/var/spool/cwl/stdout.txt"
370 'state': 'Committed',
371 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
372 'output_path': '/var/spool/cwl',
374 'container_image': 'arvados/jobs',
375 'command': ['ls', '/var/spool/cwl'],
376 'cwd': '/var/spool/cwl',
377 'scheduling_parameters': {},
381 @mock.patch("arvados.collection.Collection")
382 def test_done(self, col):
383 api = mock.MagicMock()
385 runner = mock.MagicMock()
387 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
388 runner.num_retries = 0
389 runner.ignore_docker_for_reuse = False
390 runner.intermediate_output_ttl = 0
392 runner.api.containers().get().execute.return_value = {"state":"Complete",
396 col().open.return_value = []
398 arvjob = arvados_cwl.ArvadosContainer(runner)
399 arvjob.name = "testjob"
400 arvjob.builder = mock.MagicMock()
401 arvjob.output_callback = mock.MagicMock()
402 arvjob.collect_outputs = mock.MagicMock()
403 arvjob.successCodes = [0]
404 arvjob.outdir = "/var/spool/cwl"
405 arvjob.output_ttl = 3600
407 arvjob.collect_outputs.return_value = {"out": "stuff"}
411 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
412 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
413 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
414 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
415 "modified_at": "2017-05-26T12:01:22Z"
418 self.assertFalse(api.collections().create.called)
420 arvjob.collect_outputs.assert_called_with("keep:abc+123")
421 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
422 runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
424 # The test passes no builder.resources
425 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
426 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
427 def test_mounts(self, keepdocker):
428 arv_docker_clear_cache()
430 runner = mock.MagicMock()
431 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
432 runner.ignore_docker_for_reuse = False
433 runner.intermediate_output_ttl = 0
435 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
436 runner.api.collections().get().execute.return_value = {
437 "portable_data_hash": "99999999999999999999999999999993+99"}
439 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
448 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
450 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
451 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
452 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
453 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
454 arvtool.formatgraph = None
457 "class": "Directory",
458 "location": "keep:99999999999999999999999999999994+44",
462 "location": "keep:99999999999999999999999999999994+44/file1",
466 "location": "keep:99999999999999999999999999999994+44/file2",
471 for j in arvtool.job(job_order, mock.MagicMock(), basedir="", name="test_run_mounts",
472 make_fs_access=make_fs_access, tmpdir="/tmp"):
474 runner.api.container_requests().create.assert_called_with(
475 body=JsonDiffMatcher({
477 'HOME': '/var/spool/cwl',
480 'name': 'test_run_mounts',
481 'runtime_constraints': {
485 'use_existing': True,
488 "/keep/99999999999999999999999999999994+44": {
489 "kind": "collection",
490 "portable_data_hash": "99999999999999999999999999999994+44"
492 '/tmp': {'kind': 'tmp',
493 "capacity": 1073741824 },
494 '/var/spool/cwl': {'kind': 'tmp',
495 "capacity": 1073741824 }
497 'state': 'Committed',
498 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
499 'output_path': '/var/spool/cwl',
501 'container_image': 'arvados/jobs',
502 'command': ['ls', '/var/spool/cwl'],
503 'cwd': '/var/spool/cwl',
504 'scheduling_parameters': {},