Merge branch '21666-provision-test-improvement'
[arvados.git] / sdk / cwl / tests / test_container.py
index cb57b446da5877b4e74ef0b30ae7f930e37b0c12..885ee165b0ab248ae20f5b1bfdf84577ad335af0 100644 (file)
@@ -2,17 +2,13 @@
 #
 # SPDX-License-Identifier: Apache-2.0
 
-from builtins import str
-from builtins import object
-
 import arvados_cwl
 import arvados_cwl.context
 import arvados_cwl.util
-from arvados_cwl.arvdocker import arv_docker_clear_cache
+#from arvados_cwl.arvdocker import arv_docker_clear_cache
 import copy
 import arvados.config
 import logging
-import mock
 import unittest
 import os
 import functools
@@ -23,6 +19,9 @@ import cwltool.load_tool
 from cwltool.update import INTERNAL_VERSION
 from schema_salad.ref_resolver import Loader
 from schema_salad.sourceline import cmap
+import io
+
+from unittest import mock
 
 from .matcher import JsonDiffMatcher, StripYAMLComments
 from .mock_discovery import get_rootDesc
@@ -61,7 +60,7 @@ class TestContainer(unittest.TestCase):
 
     def setUp(self):
         cwltool.process._names = set()
-        arv_docker_clear_cache()
+        #arv_docker_clear_cache()
 
     def tearDown(self):
         root_logger = logging.getLogger('')
@@ -85,7 +84,8 @@ class TestContainer(unittest.TestCase):
              "construct_tool_object": runner.arv_make_tool,
              "fetcher_constructor": functools.partial(arvados_cwl.CollectionFetcher, api_client=runner.api, fs_access=fs_access),
              "loader": Loader({}),
-             "metadata": cmap({"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"})
+             "metadata": cmap({"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"}),
+             "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__
              })
         runtimeContext = arvados_cwl.context.ArvRuntimeContext(
             {"work_api": "containers",
@@ -128,13 +128,14 @@ class TestContainer(unittest.TestCase):
     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
     def test_run(self, keepdocker):
         for enable_reuse in (True, False):
-            arv_docker_clear_cache()
+            #arv_docker_clear_cache()
 
             runner = mock.MagicMock()
             runner.ignore_docker_for_reuse = False
             runner.intermediate_output_ttl = 0
             runner.secret_store = cwltool.secrets.SecretStore()
             runner.api._rootDesc = {"revision": "20210628"}
+            runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
             keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
             runner.api.collections().get().execute.return_value = {
@@ -186,7 +187,7 @@ class TestContainer(unittest.TestCase):
                         'command': ['ls', '/var/spool/cwl'],
                         'cwd': '/var/spool/cwl',
                         'scheduling_parameters': {},
-                        'properties': {},
+                        'properties': {'cwl_input': {}},
                         'secret_mounts': {},
                         'output_storage_classes': ["default"]
                     }))
@@ -201,6 +202,7 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 3600
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -277,7 +279,7 @@ class TestContainer(unittest.TestCase):
             'scheduling_parameters': {
                 'partitions': ['blurb']
             },
-            'properties': {},
+            'properties': {'cwl_input': {}},
             'secret_mounts': {},
             'output_storage_classes': ["default"]
         }
@@ -298,6 +300,7 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -410,7 +413,7 @@ class TestContainer(unittest.TestCase):
             'cwd': '/var/spool/cwl',
             'scheduling_parameters': {
             },
-            'properties': {},
+            'properties': {'cwl_input': {}},
             'secret_mounts': {},
             'output_storage_classes': ["default"]
         }
@@ -429,6 +432,7 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -497,7 +501,7 @@ class TestContainer(unittest.TestCase):
                     'command': ['ls', '/var/spool/cwl'],
                     'cwd': '/var/spool/cwl',
                     'scheduling_parameters': {},
-                    'properties': {},
+                    'properties': {'cwl_input': {}},
                     'secret_mounts': {},
                     'output_storage_classes': ["default"]
                 }))
@@ -513,11 +517,47 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
 
+        runner.api.container_requests().get().execute.return_value = {"container_uuid":"zzzzz-xvhdp-zzzzzzzzzzzzzzz"}
+
         runner.api.containers().get().execute.return_value = {"state":"Complete",
                                                               "output": "abc+123",
                                                               "exit_code": 0}
 
