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
32 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
33 runner.api.collections().get().execute.return_value = {
34 "portable_data_hash": "99999999999999999999999999999993+99"}
36 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
42 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
44 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
45 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
46 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
47 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
48 arvtool.formatgraph = None
49 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run_"+str(enable_reuse),
50 make_fs_access=make_fs_access, tmpdir="/tmp"):
51 j.run(enable_reuse=enable_reuse)
52 runner.api.container_requests().create.assert_called_with(
53 body=JsonDiffMatcher({
55 'HOME': '/var/spool/cwl',
58 'name': 'test_run_'+str(enable_reuse),
59 'runtime_constraints': {
63 'use_existing': enable_reuse,
66 '/tmp': {'kind': 'tmp'},
67 '/var/spool/cwl': {'kind': 'tmp'}
70 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
71 'output_path': '/var/spool/cwl',
72 'container_image': 'arvados/jobs',
73 'command': ['ls', '/var/spool/cwl'],
74 'cwd': '/var/spool/cwl',
75 'scheduling_parameters': {},
79 # The test passes some fields in builder.resources
80 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
81 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
82 def test_resource_requirements(self, keepdocker):
83 arv_docker_clear_cache()
84 runner = mock.MagicMock()
85 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
86 runner.ignore_docker_for_reuse = False
87 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
89 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
90 runner.api.collections().get().execute.return_value = {
91 "portable_data_hash": "99999999999999999999999999999993+99"}
97 "class": "ResourceRequirement",
102 "class": "http://arvados.org/cwl#RuntimeConstraints",
105 "class": "http://arvados.org/cwl#APIRequirement",
107 "class": "http://arvados.org/cwl#PartitionRequirement",
112 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
113 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
114 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
115 avsc_names=avsc_names, make_fs_access=make_fs_access,
117 arvtool.formatgraph = None
118 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_resource_requirements",
119 make_fs_access=make_fs_access, tmpdir="/tmp"):
122 call_args, call_kwargs = runner.api.container_requests().create.call_args
124 call_body_expected = {
126 'HOME': '/var/spool/cwl',
129 'name': 'test_resource_requirements',
130 'runtime_constraints': {
133 'keep_cache_ram': 536870912,
136 'use_existing': True,
139 '/tmp': {'kind': 'tmp'},
140 '/var/spool/cwl': {'kind': 'tmp'}
142 'state': 'Committed',
143 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
144 'output_path': '/var/spool/cwl',
145 'container_image': 'arvados/jobs',
147 'cwd': '/var/spool/cwl',
148 'scheduling_parameters': {
149 'partitions': ['blurb']
154 call_body = call_kwargs.get('body', None)
155 self.assertNotEqual(None, call_body)
156 for key in call_body:
157 self.assertEqual(call_body_expected.get(key), call_body.get(key))
160 # The test passes some fields in builder.resources
161 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
162 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
163 @mock.patch("arvados.collection.Collection")
164 def test_initial_work_dir(self, collection_mock, keepdocker):
165 arv_docker_clear_cache()
166 runner = mock.MagicMock()
167 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
168 runner.ignore_docker_for_reuse = False
169 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
171 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
172 runner.api.collections().get().execute.return_value = {
173 "portable_data_hash": "99999999999999999999999999999993+99"}
175 sourcemock = mock.MagicMock()
176 def get_collection_mock(p):
178 return (sourcemock, p.split("/", 1)[1])
180 return (sourcemock, "")
181 runner.fs_access.get_collection.side_effect = get_collection_mock
183 vwdmock = mock.MagicMock()
184 collection_mock.return_value = vwdmock
185 vwdmock.portable_data_hash.return_value = "99999999999999999999999999999996+99"
191 "class": "InitialWorkDirRequirement",
195 "location": "keep:99999999999999999999999999999995+99/bar"
198 "class": "Directory",
200 "location": "keep:99999999999999999999999999999995+99"
204 "basename": "filename",
205 "location": "keep:99999999999999999999999999999995+99/baz/filename"
208 "class": "Directory",
209 "basename": "subdir",
210 "location": "keep:99999999999999999999999999999995+99/subdir"
215 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
216 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
217 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
218 avsc_names=avsc_names, make_fs_access=make_fs_access,
220 arvtool.formatgraph = None
221 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_initial_work_dir",
222 make_fs_access=make_fs_access, tmpdir="/tmp"):
225 call_args, call_kwargs = runner.api.container_requests().create.call_args
227 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
228 vwdmock.copy.assert_has_calls([mock.call('', 'foo2', source_collection=sourcemock)])
229 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
230 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
232 call_body_expected = {
234 'HOME': '/var/spool/cwl',
237 'name': 'test_initial_work_dir',
238 'runtime_constraints': {
242 'use_existing': True,
245 '/tmp': {'kind': 'tmp'},
246 '/var/spool/cwl': {'kind': 'tmp'},
247 '/var/spool/cwl/foo': {
248 'kind': 'collection',
250 'portable_data_hash': '99999999999999999999999999999996+99'
252 '/var/spool/cwl/foo2': {
253 'kind': 'collection',
255 'portable_data_hash': '99999999999999999999999999999996+99'
257 '/var/spool/cwl/filename': {
258 'kind': 'collection',
260 'portable_data_hash': '99999999999999999999999999999996+99'
262 '/var/spool/cwl/subdir': {
263 'kind': 'collection',
265 'portable_data_hash': '99999999999999999999999999999996+99'
268 'state': 'Committed',
269 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
270 'output_path': '/var/spool/cwl',
271 'container_image': 'arvados/jobs',
273 'cwd': '/var/spool/cwl',
274 'scheduling_parameters': {
279 call_body = call_kwargs.get('body', None)
280 self.assertNotEqual(None, call_body)
281 for key in call_body:
282 self.assertEqual(call_body_expected.get(key), call_body.get(key))
285 # Test redirecting stdin/stdout/stderr
286 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
287 def test_redirects(self, keepdocker):
288 arv_docker_clear_cache()
290 runner = mock.MagicMock()
291 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
292 runner.ignore_docker_for_reuse = False
294 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
295 runner.api.collections().get().execute.return_value = {
296 "portable_data_hash": "99999999999999999999999999999993+99"}
298 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
304 "stdout": "stdout.txt",
305 "stderr": "stderr.txt",
306 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
307 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
309 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
310 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
311 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
312 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
313 arvtool.formatgraph = None
314 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run_redirect",
315 make_fs_access=make_fs_access, tmpdir="/tmp"):
317 runner.api.container_requests().create.assert_called_with(
318 body=JsonDiffMatcher({
320 'HOME': '/var/spool/cwl',
323 'name': 'test_run_redirect',
324 'runtime_constraints': {
328 'use_existing': True,
331 '/tmp': {'kind': 'tmp'},
332 '/var/spool/cwl': {'kind': 'tmp'},
335 "path": "/var/spool/cwl/stderr.txt"
338 "kind": "collection",
340 "portable_data_hash": "99999999999999999999999999999996+99"
344 "path": "/var/spool/cwl/stdout.txt"
347 'state': 'Committed',
348 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
349 'output_path': '/var/spool/cwl',
350 'container_image': 'arvados/jobs',
351 'command': ['ls', '/var/spool/cwl'],
352 'cwd': '/var/spool/cwl',
353 'scheduling_parameters': {},
357 @mock.patch("arvados.collection.Collection")
358 def test_done(self, col):
359 api = mock.MagicMock()
361 runner = mock.MagicMock()
363 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
364 runner.num_retries = 0
365 runner.ignore_docker_for_reuse = False
367 runner.api.containers().get().execute.return_value = {"state":"Complete",
371 col().open.return_value = []
373 arvjob = arvados_cwl.ArvadosContainer(runner)
374 arvjob.name = "testjob"
375 arvjob.builder = mock.MagicMock()
376 arvjob.output_callback = mock.MagicMock()
377 arvjob.collect_outputs = mock.MagicMock()
378 arvjob.successCodes = [0]
379 arvjob.outdir = "/var/spool/cwl"
381 arvjob.collect_outputs.return_value = {"out": "stuff"}
385 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
386 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
387 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
388 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
391 self.assertFalse(api.collections().create.called)
393 arvjob.collect_outputs.assert_called_with("keep:abc+123")
394 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
396 # The test passes no builder.resources
397 # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
398 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
399 def test_mounts(self, keepdocker):
400 arv_docker_clear_cache()
402 runner = mock.MagicMock()
403 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
404 runner.ignore_docker_for_reuse = False
406 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
407 runner.api.collections().get().execute.return_value = {
408 "portable_data_hash": "99999999999999999999999999999993+99"}
410 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
419 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
421 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
422 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
423 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
424 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
425 arvtool.formatgraph = None
428 "class": "Directory",
429 "location": "keep:99999999999999999999999999999994+44",
433 "location": "keep:99999999999999999999999999999994+44/file1",
437 "location": "keep:99999999999999999999999999999994+44/file2",
442 for j in arvtool.job(job_order, mock.MagicMock(), basedir="", name="test_run_mounts",
443 make_fs_access=make_fs_access, tmpdir="/tmp"):
445 runner.api.container_requests().create.assert_called_with(
446 body=JsonDiffMatcher({
448 'HOME': '/var/spool/cwl',
451 'name': 'test_run_mounts',
452 'runtime_constraints': {
456 'use_existing': True,
459 "/keep/99999999999999999999999999999994+44": {
460 "kind": "collection",
461 "portable_data_hash": "99999999999999999999999999999994+44"
463 '/tmp': {'kind': 'tmp'},
464 '/var/spool/cwl': {'kind': 'tmp'}
466 'state': 'Committed',
467 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
468 'output_path': '/var/spool/cwl',
469 'container_image': 'arvados/jobs',
470 'command': ['ls', '/var/spool/cwl'],
471 'cwd': '/var/spool/cwl',
472 'scheduling_parameters': {},