Merge branch 'master' into 14075-uploadfiles
[arvados.git] / sdk / cwl / tests / test_submit.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 import copy
6 import cStringIO
7 import functools
8 import hashlib
9 import json
10 import logging
11 import mock
12 import sys
13 import unittest
14
15 import arvados
16 import arvados.collection
17 import arvados_cwl
18 import arvados_cwl.runner
19 import arvados.keep
20
21 from .matcher import JsonDiffMatcher, StripYAMLComments
22 from .mock_discovery import get_rootDesc
23
24 import ruamel.yaml as yaml
25
26 _rootDesc = None
27
28 def stubs(func):
29     @functools.wraps(func)
30     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
31     @mock.patch("arvados.collection.KeepClient")
32     @mock.patch("arvados.keep.KeepClient")
33     @mock.patch("arvados.events.subscribe")
34     def wrapped(self, events, keep_client1, keep_client2, keepdocker, *args, **kwargs):
35         class Stubs:
36             pass
37         stubs = Stubs()
38         stubs.events = events
39         stubs.keepdocker = keepdocker
40
41         def putstub(p, **kwargs):
42             return "%s+%i" % (hashlib.md5(p).hexdigest(), len(p))
43         keep_client1().put.side_effect = putstub
44         keep_client1.put.side_effect = putstub
45         keep_client2().put.side_effect = putstub
46         keep_client2.put.side_effect = putstub
47
48         stubs.keep_client = keep_client2
49         stubs.keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
50         stubs.fake_user_uuid = "zzzzz-tpzed-zzzzzzzzzzzzzzz"
51
52         stubs.api = mock.MagicMock()
53         stubs.api._rootDesc = get_rootDesc()
54
55         stubs.api.users().current().execute.return_value = {
56             "uuid": stubs.fake_user_uuid,
57         }
58         stubs.api.collections().list().execute.return_value = {"items": []}
59
60         class CollectionExecute(object):
61             def __init__(self, exe):
62                 self.exe = exe
63             def execute(self, num_retries=None):
64                 return self.exe
65
66         def collection_createstub(created_collections, body, ensure_unique_name=None):
67             mt = body["manifest_text"]
68             uuid = "zzzzz-4zz18-zzzzzzzzzzzzzz%d" % len(created_collections)
69             pdh = "%s+%i" % (hashlib.md5(mt).hexdigest(), len(mt))
70             created_collections[uuid] = {
71                 "uuid": uuid,
72                 "portable_data_hash": pdh,
73                 "manifest_text": mt
74             }
75             return CollectionExecute(created_collections[uuid])
76
77         def collection_getstub(created_collections, uuid):
78             for v in created_collections.itervalues():
79                 if uuid in (v["uuid"], v["portable_data_hash"]):
80                     return CollectionExecute(v)
81
82         created_collections = {
83             "99999999999999999999999999999998+99": {
84                 "uuid": "",
85                 "portable_data_hash": "99999999999999999999999999999998+99",
86                 "manifest_text": ". 99999999999999999999999999999998+99 0:0:file1.txt"
87             },
88             "99999999999999999999999999999994+99": {
89                 "uuid": "",
90                 "portable_data_hash": "99999999999999999999999999999994+99",
91                 "manifest_text": ". 99999999999999999999999999999994+99 0:0:expect_arvworkflow.cwl"
92             }
93         }
94         stubs.api.collections().create.side_effect = functools.partial(collection_createstub, created_collections)
95         stubs.api.collections().get.side_effect = functools.partial(collection_getstub, created_collections)
96
97         stubs.expect_job_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
98         stubs.api.jobs().create().execute.return_value = {
99             "uuid": stubs.expect_job_uuid,
100             "state": "Queued",
101         }
102
103         stubs.expect_container_request_uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzzz"
104         stubs.api.container_requests().create().execute.return_value = {
105             "uuid": stubs.expect_container_request_uuid,
106             "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
107             "state": "Queued"
108         }
109
110         stubs.expect_pipeline_template_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
111         stubs.api.pipeline_templates().create().execute.return_value = {
112             "uuid": stubs.expect_pipeline_template_uuid,
113         }
114         stubs.expect_job_spec = {
115             'runtime_constraints': {
116                 'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
117                 'min_ram_mb_per_node': 1024
118             },
119             'script_parameters': {
120                 'x': {
121                     'basename': 'blorp.txt',
122                     'location': 'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
123                     'class': 'File'
124                 },
125                 'y': {
126                     'basename': '99999999999999999999999999999998+99',
127                     'location': 'keep:99999999999999999999999999999998+99',
128                     'class': 'Directory'
129                 },
130                 'z': {
131                     'basename': 'anonymous',
132                     "listing": [{
133                         "basename": "renamed.txt",
134                         "class": "File",
135                         "location": "keep:99999999999999999999999999999998+99/file1.txt",
136                         "size": 0
137                     }],
138                     'class': 'Directory'
139                 },
140                 'cwl:tool': '3fffdeaa75e018172e1b583425f4ebff+60/workflow.cwl#main'
141             },
142             'repository': 'arvados',
143             'script_version': 'master',
144             'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
145             'script': 'cwl-runner'
146         }
147         stubs.pipeline_component = stubs.expect_job_spec.copy()
148         stubs.expect_pipeline_instance = {
149             'name': 'submit_wf.cwl',
150             'state': 'RunningOnServer',
151             'owner_uuid': None,
152             "components": {
153                 "cwl-runner": {
154                     'runtime_constraints': {'docker_image': 'arvados/jobs:'+arvados_cwl.__version__, 'min_ram_mb_per_node': 1024},
155                     'script_parameters': {
156                         'y': {"value": {'basename': '99999999999999999999999999999998+99', 'location': 'keep:99999999999999999999999999999998+99', 'class': 'Directory'}},
157                         'x': {"value": {
158                             'basename': 'blorp.txt',
159                             'class': 'File',
160                             'location': 'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
161                             "size": 16
162                         }},
163                         'z': {"value": {'basename': 'anonymous', 'class': 'Directory',
164                               'listing': [
165                                   {
166                                       'basename': 'renamed.txt',
167                                       'class': 'File', 'location':
168                                       'keep:99999999999999999999999999999998+99/file1.txt',
169                                       'size': 0
170                                   }
171                               ]}},
172                         'cwl:tool': '3fffdeaa75e018172e1b583425f4ebff+60/workflow.cwl#main',
173                         'arv:debug': True,
174                         'arv:enable_reuse': True,
175                         'arv:on_error': 'continue'
176                     },
177                     'repository': 'arvados',
178                     'script_version': 'master',
179                     'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
180                     'script': 'cwl-runner',
181                     'job': {'state': 'Queued', 'uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz'}
182                 }
183             }
184         }
185         stubs.pipeline_create = copy.deepcopy(stubs.expect_pipeline_instance)
186         stubs.expect_pipeline_uuid = "zzzzz-d1hrv-zzzzzzzzzzzzzzz"
187         stubs.pipeline_create["uuid"] = stubs.expect_pipeline_uuid
188         stubs.pipeline_with_job = copy.deepcopy(stubs.pipeline_create)
189         stubs.pipeline_with_job["components"]["cwl-runner"]["job"] = {
190             "uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
191             "state": "Queued"
192         }
193         stubs.api.pipeline_instances().create().execute.return_value = stubs.pipeline_create
194         stubs.api.pipeline_instances().get().execute.return_value = stubs.pipeline_with_job
195
196         with open("tests/wf/submit_wf_packed.cwl") as f:
197             expect_packed_workflow = yaml.round_trip_load(f)
198
199         stubs.expect_container_spec = {
200             'priority': 500,
201             'mounts': {
202                 '/var/spool/cwl': {
203                     'writable': True,
204                     'kind': 'collection'
205                 },
206                 '/var/lib/cwl/workflow.json': {
207                     'content': expect_packed_workflow,
208                     'kind': 'json'
209                 },
210                 'stdout': {
211                     'path': '/var/spool/cwl/cwl.output.json',
212                     'kind': 'file'
213                 },
214                 '/var/lib/cwl/cwl.input.json': {
215                     'kind': 'json',
216                     'content': {
217                         'y': {
218                             'basename': '99999999999999999999999999999998+99',
219                             'location': 'keep:99999999999999999999999999999998+99',
220                             'class': 'Directory'},
221                         'x': {
222                             'basename': u'blorp.txt',
223                             'class': 'File',
224                             'location': u'keep:169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
225                             "size": 16
226                         },
227                         'z': {'basename': 'anonymous', 'class': 'Directory', 'listing': [
228                             {'basename': 'renamed.txt',
229                              'class': 'File',
230                              'location': 'keep:99999999999999999999999999999998+99/file1.txt',
231                              'size': 0
232                             }
233                         ]}
234                     },
235                     'kind': 'json'
236                 }
237             },
238             'secret_mounts': {},
239             'state': 'Committed',
240             'command': ['arvados-cwl-runner', '--local', '--api=containers',
241                         '--no-log-timestamps', '--disable-validate',
242                         '--eval-timeout=20', '--thread-count=4',
243                         '--enable-reuse', '--debug', '--on-error=continue',
244                         '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
245             'name': 'submit_wf.cwl',
246             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
247             'output_path': '/var/spool/cwl',
248             'cwd': '/var/spool/cwl',
249             'runtime_constraints': {
250                 'API': True,
251                 'vcpus': 1,
252                 'ram': 1024*1024*1024
253             },
254             'use_existing': True,
255             'properties': {},
256             'secret_mounts': {}
257         }
258
259         stubs.expect_workflow_uuid = "zzzzz-7fd4e-zzzzzzzzzzzzzzz"
260         stubs.api.workflows().create().execute.return_value = {
261             "uuid": stubs.expect_workflow_uuid,
262         }
263         def update_mock(**kwargs):
264             stubs.updated_uuid = kwargs.get('uuid')
265             return mock.DEFAULT
266         stubs.api.workflows().update.side_effect = update_mock
267         stubs.api.workflows().update().execute.side_effect = lambda **kwargs: {
268             "uuid": stubs.updated_uuid,
269         }
270
271         return func(self, stubs, *args, **kwargs)
272     return wrapped
273
274
275 class TestSubmit(unittest.TestCase):
276     @mock.patch("arvados_cwl.runner.arv_docker_get_image")
277     @mock.patch("time.sleep")
278     @stubs
279     def test_submit(self, stubs, tm, arvdock):
280         capture_stdout = cStringIO.StringIO()
281         exited = arvados_cwl.main(
282             ["--submit", "--no-wait", "--api=jobs", "--debug",
283              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
284             capture_stdout, sys.stderr, api_client=stubs.api)
285         self.assertEqual(exited, 0)
286
287         stubs.api.collections().create.assert_has_calls([
288             mock.call(body=JsonDiffMatcher({
289                 'manifest_text':
290                 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
291                 'replication_desired': None,
292                 'name': 'submit_tool.cwl dependencies (5d373e7629203ce39e7c22af98a0f881+52)',
293             }), ensure_unique_name=False),
294             mock.call(body=JsonDiffMatcher({
295                 'manifest_text':
296                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
297                 'replication_desired': None,
298                 'name': 'submit_wf.cwl input (169f39d466a5438ac4a90e779bf750c7+53)',
299             }), ensure_unique_name=False),
300             mock.call(body=JsonDiffMatcher({
301                 'manifest_text':
302                 '. 61df2ed9ee3eb7dd9b799e5ca35305fa+1217 0:1217:workflow.cwl\n',
303                 'replication_desired': None,
304                 'name': 'submit_wf.cwl',
305             }), ensure_unique_name=True)        ])
306
307         arvdock.assert_has_calls([
308             mock.call(stubs.api, {"class": "DockerRequirement", "dockerPull": "debian:8"}, True, None),
309             mock.call(stubs.api, {'dockerPull': 'arvados/jobs:'+arvados_cwl.__version__}, True, None)
310         ])
311
312         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
313         stubs.api.pipeline_instances().create.assert_called_with(
314             body=JsonDiffMatcher(expect_pipeline))
315         self.assertEqual(capture_stdout.getvalue(),
316                          stubs.expect_pipeline_uuid + '\n')
317
318
319     @mock.patch("time.sleep")
320     @stubs
321     def test_submit_no_reuse(self, stubs, tm):
322         capture_stdout = cStringIO.StringIO()
323         exited = arvados_cwl.main(
324             ["--submit", "--no-wait", "--api=jobs", "--debug", "--disable-reuse",
325              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
326             capture_stdout, sys.stderr, api_client=stubs.api)
327         self.assertEqual(exited, 0)
328
329         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
330         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:enable_reuse"] = {"value": False}
331         expect_pipeline["properties"] = {"run_options": {"enable_job_reuse": False}}
332
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')
337
338     @stubs
339     def test_error_when_multiple_storage_classes_specified(self, stubs):
340         storage_classes = "foo,bar"
341         exited = arvados_cwl.main(
342                 ["--debug", "--storage-classes", storage_classes,
343                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
344                 sys.stdin, sys.stderr, api_client=stubs.api)
345         self.assertEqual(exited, 1)
346
347     @mock.patch("time.sleep")
348     @stubs
349     def test_submit_on_error(self, stubs, tm):
350         capture_stdout = cStringIO.StringIO()
351         exited = arvados_cwl.main(
352             ["--submit", "--no-wait", "--api=jobs", "--debug", "--on-error=stop",
353              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
354             capture_stdout, sys.stderr, api_client=stubs.api)
355         self.assertEqual(exited, 0)
356
357         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
358         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:on_error"] = "stop"
359
360         stubs.api.pipeline_instances().create.assert_called_with(
361             body=JsonDiffMatcher(expect_pipeline))
362         self.assertEqual(capture_stdout.getvalue(),
363                          stubs.expect_pipeline_uuid + '\n')
364
365
366     @mock.patch("time.sleep")
367     @stubs
368     def test_submit_runner_ram(self, stubs, tm):
369         capture_stdout = cStringIO.StringIO()
370         exited = arvados_cwl.main(
371             ["--submit", "--no-wait", "--debug", "--submit-runner-ram=2048",
372              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
373             capture_stdout, sys.stderr, api_client=stubs.api)
374         self.assertEqual(exited, 0)
375
376         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
377         expect_pipeline["components"]["cwl-runner"]["runtime_constraints"]["min_ram_mb_per_node"] = 2048
378
379         stubs.api.pipeline_instances().create.assert_called_with(
380             body=JsonDiffMatcher(expect_pipeline))
381         self.assertEqual(capture_stdout.getvalue(),
382                          stubs.expect_pipeline_uuid + '\n')
383
384
385     @mock.patch("time.sleep")
386     @stubs
387     def test_submit_invalid_runner_ram(self, stubs, tm):
388         capture_stdout = cStringIO.StringIO()
389         exited = arvados_cwl.main(
390             ["--submit", "--no-wait", "--debug", "--submit-runner-ram=-2048",
391              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
392             capture_stdout, sys.stderr, api_client=stubs.api)
393         self.assertEqual(exited, 1)
394
395     @mock.patch("time.sleep")
396     @stubs
397     def test_submit_output_name(self, stubs, tm):
398         output_name = "test_output_name"
399
400         capture_stdout = cStringIO.StringIO()
401         exited = arvados_cwl.main(
402             ["--submit", "--no-wait", "--debug", "--output-name", output_name,
403              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
404             capture_stdout, sys.stderr, api_client=stubs.api)
405         self.assertEqual(exited, 0)
406
407         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
408         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:output_name"] = output_name
409
410         stubs.api.pipeline_instances().create.assert_called_with(
411             body=JsonDiffMatcher(expect_pipeline))
412         self.assertEqual(capture_stdout.getvalue(),
413                          stubs.expect_pipeline_uuid + '\n')
414
415
416     @mock.patch("time.sleep")
417     @stubs
418     def test_submit_pipeline_name(self, stubs, tm):
419         capture_stdout = cStringIO.StringIO()
420         exited = arvados_cwl.main(
421             ["--submit", "--no-wait", "--debug", "--name=hello job 123",
422              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
423             capture_stdout, sys.stderr, api_client=stubs.api)
424         self.assertEqual(exited, 0)
425
426         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
427         expect_pipeline["name"] = "hello job 123"
428
429         stubs.api.pipeline_instances().create.assert_called_with(
430             body=JsonDiffMatcher(expect_pipeline))
431         self.assertEqual(capture_stdout.getvalue(),
432                          stubs.expect_pipeline_uuid + '\n')
433
434     @mock.patch("time.sleep")
435     @stubs
436     def test_submit_output_tags(self, stubs, tm):
437         output_tags = "tag0,tag1,tag2"
438
439         capture_stdout = cStringIO.StringIO()
440         exited = arvados_cwl.main(
441             ["--submit", "--no-wait", "--debug", "--output-tags", output_tags,
442              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
443             capture_stdout, sys.stderr, api_client=stubs.api)
444         self.assertEqual(exited, 0)
445
446         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
447         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["arv:output_tags"] = output_tags
448
449         stubs.api.pipeline_instances().create.assert_called_with(
450             body=JsonDiffMatcher(expect_pipeline))
451         self.assertEqual(capture_stdout.getvalue(),
452                          stubs.expect_pipeline_uuid + '\n')
453
454     @mock.patch("time.sleep")
455     @stubs
456     def test_submit_with_project_uuid(self, stubs, tm):
457         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
458
459         exited = arvados_cwl.main(
460             ["--submit", "--no-wait", "--debug",
461              "--project-uuid", project_uuid,
462              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
463             sys.stdout, sys.stderr, api_client=stubs.api)
464         self.assertEqual(exited, 0)
465
466         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
467         expect_pipeline["owner_uuid"] = project_uuid
468         stubs.api.pipeline_instances().create.assert_called_with(
469             body=JsonDiffMatcher(expect_pipeline))
470
471     @stubs
472     def test_submit_container(self, stubs):
473         capture_stdout = cStringIO.StringIO()
474         try:
475             exited = arvados_cwl.main(
476                 ["--submit", "--no-wait", "--api=containers", "--debug",
477                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
478                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
479             self.assertEqual(exited, 0)
480         except:
481             logging.exception("")
482
483         stubs.api.collections().create.assert_has_calls([
484             mock.call(body=JsonDiffMatcher({
485                 'manifest_text':
486                 '. 5bcc9fe8f8d5992e6cf418dc7ce4dbb3+16 0:16:blub.txt\n',
487                 'replication_desired': None,
488                 'name': 'submit_tool.cwl dependencies (5d373e7629203ce39e7c22af98a0f881+52)',
489             }), ensure_unique_name=False),
490             mock.call(body=JsonDiffMatcher({
491                 'manifest_text':
492                 '. 979af1245a12a1fed634d4222473bfdc+16 0:16:blorp.txt\n',
493                 'replication_desired': None,
494                 'name': 'submit_wf.cwl input (169f39d466a5438ac4a90e779bf750c7+53)',
495             }), ensure_unique_name=False)])
496
497         expect_container = copy.deepcopy(stubs.expect_container_spec)
498         stubs.api.container_requests().create.assert_called_with(
499             body=JsonDiffMatcher(expect_container))
500         self.assertEqual(capture_stdout.getvalue(),
501                          stubs.expect_container_request_uuid + '\n')
502
503     @stubs
504     def test_submit_container_no_reuse(self, stubs):
505         capture_stdout = cStringIO.StringIO()
506         try:
507             exited = arvados_cwl.main(
508                 ["--submit", "--no-wait", "--api=containers", "--debug", "--disable-reuse",
509                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
510                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
511             self.assertEqual(exited, 0)
512         except:
513             logging.exception("")
514
515         expect_container = copy.deepcopy(stubs.expect_container_spec)
516         expect_container["command"] = [
517             'arvados-cwl-runner', '--local', '--api=containers',
518             '--no-log-timestamps', '--disable-validate',
519             '--eval-timeout=20', '--thread-count=4',
520             '--disable-reuse', '--debug', '--on-error=continue',
521             '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
522         expect_container["use_existing"] = False
523
524         stubs.api.container_requests().create.assert_called_with(
525             body=JsonDiffMatcher(expect_container))
526         self.assertEqual(capture_stdout.getvalue(),
527                          stubs.expect_container_request_uuid + '\n')
528
529
530     @stubs
531     def test_submit_container_reuse_disabled_by_workflow(self, stubs):
532         capture_stdout = cStringIO.StringIO()
533
534         exited = arvados_cwl.main(
535             ["--submit", "--no-wait", "--api=containers", "--debug",
536              "tests/wf/submit_wf_no_reuse.cwl", "tests/submit_test_job.json"],
537             capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
538         self.assertEqual(exited, 0)
539
540         expect_container = copy.deepcopy(stubs.expect_container_spec)
541         expect_container["command"] = [
542             'arvados-cwl-runner', '--local', '--api=containers',
543             '--no-log-timestamps', '--disable-validate',
544             '--eval-timeout=20', '--thread-count=4',
545             '--disable-reuse', '--debug', '--on-error=continue',
546             '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
547         expect_container["use_existing"] = False
548         expect_container["name"] = "submit_wf_no_reuse.cwl"
549         expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][1]["hints"] = [
550             {
551                 "class": "http://arvados.org/cwl#ReuseRequirement",
552                 "enableReuse": False,
553             },
554         ]
555         expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][0]["$namespaces"] = {
556             "arv": "http://arvados.org/cwl#",
557             "cwltool": "http://commonwl.org/cwltool#"
558         }
559
560         stubs.api.container_requests().create.assert_called_with(
561             body=JsonDiffMatcher(expect_container))
562         self.assertEqual(capture_stdout.getvalue(),
563                          stubs.expect_container_request_uuid + '\n')
564
565
566     @stubs
567     def test_submit_container_on_error(self, stubs):
568         capture_stdout = cStringIO.StringIO()
569         try:
570             exited = arvados_cwl.main(
571                 ["--submit", "--no-wait", "--api=containers", "--debug", "--on-error=stop",
572                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
573                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
574             self.assertEqual(exited, 0)
575         except:
576             logging.exception("")
577
578         expect_container = copy.deepcopy(stubs.expect_container_spec)
579         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
580                                        '--no-log-timestamps', '--disable-validate',
581                                        '--eval-timeout=20', '--thread-count=4',
582                                        '--enable-reuse', '--debug', '--on-error=stop',
583                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
584
585         stubs.api.container_requests().create.assert_called_with(
586             body=JsonDiffMatcher(expect_container))
587         self.assertEqual(capture_stdout.getvalue(),
588                          stubs.expect_container_request_uuid + '\n')
589
590     @stubs
591     def test_submit_container_output_name(self, stubs):
592         output_name = "test_output_name"
593
594         capture_stdout = cStringIO.StringIO()
595         try:
596             exited = arvados_cwl.main(
597                 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-name", output_name,
598                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
599                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
600             self.assertEqual(exited, 0)
601         except:
602             logging.exception("")
603
604         expect_container = copy.deepcopy(stubs.expect_container_spec)
605         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
606                                        '--no-log-timestamps', '--disable-validate',
607                                        '--eval-timeout=20', '--thread-count=4',
608                                        '--enable-reuse',
609                                        "--output-name="+output_name, '--debug', '--on-error=continue',
610                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
611         expect_container["output_name"] = output_name
612
613         stubs.api.container_requests().create.assert_called_with(
614             body=JsonDiffMatcher(expect_container))
615         self.assertEqual(capture_stdout.getvalue(),
616                          stubs.expect_container_request_uuid + '\n')
617
618     @stubs
619     def test_submit_storage_classes(self, stubs):
620         capture_stdout = cStringIO.StringIO()
621         try:
622             exited = arvados_cwl.main(
623                 ["--debug", "--submit", "--no-wait", "--api=containers", "--storage-classes=foo",
624                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
625                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
626             self.assertEqual(exited, 0)
627         except:
628             logging.exception("")
629
630         expect_container = copy.deepcopy(stubs.expect_container_spec)
631         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
632                                        '--no-log-timestamps', '--disable-validate',
633                                        '--eval-timeout=20', '--thread-count=4',
634                                        '--enable-reuse', "--debug",
635                                        "--storage-classes=foo", '--on-error=continue',
636                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
637
638         stubs.api.container_requests().create.assert_called_with(
639             body=JsonDiffMatcher(expect_container))
640         self.assertEqual(capture_stdout.getvalue(),
641                          stubs.expect_container_request_uuid + '\n')
642
643     @mock.patch("arvados_cwl.task_queue.TaskQueue")
644     @mock.patch("arvados_cwl.arvworkflow.ArvadosWorkflow.job")
645     @mock.patch("arvados_cwl.ArvCwlRunner.make_output_collection", return_value = (None, None))
646     @stubs
647     def test_storage_classes_correctly_propagate_to_make_output_collection(self, stubs, make_output, job, tq):
648         def set_final_output(job_order, output_callback, runtimeContext):
649             output_callback("zzzzz-4zz18-zzzzzzzzzzzzzzzz", "success")
650             return []
651         job.side_effect = set_final_output
652
653         try:
654             exited = arvados_cwl.main(
655                 ["--debug", "--local", "--storage-classes=foo",
656                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
657                 sys.stdin, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
658             self.assertEqual(exited, 0)
659         except:
660             logging.exception("")
661
662         make_output.assert_called_with(u'Output of submit_wf.cwl', ['foo'], '', 'zzzzz-4zz18-zzzzzzzzzzzzzzzz')
663
664     @mock.patch("arvados_cwl.task_queue.TaskQueue")
665     @mock.patch("arvados_cwl.arvworkflow.ArvadosWorkflow.job")
666     @mock.patch("arvados_cwl.ArvCwlRunner.make_output_collection", return_value = (None, None))
667     @stubs
668     def test_default_storage_classes_correctly_propagate_to_make_output_collection(self, stubs, make_output, job, tq):
669         def set_final_output(job_order, output_callback, runtimeContext):
670             output_callback("zzzzz-4zz18-zzzzzzzzzzzzzzzz", "success")
671             return []
672         job.side_effect = set_final_output
673
674         try:
675             exited = arvados_cwl.main(
676                 ["--debug", "--local",
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)
679             self.assertEqual(exited, 0)
680         except:
681             logging.exception("")
682
683         make_output.assert_called_with(u'Output of submit_wf.cwl', ['default'], '', 'zzzzz-4zz18-zzzzzzzzzzzzzzzz')
684
685     @stubs
686     def test_submit_container_output_ttl(self, stubs):
687         capture_stdout = cStringIO.StringIO()
688         try:
689             exited = arvados_cwl.main(
690                 ["--submit", "--no-wait", "--api=containers", "--debug", "--intermediate-output-ttl", "3600",
691                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
692                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
693             self.assertEqual(exited, 0)
694         except:
695             logging.exception("")
696
697         expect_container = copy.deepcopy(stubs.expect_container_spec)
698         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
699                                        '--no-log-timestamps', '--disable-validate',
700                                        '--eval-timeout=20', '--thread-count=4',
701                                        '--enable-reuse', '--debug', '--on-error=continue',
702                                        "--intermediate-output-ttl=3600",
703                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
704
705         stubs.api.container_requests().create.assert_called_with(
706             body=JsonDiffMatcher(expect_container))
707         self.assertEqual(capture_stdout.getvalue(),
708                          stubs.expect_container_request_uuid + '\n')
709
710     @stubs
711     def test_submit_container_trash_intermediate(self, stubs):
712         capture_stdout = cStringIO.StringIO()
713         try:
714             exited = arvados_cwl.main(
715                 ["--submit", "--no-wait", "--api=containers", "--debug", "--trash-intermediate",
716                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
717                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
718             self.assertEqual(exited, 0)
719         except:
720             logging.exception("")
721
722         expect_container = copy.deepcopy(stubs.expect_container_spec)
723         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
724                                        '--no-log-timestamps', '--disable-validate',
725                                        '--eval-timeout=20', '--thread-count=4',
726                                        '--enable-reuse', '--debug', '--on-error=continue',
727                                        "--trash-intermediate",
728                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
729
730         stubs.api.container_requests().create.assert_called_with(
731             body=JsonDiffMatcher(expect_container))
732         self.assertEqual(capture_stdout.getvalue(),
733                          stubs.expect_container_request_uuid + '\n')
734
735     @stubs
736     def test_submit_container_output_tags(self, stubs):
737         output_tags = "tag0,tag1,tag2"
738
739         capture_stdout = cStringIO.StringIO()
740         try:
741             exited = arvados_cwl.main(
742                 ["--submit", "--no-wait", "--api=containers", "--debug", "--output-tags", output_tags,
743                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
744                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
745             self.assertEqual(exited, 0)
746         except:
747             logging.exception("")
748
749         expect_container = copy.deepcopy(stubs.expect_container_spec)
750         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
751                                        '--no-log-timestamps', '--disable-validate',
752                                        '--eval-timeout=20', '--thread-count=4',
753                                        '--enable-reuse',
754                                        "--output-tags="+output_tags, '--debug', '--on-error=continue',
755                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
756
757         stubs.api.container_requests().create.assert_called_with(
758             body=JsonDiffMatcher(expect_container))
759         self.assertEqual(capture_stdout.getvalue(),
760                          stubs.expect_container_request_uuid + '\n')
761
762     @stubs
763     def test_submit_container_runner_ram(self, stubs):
764         capture_stdout = cStringIO.StringIO()
765         try:
766             exited = arvados_cwl.main(
767                 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-ram=2048",
768                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
769                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
770             self.assertEqual(exited, 0)
771         except:
772             logging.exception("")
773
774         expect_container = copy.deepcopy(stubs.expect_container_spec)
775         expect_container["runtime_constraints"]["ram"] = 2048*1024*1024
776
777         stubs.api.container_requests().create.assert_called_with(
778             body=JsonDiffMatcher(expect_container))
779         self.assertEqual(capture_stdout.getvalue(),
780                          stubs.expect_container_request_uuid + '\n')
781
782     @mock.patch("arvados.collection.CollectionReader")
783     @mock.patch("time.sleep")
784     @stubs
785     def test_submit_file_keepref(self, stubs, tm, collectionReader):
786         capture_stdout = cStringIO.StringIO()
787         collectionReader().find.return_value = arvados.arvfile.ArvadosFile(mock.MagicMock(), "blorp.txt")
788         exited = arvados_cwl.main(
789             ["--submit", "--no-wait", "--api=containers", "--debug",
790              "tests/wf/submit_keepref_wf.cwl"],
791             capture_stdout, sys.stderr, api_client=stubs.api)
792         self.assertEqual(exited, 0)
793
794
795     @mock.patch("arvados.collection.CollectionReader")
796     @mock.patch("time.sleep")
797     @stubs
798     def test_submit_keepref(self, stubs, tm, reader):
799         capture_stdout = cStringIO.StringIO()
800
801         with open("tests/wf/expect_arvworkflow.cwl") as f:
802             reader().open().__enter__().read.return_value = f.read()
803
804         exited = arvados_cwl.main(
805             ["--submit", "--no-wait", "--api=containers", "--debug",
806              "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
807             capture_stdout, sys.stderr, api_client=stubs.api)
808         self.assertEqual(exited, 0)
809
810         expect_container = {
811             'priority': 500,
812             'mounts': {
813                 '/var/spool/cwl': {
814                     'writable': True,
815                     'kind': 'collection'
816                 },
817                 'stdout': {
818                     'path': '/var/spool/cwl/cwl.output.json',
819                     'kind': 'file'
820                 },
821                 '/var/lib/cwl/workflow': {
822                     'portable_data_hash': '99999999999999999999999999999994+99',
823                     'kind': 'collection'
824                 },
825                 '/var/lib/cwl/cwl.input.json': {
826                     'content': {
827                         'x': 'XxX'
828                     },
829                     'kind': 'json'
830                 }
831             }, 'state': 'Committed',
832             'output_path': '/var/spool/cwl',
833             'name': 'expect_arvworkflow.cwl#main',
834             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
835             'command': ['arvados-cwl-runner', '--local', '--api=containers',
836                         '--no-log-timestamps', '--disable-validate',
837                         '--eval-timeout=20', '--thread-count=4',
838                         '--enable-reuse', '--debug', '--on-error=continue',
839                         '/var/lib/cwl/workflow/expect_arvworkflow.cwl#main', '/var/lib/cwl/cwl.input.json'],
840             'cwd': '/var/spool/cwl',
841             'runtime_constraints': {
842                 'API': True,
843                 'vcpus': 1,
844                 'ram': 1073741824
845             },
846             'use_existing': True,
847             'properties': {},
848             'secret_mounts': {}
849         }
850
851         stubs.api.container_requests().create.assert_called_with(
852             body=JsonDiffMatcher(expect_container))
853         self.assertEqual(capture_stdout.getvalue(),
854                          stubs.expect_container_request_uuid + '\n')
855
856
857     @mock.patch("arvados.collection.CollectionReader")
858     @mock.patch("time.sleep")
859     @stubs
860     def test_submit_jobs_keepref(self, stubs, tm, reader):
861         capture_stdout = cStringIO.StringIO()
862
863         with open("tests/wf/expect_arvworkflow.cwl") as f:
864             reader().open().__enter__().read.return_value = f.read()
865
866         exited = arvados_cwl.main(
867             ["--submit", "--no-wait", "--api=jobs", "--debug",
868              "keep:99999999999999999999999999999994+99/expect_arvworkflow.cwl#main", "-x", "XxX"],
869             capture_stdout, sys.stderr, api_client=stubs.api)
870         self.assertEqual(exited, 0)
871
872         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
873         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["x"] = "XxX"
874         del expect_pipeline["components"]["cwl-runner"]["script_parameters"]["y"]
875         del expect_pipeline["components"]["cwl-runner"]["script_parameters"]["z"]
876         expect_pipeline["components"]["cwl-runner"]["script_parameters"]["cwl:tool"] = "99999999999999999999999999999994+99/expect_arvworkflow.cwl#main"
877         expect_pipeline["name"] = "expect_arvworkflow.cwl#main"
878         stubs.api.pipeline_instances().create.assert_called_with(
879             body=JsonDiffMatcher(expect_pipeline))
880
881     @mock.patch("time.sleep")
882     @stubs
883     def test_submit_arvworkflow(self, stubs, tm):
884         capture_stdout = cStringIO.StringIO()
885
886         with open("tests/wf/expect_arvworkflow.cwl") as f:
887             stubs.api.workflows().get().execute.return_value = {"definition": f.read(), "name": "a test workflow"}
888
889         exited = arvados_cwl.main(
890             ["--submit", "--no-wait", "--api=containers", "--debug",
891              "962eh-7fd4e-gkbzl62qqtfig37", "-x", "XxX"],
892             capture_stdout, sys.stderr, api_client=stubs.api)
893         self.assertEqual(exited, 0)
894
895         expect_container = {
896             'priority': 500,
897             'mounts': {
898                 '/var/spool/cwl': {
899                     'writable': True,
900                     'kind': 'collection'
901                 },
902                 'stdout': {
903                     'path': '/var/spool/cwl/cwl.output.json',
904                     'kind': 'file'
905                 },
906                 '/var/lib/cwl/workflow.json': {
907                     'kind': 'json',
908                     'content': {
909                         'cwlVersion': 'v1.0',
910                         '$graph': [
911                             {
912                                 'id': '#main',
913                                 'inputs': [
914                                     {'type': 'string', 'id': '#main/x'}
915                                 ],
916                                 'steps': [
917                                     {'in': [{'source': '#main/x', 'id': '#main/step1/x'}],
918                                      'run': '#submit_tool.cwl',
919                                      'id': '#main/step1',
920                                      'out': []}
921                                 ],
922                                 'class': 'Workflow',
923                                 'outputs': []
924                             },
925                             {
926                                 'inputs': [
927                                     {
928                                         'inputBinding': {'position': 1},
929                                         'type': 'string',
930                                         'id': '#submit_tool.cwl/x'}
931                                 ],
932                                 'requirements': [
933                                     {'dockerPull': 'debian:8', 'class': 'DockerRequirement'}
934                                 ],
935                                 'id': '#submit_tool.cwl',
936                                 'outputs': [],
937                                 'baseCommand': 'cat',
938                                 'class': 'CommandLineTool'
939                             }
940                         ]
941                     }
942                 },
943                 '/var/lib/cwl/cwl.input.json': {
944                     'content': {
945                         'x': 'XxX'
946                     },
947                     'kind': 'json'
948                 }
949             }, 'state': 'Committed',
950             'output_path': '/var/spool/cwl',
951             'name': 'a test workflow',
952             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
953             'command': ['arvados-cwl-runner', '--local', '--api=containers',
954                         '--no-log-timestamps', '--disable-validate',
955                         '--eval-timeout=20', '--thread-count=4',
956                         '--enable-reuse', '--debug', '--on-error=continue',
957                         '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json'],
958             'cwd': '/var/spool/cwl',
959             'runtime_constraints': {
960                 'API': True,
961                 'vcpus': 1,
962                 'ram': 1073741824
963             },
964             'use_existing': True,
965             'properties': {
966                 "template_uuid": "962eh-7fd4e-gkbzl62qqtfig37"
967             },
968             'secret_mounts': {}
969         }
970
971         stubs.api.container_requests().create.assert_called_with(
972             body=JsonDiffMatcher(expect_container))
973         self.assertEqual(capture_stdout.getvalue(),
974                          stubs.expect_container_request_uuid + '\n')
975
976
977     @stubs
978     def test_submit_container_name(self, stubs):
979         capture_stdout = cStringIO.StringIO()
980         try:
981             exited = arvados_cwl.main(
982                 ["--submit", "--no-wait", "--api=containers", "--debug", "--name=hello container 123",
983                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
984                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
985             self.assertEqual(exited, 0)
986         except:
987             logging.exception("")
988
989         expect_container = copy.deepcopy(stubs.expect_container_spec)
990         expect_container["name"] = "hello container 123"
991
992         stubs.api.container_requests().create.assert_called_with(
993             body=JsonDiffMatcher(expect_container))
994         self.assertEqual(capture_stdout.getvalue(),
995                          stubs.expect_container_request_uuid + '\n')
996
997
998     @stubs
999     def test_submit_container_project(self, stubs):
1000         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1001         capture_stdout = cStringIO.StringIO()
1002         try:
1003             exited = arvados_cwl.main(
1004                 ["--submit", "--no-wait", "--api=containers", "--debug", "--project-uuid="+project_uuid,
1005                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1006                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1007             self.assertEqual(exited, 0)
1008         except:
1009             logging.exception("")
1010
1011         expect_container = copy.deepcopy(stubs.expect_container_spec)
1012         expect_container["owner_uuid"] = project_uuid
1013         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1014                                        '--no-log-timestamps', '--disable-validate',
1015                                        "--eval-timeout=20", "--thread-count=4",
1016                                        '--enable-reuse', '--debug', '--on-error=continue',
1017                                        '--project-uuid='+project_uuid,
1018                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1019
1020         stubs.api.container_requests().create.assert_called_with(
1021             body=JsonDiffMatcher(expect_container))
1022         self.assertEqual(capture_stdout.getvalue(),
1023                          stubs.expect_container_request_uuid + '\n')
1024
1025     @stubs
1026     def test_submit_container_eval_timeout(self, stubs):
1027         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1028         capture_stdout = cStringIO.StringIO()
1029         try:
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                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1034             self.assertEqual(exited, 0)
1035         except:
1036             logging.exception("")
1037
1038         expect_container = copy.deepcopy(stubs.expect_container_spec)
1039         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1040                                        '--no-log-timestamps', '--disable-validate',
1041                                        '--eval-timeout=60.0', '--thread-count=4',
1042                                        '--enable-reuse', '--debug', '--on-error=continue',
1043                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1044
1045         stubs.api.container_requests().create.assert_called_with(
1046             body=JsonDiffMatcher(expect_container))
1047         self.assertEqual(capture_stdout.getvalue(),
1048                          stubs.expect_container_request_uuid + '\n')
1049
1050
1051     @stubs
1052     def test_submit_container_thread_count(self, stubs):
1053         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1054         capture_stdout = cStringIO.StringIO()
1055         try:
1056             exited = arvados_cwl.main(
1057                 ["--submit", "--no-wait", "--api=containers", "--debug", "--thread-count=20",
1058                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1059                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1060             self.assertEqual(exited, 0)
1061         except:
1062             logging.exception("")
1063
1064         expect_container = copy.deepcopy(stubs.expect_container_spec)
1065         expect_container["command"] = ['arvados-cwl-runner', '--local', '--api=containers',
1066                                        '--no-log-timestamps', '--disable-validate',
1067                                        '--eval-timeout=20', '--thread-count=20',
1068                                        '--enable-reuse', '--debug', '--on-error=continue',
1069                                        '/var/lib/cwl/workflow.json#main', '/var/lib/cwl/cwl.input.json']
1070
1071         stubs.api.container_requests().create.assert_called_with(
1072             body=JsonDiffMatcher(expect_container))
1073         self.assertEqual(capture_stdout.getvalue(),
1074                          stubs.expect_container_request_uuid + '\n')
1075
1076
1077     @stubs
1078     def test_submit_job_runner_image(self, stubs):
1079         capture_stdout = cStringIO.StringIO()
1080         try:
1081             exited = arvados_cwl.main(
1082                 ["--submit", "--no-wait", "--api=jobs", "--debug", "--submit-runner-image=arvados/jobs:123",
1083                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1084                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1085             self.assertEqual(exited, 0)
1086         except:
1087             logging.exception("")
1088
1089         stubs.expect_pipeline_instance["components"]["cwl-runner"]["runtime_constraints"]["docker_image"] = "arvados/jobs:123"
1090
1091         expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
1092         stubs.api.pipeline_instances().create.assert_called_with(
1093             body=JsonDiffMatcher(expect_pipeline))
1094         self.assertEqual(capture_stdout.getvalue(),
1095                          stubs.expect_pipeline_uuid + '\n')
1096
1097     @stubs
1098     def test_submit_container_runner_image(self, stubs):
1099         capture_stdout = cStringIO.StringIO()
1100         try:
1101             exited = arvados_cwl.main(
1102                 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-runner-image=arvados/jobs:123",
1103                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1104                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1105             self.assertEqual(exited, 0)
1106         except:
1107             logging.exception("")
1108
1109         stubs.expect_container_spec["container_image"] = "arvados/jobs:123"
1110
1111         expect_container = copy.deepcopy(stubs.expect_container_spec)
1112         stubs.api.container_requests().create.assert_called_with(
1113             body=JsonDiffMatcher(expect_container))
1114         self.assertEqual(capture_stdout.getvalue(),
1115                          stubs.expect_container_request_uuid + '\n')
1116
1117     @stubs
1118     def test_submit_priority(self, stubs):
1119         capture_stdout = cStringIO.StringIO()
1120         try:
1121             exited = arvados_cwl.main(
1122                 ["--submit", "--no-wait", "--api=containers", "--debug", "--priority=669",
1123                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1124                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1125             self.assertEqual(exited, 0)
1126         except:
1127             logging.exception("")
1128
1129         stubs.expect_container_spec["priority"] = 669
1130
1131         expect_container = copy.deepcopy(stubs.expect_container_spec)
1132         stubs.api.container_requests().create.assert_called_with(
1133             body=JsonDiffMatcher(expect_container))
1134         self.assertEqual(capture_stdout.getvalue(),
1135                          stubs.expect_container_request_uuid + '\n')
1136
1137
1138     @stubs
1139     def test_submit_wf_runner_resources(self, stubs):
1140         capture_stdout = cStringIO.StringIO()
1141         try:
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                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1146             self.assertEqual(exited, 0)
1147         except:
1148             logging.exception("")
1149
1150         expect_container = copy.deepcopy(stubs.expect_container_spec)
1151         expect_container["runtime_constraints"] = {
1152             "API": True,
1153             "vcpus": 2,
1154             "ram": 2000 * 2**20
1155         }
1156         expect_container["name"] = "submit_wf_runner_resources.cwl"
1157         expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][1]["hints"] = [
1158             {
1159                 "class": "http://arvados.org/cwl#WorkflowRunnerResources",
1160                 "coresMin": 2,
1161                 "ramMin": 2000
1162             }
1163         ]
1164         expect_container["mounts"]["/var/lib/cwl/workflow.json"]["content"]["$graph"][0]["$namespaces"] = {
1165             "arv": "http://arvados.org/cwl#",
1166         }
1167
1168         stubs.api.container_requests().create.assert_called_with(
1169             body=JsonDiffMatcher(expect_container))
1170         self.assertEqual(capture_stdout.getvalue(),
1171                          stubs.expect_container_request_uuid + '\n')
1172
1173
1174     @mock.patch("arvados.commands.keepdocker.find_one_image_hash")
1175     @mock.patch("cwltool.docker.DockerCommandLineJob.get_image")
1176     @mock.patch("arvados.api")
1177     def test_arvados_jobs_image(self, api, get_image, find_one_image_hash):
1178         arvrunner = mock.MagicMock()
1179         arvrunner.project_uuid = ""
1180         api.return_value = mock.MagicMock()
1181         arvrunner.api = api.return_value
1182         arvrunner.api.links().list().execute.side_effect = ({"items": [{"created_at": "",
1183                                                                         "head_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1184                                                                         "link_class": "docker_image_repo+tag",
1185                                                                         "name": "arvados/jobs:"+arvados_cwl.__version__,
1186                                                                         "owner_uuid": "",
1187                                                                         "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0},
1188                                                             {"items": [{"created_at": "",
1189                                                                         "head_uuid": "",
1190                                                                         "link_class": "docker_image_hash",
1191                                                                         "name": "123456",
1192                                                                         "owner_uuid": "",
1193                                                                         "properties": {"image_timestamp": ""}}], "items_available": 1, "offset": 0}
1194         )
1195         find_one_image_hash.return_value = "123456"
1196
1197         arvrunner.api.collections().list().execute.side_effect = ({"items": [{"uuid": "zzzzz-4zz18-zzzzzzzzzzzzzzb",
1198                                                                               "owner_uuid": "",
1199                                                                               "manifest_text": "",
1200                                                                               "properties": ""
1201                                                                           }], "items_available": 1, "offset": 0},)
1202         arvrunner.api.collections().create().execute.return_value = {"uuid": ""}
1203         self.assertEqual("arvados/jobs:"+arvados_cwl.__version__,
1204                          arvados_cwl.runner.arvados_jobs_image(arvrunner, "arvados/jobs:"+arvados_cwl.__version__))
1205
1206     @stubs
1207     def test_submit_secrets(self, stubs):
1208         capture_stdout = cStringIO.StringIO()
1209         try:
1210             exited = arvados_cwl.main(
1211                 ["--submit", "--no-wait", "--api=containers", "--debug",
1212                  "tests/wf/secret_wf.cwl", "tests/secret_test_job.yml"],
1213                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1214             self.assertEqual(exited, 0)
1215         except:
1216             logging.exception("")
1217
1218
1219         expect_container = {
1220             "command": [
1221                 "arvados-cwl-runner",
1222                 "--local",
1223                 "--api=containers",
1224                 "--no-log-timestamps",
1225                 "--disable-validate",
1226                 "--eval-timeout=20",
1227                 '--thread-count=4',
1228                 "--enable-reuse",
1229                 '--debug',
1230                 "--on-error=continue",
1231                 "/var/lib/cwl/workflow.json#main",
1232                 "/var/lib/cwl/cwl.input.json"
1233             ],
1234             "container_image": "arvados/jobs:"+arvados_cwl.__version__,
1235             "cwd": "/var/spool/cwl",
1236             "mounts": {
1237                 "/var/lib/cwl/cwl.input.json": {
1238                     "content": {
1239                         "pw": {
1240                             "$include": "/secrets/s0"
1241                         }
1242                     },
1243                     "kind": "json"
1244                 },
1245                 "/var/lib/cwl/workflow.json": {
1246                     "content": {
1247                         "$graph": [
1248                             {
1249                                 "$namespaces": {
1250                                     "cwltool": "http://commonwl.org/cwltool#"
1251                                 },
1252                                 "arguments": [
1253                                     "md5sum",
1254                                     "example.conf"
1255                                 ],
1256                                 "class": "CommandLineTool",
1257                                 "hints": [
1258                                     {
1259                                         "class": "http://commonwl.org/cwltool#Secrets",
1260                                         "secrets": [
1261                                             "#secret_job.cwl/pw"
1262                                         ]
1263                                     }
1264                                 ],
1265                                 "id": "#secret_job.cwl",
1266                                 "inputs": [
1267                                     {
1268                                         "id": "#secret_job.cwl/pw",
1269                                         "type": "string"
1270                                     }
1271                                 ],
1272                                 "outputs": [
1273                                     {
1274                                         "id": "#secret_job.cwl/out",
1275                                         "type": "stdout"
1276                                     }
1277                                 ],
1278                                 "stdout": "hashed_example.txt",
1279                                 "requirements": [
1280                                     {
1281                                         "class": "InitialWorkDirRequirement",
1282                                         "listing": [
1283                                             {
1284                                                 "entry": "username: user\npassword: $(inputs.pw)\n",
1285                                                 "entryname": "example.conf"
1286                                             }
1287                                         ]
1288                                     }
1289                                 ]
1290                             },
1291                             {
1292                                 "class": "Workflow",
1293                                 "hints": [
1294                                     {
1295                                         "class": "DockerRequirement",
1296                                         "dockerPull": "debian:8"
1297                                     },
1298                                     {
1299                                         "class": "http://commonwl.org/cwltool#Secrets",
1300                                         "secrets": [
1301                                             "#main/pw"
1302                                         ]
1303                                     }
1304                                 ],
1305                                 "id": "#main",
1306                                 "inputs": [
1307                                     {
1308                                         "id": "#main/pw",
1309                                         "type": "string"
1310                                     }
1311                                 ],
1312                                 "outputs": [
1313                                     {
1314                                         "id": "#main/out",
1315                                         "outputSource": "#main/step1/out",
1316                                         "type": "File"
1317                                     }
1318                                 ],
1319                                 "steps": [
1320                                     {
1321                                         "id": "#main/step1",
1322                                         "in": [
1323                                             {
1324                                                 "id": "#main/step1/pw",
1325                                                 "source": "#main/pw"
1326                                             }
1327                                         ],
1328                                         "out": [
1329                                             "#main/step1/out"
1330                                         ],
1331                                         "run": "#secret_job.cwl"
1332                                     }
1333                                 ]
1334                             }
1335                         ],
1336                         "cwlVersion": "v1.0"
1337                     },
1338                     "kind": "json"
1339                 },
1340                 "/var/spool/cwl": {
1341                     "kind": "collection",
1342                     "writable": True
1343                 },
1344                 "stdout": {
1345                     "kind": "file",
1346                     "path": "/var/spool/cwl/cwl.output.json"
1347                 }
1348             },
1349             "name": "secret_wf.cwl",
1350             "output_path": "/var/spool/cwl",
1351             "priority": 500,
1352             "properties": {},
1353             "runtime_constraints": {
1354                 "API": True,
1355                 "ram": 1073741824,
1356                 "vcpus": 1
1357             },
1358             "secret_mounts": {
1359                 "/secrets/s0": {
1360                     "content": "blorp",
1361                     "kind": "text"
1362                 }
1363             },
1364             "state": "Committed",
1365             "use_existing": True
1366         }
1367
1368         stubs.api.container_requests().create.assert_called_with(
1369             body=JsonDiffMatcher(expect_container))
1370         self.assertEqual(capture_stdout.getvalue(),
1371                          stubs.expect_container_request_uuid + '\n')
1372
1373     @stubs
1374     def test_submit_request_uuid(self, stubs):
1375         stubs.expect_container_request_uuid = "zzzzz-xvhdp-yyyyyyyyyyyyyyy"
1376
1377         stubs.api.container_requests().update().execute.return_value = {
1378             "uuid": stubs.expect_container_request_uuid,
1379             "container_uuid": "zzzzz-dz642-zzzzzzzzzzzzzzz",
1380             "state": "Queued"
1381         }
1382
1383         capture_stdout = cStringIO.StringIO()
1384         try:
1385             exited = arvados_cwl.main(
1386                 ["--submit", "--no-wait", "--api=containers", "--debug", "--submit-request-uuid=zzzzz-xvhdp-yyyyyyyyyyyyyyy",
1387                  "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1388                 capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
1389             self.assertEqual(exited, 0)
1390         except:
1391             logging.exception("")
1392
1393         stubs.api.container_requests().update.assert_called_with(
1394             uuid="zzzzz-xvhdp-yyyyyyyyyyyyyyy", body=JsonDiffMatcher(stubs.expect_container_spec))
1395         self.assertEqual(capture_stdout.getvalue(),
1396                          stubs.expect_container_request_uuid + '\n')
1397
1398
1399 class TestCreateTemplate(unittest.TestCase):
1400     existing_template_uuid = "zzzzz-d1hrv-validworkfloyml"
1401
1402     def _adjust_script_params(self, expect_component):
1403         expect_component['script_parameters']['x'] = {
1404             'dataclass': 'File',
1405             'required': True,
1406             'type': 'File',
1407             'value': '169f39d466a5438ac4a90e779bf750c7+53/blorp.txt',
1408         }
1409         expect_component['script_parameters']['y'] = {
1410             'dataclass': 'Collection',
1411             'required': True,
1412             'type': 'Directory',
1413             'value': '99999999999999999999999999999998+99',
1414         }
1415         expect_component['script_parameters']['z'] = {
1416             'dataclass': 'Collection',
1417             'required': True,
1418             'type': 'Directory',
1419         }
1420
1421     @stubs
1422     def test_create(self, stubs):
1423         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1424
1425         capture_stdout = cStringIO.StringIO()
1426
1427         exited = arvados_cwl.main(
1428             ["--create-workflow", "--debug",
1429              "--api=jobs",
1430              "--project-uuid", project_uuid,
1431              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1432             capture_stdout, sys.stderr, api_client=stubs.api)
1433         self.assertEqual(exited, 0)
1434
1435         stubs.api.pipeline_instances().create.refute_called()
1436         stubs.api.jobs().create.refute_called()
1437
1438         expect_component = copy.deepcopy(stubs.expect_job_spec)
1439         self._adjust_script_params(expect_component)
1440         expect_template = {
1441             "components": {
1442                 "submit_wf.cwl": expect_component,
1443             },
1444             "name": "submit_wf.cwl",
1445             "owner_uuid": project_uuid,
1446         }
1447         stubs.api.pipeline_templates().create.assert_called_with(
1448             body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1449
1450         self.assertEqual(capture_stdout.getvalue(),
1451                          stubs.expect_pipeline_template_uuid + '\n')
1452
1453
1454     @stubs
1455     def test_create_name(self, stubs):
1456         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1457
1458         capture_stdout = cStringIO.StringIO()
1459
1460         exited = arvados_cwl.main(
1461             ["--create-workflow", "--debug",
1462              "--project-uuid", project_uuid,
1463              "--api=jobs",
1464              "--name", "testing 123",
1465              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1466             capture_stdout, sys.stderr, api_client=stubs.api)
1467         self.assertEqual(exited, 0)
1468
1469         stubs.api.pipeline_instances().create.refute_called()
1470         stubs.api.jobs().create.refute_called()
1471
1472         expect_component = copy.deepcopy(stubs.expect_job_spec)
1473         self._adjust_script_params(expect_component)
1474         expect_template = {
1475             "components": {
1476                 "testing 123": expect_component,
1477             },
1478             "name": "testing 123",
1479             "owner_uuid": project_uuid,
1480         }
1481         stubs.api.pipeline_templates().create.assert_called_with(
1482             body=JsonDiffMatcher(expect_template), ensure_unique_name=True)
1483
1484         self.assertEqual(capture_stdout.getvalue(),
1485                          stubs.expect_pipeline_template_uuid + '\n')
1486
1487
1488     @stubs
1489     def test_update_name(self, stubs):
1490         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1491
1492         capture_stdout = cStringIO.StringIO()
1493
1494         exited = arvados_cwl.main(
1495             ["--update-workflow", self.existing_template_uuid,
1496              "--debug",
1497              "--project-uuid", project_uuid,
1498              "--api=jobs",
1499              "--name", "testing 123",
1500              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1501             capture_stdout, sys.stderr, api_client=stubs.api)
1502         self.assertEqual(exited, 0)
1503
1504         stubs.api.pipeline_instances().create.refute_called()
1505         stubs.api.jobs().create.refute_called()
1506
1507         expect_component = copy.deepcopy(stubs.expect_job_spec)
1508         self._adjust_script_params(expect_component)
1509         expect_template = {
1510             "components": {
1511                 "testing 123": expect_component,
1512             },
1513             "name": "testing 123",
1514             "owner_uuid": project_uuid,
1515         }
1516         stubs.api.pipeline_templates().create.refute_called()
1517         stubs.api.pipeline_templates().update.assert_called_with(
1518             body=JsonDiffMatcher(expect_template), uuid=self.existing_template_uuid)
1519
1520         self.assertEqual(capture_stdout.getvalue(),
1521                          self.existing_template_uuid + '\n')
1522
1523
1524 class TestCreateWorkflow(unittest.TestCase):
1525     existing_workflow_uuid = "zzzzz-7fd4e-validworkfloyml"
1526     expect_workflow = StripYAMLComments(
1527         open("tests/wf/expect_packed.cwl").read())
1528
1529     @stubs
1530     def test_create(self, stubs):
1531         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1532
1533         capture_stdout = cStringIO.StringIO()
1534
1535         exited = arvados_cwl.main(
1536             ["--create-workflow", "--debug",
1537              "--api=containers",
1538              "--project-uuid", project_uuid,
1539              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1540             capture_stdout, sys.stderr, api_client=stubs.api)
1541         self.assertEqual(exited, 0)
1542
1543         stubs.api.pipeline_templates().create.refute_called()
1544         stubs.api.container_requests().create.refute_called()
1545
1546         body = {
1547             "workflow": {
1548                 "owner_uuid": project_uuid,
1549                 "name": "submit_wf.cwl",
1550                 "description": "",
1551                 "definition": self.expect_workflow,
1552             }
1553         }
1554         stubs.api.workflows().create.assert_called_with(
1555             body=JsonDiffMatcher(body))
1556
1557         self.assertEqual(capture_stdout.getvalue(),
1558                          stubs.expect_workflow_uuid + '\n')
1559
1560
1561     @stubs
1562     def test_create_name(self, stubs):
1563         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1564
1565         capture_stdout = cStringIO.StringIO()
1566
1567         exited = arvados_cwl.main(
1568             ["--create-workflow", "--debug",
1569              "--api=containers",
1570              "--project-uuid", project_uuid,
1571              "--name", "testing 123",
1572              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1573             capture_stdout, sys.stderr, api_client=stubs.api)
1574         self.assertEqual(exited, 0)
1575
1576         stubs.api.pipeline_templates().create.refute_called()
1577         stubs.api.container_requests().create.refute_called()
1578
1579         body = {
1580             "workflow": {
1581                 "owner_uuid": project_uuid,
1582                 "name": "testing 123",
1583                 "description": "",
1584                 "definition": self.expect_workflow,
1585             }
1586         }
1587         stubs.api.workflows().create.assert_called_with(
1588             body=JsonDiffMatcher(body))
1589
1590         self.assertEqual(capture_stdout.getvalue(),
1591                          stubs.expect_workflow_uuid + '\n')
1592
1593     @stubs
1594     def test_incompatible_api(self, stubs):
1595         capture_stderr = cStringIO.StringIO()
1596         logging.getLogger('arvados.cwl-runner').addHandler(
1597             logging.StreamHandler(capture_stderr))
1598
1599         exited = arvados_cwl.main(
1600             ["--update-workflow", self.existing_workflow_uuid,
1601              "--api=jobs",
1602              "--debug",
1603              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1604             sys.stderr, sys.stderr, api_client=stubs.api)
1605         self.assertEqual(exited, 1)
1606         self.assertRegexpMatches(
1607             capture_stderr.getvalue(),
1608             "--update-workflow arg '{}' uses 'containers' API, but --api='jobs' specified".format(self.existing_workflow_uuid))
1609
1610     @stubs
1611     def test_update(self, stubs):
1612         capture_stdout = cStringIO.StringIO()
1613
1614         exited = arvados_cwl.main(
1615             ["--update-workflow", self.existing_workflow_uuid,
1616              "--debug",
1617              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1618             capture_stdout, sys.stderr, api_client=stubs.api)
1619         self.assertEqual(exited, 0)
1620
1621         body = {
1622             "workflow": {
1623                 "name": "submit_wf.cwl",
1624                 "description": "",
1625                 "definition": self.expect_workflow,
1626             }
1627         }
1628         stubs.api.workflows().update.assert_called_with(
1629             uuid=self.existing_workflow_uuid,
1630             body=JsonDiffMatcher(body))
1631         self.assertEqual(capture_stdout.getvalue(),
1632                          self.existing_workflow_uuid + '\n')
1633
1634
1635     @stubs
1636     def test_update_name(self, stubs):
1637         capture_stdout = cStringIO.StringIO()
1638
1639         exited = arvados_cwl.main(
1640             ["--update-workflow", self.existing_workflow_uuid,
1641              "--debug", "--name", "testing 123",
1642              "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
1643             capture_stdout, sys.stderr, api_client=stubs.api)
1644         self.assertEqual(exited, 0)
1645
1646         body = {
1647             "workflow": {
1648                 "name": "testing 123",
1649                 "description": "",
1650                 "definition": self.expect_workflow,
1651             }
1652         }
1653         stubs.api.workflows().update.assert_called_with(
1654             uuid=self.existing_workflow_uuid,
1655             body=JsonDiffMatcher(body))
1656         self.assertEqual(capture_stdout.getvalue(),
1657                          self.existing_workflow_uuid + '\n')
1658
1659
1660     @stubs
1661     def test_create_collection_per_tool(self, stubs):
1662         project_uuid = 'zzzzz-j7d0g-zzzzzzzzzzzzzzz'
1663
1664         capture_stdout = cStringIO.StringIO()
1665
1666         exited = arvados_cwl.main(
1667             ["--create-workflow", "--debug",
1668              "--api=containers",
1669              "--project-uuid", project_uuid,
1670              "tests/collection_per_tool/collection_per_tool.cwl"],
1671             capture_stdout, sys.stderr, api_client=stubs.api)
1672         self.assertEqual(exited, 0)
1673
1674         toolfile = "tests/collection_per_tool/collection_per_tool_packed.cwl"
1675         expect_workflow = StripYAMLComments(open(toolfile).read())
1676
1677         body = {
1678             "workflow": {
1679                 "owner_uuid": project_uuid,
1680                 "name": "collection_per_tool.cwl",
1681                 "description": "",
1682                 "definition": expect_workflow,
1683             }
1684         }
1685         stubs.api.workflows().create.assert_called_with(
1686             body=JsonDiffMatcher(body))
1687
1688         self.assertEqual(capture_stdout.getvalue(),
1689                          stubs.expect_workflow_uuid + '\n')
1690
1691 class TestTemplateInputs(unittest.TestCase):
1692     expect_template = {
1693         "components": {
1694             "inputs_test.cwl": {
1695                 'runtime_constraints': {
1696                     'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
1697                     'min_ram_mb_per_node': 1024
1698                 },
1699                 'script_parameters': {
1700                     'cwl:tool':
1701                     '6c5ee1cd606088106d9f28367cde1e41+60/workflow.cwl#main',
1702                     'optionalFloatInput': None,
1703                     'fileInput': {
1704                         'type': 'File',
1705                         'dataclass': 'File',
1706                         'required': True,
1707                         'title': "It's a file; we expect to find some characters in it.",
1708                         'description': 'If there were anything further to say, it would be said here,\nor here.'
1709                     },
1710                     'floatInput': {
1711                         'type': 'float',
1712                         'dataclass': 'number',
1713                         'required': True,
1714                         'title': 'Floats like a duck',
1715                         'default': 0.1,
1716                         'value': 0.1,
1717                     },
1718                     'optionalFloatInput': {
1719                         'type': ['null', 'float'],
1720                         'dataclass': 'number',
1721                         'required': False,
1722                     },
1723                     'boolInput': {
1724                         'type': 'boolean',
1725                         'dataclass': 'boolean',
1726                         'required': True,
1727                         'title': 'True or false?',
1728                     },
1729                 },
1730                 'repository': 'arvados',
1731                 'script_version': 'master',
1732                 'minimum_script_version': '570509ab4d2ef93d870fd2b1f2eab178afb1bad9',
1733                 'script': 'cwl-runner',
1734             },
1735         },
1736         "name": "inputs_test.cwl",
1737     }
1738
1739     @stubs
1740     def test_inputs_empty(self, stubs):
1741         exited = arvados_cwl.main(
1742             ["--create-template",
1743              "tests/wf/inputs_test.cwl", "tests/order/empty_order.json"],
1744             cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
1745         self.assertEqual(exited, 0)
1746
1747         stubs.api.pipeline_templates().create.assert_called_with(
1748             body=JsonDiffMatcher(self.expect_template), ensure_unique_name=True)
1749
1750     @stubs
1751     def test_inputs(self, stubs):
1752         exited = arvados_cwl.main(
1753             ["--create-template",
1754              "tests/wf/inputs_test.cwl", "tests/order/inputs_test_order.json"],
1755             cStringIO.StringIO(), sys.stderr, api_client=stubs.api)
1756         self.assertEqual(exited, 0)
1757
1758         expect_template = copy.deepcopy(self.expect_template)
1759         params = expect_template[
1760             "components"]["inputs_test.cwl"]["script_parameters"]
1761         params["fileInput"]["value"] = '169f39d466a5438ac4a90e779bf750c7+53/blorp.txt'
1762         params["cwl:tool"] = '6c5ee1cd606088106d9f28367cde1e41+60/workflow.cwl#main'
1763         params["floatInput"]["value"] = 1.234
1764         params["boolInput"]["value"] = True
1765
1766         stubs.api.pipeline_templates().create.assert_called_with(
1767             body=JsonDiffMatcher(expect_template), ensure_unique_name=True)