-        col().open.return_value = []
+        # Need to noop-out the close method otherwise it gets
+        # discarded when closed and we can't call getvalue() to check
+        # it.
+        class NoopCloseStringIO(io.StringIO):
+            def close(self):
+                pass
+
+        usage_report = NoopCloseStringIO()
+        def colreader_action(name, mode):
+            nonlocal usage_report
+            if name == "node.json":
+                return io.StringIO("""{
+    "ProviderType": "c5.large",
+    "VCPUs": 2,
+    "RAM": 4294967296,
+    "IncludedScratch": 8000000000000,
+    "AddedScratch": 0,
+    "Price": 0.085,
+    "Preemptible": false,
+    "CUDA": {
+        "DriverVersion": "",
+        "HardwareCapability": "",
+        "DeviceCount": 0
+    }
+}""")
+            if name == 'crunchstat.txt':
+                return open("tests/container_request_9tee4-xvhdp-kk0ja1cl8b2kr1y-arv-mount.txt", "rt")
+            if name == 'arv-mount.txt':
+                return open("tests/container_request_9tee4-xvhdp-kk0ja1cl8b2kr1y-crunchstat.txt", "rt")
+            if name == 'usage_report.html':
+                return usage_report
+            return None
+
+        col().open.side_effect = colreader_action
+        col().__iter__.return_value = ['node.json', 'crunchstat.txt', 'arv-mount.txt']
 
         loadingContext, runtimeContext = self.helper(runner)
 
@@ -534,6 +574,7 @@ class TestContainer(unittest.TestCase):
         arvjob.successCodes = [0]
         arvjob.outdir = "/var/spool/cwl"
         arvjob.output_ttl = 3600
+        arvjob.uuid = "zzzzz-xvhdp-zzzzzzzzzzzzzz1"
 
         arvjob.collect_outputs.return_value = {"out": "stuff"}
 
@@ -543,16 +584,25 @@ class TestContainer(unittest.TestCase):
             "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
             "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
             "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
-            "modified_at": "2017-05-26T12:01:22Z"
+            "modified_at": "2017-05-26T12:01:22Z",
+            "properties": {},
+            "name": "testjob"
         })
 
         self.assertFalse(api.collections().create.called)
         self.assertFalse(runner.runtime_status_error.called)
 
+        # Assert that something was written to the usage report
+        self.assertTrue(len(usage_report.getvalue()) > 0)
+
         arvjob.collect_outputs.assert_called_with("keep:abc+123", 0)
         arvjob.output_callback.assert_called_with({"out": "stuff"}, "success")
         runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2")
 
+        runner.api.container_requests().update.assert_called_with(uuid="zzzzz-xvhdp-zzzzzzzzzzzzzz1",
+                                                                  body={'container_request': {'properties': {'cwl_output': {'out': 'stuff'}}}})
+
+
     # Test to make sure we dont call runtime_status_update if we already did
     # some where higher up in the call stack
     @mock.patch("arvados_cwl.util.get_current_container")
@@ -636,14 +686,18 @@ class TestContainer(unittest.TestCase):
             "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2",
             "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz",
             "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz",
-            "modified_at": "2017-05-26T12:01:22Z"
+            "modified_at": "2017-05-26T12:01:22Z",
+            "properties": {}
         })
 
-        rts_mock.assert_called_with(
-            'error',
-            'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
-            '  ** log is empty **'
-        )
+        rts_mock.assert_has_calls([
+            mock.call('error',
+                      'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:',
+                      '  ** log is empty **'
+                      ),
+            mock.call('warning',
+                      'arvados.cwl-runner: [container testjob] unable to generate resource usage report'
+        )])
         arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail")
 
     # The test passes no builder.resources
@@ -655,6 +709,7 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -733,7 +788,38 @@ class TestContainer(unittest.TestCase):
                     'command': ['ls', '/var/spool/cwl'],
                     'cwd': '/var/spool/cwl',
                     'scheduling_parameters': {},
-                    'properties': {},
+                    'properties': {'cwl_input': {
+                        "p1": {
+                            "basename": "99999999999999999999999999999994+44",
+                            "class": "Directory",
+                            "dirname": "/keep",
+                            "http://arvados.org/cwl#collectionUUID": "zzzzz-4zz18-zzzzzzzzzzzzzzz",
+                            "listing": [
+                                {
+                                    "basename": "file1",
+                                    "class": "File",
+                                    "dirname": "/keep/99999999999999999999999999999994+44",
+                                    "location": "keep:99999999999999999999999999999994+44/file1",
+                                    "nameext": "",
+                                    "nameroot": "file1",
+                                    "path": "/keep/99999999999999999999999999999994+44/file1",
+                                    "size": 0
+                                },
+                                {
+                                    "basename": "file2",
+                                    "class": "File",
+                                    "dirname": "/keep/99999999999999999999999999999994+44",
+                                    "location": "keep:99999999999999999999999999999994+44/file2",
+                                    "nameext": "",
+                                    "nameroot": "file2",
+                                    "path": "/keep/99999999999999999999999999999994+44/file2",
+                                    "size": 0
+                                }
+                            ],
+                            "location": "keep:99999999999999999999999999999994+44",
+                            "path": "/keep/99999999999999999999999999999994+44"
+                        }
+                    }},
                     'secret_mounts': {},
                     'output_storage_classes': ["default"]
                 }))
