12 import arvados.collection
14 import arvados_cwl.runner
17 from .matcher import JsonDiffMatcher
18 from .mock_discovery import get_rootDesc
23 @functools.wraps(func)
24 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
25 @mock.patch("arvados.collection.KeepClient")
26 @mock.patch("arvados.keep.KeepClient")
27 @mock.patch("arvados.events.subscribe")
28 def wrapped(self, events, keep_client1, keep_client2, keepdocker, *args, **kwargs):
33 stubs.keepdocker = keepdocker
36 def putstub(p, **kwargs):
37 return "%s+%i" % (hashlib.md5(p).hexdigest(), len(p))
38 keep_client1().put.side_effect = putstub
39 keep_client1.put.side_effect = putstub
40 keep_client2().put.side_effect = putstub
41 keep_client2.put.side_effect = putstub
43 stubs.keep_client = keep_client2
44 stubs.keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
45 stubs.fake_user_uuid = "zzzzz-tpzed-zzzzzzzzzzzzzzz"
47 stubs.api = mock.MagicMock()
48 stubs.api._rootDesc = get_rootDesc()
50 stubs.api.users().current().execute.return_value = {
51 "uuid": stubs.fake_user_uuid,
53 stubs.api.collections().list().execute.return_value = {"items": []}
54 stubs.api.collections().create().execute.side_effect = ({
55 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1",
56 "portable_data_hash": "99999999999999999999999999999991+99",
59 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
60 "portable_data_hash": "99999999999999999999999999999992+99",
61 "manifest_text": "./tool 00000000000000000000000000000000+0 0:0:submit_tool.cwl 0:0:blub.txt"
64 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz4",
65 "portable_data_hash": "99999999999999999999999999999994+99",
69 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz5",
70 "portable_data_hash": "99999999999999999999999999999995+99",
74 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz6",
75 "portable_data_hash": "99999999999999999999999999999996+99",
79 stubs.api.collections().get().execute.return_value = {
80 "portable_data_hash": "99999999999999999999999999999993+99", "manifest_text": "./tool 00000000000000000000000000000000+0 0:0:submit_tool.cwl 0:0:blub.txt"}
82 stubs.expect_job_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
83 stubs.api.jobs().create().execute.return_value = {
84 "uuid": stubs.expect_job_uuid,
88 stubs.expect_container_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
89 stubs.api.container_requests().create().execute.return_value = {
90 "uuid": stubs.expect_container_request_uuid,
91 "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
95 stubs.expect_pipeline_template_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
96 stubs.api.pipeline_templates().create().execute.return_value = {
97 "uuid": stubs.expect_pipeline_template_uuid,
99 stubs.expect_job_spec = {
100 'runtime_constraints': {
101 'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
102 'min_ram_mb_per_node': 1024
104 'script_parameters': {
106 'basename': 'blorp.txt',
107 'location': 'keep:99999999999999999999999999999994+99/blorp.txt',
111 'basename': '99999999999999999999999999999998+99',
112 'location': 'keep:99999999999999999999999999999998+99',
116 'basename': 'anonymous',
118 "basename": "renamed.txt",
120 "location": "keep:99999999999999999999999999999998+99/file1.txt"
125 '99999999999999999999999999999991+99/wf/submit_wf.cwl'
127 'repository': 'arvados',
128 'script_version': 'master',
129 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
130 'script': 'cwl-runner'
132 stubs.pipeline_component = stubs.expect_job_spec.copy()
133 stubs.expect_pipeline_instance = {
134 'name': 'submit_wf.cwl',
135 'state': 'RunningOnServer',
139 'runtime_constraints': {'docker_image': 'arvados/jobs:'+arvados_cwl.__version__, 'min_ram_mb_per_node': 1024},
140 'script_parameters': {
141 'y': {"value": {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'}},
142 'x': {"value": {'basename': 'blorp.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999994+99/blorp.txt'}},
143 'z': {"value": {'basename': 'anonymous', 'class': 'Directory',
145 {'basename': 'renamed.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999998+99/file1.txt'}
147 'cwl:tool': '99999999999999999999999999999991+99/wf/submit_wf.cwl',
148 'arv:enable_reuse': True
150 'repository': 'arvados',
151 'script_version': 'master',
152 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
153 'script': 'cwl-runner',
154 'job': {'state': 'Queued', 'uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'}
158 stubs.pipeline_create = copy.deepcopy(stubs.expect_pipeline_instance)
159 stubs.expect_pipeline_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
160 stubs.pipeline_create["uuid"] = stubs.expect_pipeline_uuid
161 stubs.pipeline_with_job = copy.deepcopy(stubs.pipeline_create)
162 stubs.pipeline_with_job["components"]["cwl-runner"]["job"] = {
163 "uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
166 stubs.api.pipeline_instances().create().execute.return_value = stubs.pipeline_create
167 stubs.api.pipeline_instances().get().execute.return_value = stubs.pipeline_with_job
169 stubs.expect_container_spec = {
176 '/var/lib/cwl/workflow': {
177 'portable_data_hash': '99999999999999999999999999999991+99',
181 'path': '/var/spool/cwl/cwl.output.json',
184 '/var/lib/cwl/cwl.input.json': {
187 'y': {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'},
188 'x': {'basename': u'blorp.txt', 'class': 'File', 'location': u'keep:99999999999999999999999999999994+99/blorp.txt'},
189 'z': {'basename': 'anonymous', 'class': 'Directory', 'listing': [
190 {'basename': 'renamed.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999998+99/file1.txt'}
196 'state': 'Committed',
198 'command': ['arvados-cwl-runner', '--local', '--api=containers', '--enable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json'],
199 'name': 'submit_wf.cwl',
200 'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
201 'output_path': '/var/spool/cwl',
202 'cwd': '/var/spool/cwl',
203 'runtime_constraints': {
206 'ram': 1024*1024*1024
211 stubs.expect_workflow_uuid = "zzzzz-7fd4e-zzzzzzzzzzzzzzz"
212 stubs.api.workflows().create().execute.return_value = {
213 "uuid": stubs.expect_workflow_uuid,
215 def update_mock(**kwargs):
216 stubs.updated_uuid = kwargs.get('uuid')
218 stubs.api.workflows().update.side_effect = update_mock
219 stubs.api.workflows().update().execute.side_effect = lambda **kwargs: {
220 "uuid": stubs.updated_uuid,
223 return func(self, stubs, *args, **kwargs)
227 class TestSubmit(unittest.TestCase):
228 @mock.patch("time.sleep")
230 def test_submit(self, stubs, tm):
231 capture_stdout = cStringIO.StringIO()
232 exited = arvados_cwl.main(
233 ["--submit", "--no-wait", "--debug",
234 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
235 capture_stdout, sys.stderr, api_client=stubs.api)
236 self.assertEqual(exited, 0)
238 stubs.api.collections().create.assert_has_calls([
242 './tool d51232d96b6116d964a69bfb7e0c73bf+450 '
243 '0:16:blub.txt 16:434:submit_tool.cwl\n./wf '
244 'cc2ffb940e60adf1b2b282c67587e43d+413 0:413:submit_wf.cwl\n',
246 'name': 'submit_wf.cwl',
247 }, ensure_unique_name=True),
248 mock.call().execute(),
249 mock.call(body={'manifest_text': '. d41d8cd98f00b204e9800998ecf8427e+0 '
250 '0:0:blub.txt 0:0:submit_tool.cwl\n',
251 'replication_desired': None,
252 'name': 'New collection'
253 }, ensure_unique_name=True),
254 mock.call().execute(num_retries=4),
257 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
260 }, ensure_unique_name=True),
261 mock.call().execute()])
263 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
264 stubs.api.pipeline_instances().create.assert_called_with(
265 body=expect_pipeline)
266 self.assertEqual(capture_stdout.getvalue(),
267 stubs.expect_pipeline_uuid + '\n')
270 @mock.patch("time.sleep")
272 def test_submit_no_reuse(self, stubs, tm):
273 capture_stdout = cStringIO.StringIO()
274 exited = arvados_cwl.main(
275 ["--submit", "--no-wait", "--debug", "--disable-reuse",
276 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
277 capture_stdout, sys.stderr, api_client=stubs.api)
278 self.assertEqual(exited, 0)
280 stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:enable_reuse"] = {"value": False}
282 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
283 stubs.api.pipeline_instances().create.assert_called_with(
284 body=JsonDiffMatcher(expect_pipeline))
285 self.assertEqual(capture_stdout.getvalue(),
286 stubs.expect_pipeline_uuid + '\n')
289 @mock.patch("time.sleep")
291 def test_submit_runner_ram(self, stubs, tm):
292 capture_stdout = cStringIO.StringIO()
293 exited = arvados_cwl.main(
294 ["--submit", "--no-wait", "--debug", "--submit-runner-ram=2048",
295 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
296 capture_stdout, sys.stderr, api_client=stubs.api)
297 self.assertEqual(exited, 0)
299 stubs.expect_pipeline_instance["components"]["cwl-runner"]["runtime_constraints"]["min_ram_mb_per_node"] = 2048
301 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
302 stubs.api.pipeline_instances().create.assert_called_with(
303 body=JsonDiffMatcher(expect_pipeline))
304 self.assertEqual(capture_stdout.getvalue(),
305 stubs.expect_pipeline_uuid + '\n')
308 @mock.patch("time.sleep")
310 def test_submit_invalid_runner_ram(self, stubs, tm):
311 capture_stdout = cStringIO.StringIO()
312 exited = arvados_cwl.main(
313 ["--submit", "--no-wait", "--debug", "--submit-runner-ram=-2048",
314 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
315 capture_stdout, sys.stderr, api_client=stubs.api)
316 self.assertEqual(exited, 1)
318 @mock.patch("time.sleep")
320 def test_submit_output_name(self, stubs, tm):
321 output_name = "test_output_name"
323 capture_stdout = cStringIO.StringIO()
324 exited = arvados_cwl.main(
325 ["--submit", "--no-wait", "--debug", "--output-name", output_name,
326 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
327 capture_stdout, sys.stderr, api_client=stubs.api)
328 self.assertEqual(exited, 0)
330 stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:output_name"] = output_name
332 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
333 stubs.api.pipeline_instances().create.assert_called_with(
334 body=JsonDiffMatcher(expect_pipeline))
335 self.assertEqual(capture_stdout.getvalue(),
336 stubs.expect_pipeline_uuid + '\n')
339 @mock.patch("time.sleep")
341 def test_submit_pipeline_name(self, stubs, tm):
342 capture_stdout = cStringIO.StringIO()
343 exited = arvados_cwl.main(
344 ["--submit", "--no-wait", "--debug", "--name=hello job 123",
345 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
346 capture_stdout, sys.stderr, api_client=stubs.api)
347 self.assertEqual(exited, 0)
349 stubs.expect_pipeline_instance["name"] = "hello job 123"
351 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
352 stubs.api.pipeline_instances().create.assert_called_with(
353 body=expect_pipeline)
354 self.assertEqual(capture_stdout.getvalue(),
355 stubs.expect_pipeline_uuid + '\n')
357 @mock.patch("time.sleep")
359 def test_submit_output_tags(self, stubs, tm):
360 output_tags = "tag0,tag1,tag2"
362 capture_stdout = cStringIO.StringIO()
363 exited = arvados_cwl.main(
364 ["--submit", "--no-wait", "--debug", "--output-tags", output_tags,
365 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
366 capture_stdout, sys.stderr, api_client=stubs.api)
367 self.assertEqual(exited, 0)
369 stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:output_tags"] = output_tags
371 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
372 stubs.api.pipeline_instances().create.assert_called_with(
373 body=JsonDiffMatcher(expect_pipeline))
374 self.assertEqual(capture_stdout.getvalue(),
375 stubs.expect_pipeline_uuid + '\n')
377 @mock.patch("time.sleep")
379 def test_submit_with_project_uuid(self, stubs, tm):
380 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
382 exited = arvados_cwl.main(
383 ["--submit", "--no-wait",
384 "--project-uuid", project_uuid,
385 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
386 sys.stdout, sys.stderr, api_client=stubs.api)
387 self.assertEqual(exited, 0)
389 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
390 expect_pipeline["owner_uuid"] = project_uuid
391 stubs.api.pipeline_instances().create.assert_called_with(
392 body=JsonDiffMatcher(expect_pipeline))
395 def test_submit_container(self, stubs):
396 capture_stdout = cStringIO.StringIO()
398 exited = arvados_cwl.main(
399 ["--submit", "--no-wait", "--api=containers", "--debug",
400 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
401 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
402 self.assertEqual(exited, 0)
404 logging.exception("")
406 stubs.api.collections().create.assert_has_calls([
410 './tool d51232d96b6116d964a69bfb7e0c73bf+450 '
411 '0:16:blub.txt 16:434:submit_tool.cwl\n./wf '
412 'cc2ffb940e60adf1b2b282c67587e43d+413 0:413:submit_wf.cwl\n',
414 'name': 'submit_wf.cwl',
415 }, ensure_unique_name=True),
416 mock.call().execute(),
417 mock.call(body={'manifest_text': '. d41d8cd98f00b204e9800998ecf8427e+0 '
418 '0:0:blub.txt 0:0:submit_tool.cwl\n',
419 'name': 'New collection',
420 'replication_desired': None,
421 }, ensure_unique_name=True),
422 mock.call().execute(num_retries=4),
425 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
428 }, ensure_unique_name=True),
429 mock.call().execute()])
431 expect_container = copy.deepcopy(stubs.expect_container_spec)
432 stubs.api.container_requests().create.assert_called_with(
433 body=JsonDiffMatcher(expect_container))
434 self.assertEqual(capture_stdout.getvalue(),
435 stubs.expect_container_request_uuid + '\n')
438 def test_submit_container_no_reuse(self, stubs):
439 capture_stdout = cStringIO.StringIO()
441 exited = arvados_cwl.main(
442 ["--submit", "--no-wait", "--api=containers", "--debug", "--disable-reuse",
443 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
444 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
445 self.assertEqual(exited, 0)
447 logging.exception("")
449 stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--disable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
451 expect_container = copy.deepcopy(stubs.expect_container_spec)
452 stubs.api.container_requests().create.assert_called_with(
453 body=JsonDiffMatcher(expect_container))
454 self.assertEqual(capture_stdout.getvalue(),
455 stubs.expect_container_request_uuid + '\n')
458 def test_submit_container_output_name(self, stubs):
459 output_name = "test_output_name"
461 capture_stdout = cStringIO.StringIO()
463 exited = arvados_cwl.main(
464 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-name", output_name,
465 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
466 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
467 self.assertEqual(exited, 0)
469 logging.exception("")
471 stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', "--output-name="+output_name, '--enable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
473 expect_container = copy.deepcopy(stubs.expect_container_spec)
474 stubs.api.container_requests().create.assert_called_with(
475 body=JsonDiffMatcher(expect_container))
476 self.assertEqual(capture_stdout.getvalue(),
477 stubs.expect_container_request_uuid + '\n')
480 def test_submit_container_output_tags(self, stubs):
481 output_tags = "tag0,tag1,tag2"
483 capture_stdout = cStringIO.StringIO()
485 exited = arvados_cwl.main(
486 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-tags", output_tags,
487 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
488 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
489 self.assertEqual(exited, 0)
491 logging.exception("")
493 stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', "--output-tags="+output_tags, '--enable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/cwl.input.json']
495 expect_container = copy.deepcopy(stubs.expect_container_spec)
496 stubs.api.container_requests().create.assert_called_with(
497 body=JsonDiffMatcher(expect_container))
498 self.assertEqual(capture_stdout.getvalue(),
499 stubs.expect_container_request_uuid + '\n')
502 def test_submit_container_runner_ram(self, stubs):
503 capture_stdout = cStringIO.StringIO()
505 exited = arvados_cwl.main(
506 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-ram=2048",
507 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
508 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
509 self.assertEqual(exited, 0)
511 logging.exception("")
513 stubs.expect_container_spec["runtime_constraints"]["ram"] = 2048*1024*1024
515 expect_container = copy.deepcopy(stubs.expect_container_spec)
516 stubs.api.container_requests().create.assert_called_with(
517 body=JsonDiffMatcher(expect_container))
518 self.assertEqual(capture_stdout.getvalue(),
519 stubs.expect_container_request_uuid + '\n')
521 @mock.patch("arvados.collection.CollectionReader")
522 @mock.patch("time.sleep")
524 def test_submit_file_keepref(self, stubs, tm, collectionReader):
525 capture_stdout = cStringIO.StringIO()
526 exited = arvados_cwl.main(
527 ["--submit", "--no-wait", "--api=containers", "--debug",
528 "tests/wf/submit_keepref_wf.cwl"],
529 capture_stdout, sys.stderr, api_client=stubs.api)
530 self.assertEqual(exited, 0)
533 @mock.patch("arvados.collection.CollectionReader")
534 @mock.patch("time.sleep")
536 def test_submit_keepref(self, stubs, tm, reader):
537 capture_stdout = cStringIO.StringIO()
539 with open("tests/wf/expect_arvworkflow.cwl") as f:
540 reader().open().__enter__().read.return_value = f.read()
542 exited = arvados_cwl.main(
543 ["--submit", "--no-wait", "--api=containers", "--debug",
544 "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
545 capture_stdout, sys.stderr, api_client=stubs.api)
546 self.assertEqual(exited, 0)
556 'path': '/var/spool/cwl/cwl.output.json',
559 '/var/lib/cwl/workflow': {
560 'portable_data_hash': '99999999999999999999999999999994+99',
563 '/var/lib/cwl/cwl.input.json': {
569 }, 'state': 'Committed',
571 'output_path': '/var/spool/cwl',
572 'name': 'expect_arvworkflow.cwl#main',
573 'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
574 'command': ['arvados-cwl-runner', '--local', '--api=containers', '--enable-reuse', '/var/lib/cwl/workflow/expect_arvworkflow.cwl#main', '/var/lib/cwl/cwl.input.json'],
575 'cwd': '/var/spool/cwl',
576 'runtime_constraints': {
584 stubs.api.container_requests().create.assert_called_with(
585 body=JsonDiffMatcher(expect_container))
586 self.assertEqual(capture_stdout.getvalue(),
587 stubs.expect_container_request_uuid + '\n')
589 @mock.patch("time.sleep")
591 def test_submit_arvworkflow(self, stubs, tm):
592 capture_stdout = cStringIO.StringIO()
594 with open("tests/wf/expect_arvworkflow.cwl") as f:
595 stubs.api.workflows().get().execute.return_value = {"definition": f.read(), "name": "a test workflow"}
597 exited = arvados_cwl.main(
598 ["--submit", "--no-wait", "--api=containers", "--debug",
599 "962eh-7fd4e-gkbzl62qqtfig37", "-x", "XxX"],
600 capture_stdout, sys.stderr, api_client=stubs.api)
601 self.assertEqual(exited, 0)
611 'path': '/var/spool/cwl/cwl.output.json',
614 '/var/lib/cwl/workflow.json': {
617 'cwlVersion': 'v1.0',
622 'inputBinding': {'position': 1},
624 'id': '#submit_tool.cwl/x'}
627 {'dockerPull': 'debian:8', 'class': 'DockerRequirement'}
629 'id': '#submit_tool.cwl',
631 'baseCommand': 'cat',
632 'class': 'CommandLineTool'
636 {'type': 'string', 'id': '#main/x'}
639 {'in': [{'source': '#main/x', 'id': '#main/step1/x'}],
640 'run': '#submit_tool.cwl',
650 '/var/lib/cwl/cwl.input.json': {
656 }, 'state': 'Committed',
658 'output_path': '/var/spool/cwl',
659 'name': 'a test workflow',
660 'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
661 'command': ['arvados-cwl-runner', '--local', '--api=containers', '--enable-reuse', '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
662 'cwd': '/var/spool/cwl',
663 'runtime_constraints': {
669 "template_uuid": "962eh-7fd4e-gkbzl62qqtfig37"
673 stubs.api.container_requests().create.assert_called_with(
674 body=JsonDiffMatcher(expect_container))
675 self.assertEqual(capture_stdout.getvalue(),
676 stubs.expect_container_request_uuid + '\n')
680 def test_submit_container_name(self, stubs):
681 capture_stdout = cStringIO.StringIO()
683 exited = arvados_cwl.main(
684 ["--submit", "--no-wait", "--api=containers", "--debug", "--name=hello container 123",
685 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
686 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
687 self.assertEqual(exited, 0)
689 logging.exception("")
691 stubs.expect_container_spec["name"] = "hello container 123"
693 expect_container = copy.deepcopy(stubs.expect_container_spec)
694 stubs.api.container_requests().create.assert_called_with(
695 body=expect_container)
696 self.assertEqual(capture_stdout.getvalue(),
697 stubs.expect_container_request_uuid + '\n')
700 @mock.patch("arvados.commands.keepdocker.find_one_image_hash")
701 @mock.patch("cwltool.docker.get_image")
702 @mock.patch("arvados.api")
703 def test_arvados_jobs_image(self, api, get_image, find_one_image_hash):
704 arvrunner = mock.MagicMock()
705 arvrunner.project_uuid = ""
706 api.return_value = mock.MagicMock()
707 arvrunner.api = api.return_value
708 arvrunner.api.links().list().execute.side_effect = ({"items": [], "items_available": 0, "offset": 0},
709 {"items": [], "items_available": 0, "offset": 0},
710 {"items": [], "items_available": 0, "offset": 0},
711 {"items": [{"created_at": "",
713 "link_class": "docker_image_hash",
716 "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0},
717 {"items": [], "items_available": 0, "offset": 0},
718 {"items": [{"created_at": "",
720 "link_class": "docker_image_repo+tag",
721 "name": "arvados/jobs:"+arvados_cwl.__version__,
723 "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0},
724 {"items": [{"created_at": "",
726 "link_class": "docker_image_hash",
729 "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0} ,
731 find_one_image_hash.return_value = "123456"
733 arvrunner.api.collections().list().execute.side_effect = ({"items": [], "items_available": 0, "offset": 0},
734 {"items": [{"uuid": "",
738 }], "items_available": 1, "offset": 0},
739 {"items": [{"uuid": ""}], "items_available": 1, "offset": 0})
740 arvrunner.api.collections().create().execute.return_value = {"uuid": ""}
741 self.assertEqual("arvados/jobs:"+arvados_cwl.__version__, arvados_cwl.runner.arvados_jobs_image(arvrunner))
743 class TestCreateTemplate(unittest.TestCase):
744 existing_template_uuid = "zzzzz-d1hrv-validworkfloyml"
746 def _adjust_script_params(self, expect_component):
747 expect_component['script_parameters']['x'] = {
751 'value': '99999999999999999999999999999994+99/blorp.txt',
753 expect_component['script_parameters']['y'] = {
754 'dataclass': 'Collection',
757 'value': '99999999999999999999999999999998+99',
759 expect_component['script_parameters']['z'] = {
760 'dataclass': 'Collection',
766 def test_create(self, stubs):
767 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
769 capture_stdout = cStringIO.StringIO()
771 exited = arvados_cwl.main(
772 ["--create-workflow", "--debug",
774 "--project-uuid", project_uuid,
775 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
776 capture_stdout, sys.stderr, api_client=stubs.api)
777 self.assertEqual(exited, 0)
779 stubs.api.pipeline_instances().create.refute_called()
780 stubs.api.jobs().create.refute_called()
782 expect_component = copy.deepcopy(stubs.expect_job_spec)
783 self._adjust_script_params(expect_component)
786 "submit_wf.cwl": expect_component,
788 "name": "submit_wf.cwl",
789 "owner_uuid": project_uuid,
791 stubs.api.pipeline_templates().create.assert_called_with(
792 body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
794 self.assertEqual(capture_stdout.getvalue(),
795 stubs.expect_pipeline_template_uuid + '\n')
799 def test_create_name(self, stubs):
800 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
802 capture_stdout = cStringIO.StringIO()
804 exited = arvados_cwl.main(
805 ["--create-workflow", "--debug",
806 "--project-uuid", project_uuid,
808 "--name", "testing 123",
809 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
810 capture_stdout, sys.stderr, api_client=stubs.api)
811 self.assertEqual(exited, 0)
813 stubs.api.pipeline_instances().create.refute_called()
814 stubs.api.jobs().create.refute_called()
816 expect_component = copy.deepcopy(stubs.expect_job_spec)
817 self._adjust_script_params(expect_component)
820 "testing 123": expect_component,
822 "name": "testing 123",
823 "owner_uuid": project_uuid,
825 stubs.api.pipeline_templates().create.assert_called_with(
826 body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
828 self.assertEqual(capture_stdout.getvalue(),
829 stubs.expect_pipeline_template_uuid + '\n')
833 def test_update_name(self, stubs):
834 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
836 capture_stdout = cStringIO.StringIO()
838 exited = arvados_cwl.main(
839 ["--update-workflow", self.existing_template_uuid,
841 "--project-uuid", project_uuid,
843 "--name", "testing 123",
844 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
845 capture_stdout, sys.stderr, api_client=stubs.api)
846 self.assertEqual(exited, 0)
848 stubs.api.pipeline_instances().create.refute_called()
849 stubs.api.jobs().create.refute_called()
851 expect_component = copy.deepcopy(stubs.expect_job_spec)
852 self._adjust_script_params(expect_component)
855 "testing 123": expect_component,
857 "name": "testing 123",
858 "owner_uuid": project_uuid,
860 stubs.api.pipeline_templates().create.refute_called()
861 stubs.api.pipeline_templates().update.assert_called_with(
862 body=JsonDiffMatcher(expect_template), uuid=self.existing_template_uuid)
864 self.assertEqual(capture_stdout.getvalue(),
865 self.existing_template_uuid + '\n')
868 class TestCreateWorkflow(unittest.TestCase):
869 existing_workflow_uuid = "zzzzz-7fd4e-validworkfloyml"
870 expect_workflow = open("tests/wf/expect_packed.cwl").read()
873 def test_create(self, stubs):
874 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
876 capture_stdout = cStringIO.StringIO()
878 exited = arvados_cwl.main(
879 ["--create-workflow", "--debug",
881 "--project-uuid", project_uuid,
882 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
883 capture_stdout, sys.stderr, api_client=stubs.api)
884 self.assertEqual(exited, 0)
886 stubs.api.pipeline_templates().create.refute_called()
887 stubs.api.container_requests().create.refute_called()
891 "owner_uuid": project_uuid,
892 "name": "submit_wf.cwl",
894 "definition": self.expect_workflow,
897 stubs.api.workflows().create.assert_called_with(
898 body=JsonDiffMatcher(body))
900 self.assertEqual(capture_stdout.getvalue(),
901 stubs.expect_workflow_uuid + '\n')
905 def test_create_name(self, stubs):
906 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
908 capture_stdout = cStringIO.StringIO()
910 exited = arvados_cwl.main(
911 ["--create-workflow", "--debug",
913 "--project-uuid", project_uuid,
914 "--name", "testing 123",
915 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
916 capture_stdout, sys.stderr, api_client=stubs.api)
917 self.assertEqual(exited, 0)
919 stubs.api.pipeline_templates().create.refute_called()
920 stubs.api.container_requests().create.refute_called()
924 "owner_uuid": project_uuid,
925 "name": "testing 123",
927 "definition": self.expect_workflow,
930 stubs.api.workflows().create.assert_called_with(
931 body=JsonDiffMatcher(body))
933 self.assertEqual(capture_stdout.getvalue(),
934 stubs.expect_workflow_uuid + '\n')
937 def test_incompatible_api(self, stubs):
938 capture_stderr = cStringIO.StringIO()
939 logging.getLogger('arvados.cwl-runner').addHandler(
940 logging.StreamHandler(capture_stderr))
942 exited = arvados_cwl.main(
943 ["--update-workflow", self.existing_workflow_uuid,
946 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
947 sys.stderr, sys.stderr, api_client=stubs.api)
948 self.assertEqual(exited, 1)
949 self.assertRegexpMatches(
950 capture_stderr.getvalue(),
951 "--update-workflow arg '{}' uses 'containers' API, but --api='jobs' specified".format(self.existing_workflow_uuid))
954 def test_update(self, stubs):
955 capture_stdout = cStringIO.StringIO()
957 exited = arvados_cwl.main(
958 ["--update-workflow", self.existing_workflow_uuid,
960 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
961 capture_stdout, sys.stderr, api_client=stubs.api)
962 self.assertEqual(exited, 0)
966 "name": "submit_wf.cwl",
968 "definition": self.expect_workflow,
971 stubs.api.workflows().update.assert_called_with(
972 uuid=self.existing_workflow_uuid,
973 body=JsonDiffMatcher(body))
974 self.assertEqual(capture_stdout.getvalue(),
975 self.existing_workflow_uuid + '\n')
979 def test_update_name(self, stubs):
980 capture_stdout = cStringIO.StringIO()
982 exited = arvados_cwl.main(
983 ["--update-workflow", self.existing_workflow_uuid,
984 "--debug", "--name", "testing 123",
985 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
986 capture_stdout, sys.stderr, api_client=stubs.api)
987 self.assertEqual(exited, 0)
991 "name": "testing 123",
993 "definition": self.expect_workflow,
996 stubs.api.workflows().update.assert_called_with(
997 uuid=self.existing_workflow_uuid,
998 body=JsonDiffMatcher(body))
999 self.assertEqual(capture_stdout.getvalue(),
1000 self.existing_workflow_uuid + '\n')
1003 class TestTemplateInputs(unittest.TestCase):
1006 "inputs_test.cwl": {
1007 'runtime_constraints': {
1008 'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
1009 'min_ram_mb_per_node': 1024
1011 'script_parameters': {
1013 '99999999999999999999999999999991+99/'
1014 'wf/inputs_test.cwl',
1015 'optionalFloatInput': None,
1018 'dataclass': 'File',
1020 'title': "It's a file; we expect to find some characters in it.",
1021 'description': 'If there were anything further to say, it would be said here,\nor here.'
1025 'dataclass': 'number',
1027 'title': 'Floats like a duck',
1031 'optionalFloatInput': {
1032 'type': ['null', 'float'],
1033 'dataclass': 'number',
1038 'dataclass': 'boolean',
1040 'title': 'True or false?',
1043 'repository': 'arvados',
1044 'script_version': 'master',
1045 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
1046 'script': 'cwl-runner',
1049 "name": "inputs_test.cwl",
1053 def test_inputs_empty(self, stubs):
1054 exited = arvados_cwl.main(
1055 ["--create-template",
1056 "tests/wf/inputs_test.cwl", "tests/order/empty_order.json"],
1057 cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
1058 self.assertEqual(exited, 0)
1060 stubs.api.pipeline_templates().create.assert_called_with(
1061 body=JsonDiffMatcher(self.expect_template), ensure_unique_name=True)
1064 def test_inputs(self, stubs):
1065 exited = arvados_cwl.main(
1066 ["--create-template",
1067 "tests/wf/inputs_test.cwl", "tests/order/inputs_test_order.json"],
1068 cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
1069 self.assertEqual(exited, 0)
1071 expect_template = copy.deepcopy(self.expect_template)
1072 params = expect_template[
1073 "components"]["inputs_test.cwl"]["script_parameters"]
1074 params["fileInput"]["value"] = '99999999999999999999999999999994+99/blorp.txt'
1075 params["floatInput"]["value"] = 1.234
1076 params["boolInput"]["value"] = True
1078 stubs.api.pipeline_templates().create.assert_called_with(
1079 body=JsonDiffMatcher(expect_template), ensure_unique_name=True)