1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: Apache-2.0
5 from future import standard_library
6 standard_library.install_aliases()
7 from builtins import object
8 from builtins import str
9 from future.utils import viewvalues
21 from io import BytesIO, StringIO
24 import arvados.collection
26 import arvados_cwl.executor
27 import arvados_cwl.runner
30 from .matcher import JsonDiffMatcher, StripYAMLComments
31 from .mock_discovery import get_rootDesc
33 import ruamel.yaml as yaml
38 @functools.wraps(func)
39 @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
40 @mock.patch("arvados.collection.KeepClient")
41 @mock.patch("arvados.keep.KeepClient")
42 @mock.patch("arvados.events.subscribe")
43 def wrapped(self, events, keep_client1, keep_client2, keepdocker, *args, **kwargs):
48 stubs.keepdocker = keepdocker
50 def putstub(p, **kwargs):
51 return "%s+%i" % (hashlib.md5(p).hexdigest(), len(p))
52 keep_client1().put.side_effect = putstub
53 keep_client1.put.side_effect = putstub
54 keep_client2().put.side_effect = putstub
55 keep_client2.put.side_effect = putstub
57 stubs.keep_client = keep_client2
58 stubs.docker_images = {
59 "arvados/jobs:"+arvados_cwl.__version__: [("zzzzz-4zz18-zzzzzzzzzzzzzd3", "")],
60 "debian:8": [("zzzzz-4zz18-zzzzzzzzzzzzzd4", "")],
61 "arvados/jobs:123": [("zzzzz-4zz18-zzzzzzzzzzzzzd5", "")],
62 "arvados/jobs:latest": [("zzzzz-4zz18-zzzzzzzzzzzzzd6", "")],
64 def kd(a, b, image_name=None, image_tag=None):
65 return stubs.docker_images.get("%s:%s" % (image_name, image_tag), [])
66 stubs.keepdocker.side_effect = kd
68 stubs.fake_user_uuid = "zzzzz-tpzed-zzzzzzzzzzzzzzz"
69 stubs.fake_container_uuid = "zzzzz-dz642-zzzzzzzzzzzzzzz"
71 if sys.version_info[0] < 3:
72 stubs.capture_stdout = BytesIO()
74 stubs.capture_stdout = StringIO()
76 stubs.api = mock.MagicMock()
77 stubs.api._rootDesc = get_rootDesc()
79 stubs.api.users().current().execute.return_value = {
80 "uuid": stubs.fake_user_uuid,
82 stubs.api.collections().list().execute.return_value = {"items": []}
83 stubs.api.containers().current().execute.return_value = {
84 "uuid": stubs.fake_container_uuid,
87 class CollectionExecute(object):
88 def __init__(self, exe):
90 def execute(self, num_retries=None):
93 def collection_createstub(created_collections, body, ensure_unique_name=None):
94 mt = body["manifest_text"].encode('utf-8')
95 uuid = "zzzzz-4zz18-zzzzzzzzzzzzzx%d" % len(created_collections)
96 pdh = "%s+%i" % (hashlib.md5(mt).hexdigest(), len(mt))
97 created_collections[uuid] = {
99 "portable_data_hash": pdh,
100 "manifest_text": mt.decode('utf-8')
102 return CollectionExecute(created_collections[uuid])
104 def collection_getstub(created_collections, uuid):
105 for v in viewvalues(created_collections):
106 if uuid in (v["uuid"], v["portable_data_hash"]):
107 return CollectionExecute(v)
109 created_collections = {
110 "99999999999999999999999999999998+99": {
112 "portable_data_hash": "99999999999999999999999999999998+99",
113 "manifest_text": ". 99999999999999999999999999999998+99 0:0:file1.txt"
115 "99999999999999999999999999999994+99": {
117 "portable_data_hash": "99999999999999999999999999999994+99",
118 "manifest_text": ". 99999999999999999999999999999994+99 0:0:expect_arvworkflow.cwl"
120 "zzzzz-4zz18-zzzzzzzzzzzzzd3": {
121 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzd3",
122 "portable_data_hash": "999999999999999999999999999999d3+99",
125 "zzzzz-4zz18-zzzzzzzzzzzzzd4": {
126 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzd4",
127 "portable_data_hash": "999999999999999999999999999999d4+99",
130 "zzzzz-4zz18-zzzzzzzzzzzzzd5": {
131 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzd5",
132 "portable_data_hash": "999999999999999999999999999999d5+99",
135 "zzzzz-4zz18-zzzzzzzzzzzzzd6": {
136 "uuid": "zzzzz-4zz18-zzzzzzzzzzzzzd6",
137 "portable_data_hash": "999999999999999999999999999999d6+99",
141 stubs.api.collections().create.side_effect = functools.partial(collection_createstub, created_collections)
142 stubs.api.collections().get.side_effect = functools.partial(collection_getstub, created_collections)
144 stubs.expect_job_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
145 stubs.api.jobs().create().execute.return_value = {
146 "uuid": stubs.expect_job_uuid,
150 stubs.expect_container_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
151 stubs.api.container_requests().create().execute.return_value = {
152 "uuid": stubs.expect_container_request_uuid,
153 "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
157 stubs.expect_pipeline_template_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
158 stubs.api.pipeline_templates().create().execute.return_value = {
159 "uuid": stubs.expect_pipeline_template_uuid,
161 stubs.expect_job_spec = {
162 'runtime_constraints': {
163 'docker_image': '999999999999999999999999999999d3+99',
164 'min_ram_mb_per_node': 1024
166 'script_parameters': {
168 'basename': 'blorp.txt',
169 'location': 'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
173 'basename': '99999999999999999999999999999998+99',
174 'location': 'keep:99999999999999999999999999999998+99',
178 'basename': 'anonymous',
180 "basename": "renamed.txt",
182 "location": "keep:99999999999999999999999999999998+99/file1.txt",
187 'cwl:tool': '57ad063d64c60dbddc027791f0649211+60/workflow.cwl#main'
189 'repository': 'arvados',
190 'script_version': 'master',
191 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
192 'script': 'cwl-runner'
194 stubs.pipeline_component = stubs.expect_job_spec.copy()
195 stubs.expect_pipeline_instance = {
196 'name': 'submit_wf.cwl',
197 'state': 'RunningOnServer',
201 'runtime_constraints': {'docker_image': '999999999999999999999999999999d3+99', 'min_ram_mb_per_node': 1024},
202 'script_parameters': {
203 'y': {"value": {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'}},
205 'basename': 'blorp.txt',
207 'location': 'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
210 'z': {"value": {'basename': 'anonymous', 'class': 'Directory',
213 'basename': 'renamed.txt',
214 'class': 'File', 'location':
215 'keep:99999999999999999999999999999998+99/file1.txt',
219 'cwl:tool': '57ad063d64c60dbddc027791f0649211+60/workflow.cwl#main',
221 'arv:enable_reuse': True,
222 'arv:on_error': 'continue'
224 'repository': 'arvados',
225 'script_version': 'master',
226 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
227 'script': 'cwl-runner',
228 'job': {'state': 'Queued', 'uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'}
232 stubs.pipeline_create = copy.deepcopy(stubs.expect_pipeline_instance)
233 stubs.expect_pipeline_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
234 stubs.pipeline_create["uuid"] = stubs.expect_pipeline_uuid
235 stubs.pipeline_with_job = copy.deepcopy(stubs.pipeline_create)
236 stubs.pipeline_with_job["components"]["cwl-runner"]["job"] = {
237 "uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
240 stubs.api.pipeline_instances().create().execute.return_value = stubs.pipeline_create
241 stubs.api.pipeline_instances().get().execute.return_value = stubs.pipeline_with_job
243 with open("tests/wf/submit_wf_packed.cwl") as f:
244 expect_packed_workflow = yaml.round_trip_load(f)
246 stubs.expect_container_spec = {
253 '/var/lib/cwl/workflow.json': {
254 'content': expect_packed_workflow,
258 'path': '/var/spool/cwl/cwl.output.json',
261 '/var/lib/cwl/cwl.input.json': {
265 'basename': '99999999999999999999999999999998+99',
266 'location': 'keep:99999999999999999999999999999998+99',
267 'class': 'Directory'},
269 'basename': u'blorp.txt',
271 'location': u'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
274 'z': {'basename': 'anonymous', 'class': 'Directory', 'listing': [
275 {'basename': 'renamed.txt',
277 'location': 'keep:99999999999999999999999999999998+99/file1.txt',
286 'state': 'Committed',
287 'command': ['arvados-cwl-runner', '--local', '--api=containers',
288 '--no-log-timestamps', '--disable-validate',
289 '--eval-timeout=20', '--thread-count=1',
290 '--enable-reuse', "--collection-cache-size=256", '--debug', '--on-error=continue',
291 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
292 'name': 'submit_wf.cwl',
293 'container_image': '999999999999999999999999999999d3+99',
294 'output_path': '/var/spool/cwl',
295 'cwd': '/var/spool/cwl',
296 'runtime_constraints': {
299 'ram': (1024+256)*1024*1024
301 'use_existing': True,
306 stubs.expect_workflow_uuid = "zzzzz-7fd4e-zzzzzzzzzzzzzzz"
307 stubs.api.workflows().create().execute.return_value = {
308 "uuid": stubs.expect_workflow_uuid,
310 def update_mock(**kwargs):
311 stubs.updated_uuid = kwargs.get('uuid')
313 stubs.api.workflows().update.side_effect = update_mock
314 stubs.api.workflows().update().execute.side_effect = lambda **kwargs: {
315 "uuid": stubs.updated_uuid,
318 return func(self, stubs, *args, **kwargs)
322 class TestSubmit(unittest.TestCase):
323 @mock.patch("arvados_cwl.arvdocker.arv_docker_get_image")
324 @mock.patch("time.sleep")
326 def test_submit(self, stubs, tm, arvdock):
327 def get_image(api_client, dockerRequirement, pull_image, project_uuid):
328 if dockerRequirement["dockerPull"] == 'arvados/jobs:'+arvados_cwl.__version__:
329 return '999999999999999999999999999999d3+99'
330 elif dockerRequirement["dockerPull"] == "debian:8":
331 return '999999999999999999999999999999d4+99'
332 arvdock.side_effect = get_image
334 exited = arvados_cwl.main(
335 ["--submit", "--no-wait", "--api=jobs", "--debug",
336 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
337 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
339 stubs.api.collections().create.assert_has_calls([
340 mock.call(body=JsonDiffMatcher({
342 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
343 'replication_desired': None,
344 'name': 'submit_tool.cwl dependencies (5d373e7629203ce39e7c22af98a0f881+52)',
345 }), ensure_unique_name=False),
346 mock.call(body=JsonDiffMatcher({
348 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
349 'replication_desired': None,
350 'name': 'submit_wf.cwl input (169f39d466a5438ac4a90e779bf750c7+53)',
351 }), ensure_unique_name=False),
352 mock.call(body=JsonDiffMatcher({
354 ". 68089141fbf7e020ac90a9d6a575bc8f+1312 0:1312:workflow.cwl\n",
355 'replication_desired': None,
356 'name': 'submit_wf.cwl',
357 }), ensure_unique_name=True) ])
359 arvdock.assert_has_calls([
360 mock.call(stubs.api, {"class": "DockerRequirement", "dockerPull": "debian:8"}, True, None),
361 mock.call(stubs.api, {"class": "DockerRequirement", "dockerPull": "debian:8", 'http://arvados.org/cwl#dockerCollectionPDH': '999999999999999999999999999999d4+99'}, True, None),
362 mock.call(stubs.api, {'dockerPull': 'arvados/jobs:'+arvados_cwl.__version__}, True, None)
365 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
366 stubs.api.pipeline_instances().create.assert_called_with(
367 body=JsonDiffMatcher(expect_pipeline))
368 self.assertEqual(stubs.capture_stdout.getvalue(),
369 stubs.expect_pipeline_uuid + '\n')
370 self.assertEqual(exited, 0)
372 @mock.patch("time.sleep")
374 def test_submit_no_reuse(self, stubs, tm):
375 exited = arvados_cwl.main(
376 ["--submit", "--no-wait", "--api=jobs", "--debug", "--disable-reuse",
377 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
378 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
380 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
381 expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:enable_reuse"] = {"value": False}
382 expect_pipeline["properties"] = {"run_options": {"enable_job_reuse": False}}
384 stubs.api.pipeline_instances().create.assert_called_with(
385 body=JsonDiffMatcher(expect_pipeline))
386 self.assertEqual(stubs.capture_stdout.getvalue(),
387 stubs.expect_pipeline_uuid + '\n')
388 self.assertEqual(exited, 0)
391 def test_error_when_multiple_storage_classes_specified(self, stubs):
392 storage_classes = "foo,bar"
393 exited = arvados_cwl.main(
394 ["--debug", "--storage-classes", storage_classes,
395 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
396 sys.stdin, sys.stderr, api_client=stubs.api)
397 self.assertEqual(exited, 1)
399 @mock.patch("time.sleep")
401 def test_submit_on_error(self, stubs, tm):
402 exited = arvados_cwl.main(
403 ["--submit", "--no-wait", "--api=jobs", "--debug", "--on-error=stop",
404 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
405 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
407 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
408 expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:on_error"] = "stop"
410 stubs.api.pipeline_instances().create.assert_called_with(
411 body=JsonDiffMatcher(expect_pipeline))
412 self.assertEqual(stubs.capture_stdout.getvalue(),
413 stubs.expect_pipeline_uuid + '\n')
414 self.assertEqual(exited, 0)
416 @mock.patch("time.sleep")
418 def test_submit_runner_ram(self, stubs, tm):
419 exited = arvados_cwl.main(
420 ["--submit", "--no-wait", "--debug", "--submit-runner-ram=2048",
421 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
422 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
424 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
425 expect_pipeline["components"]["cwl-runner"]["runtime_constraints"]["min_ram_mb_per_node"] = 2048
427 stubs.api.pipeline_instances().create.assert_called_with(
428 body=JsonDiffMatcher(expect_pipeline))
429 self.assertEqual(stubs.capture_stdout.getvalue(),
430 stubs.expect_pipeline_uuid + '\n')
431 self.assertEqual(exited, 0)
433 @mock.patch("time.sleep")
435 def test_submit_invalid_runner_ram(self, stubs, tm):
436 exited = arvados_cwl.main(
437 ["--submit", "--no-wait", "--debug", "--submit-runner-ram=-2048",
438 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
439 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
440 self.assertEqual(exited, 1)
442 @mock.patch("time.sleep")
444 def test_submit_output_name(self, stubs, tm):
445 output_name = "test_output_name"
447 exited = arvados_cwl.main(
448 ["--submit", "--no-wait", "--debug", "--output-name", output_name,
449 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
450 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
452 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
453 expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:output_name"] = output_name
455 stubs.api.pipeline_instances().create.assert_called_with(
456 body=JsonDiffMatcher(expect_pipeline))
457 self.assertEqual(stubs.capture_stdout.getvalue(),
458 stubs.expect_pipeline_uuid + '\n')
459 self.assertEqual(exited, 0)
461 @mock.patch("time.sleep")
463 def test_submit_pipeline_name(self, stubs, tm):
464 exited = arvados_cwl.main(
465 ["--submit", "--no-wait", "--debug", "--name=hello job 123",
466 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
467 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
468 self.assertEqual(exited, 0)
470 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
471 expect_pipeline["name"] = "hello job 123"
473 stubs.api.pipeline_instances().create.assert_called_with(
474 body=JsonDiffMatcher(expect_pipeline))
475 self.assertEqual(stubs.capture_stdout.getvalue(),
476 stubs.expect_pipeline_uuid + '\n')
478 @mock.patch("time.sleep")
480 def test_submit_output_tags(self, stubs, tm):
481 output_tags = "tag0,tag1,tag2"
483 exited = arvados_cwl.main(
484 ["--submit", "--no-wait", "--debug", "--output-tags", output_tags,
485 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
486 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
487 self.assertEqual(exited, 0)
489 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
490 expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:output_tags"] = output_tags
492 stubs.api.pipeline_instances().create.assert_called_with(
493 body=JsonDiffMatcher(expect_pipeline))
494 self.assertEqual(stubs.capture_stdout.getvalue(),
495 stubs.expect_pipeline_uuid + '\n')
497 @mock.patch("time.sleep")
499 def test_submit_with_project_uuid(self, stubs, tm):
500 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
502 exited = arvados_cwl.main(
503 ["--submit", "--no-wait", "--debug",
504 "--project-uuid", project_uuid,
505 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
506 sys.stdout, sys.stderr, api_client=stubs.api)
507 self.assertEqual(exited, 0)
509 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
510 expect_pipeline["owner_uuid"] = project_uuid
511 stubs.api.pipeline_instances().create.assert_called_with(
512 body=JsonDiffMatcher(expect_pipeline))
515 def test_submit_container(self, stubs):
516 exited = arvados_cwl.main(
517 ["--submit", "--no-wait", "--api=containers", "--debug",
518 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
519 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
521 stubs.api.collections().create.assert_has_calls([
522 mock.call(body=JsonDiffMatcher({
524 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
525 'replication_desired': None,
526 'name': 'submit_tool.cwl dependencies (5d373e7629203ce39e7c22af98a0f881+52)',
527 }), ensure_unique_name=False),
528 mock.call(body=JsonDiffMatcher({
530 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
531 'replication_desired': None,
532 'name': 'submit_wf.cwl input (169f39d466a5438ac4a90e779bf750c7+53)',
533 }), ensure_unique_name=False)])
535 expect_container = copy.deepcopy(stubs.expect_container_spec)
536 stubs.api.container_requests().create.assert_called_with(
537 body=JsonDiffMatcher(expect_container))
538 self.assertEqual(stubs.capture_stdout.getvalue(),
539 stubs.expect_container_request_uuid + '\n')
540 self.assertEqual(exited, 0)
543 def test_submit_container_no_reuse(self, stubs):
544 exited = arvados_cwl.main(
545 ["--submit", "--no-wait", "--api=containers", "--debug", "--disable-reuse",
546 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
547 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
549 expect_container = copy.deepcopy(stubs.expect_container_spec)
550 expect_container["command"] = [
551 'arvados-cwl-runner', '--local', '--api=containers',
552 '--no-log-timestamps', '--disable-validate',
553 '--eval-timeout=20', '--thread-count=1',
554 '--disable-reuse', "--collection-cache-size=256",
555 '--debug', '--on-error=continue',
556 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
557 expect_container["use_existing"] = False
559 stubs.api.container_requests().create.assert_called_with(
560 body=JsonDiffMatcher(expect_container))
561 self.assertEqual(stubs.capture_stdout.getvalue(),
562 stubs.expect_container_request_uuid + '\n')
563 self.assertEqual(exited, 0)
566 def test_submit_container_reuse_disabled_by_workflow(self, stubs):
567 exited = arvados_cwl.main(
568 ["--submit", "--no-wait", "--api=containers", "--debug",
569 "tests/wf/submit_wf_no_reuse.cwl", "tests/submit_test_job.json"],
570 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
571 self.assertEqual(exited, 0)
573 expect_container = copy.deepcopy(stubs.expect_container_spec)
574 expect_container["command"] = [
575 'arvados-cwl-runner', '--local', '--api=containers',
576 '--no-log-timestamps', '--disable-validate',
577 '--eval-timeout=20', '--thread-count=1',
578 '--disable-reuse', "--collection-cache-size=256", '--debug', '--on-error=continue',
579 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
580 expect_container["use_existing"] = False
581 expect_container["name"] = "submit_wf_no_reuse.cwl"
582 expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][1]["hints"] = [
584 "class": "http://arvados.org/cwl#ReuseRequirement",
585 "enableReuse": False,
588 expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][0]["$namespaces"] = {
589 "arv": "http://arvados.org/cwl#",
590 "cwltool": "http://commonwl.org/cwltool#"
593 stubs.api.container_requests().create.assert_called_with(
594 body=JsonDiffMatcher(expect_container))
595 self.assertEqual(stubs.capture_stdout.getvalue(),
596 stubs.expect_container_request_uuid + '\n')
600 def test_submit_container_on_error(self, stubs):
601 exited = arvados_cwl.main(
602 ["--submit", "--no-wait", "--api=containers", "--debug", "--on-error=stop",
603 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
604 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
606 expect_container = copy.deepcopy(stubs.expect_container_spec)
607 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
608 '--no-log-timestamps', '--disable-validate',
609 '--eval-timeout=20', '--thread-count=1',
610 '--enable-reuse', "--collection-cache-size=256",
611 '--debug', '--on-error=stop',
612 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
614 stubs.api.container_requests().create.assert_called_with(
615 body=JsonDiffMatcher(expect_container))
616 self.assertEqual(stubs.capture_stdout.getvalue(),
617 stubs.expect_container_request_uuid + '\n')
618 self.assertEqual(exited, 0)
621 def test_submit_container_output_name(self, stubs):
622 output_name = "test_output_name"
624 exited = arvados_cwl.main(
625 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-name", output_name,
626 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
627 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
629 expect_container = copy.deepcopy(stubs.expect_container_spec)
630 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
631 '--no-log-timestamps', '--disable-validate',
632 '--eval-timeout=20', '--thread-count=1',
633 '--enable-reuse', "--collection-cache-size=256",
634 "--output-name="+output_name, '--debug', '--on-error=continue',
635 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
636 expect_container["output_name"] = output_name
638 stubs.api.container_requests().create.assert_called_with(
639 body=JsonDiffMatcher(expect_container))
640 self.assertEqual(stubs.capture_stdout.getvalue(),
641 stubs.expect_container_request_uuid + '\n')
642 self.assertEqual(exited, 0)
645 def test_submit_storage_classes(self, stubs):
646 exited = arvados_cwl.main(
647 ["--debug", "--submit", "--no-wait", "--api=containers", "--storage-classes=foo",
648 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
649 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
651 expect_container = copy.deepcopy(stubs.expect_container_spec)
652 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
653 '--no-log-timestamps', '--disable-validate',
654 '--eval-timeout=20', '--thread-count=1',
655 '--enable-reuse', "--collection-cache-size=256", "--debug",
656 "--storage-classes=foo", '--on-error=continue',
657 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
659 stubs.api.container_requests().create.assert_called_with(
660 body=JsonDiffMatcher(expect_container))
661 self.assertEqual(stubs.capture_stdout.getvalue(),
662 stubs.expect_container_request_uuid + '\n')
663 self.assertEqual(exited, 0)
665 @mock.patch("arvados_cwl.task_queue.TaskQueue")
666 @mock.patch("arvados_cwl.arvworkflow.ArvadosWorkflow.job")
667 @mock.patch("arvados_cwl.executor.ArvCwlExecutor.make_output_collection", return_value = (None, None))
669 def test_storage_classes_correctly_propagate_to_make_output_collection(self, stubs, make_output, job, tq):
670 def set_final_output(job_order, output_callback, runtimeContext):
671 output_callback("zzzzz-4zz18-zzzzzzzzzzzzzzzz", "success")
673 job.side_effect = set_final_output
675 exited = arvados_cwl.main(
676 ["--debug", "--local", "--storage-classes=foo",
677 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
678 sys.stdin, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
680 make_output.assert_called_with(u'Output of submit_wf.cwl', ['foo'], '', 'zzzzz-4zz18-zzzzzzzzzzzzzzzz')
681 self.assertEqual(exited, 0)
683 @mock.patch("arvados_cwl.task_queue.TaskQueue")
684 @mock.patch("arvados_cwl.arvworkflow.ArvadosWorkflow.job")
685 @mock.patch("arvados_cwl.executor.ArvCwlExecutor.make_output_collection", return_value = (None, None))
687 def test_default_storage_classes_correctly_propagate_to_make_output_collection(self, stubs, make_output, job, tq):
688 def set_final_output(job_order, output_callback, runtimeContext):
689 output_callback("zzzzz-4zz18-zzzzzzzzzzzzzzzz", "success")
691 job.side_effect = set_final_output
693 exited = arvados_cwl.main(
694 ["--debug", "--local",
695 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
696 sys.stdin, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
698 make_output.assert_called_with(u'Output of submit_wf.cwl', ['default'], '', 'zzzzz-4zz18-zzzzzzzzzzzzzzzz')
699 self.assertEqual(exited, 0)
702 def test_submit_container_output_ttl(self, stubs):
703 exited = arvados_cwl.main(
704 ["--submit", "--no-wait", "--api=containers", "--debug", "--intermediate-output-ttl", "3600",
705 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
706 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
708 expect_container = copy.deepcopy(stubs.expect_container_spec)
709 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
710 '--no-log-timestamps', '--disable-validate',
711 '--eval-timeout=20', '--thread-count=1',
712 '--enable-reuse', "--collection-cache-size=256", '--debug',
713 '--on-error=continue',
714 "--intermediate-output-ttl=3600",
715 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
717 stubs.api.container_requests().create.assert_called_with(
718 body=JsonDiffMatcher(expect_container))
719 self.assertEqual(stubs.capture_stdout.getvalue(),
720 stubs.expect_container_request_uuid + '\n')
721 self.assertEqual(exited, 0)
724 def test_submit_container_trash_intermediate(self, stubs):
725 exited = arvados_cwl.main(
726 ["--submit", "--no-wait", "--api=containers", "--debug", "--trash-intermediate",
727 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
728 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
731 expect_container = copy.deepcopy(stubs.expect_container_spec)
732 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
733 '--no-log-timestamps', '--disable-validate',
734 '--eval-timeout=20', '--thread-count=1',
735 '--enable-reuse', "--collection-cache-size=256",
736 '--debug', '--on-error=continue',
737 "--trash-intermediate",
738 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
740 stubs.api.container_requests().create.assert_called_with(
741 body=JsonDiffMatcher(expect_container))
742 self.assertEqual(stubs.capture_stdout.getvalue(),
743 stubs.expect_container_request_uuid + '\n')
744 self.assertEqual(exited, 0)
747 def test_submit_container_output_tags(self, stubs):
748 output_tags = "tag0,tag1,tag2"
750 exited = arvados_cwl.main(
751 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-tags", output_tags,
752 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
753 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
755 expect_container = copy.deepcopy(stubs.expect_container_spec)
756 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
757 '--no-log-timestamps', '--disable-validate',
758 '--eval-timeout=20', '--thread-count=1',
759 '--enable-reuse', "--collection-cache-size=256",
760 "--output-tags="+output_tags, '--debug', '--on-error=continue',
761 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
763 stubs.api.container_requests().create.assert_called_with(
764 body=JsonDiffMatcher(expect_container))
765 self.assertEqual(stubs.capture_stdout.getvalue(),
766 stubs.expect_container_request_uuid + '\n')
767 self.assertEqual(exited, 0)
770 def test_submit_container_runner_ram(self, stubs):
771 exited = arvados_cwl.main(
772 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-ram=2048",
773 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
774 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
776 expect_container = copy.deepcopy(stubs.expect_container_spec)
777 expect_container["runtime_constraints"]["ram"] = (2048+256)*1024*1024
779 stubs.api.container_requests().create.assert_called_with(
780 body=JsonDiffMatcher(expect_container))
781 self.assertEqual(stubs.capture_stdout.getvalue(),
782 stubs.expect_container_request_uuid + '\n')
783 self.assertEqual(exited, 0)
785 @mock.patch("arvados.collection.CollectionReader")
786 @mock.patch("time.sleep")
788 def test_submit_file_keepref(self, stubs, tm, collectionReader):
789 collectionReader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "blorp.txt")
790 exited = arvados_cwl.main(
791 ["--submit", "--no-wait", "--api=containers", "--debug",
792 "tests/wf/submit_keepref_wf.cwl"],
793 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
794 self.assertEqual(exited, 0)
796 @mock.patch("arvados.collection.CollectionReader")
797 @mock.patch("time.sleep")
799 def test_submit_keepref(self, stubs, tm, reader):
800 with open("tests/wf/expect_arvworkflow.cwl") as f:
801 reader().open().__enter__().read.return_value = f.read()
803 exited = arvados_cwl.main(
804 ["--submit", "--no-wait", "--api=containers", "--debug",
805 "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
806 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
816 'path': '/var/spool/cwl/cwl.output.json',
819 '/var/lib/cwl/workflow': {
820 'portable_data_hash': '99999999999999999999999999999994+99',
823 '/var/lib/cwl/cwl.input.json': {
829 }, 'state': 'Committed',
830 'output_path': '/var/spool/cwl',
831 'name': 'expect_arvworkflow.cwl#main',
832 'container_image': '999999999999999999999999999999d3+99',
833 'command': ['arvados-cwl-runner', '--local', '--api=containers',
834 '--no-log-timestamps', '--disable-validate',
835 '--eval-timeout=20', '--thread-count=1',
836 '--enable-reuse', "--collection-cache-size=256", '--debug', '--on-error=continue',
837 '/var/lib/cwl/workflow/expect_arvworkflow.cwl#main', '/var/lib/cwl/cwl.input.json'],
838 'cwd': '/var/spool/cwl',
839 'runtime_constraints': {
844 'use_existing': True,
849 stubs.api.container_requests().create.assert_called_with(
850 body=JsonDiffMatcher(expect_container))
851 self.assertEqual(stubs.capture_stdout.getvalue(),
852 stubs.expect_container_request_uuid + '\n')
853 self.assertEqual(exited, 0)
855 @mock.patch("arvados.collection.CollectionReader")
856 @mock.patch("time.sleep")
858 def test_submit_jobs_keepref(self, stubs, tm, reader):
859 with open("tests/wf/expect_arvworkflow.cwl") as f:
860 reader().open().__enter__().read.return_value = f.read()
862 exited = arvados_cwl.main(
863 ["--submit", "--no-wait", "--api=jobs", "--debug",
864 "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
865 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
867 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
868 expect_pipeline["components"]["cwl-runner"]["script_parameters"]["x"] = "XxX"
869 del expect_pipeline["components"]["cwl-runner"]["script_parameters"]["y"]
870 del expect_pipeline["components"]["cwl-runner"]["script_parameters"]["z"]
871 expect_pipeline["components"]["cwl-runner"]["script_parameters"]["cwl:tool"] = "99999999999999999999999999999994+99/expect_arvworkflow.cwl#main"
872 expect_pipeline["name"] = "expect_arvworkflow.cwl#main"
873 stubs.api.pipeline_instances().create.assert_called_with(
874 body=JsonDiffMatcher(expect_pipeline))
875 self.assertEqual(exited, 0)
877 @mock.patch("time.sleep")
879 def test_submit_arvworkflow(self, stubs, tm):
880 with open("tests/wf/expect_arvworkflow.cwl") as f:
881 stubs.api.workflows().get().execute.return_value = {"definition": f.read(), "name": "a test workflow"}
883 exited = arvados_cwl.main(
884 ["--submit", "--no-wait", "--api=containers", "--debug",
885 "962eh-7fd4e-gkbzl62qqtfig37", "-x", "XxX"],
886 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
896 'path': '/var/spool/cwl/cwl.output.json',
899 '/var/lib/cwl/workflow.json': {
902 'cwlVersion': 'v1.0',
907 {'type': 'string', 'id': '#main/x'}
910 {'in': [{'source': '#main/x', 'id': '#main/step1/x'}],
911 'run': '#submit_tool.cwl',
921 'inputBinding': {'position': 1},
923 'id': '#submit_tool.cwl/x'}
927 'dockerPull': 'debian:8',
928 'class': 'DockerRequirement',
929 "http://arvados.org/cwl#dockerCollectionPDH": "999999999999999999999999999999d4+99"
932 'id': '#submit_tool.cwl',
934 'baseCommand': 'cat',
935 'class': 'CommandLineTool'
940 '/var/lib/cwl/cwl.input.json': {
946 }, 'state': 'Committed',
947 'output_path': '/var/spool/cwl',
948 'name': 'a test workflow',
949 'container_image': "999999999999999999999999999999d3+99",
950 'command': ['arvados-cwl-runner', '--local', '--api=containers',
951 '--no-log-timestamps', '--disable-validate',
952 '--eval-timeout=20', '--thread-count=1',
953 '--enable-reuse', "--collection-cache-size=256", '--debug', '--on-error=continue',
954 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
955 'cwd': '/var/spool/cwl',
956 'runtime_constraints': {
961 'use_existing': True,
963 "template_uuid": "962eh-7fd4e-gkbzl62qqtfig37"
968 stubs.api.container_requests().create.assert_called_with(
969 body=JsonDiffMatcher(expect_container))
970 self.assertEqual(stubs.capture_stdout.getvalue(),
971 stubs.expect_container_request_uuid + '\n')
972 self.assertEqual(exited, 0)
975 def test_submit_container_name(self, stubs):
976 exited = arvados_cwl.main(
977 ["--submit", "--no-wait", "--api=containers", "--debug", "--name=hello container 123",
978 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
979 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
981 expect_container = copy.deepcopy(stubs.expect_container_spec)
982 expect_container["name"] = "hello container 123"
984 stubs.api.container_requests().create.assert_called_with(
985 body=JsonDiffMatcher(expect_container))
986 self.assertEqual(stubs.capture_stdout.getvalue(),
987 stubs.expect_container_request_uuid + '\n')
988 self.assertEqual(exited, 0)
991 def test_submit_missing_input(self, stubs):
992 exited = arvados_cwl.main(
993 ["--submit", "--no-wait", "--api=containers", "--debug",
994 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
995 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
996 self.assertEqual(exited, 0)
998 exited = arvados_cwl.main(
999 ["--submit", "--no-wait", "--api=containers", "--debug",
1000 "tests/wf/submit_wf.cwl", "tests/submit_test_job_missing.json"],
1001 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1002 self.assertEqual(exited, 1)
1005 def test_submit_container_project(self, stubs):
1006 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1007 exited = arvados_cwl.main(
1008 ["--submit", "--no-wait", "--api=containers", "--debug", "--project-uuid="+project_uuid,
1009 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1010 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1012 expect_container = copy.deepcopy(stubs.expect_container_spec)
1013 expect_container["owner_uuid"] = project_uuid
1014 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1015 '--no-log-timestamps', '--disable-validate',
1016 "--eval-timeout=20", "--thread-count=1",
1017 '--enable-reuse', "--collection-cache-size=256", '--debug',
1018 '--on-error=continue',
1019 '--project-uuid='+project_uuid,
1020 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1022 stubs.api.container_requests().create.assert_called_with(
1023 body=JsonDiffMatcher(expect_container))
1024 self.assertEqual(stubs.capture_stdout.getvalue(),
1025 stubs.expect_container_request_uuid + '\n')
1026 self.assertEqual(exited, 0)
1029 def test_submit_container_eval_timeout(self, stubs):
1030 exited = arvados_cwl.main(
1031 ["--submit", "--no-wait", "--api=containers", "--debug", "--eval-timeout=60",
1032 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1033 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1035 expect_container = copy.deepcopy(stubs.expect_container_spec)
1036 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1037 '--no-log-timestamps', '--disable-validate',
1038 '--eval-timeout=60.0', '--thread-count=1',
1039 '--enable-reuse', "--collection-cache-size=256",
1040 '--debug', '--on-error=continue',
1041 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1043 stubs.api.container_requests().create.assert_called_with(
1044 body=JsonDiffMatcher(expect_container))
1045 self.assertEqual(stubs.capture_stdout.getvalue(),
1046 stubs.expect_container_request_uuid + '\n')
1047 self.assertEqual(exited, 0)
1050 def test_submit_container_collection_cache(self, stubs):
1051 exited = arvados_cwl.main(
1052 ["--submit", "--no-wait", "--api=containers", "--debug", "--collection-cache-size=500",
1053 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1054 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1056 expect_container = copy.deepcopy(stubs.expect_container_spec)
1057 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1058 '--no-log-timestamps', '--disable-validate',
1059 '--eval-timeout=20', '--thread-count=1',
1060 '--enable-reuse', "--collection-cache-size=500",
1061 '--debug', '--on-error=continue',
1062 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1063 expect_container["runtime_constraints"]["ram"] = (1024+500)*1024*1024
1065 stubs.api.container_requests().create.assert_called_with(
1066 body=JsonDiffMatcher(expect_container))
1067 self.assertEqual(stubs.capture_stdout.getvalue(),
1068 stubs.expect_container_request_uuid + '\n')
1069 self.assertEqual(exited, 0)
1072 def test_submit_container_thread_count(self, stubs):
1073 exited = arvados_cwl.main(
1074 ["--submit", "--no-wait", "--api=containers", "--debug", "--thread-count=20",
1075 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1076 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1078 expect_container = copy.deepcopy(stubs.expect_container_spec)
1079 expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1080 '--no-log-timestamps', '--disable-validate',
1081 '--eval-timeout=20', '--thread-count=20',
1082 '--enable-reuse', "--collection-cache-size=256",
1083 '--debug', '--on-error=continue',
1084 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1086 stubs.api.container_requests().create.assert_called_with(
1087 body=JsonDiffMatcher(expect_container))
1088 self.assertEqual(stubs.capture_stdout.getvalue(),
1089 stubs.expect_container_request_uuid + '\n')
1090 self.assertEqual(exited, 0)
1093 def test_submit_job_runner_image(self, stubs):
1094 exited = arvados_cwl.main(
1095 ["--submit", "--no-wait", "--api=jobs", "--debug", "--submit-runner-image=arvados/jobs:123",
1096 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1097 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1099 stubs.expect_pipeline_instance["components"]["cwl-runner"]["runtime_constraints"]["docker_image"] = "999999999999999999999999999999d5+99"
1101 expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
1102 stubs.api.pipeline_instances().create.assert_called_with(
1103 body=JsonDiffMatcher(expect_pipeline))
1104 self.assertEqual(stubs.capture_stdout.getvalue(),
1105 stubs.expect_pipeline_uuid + '\n')
1106 self.assertEqual(exited, 0)
1109 def test_submit_container_runner_image(self, stubs):
1110 exited = arvados_cwl.main(
1111 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-image=arvados/jobs:123",
1112 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1113 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1115 stubs.expect_container_spec["container_image"] = "999999999999999999999999999999d5+99"
1117 expect_container = copy.deepcopy(stubs.expect_container_spec)
1118 stubs.api.container_requests().create.assert_called_with(
1119 body=JsonDiffMatcher(expect_container))
1120 self.assertEqual(stubs.capture_stdout.getvalue(),
1121 stubs.expect_container_request_uuid + '\n')
1122 self.assertEqual(exited, 0)
1125 def test_submit_priority(self, stubs):
1126 exited = arvados_cwl.main(
1127 ["--submit", "--no-wait", "--api=containers", "--debug", "--priority=669",
1128 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1129 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1131 stubs.expect_container_spec["priority"] = 669
1133 expect_container = copy.deepcopy(stubs.expect_container_spec)
1134 stubs.api.container_requests().create.assert_called_with(
1135 body=JsonDiffMatcher(expect_container))
1136 self.assertEqual(stubs.capture_stdout.getvalue(),
1137 stubs.expect_container_request_uuid + '\n')
1138 self.assertEqual(exited, 0)
1141 def test_submit_wf_runner_resources(self, stubs):
1142 exited = arvados_cwl.main(
1143 ["--submit", "--no-wait", "--api=containers", "--debug",
1144 "tests/wf/submit_wf_runner_resources.cwl", "tests/submit_test_job.json"],
1145 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1147 expect_container = copy.deepcopy(stubs.expect_container_spec)
1148 expect_container["runtime_constraints"] = {
1151 "ram": (2000+512) * 2**20
1153 expect_container["name"] = "submit_wf_runner_resources.cwl"
1154 expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][1]["hints"] = [
1156 "class": "http://arvados.org/cwl#WorkflowRunnerResources",
1162 expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][0]["$namespaces"] = {
1163 "arv": "http://arvados.org/cwl#",
1165 expect_container['command'] = ['arvados-cwl-runner', '--local', '--api=containers',
1166 '--no-log-timestamps', '--disable-validate',
1167 '--eval-timeout=20', '--thread-count=1',
1168 '--enable-reuse', "--collection-cache-size=512", '--debug', '--on-error=continue',
1169 '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1171 stubs.api.container_requests().create.assert_called_with(
1172 body=JsonDiffMatcher(expect_container))
1173 self.assertEqual(stubs.capture_stdout.getvalue(),
1174 stubs.expect_container_request_uuid + '\n')
1175 self.assertEqual(exited, 0)
1178 arvados_cwl.arvdocker.arv_docker_clear_cache()
1180 @mock.patch("arvados.commands.keepdocker.find_one_image_hash")
1181 @mock.patch("cwltool.docker.DockerCommandLineJob.get_image")
1182 @mock.patch("arvados.api")
1183 def test_arvados_jobs_image(self, api, get_image, find_one_image_hash):
1184 arvados_cwl.arvdocker.arv_docker_clear_cache()
1186 arvrunner = mock.MagicMock()
1187 arvrunner.project_uuid = ""
1188 api.return_value = mock.MagicMock()
1189 arvrunner.api = api.return_value
1190 arvrunner.api.links().list().execute.side_effect = ({"items": [{"created_at": "",
1191 "head_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1192 "link_class": "docker_image_repo+tag",
1193 "name": "arvados/jobs:"+arvados_cwl.__version__,
1195 "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0},
1196 {"items": [{"created_at": "",
1198 "link_class": "docker_image_hash",
1201 "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0}
1203 find_one_image_hash.return_value = "123456"
1205 arvrunner.api.collections().list().execute.side_effect = ({"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1207 "manifest_text": "",
1209 }], "items_available": 1, "offset": 0},)
1210 arvrunner.api.collections().create().execute.return_value = {"uuid": ""}
1211 arvrunner.api.collections().get().execute.return_value = {"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1212 "portable_data_hash": "9999999999999999999999999999999b+99"}
1213 self.assertEqual("9999999999999999999999999999999b+99",
1214 arvados_cwl.runner.arvados_jobs_image(arvrunner, "arvados/jobs:"+arvados_cwl.__version__))
1218 def test_submit_secrets(self, stubs):
1219 exited = arvados_cwl.main(
1220 ["--submit", "--no-wait", "--api=containers", "--debug",
1221 "tests/wf/secret_wf.cwl", "tests/secret_test_job.yml"],
1222 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1224 expect_container = {
1226 "arvados-cwl-runner",
1229 "--no-log-timestamps",
1230 "--disable-validate",
1231 "--eval-timeout=20",
1234 "--collection-cache-size=256",
1236 "--on-error=continue",
1237 "/var/lib/cwl/workflow.json#main",
1238 "/var/lib/cwl/cwl.input.json"
1240 "container_image": "999999999999999999999999999999d3+99",
1241 "cwd": "/var/spool/cwl",
1243 "/var/lib/cwl/cwl.input.json": {
1246 "$include": "/secrets/s0"
1251 "/var/lib/cwl/workflow.json": {
1256 "cwltool": "http://commonwl.org/cwltool#"
1262 "class": "CommandLineTool",
1265 "class": "http://commonwl.org/cwltool#Secrets",
1267 "#secret_job.cwl/pw"
1271 "id": "#secret_job.cwl",
1274 "id": "#secret_job.cwl/pw",
1280 "id": "#secret_job.cwl/out",
1284 "stdout": "hashed_example.txt",
1287 "class": "InitialWorkDirRequirement",
1290 "entry": "username: user\npassword: $(inputs.pw)\n",
1291 "entryname": "example.conf"
1298 "class": "Workflow",
1301 "class": "DockerRequirement",
1302 "dockerPull": "debian:8",
1303 "http://arvados.org/cwl#dockerCollectionPDH": "999999999999999999999999999999d4+99"
1306 "class": "http://commonwl.org/cwltool#Secrets",
1322 "outputSource": "#main/step1/out",
1328 "id": "#main/step1",
1331 "id": "#main/step1/pw",
1332 "source": "#main/pw"
1338 "run": "#secret_job.cwl"
1343 "cwlVersion": "v1.0"
1348 "kind": "collection",
1353 "path": "/var/spool/cwl/cwl.output.json"
1356 "name": "secret_wf.cwl",
1357 "output_path": "/var/spool/cwl",
1360 "runtime_constraints": {
1371 "state": "Committed",
1372 "use_existing": True
1375 stubs.api.container_requests().create.assert_called_with(
1376 body=JsonDiffMatcher(expect_container))
1377 self.assertEqual(stubs.capture_stdout.getvalue(),
1378 stubs.expect_container_request_uuid + '\n')
1379 self.assertEqual(exited, 0)
1382 def test_submit_request_uuid(self, stubs):
1383 stubs.expect_container_request_uuid = "zzzzz-xvhdp-yyyyyyyyyyyyyyy"
1385 stubs.api.container_requests().update().execute.return_value = {
1386 "uuid": stubs.expect_container_request_uuid,
1387 "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
1391 exited = arvados_cwl.main(
1392 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-request-uuid=zzzzz-xvhdp-yyyyyyyyyyyyyyy",
1393 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1394 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1396 stubs.api.container_requests().update.assert_called_with(
1397 uuid="zzzzz-xvhdp-yyyyyyyyyyyyyyy", body=JsonDiffMatcher(stubs.expect_container_spec))
1398 self.assertEqual(stubs.capture_stdout.getvalue(),
1399 stubs.expect_container_request_uuid + '\n')
1400 self.assertEqual(exited, 0)
1403 def test_submit_container_cluster_id(self, stubs):
1404 stubs.api._rootDesc["remoteHosts"]["zbbbb"] = "123"
1406 exited = arvados_cwl.main(
1407 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-cluster=zbbbb",
1408 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1409 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1411 expect_container = copy.deepcopy(stubs.expect_container_spec)
1413 stubs.api.container_requests().create.assert_called_with(
1414 body=JsonDiffMatcher(expect_container), cluster_id="zbbbb")
1415 self.assertEqual(stubs.capture_stdout.getvalue(),
1416 stubs.expect_container_request_uuid + '\n')
1417 self.assertEqual(exited, 0)
1420 def test_submit_validate_cluster_id(self, stubs):
1421 stubs.api._rootDesc["remoteHosts"]["zbbbb"] = "123"
1422 exited = arvados_cwl.main(
1423 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-cluster=zcccc",
1424 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1425 stubs.capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1426 self.assertEqual(exited, 1)
1429 class TestCreateTemplate(unittest.TestCase):
1430 existing_template_uuid = "zzzzz-d1hrv-validworkfloyml"
1432 def _adjust_script_params(self, expect_component):
1433 expect_component['script_parameters']['x'] = {
1434 'dataclass': 'File',
1437 'value': '169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
1439 expect_component['script_parameters']['y'] = {
1440 'dataclass': 'Collection',
1442 'type': 'Directory',
1443 'value': '99999999999999999999999999999998+99',
1445 expect_component['script_parameters']['z'] = {
1446 'dataclass': 'Collection',
1448 'type': 'Directory',
1452 def test_create(self, stubs):
1453 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1455 exited = arvados_cwl.main(
1456 ["--create-workflow", "--debug",
1458 "--project-uuid", project_uuid,
1459 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1460 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1462 stubs.api.pipeline_instances().create.refute_called()
1463 stubs.api.jobs().create.refute_called()
1465 expect_component = copy.deepcopy(stubs.expect_job_spec)
1466 self._adjust_script_params(expect_component)
1469 "submit_wf.cwl": expect_component,
1471 "name": "submit_wf.cwl",
1472 "owner_uuid": project_uuid,
1474 stubs.api.pipeline_templates().create.assert_called_with(
1475 body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1477 self.assertEqual(stubs.capture_stdout.getvalue(),
1478 stubs.expect_pipeline_template_uuid + '\n')
1479 self.assertEqual(exited, 0)
1482 def test_create_name(self, stubs):
1483 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1485 exited = arvados_cwl.main(
1486 ["--create-workflow", "--debug",
1487 "--project-uuid", project_uuid,
1489 "--name", "testing 123",
1490 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1491 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1493 stubs.api.pipeline_instances().create.refute_called()
1494 stubs.api.jobs().create.refute_called()
1496 expect_component = copy.deepcopy(stubs.expect_job_spec)
1497 self._adjust_script_params(expect_component)
1500 "testing 123": expect_component,
1502 "name": "testing 123",
1503 "owner_uuid": project_uuid,
1505 stubs.api.pipeline_templates().create.assert_called_with(
1506 body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1508 self.assertEqual(stubs.capture_stdout.getvalue(),
1509 stubs.expect_pipeline_template_uuid + '\n')
1510 self.assertEqual(exited, 0)
1513 def test_update_name(self, stubs):
1514 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1516 exited = arvados_cwl.main(
1517 ["--update-workflow", self.existing_template_uuid,
1519 "--project-uuid", project_uuid,
1521 "--name", "testing 123",
1522 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1523 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1525 stubs.api.pipeline_instances().create.refute_called()
1526 stubs.api.jobs().create.refute_called()
1528 expect_component = copy.deepcopy(stubs.expect_job_spec)
1529 self._adjust_script_params(expect_component)
1532 "testing 123": expect_component,
1534 "name": "testing 123",
1535 "owner_uuid": project_uuid,
1537 stubs.api.pipeline_templates().create.refute_called()
1538 stubs.api.pipeline_templates().update.assert_called_with(
1539 body=JsonDiffMatcher(expect_template), uuid=self.existing_template_uuid)
1541 self.assertEqual(stubs.capture_stdout.getvalue(),
1542 self.existing_template_uuid + '\n')
1543 self.assertEqual(exited, 0)
1546 class TestCreateWorkflow(unittest.TestCase):
1547 existing_workflow_uuid = "zzzzz-7fd4e-validworkfloyml"
1548 expect_workflow = StripYAMLComments(
1549 open("tests/wf/expect_packed.cwl").read())
1552 def test_create(self, stubs):
1553 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1555 exited = arvados_cwl.main(
1556 ["--create-workflow", "--debug",
1558 "--project-uuid", project_uuid,
1559 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1560 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1562 stubs.api.pipeline_templates().create.refute_called()
1563 stubs.api.container_requests().create.refute_called()
1567 "owner_uuid": project_uuid,
1568 "name": "submit_wf.cwl",
1570 "definition": self.expect_workflow,
1573 stubs.api.workflows().create.assert_called_with(
1574 body=JsonDiffMatcher(body))
1576 self.assertEqual(stubs.capture_stdout.getvalue(),
1577 stubs.expect_workflow_uuid + '\n')
1578 self.assertEqual(exited, 0)
1581 def test_create_name(self, stubs):
1582 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1584 exited = arvados_cwl.main(
1585 ["--create-workflow", "--debug",
1587 "--project-uuid", project_uuid,
1588 "--name", "testing 123",
1589 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1590 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1592 stubs.api.pipeline_templates().create.refute_called()
1593 stubs.api.container_requests().create.refute_called()
1597 "owner_uuid": project_uuid,
1598 "name": "testing 123",
1600 "definition": self.expect_workflow,
1603 stubs.api.workflows().create.assert_called_with(
1604 body=JsonDiffMatcher(body))
1606 self.assertEqual(stubs.capture_stdout.getvalue(),
1607 stubs.expect_workflow_uuid + '\n')
1608 self.assertEqual(exited, 0)
1611 def test_incompatible_api(self, stubs):
1612 capture_stderr = io.StringIO()
1613 acr_logger = logging.getLogger('arvados.cwl-runner')
1614 stderr_logger = logging.StreamHandler(capture_stderr)
1615 acr_logger.addHandler(stderr_logger)
1617 exited = arvados_cwl.main(
1618 ["--update-workflow", self.existing_workflow_uuid,
1621 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1622 sys.stderr, sys.stderr, api_client=stubs.api)
1623 self.assertEqual(exited, 1)
1624 self.assertRegexpMatches(
1625 capture_stderr.getvalue(),
1626 "--update-workflow arg '{}' uses 'containers' API, but --api='jobs' specified".format(self.existing_workflow_uuid))
1627 acr_logger.removeHandler(stderr_logger)
1630 def test_update(self, stubs):
1631 exited = arvados_cwl.main(
1632 ["--update-workflow", self.existing_workflow_uuid,
1634 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1635 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1639 "name": "submit_wf.cwl",
1641 "definition": self.expect_workflow,
1644 stubs.api.workflows().update.assert_called_with(
1645 uuid=self.existing_workflow_uuid,
1646 body=JsonDiffMatcher(body))
1647 self.assertEqual(stubs.capture_stdout.getvalue(),
1648 self.existing_workflow_uuid + '\n')
1649 self.assertEqual(exited, 0)
1652 def test_update_name(self, stubs):
1653 exited = arvados_cwl.main(
1654 ["--update-workflow", self.existing_workflow_uuid,
1655 "--debug", "--name", "testing 123",
1656 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1657 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1661 "name": "testing 123",
1663 "definition": self.expect_workflow,
1666 stubs.api.workflows().update.assert_called_with(
1667 uuid=self.existing_workflow_uuid,
1668 body=JsonDiffMatcher(body))
1669 self.assertEqual(stubs.capture_stdout.getvalue(),
1670 self.existing_workflow_uuid + '\n')
1671 self.assertEqual(exited, 0)
1674 def test_create_collection_per_tool(self, stubs):
1675 project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1677 exited = arvados_cwl.main(
1678 ["--create-workflow", "--debug",
1680 "--project-uuid", project_uuid,
1681 "tests/collection_per_tool/collection_per_tool.cwl"],
1682 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1684 toolfile = "tests/collection_per_tool/collection_per_tool_packed.cwl"
1685 expect_workflow = StripYAMLComments(open(toolfile).read())
1689 "owner_uuid": project_uuid,
1690 "name": "collection_per_tool.cwl",
1692 "definition": expect_workflow,
1695 stubs.api.workflows().create.assert_called_with(
1696 body=JsonDiffMatcher(body))
1698 self.assertEqual(stubs.capture_stdout.getvalue(),
1699 stubs.expect_workflow_uuid + '\n')
1700 self.assertEqual(exited, 0)
1702 class TestTemplateInputs(unittest.TestCase):
1705 "inputs_test.cwl": {
1706 'runtime_constraints': {
1707 'docker_image': '999999999999999999999999999999d3+99',
1708 'min_ram_mb_per_node': 1024
1710 'script_parameters': {
1712 'a2de777156fb700f1363b1f2e370adca+60/workflow.cwl#main',
1713 'optionalFloatInput': None,
1716 'dataclass': 'File',
1718 'title': "It's a file; we expect to find some characters in it.",
1719 'description': 'If there were anything further to say, it would be said here,\nor here.'
1723 'dataclass': 'number',
1725 'title': 'Floats like a duck',
1729 'optionalFloatInput': {
1730 'type': ['null', 'float'],
1731 'dataclass': 'number',
1736 'dataclass': 'boolean',
1738 'title': 'True or false?',
1741 'repository': 'arvados',
1742 'script_version': 'master',
1743 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
1744 'script': 'cwl-runner',
1747 "name": "inputs_test.cwl",
1751 def test_inputs_empty(self, stubs):
1752 exited = arvados_cwl.main(
1753 ["--create-template",
1754 "tests/wf/inputs_test.cwl", "tests/order/empty_order.json"],
1755 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1757 stubs.api.pipeline_templates().create.assert_called_with(
1758 body=JsonDiffMatcher(self.expect_template), ensure_unique_name=True)
1760 self.assertEqual(exited, 0)
1763 def test_inputs(self, stubs):
1764 exited = arvados_cwl.main(
1765 ["--create-template",
1766 "tests/wf/inputs_test.cwl", "tests/order/inputs_test_order.json"],
1767 stubs.capture_stdout, sys.stderr, api_client=stubs.api)
1769 expect_template = copy.deepcopy(self.expect_template)
1770 params = expect_template[
1771 "components"]["inputs_test.cwl"]["script_parameters"]
1772 params["fileInput"]["value"] = '169f39d466a5438ac4a90e779bf750c7+53/blorp.txt'
1773 params["cwl:tool"] = 'a2de777156fb700f1363b1f2e370adca+60/workflow.cwl#main'
1774 params["floatInput"]["value"] = 1.234
1775 params["boolInput"]["value"] = True
1777 stubs.api.pipeline_templates().create.assert_called_with(
1778 body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1779 self.assertEqual(exited, 0)