10448: Tests include enable_reuse flag and that --disable-reuse is passed to
authorPeter Amstutz <peter.amstutz@curoverse.com>
Thu, 3 Nov 2016 14:29:07 +0000 (10:29 -0400)
committerPeter Amstutz <peter.amstutz@curoverse.com>
Thu, 3 Nov 2016 17:22:28 +0000 (13:22 -0400)
inner cwl-runner job or container.

sdk/cwl/arvados_cwl/arvjob.py
sdk/cwl/arvados_cwl/runner.py
sdk/cwl/tests/mock_discovery.py [new file with mode: 0644]
sdk/cwl/tests/test_container.py
sdk/cwl/tests/test_job.py
sdk/cwl/tests/test_make_output.py
sdk/cwl/tests/test_pathmapper.py
sdk/cwl/tests/test_submit.py

index e074046c593b6d5d3c616769acab09531b021edd..759bf0bf34abc0ae8ddc1a5c7b14baccd8f15d49 100644 (file)
@@ -321,6 +321,7 @@ class RunnerTemplate(object):
         Specifically, translate CWL input specs to Arvados pipeline
         format, like {"dataclass":"File","value":"xyz"}.
         """
+
         spec = self.job.arvados_job_spec()
 
         # Most of the component spec is exactly the same as the job
index 49d37ebd5aec177f0958088983f483123ce113b3..6f157db3c6836c0769e622764fe93b04fe14d398 100644 (file)
@@ -122,7 +122,6 @@ def upload_instance(arvrunner, name, tool, job_order):
                                              tool.tool,
                                              tool.tool["id"],
                                              True)
-
         jobmapper = upload_dependencies(arvrunner,
                                         os.path.basename(job_order.get("id", "#")),
                                         tool.doc_loader,
diff --git a/sdk/cwl/tests/mock_discovery.py b/sdk/cwl/tests/mock_discovery.py
new file mode 100644 (file)
index 0000000..f31ff22
--- /dev/null
@@ -0,0 +1,13 @@
+import json
+import arvados
+
+_rootDesc = None
+
+def get_rootDesc():
+    global _rootDesc
+    if not _rootDesc:
+        try:
+            _rootDesc = arvados.api('v1')._rootDesc
+        except ValueError:
+            raise Exception("Test requires an running API server to fetch discovery document")
+    return _rootDesc
index 3cae2514217519a0836630e376f3507b0354a04f..8ca5398e82f7ba4606bc7ac212e2a61e987872a9 100644 (file)
@@ -20,50 +20,53 @@ class TestContainer(unittest.TestCase):
     # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
     def test_run(self, keepdocker):
-        runner = mock.MagicMock()
-        runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
-        runner.ignore_docker_for_reuse = False
-
-        keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
-        runner.api.collections().get().execute.return_value = {
-            "portable_data_hash": "99999999999999999999999999999993+99"}
-
-        document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
-
-        tool = {
-            "inputs": [],
-            "outputs": [],
-            "baseCommand": "ls",
-            "arguments": [{"valueFrom": "$(runtime.outdir)"}]
-        }
-        make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
-        arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
-                                                 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
-        arvtool.formatgraph = None
-        for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run",
-                             make_fs_access=make_fs_access, tmpdir="/tmp"):
-            j.run()
-            runner.api.container_requests().create.assert_called_with(
-                body={
-                    'environment': {
-                        'HOME': '/var/spool/cwl',
-                        'TMPDIR': '/tmp'
-                    },
-                    'name': 'test_run',
-                    'runtime_constraints': {
-                        'vcpus': 1,
-                        'ram': 1073741824
-                    }, 'priority': 1,
-                    'mounts': {
-                        '/var/spool/cwl': {'kind': 'tmp'}
-                    },
-                    'state': 'Committed',
-                    'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
-                    'output_path': '/var/spool/cwl',
-                    'container_image': '99999999999999999999999999999993+99',
-                    'command': ['ls', '/var/spool/cwl'],
-                    'cwd': '/var/spool/cwl'
-                })
+        for enable_reuse in (True, False):
+            runner = mock.MagicMock()
+            runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
+            runner.ignore_docker_for_reuse = False
+
+            keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
+            runner.api.collections().get().execute.return_value = {
+                "portable_data_hash": "99999999999999999999999999999993+99"}
+
+            document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
+
+            tool = {
+                "inputs": [],
+                "outputs": [],
+                "baseCommand": "ls",
+                "arguments": [{"valueFrom": "$(runtime.outdir)"}]
+            }
+            make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
+            arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="containers", avsc_names=avsc_names,
+                                                     basedir="", make_fs_access=make_fs_access, loader=Loader({}))
+            arvtool.formatgraph = None
+            for j in arvtool.job({}, mock.MagicMock(), basedir="", name="test_run_"+str(enable_reuse),
+                                 make_fs_access=make_fs_access, tmpdir="/tmp"):
+                j.run(enable_reuse=enable_reuse)
+                runner.api.container_requests().create.assert_called_with(
+                    body={
+                        'environment': {
+                            'HOME': '/var/spool/cwl',
+                            'TMPDIR': '/tmp'
+                        },
+                        'name': 'test_run_'+str(enable_reuse),
+                        'runtime_constraints': {
+                            'vcpus': 1,
+                            'ram': 1073741824
+                        },
+                        'use_existing': enable_reuse,
+                        'priority': 1,
+                        'mounts': {
+                            '/var/spool/cwl': {'kind': 'tmp'}
+                        },
+                        'state': 'Committed',
+                        'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
+                        'output_path': '/var/spool/cwl',
+                        'container_image': '99999999999999999999999999999993+99',
+                        'command': ['ls', '/var/spool/cwl'],
+                        'cwd': '/var/spool/cwl'
+                    })
 
     # The test passes some fields in builder.resources
     # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
@@ -119,7 +122,9 @@ class TestContainer(unittest.TestCase):
                     'keep_cache_ram': 512,
                     'API': True,
                     'partition': ['blurb']
-                }, 'priority': 1,
+                },
+                'use_existing': True,
+                'priority': 1,
                 'mounts': {
                     '/var/spool/cwl': {'kind': 'tmp'}
                 },
index 93b5d39ffa6eb7e45efba47c64b69fb7a95a33d8..f1c6ba01b157e9e16edc08ce622056d694ab1081 100644 (file)
@@ -4,71 +4,73 @@ import logging
 import mock
 import os
 import unittest
+import copy
 
 import arvados
 import arvados_cwl
 import cwltool.process
 from schema_salad.ref_resolver import Loader
+from .mock_discovery import get_rootDesc
 
 if not os.getenv('ARVADOS_DEBUG'):
     logging.getLogger('arvados.cwl-runner').setLevel(logging.WARN)
     logging.getLogger('arvados.arv-run').setLevel(logging.WARN)
 
-
 class TestJob(unittest.TestCase):
 
     # The test passes no builder.resources
     # Hence the default resources will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
     @mock.patch('arvados.commands.keepdocker.list_images_in_arv')
     def test_run(self, list_images_in_arv):
-        runner = mock.MagicMock()
-        runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
-        runner.ignore_docker_for_reuse = False
-        runner.num_retries = 0
-        document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
-
-        list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
-        runner.api.collections().get().execute.return_vaulue = {"portable_data_hash": "99999999999999999999999999999993+99"}
-
-        tool = {
-            "inputs": [],
-            "outputs": [],
-            "baseCommand": "ls",
-            "arguments": [{"valueFrom": "$(runtime.outdir)"}]
-        }
-        make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
-        arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="jobs", avsc_names=avsc_names,
-                                                 basedir="", make_fs_access=make_fs_access, loader=Loader({}))
-        arvtool.formatgraph = None
-        for j in arvtool.job({}, mock.MagicMock(), basedir="", make_fs_access=make_fs_access):
-            j.run()
-            runner.api.jobs().create.assert_called_with(
-                body={
-                    'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
-                    'runtime_constraints': {},
-                    'script_parameters': {
-                        'tasks': [{
-                            'task.env': {'HOME': '$(task.outdir)', 'TMPDIR': '$(task.tmpdir)'},
-                            'command': ['ls', '$(task.outdir)']
-                        }],
+        for enable_reuse in (True, False):
+            runner = mock.MagicMock()
+            runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz"
+            runner.ignore_docker_for_reuse = False
+            runner.num_retries = 0
+            document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
+
+            list_images_in_arv.return_value = [["zzzzz-4zz18-zzzzzzzzzzzzzzz"]]
+            runner.api.collections().get().execute.return_vaulue = {"portable_data_hash": "99999999999999999999999999999993+99"}
+
+            tool = {
+                "inputs": [],
+                "outputs": [],
+                "baseCommand": "ls",
+                "arguments": [{"valueFrom": "$(runtime.outdir)"}]
+            }
+            make_fs_access=functools.partial(arvados_cwl.CollectionFsAccess, api_client=runner.api)
+            arvtool = arvados_cwl.ArvadosCommandTool(runner, tool, work_api="jobs", avsc_names=avsc_names,
+                                                     basedir="", make_fs_access=make_fs_access, loader=Loader({}))
+            arvtool.formatgraph = None
+            for j in arvtool.job({}, mock.MagicMock(), basedir="", make_fs_access=make_fs_access):
+                j.run(enable_reuse=enable_reuse)
+                runner.api.jobs().create.assert_called_with(
+                    body={
+                        'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz',
+                        'runtime_constraints': {},
+                        'script_parameters': {
+                            'tasks': [{
+                                'task.env': {'HOME': '$(task.outdir)', 'TMPDIR': '$(task.tmpdir)'},
+                                'command': ['ls', '$(task.outdir)']
+                            }],
+                        },
+                        'script_version': 'master',
+                        'minimum_script_version': '9e5b98e8f5f4727856b53447191f9c06e3da2ba6',
+                        'repository': 'arvados',
+                        'script': 'crunchrunner',
+                        'runtime_constraints': {
+                            'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
+                            'min_cores_per_node': 1,
+                            'min_ram_mb_per_node': 1024,
+                            'min_scratch_mb_per_node': 2048 # tmpdirSize + outdirSize
+                        }
                     },
-                    'script_version': 'master',
-                    'minimum_script_version': '9e5b98e8f5f4727856b53447191f9c06e3da2ba6',
-                    'repository': 'arvados',
-                    'script': 'crunchrunner',
-                    'runtime_constraints': {
-                        'docker_image': 'arvados/jobs:'+arvados_cwl.__version__,
-                        'min_cores_per_node': 1,
-                        'min_ram_mb_per_node': 1024,
-                        'min_scratch_mb_per_node': 2048 # tmpdirSize + outdirSize
-                    }
-                },
-                find_or_create=True,
-                filters=[['repository', '=', 'arvados'],
-                         ['script', '=', 'crunchrunner'],
-                         ['script_version', 'in git', '9e5b98e8f5f4727856b53447191f9c06e3da2ba6'],
-                         ['docker_image_locator', 'in docker', 'arvados/jobs:'+arvados_cwl.__version__]]
-            )
+                    find_or_create=enable_reuse,
+                    filters=[['repository', '=', 'arvados'],
+                             ['script', '=', 'crunchrunner'],
+                             ['script_version', 'in git', '9e5b98e8f5f4727856b53447191f9c06e3da2ba6'],
+                             ['docker_image_locator', 'in docker', 'arvados/jobs:'+arvados_cwl.__version__]]
+                )
 
     # The test passes some fields in builder.resources
     # For the remaining fields, the defaults will apply: {'cores': 1, 'ram': 1024, 'outdirSize': 1024, 'tmpdirSize': 1024}
@@ -226,7 +228,8 @@ class TestWorkflow(unittest.TestCase):
         arvados_cwl.add_arv_hints()
 
         api = mock.MagicMock()
-        api._rootDesc = arvados.api('v1')._rootDesc
+        api._rootDesc = get_rootDesc()
+
         runner = arvados_cwl.ArvCwlRunner(api)
         self.assertEqual(runner.work_api, 'jobs')
 
@@ -291,7 +294,7 @@ class TestWorkflow(unittest.TestCase):
         arvados_cwl.add_arv_hints()
 
         api = mock.MagicMock()
-        api._rootDesc = arvados.api('v1')._rootDesc
+        api._rootDesc = copy.deepcopy(get_rootDesc())
         del api._rootDesc.get('resources')['jobs']['methods']['create']
         runner = arvados_cwl.ArvCwlRunner(api)
         self.assertEqual(runner.work_api, 'containers')
index cd66eb15065059579e718150372d1f0c03247688..0b08b2e6ec10b36cfe7dabf9be0263050893d878 100644 (file)
@@ -8,11 +8,12 @@ import unittest
 
 import arvados
 import arvados_cwl
+from .mock_discovery import get_rootDesc
 
 class TestMakeOutput(unittest.TestCase):
     def setUp(self):
         self.api = mock.MagicMock()
-        self.api._rootDesc = arvados.api('v1')._rootDesc
+        self.api._rootDesc = get_rootDesc()
 
     @mock.patch("arvados.collection.Collection")
     @mock.patch("arvados.collection.CollectionReader")
index 57958f78d0a41a97b9d5b0aa5f10a2cf10563b22..3b6af04b293e8f48e320fa6508716d3c6d27faf6 100644 (file)
@@ -12,6 +12,7 @@ import arvados.collection
 import arvados_cwl
 
 from cwltool.pathmapper import MapperEnt
+from .mock_discovery import get_rootDesc
 
 from arvados_cwl.pathmapper import ArvPathMapper
 
@@ -23,7 +24,7 @@ def upload_mock(files, api, dry_run=False, num_retries=0, project=None, fnPatter
 class TestPathmap(unittest.TestCase):
     def setUp(self):
         self.api = mock.MagicMock()
-        self.api._rootDesc = arvados.api('v1')._rootDesc
+        self.api._rootDesc = get_rootDesc()
 
     def test_keepref(self):
         """Test direct keep references."""
index 7faef6992c37b3d4212db8a58a5606a2e0b0eed8..409557ccaf4c174c1d4f0cdc8c1ebafdcf1265f9 100644 (file)
@@ -14,32 +14,38 @@ import arvados_cwl
 import arvados.keep
 
 from .matcher import JsonDiffMatcher
+from .mock_discovery import get_rootDesc
 
+_rootDesc = None
 
 def stubs(func):
     @functools.wraps(func)
     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
     @mock.patch("arvados.collection.KeepClient")
+    @mock.patch("arvados.keep.KeepClient")
     @mock.patch("arvados.events.subscribe")
-    def wrapped(self, events, keep_client, keepdocker, *args, **kwargs):
+    def wrapped(self, events, keep_client1, keep_client2, keepdocker, *args, **kwargs):
         class Stubs:
             pass
         stubs = Stubs()
         stubs.events = events
         stubs.keepdocker = keepdocker
-        stubs.keep_client = keep_client
+
 
         def putstub(p, **kwargs):
             return "%s+%i" % (hashlib.md5(p).hexdigest(), len(p))
-        stubs.keep_client().put.side_effect = putstub
-        stubs.keep_client.put.side_effect = putstub
+        keep_client1().put.side_effect = putstub
+        keep_client1.put.side_effect = putstub
+        keep_client2().put.side_effect = putstub
+        keep_client2.put.side_effect = putstub
 
+        stubs.keep_client = keep_client2
         stubs.keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         stubs.fake_user_uuid = "zzzzz-tpzed-zzzzzzzzzzzzzzz"
 
-
         stubs.api = mock.MagicMock()
-        stubs.api._rootDesc = arvados.api('v1')._rootDesc
+        stubs.api._rootDesc = get_rootDesc()
+
         stubs.api.users().current().execute.return_value = {
             "uuid": stubs.fake_user_uuid,
         }
@@ -134,7 +140,8 @@ def stubs(func):
                               'listing': [
                                   {'basename': 'renamed.txt', 'class': 'File', 'location': 'keep:99999999999999999999999999999998+99/file1.txt'}
                               ]}},
-                        'cwl:tool': '99999999999999999999999999999991+99/wf/submit_wf.cwl'
+                        'cwl:tool': '99999999999999999999999999999991+99/wf/submit_wf.cwl',
+                        'arv:enable_reuse': True
                     },
                     'repository': 'arvados',
                     'script_version': arvados_cwl.__version__,
@@ -176,7 +183,7 @@ def stubs(func):
             },
             'state': 'Committed',
             'owner_uuid': 'zzzzz-tpzed-zzzzzzzzzzzzzzz',
-            'command': ['arvados-cwl-runner', '--local', '--api=containers', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/job/cwl.input.json'],
+            'command': ['arvados-cwl-runner', '--local', '--api=containers', '--enable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/job/cwl.input.json'],
             'name': 'submit_wf.cwl',
             'container_image': 'arvados/jobs:'+arvados_cwl.__version__,
             'output_path': '/var/spool/cwl',
@@ -241,6 +248,26 @@ class TestSubmit(unittest.TestCase):
         self.assertEqual(capture_stdout.getvalue(),
                          stubs.expect_pipeline_uuid + '\n')
 
+
+    @mock.patch("time.sleep")
+    @stubs
+    def test_submit_no_reuse(self, stubs, tm):
+        capture_stdout = cStringIO.StringIO()
+        exited = arvados_cwl.main(
+            ["--submit", "--no-wait", "--debug", "--disable-reuse",
+             "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+            capture_stdout, sys.stderr, api_client=stubs.api)
+        self.assertEqual(exited, 0)
+
+        stubs.expect_pipeline_instance["components"]["cwl-runner"]["script_parameters"]["arv:enable_reuse"] = {"value": False}
+
+        expect_pipeline = copy.deepcopy(stubs.expect_pipeline_instance)
+        expect_pipeline["owner_uuid"] = stubs.fake_user_uuid
+        stubs.api.pipeline_instances().create.assert_called_with(
+            body=expect_pipeline)
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_pipeline_uuid + '\n')
+
     @mock.patch("time.sleep")
     @stubs
     def test_submit_with_project_uuid(self, stubs, tm):
@@ -303,6 +330,27 @@ class TestSubmit(unittest.TestCase):
         self.assertEqual(capture_stdout.getvalue(),
                          stubs.expect_container_request_uuid + '\n')
 
+    @stubs
+    def test_submit_container_no_reuse(self, stubs):
+        capture_stdout = cStringIO.StringIO()
+        try:
+            exited = arvados_cwl.main(
+                ["--submit", "--no-wait", "--api=containers", "--debug", "--disable-reuse",
+                 "tests/wf/submit_wf.cwl", "tests/submit_test_job.json"],
+                capture_stdout, sys.stderr, api_client=stubs.api, keep_client=stubs.keep_client)
+            self.assertEqual(exited, 0)
+        except:
+            logging.exception("")
+
+        stubs.expect_container_spec["command"] = ['arvados-cwl-runner', '--local', '--api=containers', '--disable-reuse', '/var/lib/cwl/workflow/submit_wf.cwl', '/var/lib/cwl/job/cwl.input.json']
+
+        expect_container = copy.deepcopy(stubs.expect_container_spec)
+        expect_container["owner_uuid"] = stubs.fake_user_uuid
+        stubs.api.container_requests().create.assert_called_with(
+            body=expect_container)
+        self.assertEqual(capture_stdout.getvalue(),
+                         stubs.expect_container_request_uuid + '\n')
+
 
 class TestCreateTemplate(unittest.TestCase):
     @stubs