@@ -748,6 +834,7 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -827,7 +914,7 @@ class TestContainer(unittest.TestCase):
                     'command': ['md5sum', 'example.conf'],
                     'cwd': '/var/spool/cwl',
                     'scheduling_parameters': {},
-                    'properties': {},
+                    'properties': {'cwl_input': job_order},
                     "secret_mounts": {
                         "/var/spool/cwl/example.conf": {
                             "content": "username: user\npassword: blorp\n",
@@ -846,6 +933,7 @@ class TestContainer(unittest.TestCase):
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -884,13 +972,14 @@ 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_setting_storage_class(self, keepdocker):
-        arv_docker_clear_cache()
+        #arv_docker_clear_cache()
 
         runner = mock.MagicMock()
         runner.ignore_docker_for_reuse = False
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -949,7 +1038,7 @@ class TestContainer(unittest.TestCase):
                     'command': ['ls', '/var/spool/cwl'],
                     'cwd': '/var/spool/cwl',
                     'scheduling_parameters': {},
-                    'properties': {},
+                    'properties': {'cwl_input': {}},
                     'secret_mounts': {},
                     'output_storage_classes': ["foo_sc", "bar_sc"]
                 }))
@@ -959,13 +1048,14 @@ 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_setting_process_properties(self, keepdocker):
-        arv_docker_clear_cache()
+        #arv_docker_clear_cache()
 
         runner = mock.MagicMock()
         runner.ignore_docker_for_reuse = False
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -1037,6 +1127,7 @@ class TestContainer(unittest.TestCase):
                     'scheduling_parameters': {},
                     'properties': {
                         "baz": "blorp",
+                        "cwl_input": {"x": "blorp"},
                         "foo": "bar",
                         "quux": {
                             "q1": 1,
@@ -1053,13 +1144,14 @@ class TestContainer(unittest.TestCase):
     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
     def test_cuda_requirement(self, keepdocker):
         arvados_cwl.add_arv_hints()
-        arv_docker_clear_cache()
+        #arv_docker_clear_cache()
 
         runner = mock.MagicMock()
         runner.ignore_docker_for_reuse = False
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
         runner.api.collections().get().execute.return_value = {
@@ -1145,7 +1237,7 @@ class TestContainer(unittest.TestCase):
                         'command': ['nvidia-smi'],
                         'cwd': '/var/spool/cwl',
                         'scheduling_parameters': {},
-                        'properties': {},
+                        'properties': {'cwl_input': {}},
                         'secret_mounts': {},
                         'output_storage_classes': ["default"]
                     }))
@@ -1157,13 +1249,13 @@ class TestContainer(unittest.TestCase):
     @mock.patch("arvados.commands.keepdocker.list_images_in_arv")
     def test_match_local_docker(self, keepdocker, determine_image_id):
         arvados_cwl.add_arv_hints()
-        arv_docker_clear_cache()
 
         runner = mock.MagicMock()
         runner.ignore_docker_for_reuse = False
         runner.intermediate_output_ttl = 0
         runner.secret_store = cwltool.secrets.SecretStore()
         runner.api._rootDesc = {"revision": "20210628"}
+        runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz4", {"dockerhash": "456"}),
                                    ("zzzzz-4zz18-zzzzzzzzzzzzzz3", {"dockerhash": "123"})]
@@ -1219,7 +1311,7 @@ class TestContainer(unittest.TestCase):
             'command': ['echo'],
             'cwd': '/var/spool/cwl',
             'scheduling_parameters': {},
-            'properties': {},
+            'properties': {'cwl_input': {}},
             'secret_mounts': {},
             'output_storage_classes': ["default"]
         }
@@ -1230,7 +1322,7 @@ class TestContainer(unittest.TestCase):
             runner.api.container_requests().create.assert_called_with(
                 body=JsonDiffMatcher(container_request))
 
-        arv_docker_clear_cache()
+        runtimeContext.cached_docker_lookups.clear()
         runtimeContext.match_local_docker = True
         container_request['container_image'] = '99999999999999999999999999999993+99'
         container_request['name'] = 'test_run_True_2'
