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 '/var/spool/cwl': {'kind': 'tmp'}
69 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
70 'output_path': '/var/spool/cwl',
71 'container_image': 'arvados/jobs',
72 'command': ['ls', '/var/spool/cwl'],
73 'cwd': '/var/spool/cwl',
74 'scheduling_parameters': {},
78 # The test passes some fields in builder.resources
79 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
80 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
81 def test_resource_requirements(self, keepdocker):
82 arv_docker_clear_cache()
83 runner = mock.MagicMock()
84 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
85 runner.ignore_docker_for_reuse = False
86 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
88 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
89 runner.api.collections().get().execute.return_value = {
90 "portable_data_hash": "99999999999999999999999999999993+99"}
96 "class": "ResourceRequirement",
101 "class": "http://arvados.org/cwl#RuntimeConstraints",
104 "class": "http://arvados.org/cwl#APIRequirement",
106 "class": "http://arvados.org/cwl#PartitionRequirement",
111 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
112 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
113 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
114 avsc_names=avsc_names, make_fs_access=make_fs_access,
116 arvtool.formatgraph = None
117 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_resource_requirements",
118 make_fs_access=make_fs_access, tmpdir="/tmp"):
121 call_args, call_kwargs = runner.api.container_requests().create.call_args
123 call_body_expected = {
125 'HOME': '/var/spool/cwl',
128 'name': 'test_resource_requirements',
129 'runtime_constraints': {
132 'keep_cache_ram': 536870912,
135 'use_existing': True,
138 '/var/spool/cwl': {'kind': 'tmp'}
140 'state': 'Committed',
141 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
142 'output_path': '/var/spool/cwl',
143 'container_image': 'arvados/jobs',
145 'cwd': '/var/spool/cwl',
146 'scheduling_parameters': {
147 'partitions': ['blurb']
152 call_body = call_kwargs.get('body', None)
153 self.assertNotEqual(None, call_body)
154 for key in call_body:
155 self.assertEqual(call_body_expected.get(key), call_body.get(key))
158 # The test passes some fields in builder.resources
159 # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
160 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
161 @mock.patch("arvados.collection.Collection")
162 def test_initial_work_dir(self, collection_mock, keepdocker):
163 arv_docker_clear_cache()
164 runner = mock.MagicMock()
165 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
166 runner.ignore_docker_for_reuse = False
167 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
169 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
170 runner.api.collections().get().execute.return_value = {
171 "portable_data_hash": "99999999999999999999999999999993+99"}
173 sourcemock = mock.MagicMock()
174 def get_collection_mock(p):
176 return (sourcemock, p.split("/", 1)[1])
178 return (sourcemock, "")
179 runner.fs_access.get_collection.side_effect = get_collection_mock
181 vwdmock = mock.MagicMock()
182 collection_mock.return_value = vwdmock
183 vwdmock.portable_data_hash.return_value = "99999999999999999999999999999996+99"
189 "class": "InitialWorkDirRequirement",
193 "location": "keep:99999999999999999999999999999995+99/bar"
196 "class": "Directory",
198 "location": "keep:99999999999999999999999999999995+99"
202 "basename": "filename",
203 "location": "keep:99999999999999999999999999999995+99/baz/filename"
206 "class": "Directory",
207 "basename": "subdir",
208 "location": "keep:99999999999999999999999999999995+99/subdir"
213 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
214 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
215 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers",
216 avsc_names=avsc_names, make_fs_access=make_fs_access,
218 arvtool.formatgraph = None
219 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_initial_work_dir",
220 make_fs_access=make_fs_access, tmpdir="/tmp"):
223 call_args, call_kwargs = runner.api.container_requests().create.call_args
225 vwdmock.copy.assert_has_calls([mock.call('bar', 'foo', source_collection=sourcemock)])
226 vwdmock.copy.assert_has_calls([mock.call('', 'foo2', source_collection=sourcemock)])
227 vwdmock.copy.assert_has_calls([mock.call('baz/filename', 'filename', source_collection=sourcemock)])
228 vwdmock.copy.assert_has_calls([mock.call('subdir', 'subdir', source_collection=sourcemock)])
230 call_body_expected = {
232 'HOME': '/var/spool/cwl',
235 'name': 'test_initial_work_dir',
236 'runtime_constraints': {
240 'use_existing': True,
243 '/var/spool/cwl': {'kind': 'tmp'},
244 '/var/spool/cwl/foo': {
245 'kind': 'collection',
247 'portable_data_hash': '99999999999999999999999999999996+99'
249 '/var/spool/cwl/foo2': {
250 'kind': 'collection',
252 'portable_data_hash': '99999999999999999999999999999996+99'
254 '/var/spool/cwl/filename': {
255 'kind': 'collection',
257 'portable_data_hash': '99999999999999999999999999999996+99'
259 '/var/spool/cwl/subdir': {
260 'kind': 'collection',
262 'portable_data_hash': '99999999999999999999999999999996+99'
265 'state': 'Committed',
266 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
267 'output_path': '/var/spool/cwl',
268 'container_image': 'arvados/jobs',
270 'cwd': '/var/spool/cwl',
271 'scheduling_parameters': {
276 call_body = call_kwargs.get('body', None)
277 self.assertNotEqual(None, call_body)
278 for key in call_body:
279 self.assertEqual(call_body_expected.get(key), call_body.get(key))
282 # Test redirecting stdin/stdout/stderr
283 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
284 def test_redirects(self, keepdocker):
285 arv_docker_clear_cache()
287 runner = mock.MagicMock()
288 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
289 runner.ignore_docker_for_reuse = False
291 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
292 runner.api.collections().get().execute.return_value = {
293 "portable_data_hash": "99999999999999999999999999999993+99"}
295 document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
301 "stdout": "stdout.txt",
302 "stderr": "stderr.txt",
303 "stdin": "/keep/99999999999999999999999999999996+99/file.txt",
304 "arguments": [{"valueFrom": "$(runtime.outdir)"}]
306 make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess,
307 collection_cache=arvados_cwl.CollectionCache(runner.api, None, 0))
308 arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
309 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
310 arvtool.formatgraph = None
311 for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run_redirect",
312 make_fs_access=make_fs_access, tmpdir="/tmp"):
314 runner.api.container_requests().create.assert_called_with(
315 body=JsonDiffMatcher({
317 'HOME': '/var/spool/cwl',
320 'name': 'test_run_redirect',
321 'runtime_constraints': {
325 'use_existing': True,
328 '/var/spool/cwl': {'kind': 'tmp'},
331 "path": "/var/spool/cwl/stderr.txt"
334 "kind": "collection",
336 "portable_data_hash": "99999999999999999999999999999996+99"
340 "path": "/var/spool/cwl/stdout.txt"
343 'state': 'Committed',
344 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
345 'output_path': '/var/spool/cwl',
346 'container_image': 'arvados/jobs',
347 'command': ['ls', '/var/spool/cwl'],
348 'cwd': '/var/spool/cwl',
349 'scheduling_parameters': {},
353 @mock.patch("arvados.collection.Collection")
354 def test_done(self, col):
355 api = mock.MagicMock()
357 runner = mock.MagicMock()
359 runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
360 runner.num_retries = 0
361 runner.ignore_docker_for_reuse = False
363 runner.api.containers().get().execute.return_value = {"state":"Complete",
367 col().open.return_value = []
369 arvjob = arvados_cwl.ArvadosContainer(runner)
370 arvjob.name = "testjob"
371 arvjob.builder = mock.MagicMock()
372 arvjob.output_callback = mock.MagicMock()
373 arvjob.collect_outputs = mock.MagicMock()
374 arvjob.successCodes = [0]
375 arvjob.outdir = "/var/spool/cwl"
377 arvjob.collect_outputs.return_value = {"out": "stuff"}
381 "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
382 "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
383 "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
384 "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
387 self.assertFalse(api.collections().create.called)
389 arvjob.collect_outputs.assert_called_with("keep:abc+123")
390 arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")