@@ -1248,13 +1340,14 @@ class TestContainer(unittest.TestCase):
         arvados_cwl.add_arv_hints()
         for enable_preemptible in (None, True, False):
             for preemptible_hint in (None, True, False):
-                arv_docker_clear_cache()
+                #arv_docker_clear_cache()
 
                 runner = mock.MagicMock()
                 runner.ignore_docker_for_reuse = False
                 runner.intermediate_output_ttl = 0
                 runner.secret_store = cwltool.secrets.SecretStore()
                 runner.api._rootDesc = {"revision": "20210628"}
+                runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
                 keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
                 runner.api.collections().get().execute.return_value = {
@@ -1332,7 +1425,7 @@ class TestContainer(unittest.TestCase):
                             'command': ['ls', '/var/spool/cwl'],
                             'cwd': '/var/spool/cwl',
                             'scheduling_parameters': sched,
-                            'properties': {},
+                            'properties': {'cwl_input': {}},
                             'secret_mounts': {},
                             'output_storage_classes': ["default"]
                         }))
@@ -1347,6 +1440,7 @@ class TestContainer(unittest.TestCase):
             runner.intermediate_output_ttl = 0
             runner.secret_store = cwltool.secrets.SecretStore()
             runner.api._rootDesc = {"revision": rev}
+            runner.api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
             keepdocker.return_value = [("zzzzz-4zz18-zzzzzzzzzzzzzz3", "")]
             runner.api.collections().get().execute.return_value = {
@@ -1393,7 +1487,7 @@ class TestContainer(unittest.TestCase):
 class TestWorkflow(unittest.TestCase):
     def setUp(self):
         cwltool.process._names = set()
-        arv_docker_clear_cache()
+        #arv_docker_clear_cache()
 
     def helper(self, runner, enable_reuse=True):
         document_loader, avsc_names, schema_metadata, metaschema_loader = cwltool.process.get_schema("v1.0")
@@ -1412,7 +1506,8 @@ class TestWorkflow(unittest.TestCase):
              "make_fs_access": make_fs_access,
              "loader": document_loader,
              "metadata": {"cwlVersion": INTERNAL_VERSION, "http://commonwl.org/cwltool#original_cwlVersion": "v1.0"},
-             "construct_tool_object": runner.arv_make_tool})
+             "construct_tool_object": runner.arv_make_tool,
+             "default_docker_image": "arvados/jobs:"+arvados_cwl.__version__})
         runtimeContext = arvados_cwl.context.ArvRuntimeContext(
             {"work_api": "containers",
              "basedir": "",
@@ -1434,6 +1529,7 @@ class TestWorkflow(unittest.TestCase):
 
         api = mock.MagicMock()
         api._rootDesc = get_rootDesc()
+        api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         runner = arvados_cwl.executor.ArvCwlExecutor(api)
         self.assertEqual(runner.work_api, 'containers')
@@ -1521,7 +1617,19 @@ class TestWorkflow(unittest.TestCase):
                 "output_path": "/var/spool/cwl",
                 "output_ttl": 0,
                 "priority": 500,
-                "properties": {},
+                "properties": {'cwl_input': {
+                        "fileblub": {
+                            "basename": "token.txt",
+                            "class": "File",
+                            "dirname": "/keep/99999999999999999999999999999999+118",
+                            "location": "keep:99999999999999999999999999999999+118/token.txt",
+                            "nameext": ".txt",
+                            "nameroot": "token",
+                            "path": "/keep/99999999999999999999999999999999+118/token.txt",
+                            "size": 0
+                        },
+                        "sleeptime": 5
+                }},
                 "runtime_constraints": {
                     "ram": 1073741824,
                     "vcpus": 1
@@ -1554,6 +1662,7 @@ class TestWorkflow(unittest.TestCase):
 
         api = mock.MagicMock()
         api._rootDesc = get_rootDesc()
+        api.config.return_value = {"Containers": {"DefaultKeepCacheRAM": 256<<20}}
 
         runner = arvados_cwl.executor.ArvCwlExecutor(api)
         self.assertEqual(runner.work_api, 'containers')
@@ -1594,7 +1703,7 @@ class TestWorkflow(unittest.TestCase):
                 'name': u'echo-subwf',
                 'secret_mounts': {},
                 'runtime_constraints': {'API': True, 'vcpus': 3, 'ram': 1073741824},
-                'properties': {},
+                'properties': {'cwl_input': {}},
                 'priority': 500,
                 'mounts': {
                     '/var/spool/cwl/cwl.input.yml': {