X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/21bf21abe900918a7882f7e43c102418e04159be..0818f8a837bd9d60132f0c7bc8cd06f15e0002d2:/sdk/cwl/tests/test_container.py diff --git a/sdk/cwl/tests/test_container.py b/sdk/cwl/tests/test_container.py index 3f8a32816d..4d6bcd2f68 100644 --- a/sdk/cwl/tests/test_container.py +++ b/sdk/cwl/tests/test_container.py @@ -2,9 +2,15 @@ # # 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 +import copy +import arvados.config import logging import mock import unittest @@ -16,11 +22,38 @@ from schema_salad.ref_resolver import Loader from schema_salad.sourceline import cmap from .matcher import JsonDiffMatcher +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 CollectionMock(object): + def __init__(self, vwdmock, *args, **kwargs): + self.vwdmock = vwdmock + self.count = 0 + + def open(self, *args, **kwargs): + self.count += 1 + return self.vwdmock.open(*args, **kwargs) + + def copy(self, *args, **kwargs): + self.count += 1 + self.vwdmock.copy(*args, **kwargs) + + def save_new(self, *args, **kwargs): + pass + + def __len__(self): + return self.count + + def portable_data_hash(self): + if self.count == 0: + return arvados.config.EMPTY_BLOCK_LOCATOR + else: + return "99999999999999999999999999999996+99" + + class TestContainer(unittest.TestCase): def helper(self, runner, enable_reuse=True): @@ -41,7 +74,9 @@ class TestContainer(unittest.TestCase): "make_fs_access": make_fs_access, "tmpdir": "/tmp", "enable_reuse": enable_reuse, - "priority": 500}) + "priority": 500, + "project_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz" + }) return loadingContext, runtimeContext @@ -53,7 +88,6 @@ class TestContainer(unittest.TestCase): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 runner.secret_store = cwltool.secrets.SecretStore() @@ -103,7 +137,7 @@ class TestContainer(unittest.TestCase): 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz', 'output_path': '/var/spool/cwl', 'output_ttl': 0, - 'container_image': 'arvados/jobs', + 'container_image': '99999999999999999999999999999993+99', 'command': ['ls', '/var/spool/cwl'], 'cwd': '/var/spool/cwl', 'scheduling_parameters': {}, @@ -117,7 +151,6 @@ class TestContainer(unittest.TestCase): def test_resource_requirements(self, keepdocker): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 3600 runner.secret_store = cwltool.secrets.SecretStore() @@ -190,7 +223,7 @@ class TestContainer(unittest.TestCase): 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz', 'output_path': '/var/spool/cwl', 'output_ttl': 7200, - 'container_image': 'arvados/jobs', + 'container_image': '99999999999999999999999999999993+99', 'command': ['ls'], 'cwd': '/var/spool/cwl', 'scheduling_parameters': { @@ -213,7 +246,6 @@ class TestContainer(unittest.TestCase): def test_initial_work_dir(self, collection_mock, keepdocker): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 runner.secret_store = cwltool.secrets.SecretStore() @@ -231,8 +263,7 @@ class TestContainer(unittest.TestCase): runner.fs_access.get_collection.side_effect = get_collection_mock vwdmock = mock.MagicMock() - collection_mock.return_value = vwdmock - vwdmock.portable_data_hash.return_value = "99999999999999999999999999999996+99" + collection_mock.side_effect = lambda *args, **kwargs: CollectionMock(vwdmock, *args, **kwargs) tool = cmap({ "inputs": [], @@ -323,7 +354,7 @@ class TestContainer(unittest.TestCase): 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz', 'output_path': '/var/spool/cwl', 'output_ttl': 0, - 'container_image': 'arvados/jobs', + 'container_image': '99999999999999999999999999999993+99', 'command': ['ls'], 'cwd': '/var/spool/cwl', 'scheduling_parameters': { @@ -344,7 +375,6 @@ class TestContainer(unittest.TestCase): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 runner.secret_store = cwltool.secrets.SecretStore() @@ -411,7 +441,7 @@ class TestContainer(unittest.TestCase): 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz', 'output_path': '/var/spool/cwl', 'output_ttl': 0, - 'container_image': 'arvados/jobs', + 'container_image': '99999999999999999999999999999993+99', 'command': ['ls', '/var/spool/cwl'], 'cwd': '/var/spool/cwl', 'scheduling_parameters': {}, @@ -425,7 +455,6 @@ class TestContainer(unittest.TestCase): runner = mock.MagicMock() runner.api = api - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.num_retries = 0 runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 @@ -437,7 +466,10 @@ class TestContainer(unittest.TestCase): col().open.return_value = [] + loadingContext, runtimeContext = self.helper(runner) + arvjob = arvados_cwl.ArvadosContainer(runner, + runtimeContext, mock.MagicMock(), {}, None, @@ -462,11 +494,108 @@ class TestContainer(unittest.TestCase): }) self.assertFalse(api.collections().create.called) + self.assertFalse(runner.runtime_status_error.called) arvjob.collect_outputs.assert_called_with("keep:abc+123") arvjob.output_callback.assert_called_with({"out": "stuff"}, "success") runner.add_intermediate_output.assert_called_with("zzzzz-4zz18-zzzzzzzzzzzzzz2") + # 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") + def test_infinite_runtime_status_update(self, gcc_mock): + api = mock.MagicMock() + api._rootDesc = copy.deepcopy(get_rootDesc()) + del api._rootDesc.get('resources')['jobs']['methods']['create'] + + # Make sure ArvCwlExecutor thinks it's running inside a container so it + # adds the logging handler that will call runtime_status_update() + runner = arvados_cwl.ArvCwlExecutor(api) + self.assertEqual(runner.work_api, 'containers') + root_logger = logging.getLogger('') + handlerClasses = [h.__class__ for h in root_logger.handlers] + self.assertTrue(arvados_cwl.RuntimeStatusLoggingHandler in handlerClasses) + + # get_current_container is invoked when we call runtime_status_update + # so try and log again! + gcc_mock.side_effect = lambda *args: root_logger.error("Second Error") + try: + root_logger.error("First Error") + except RuntimeError as e: + self.fail(str(e)) + + @mock.patch("arvados_cwl.util.get_current_container") + @mock.patch("arvados.collection.CollectionReader") + @mock.patch("arvados.collection.Collection") + def test_child_failure(self, col, reader, gcc_mock): + api = mock.MagicMock() + api._rootDesc = copy.deepcopy(get_rootDesc()) + del api._rootDesc.get('resources')['jobs']['methods']['create'] + + # Set up runner with mocked runtime_status_update() + self.assertFalse(gcc_mock.called) + runtime_status_update = mock.MagicMock() + arvados_cwl.ArvCwlExecutor.runtime_status_update = runtime_status_update + runner = arvados_cwl.ArvCwlExecutor(api) + self.assertEqual(runner.work_api, 'containers') + + # Make sure ArvCwlExecutor thinks it's running inside a container so it + # adds the logging handler that will call runtime_status_update() mock + gcc_mock.return_value = {"uuid" : "zzzzz-dz642-zzzzzzzzzzzzzzz"} + self.assertTrue(gcc_mock.called) + root_logger = logging.getLogger('') + handlerClasses = [h.__class__ for h in root_logger.handlers] + self.assertTrue(arvados_cwl.RuntimeStatusLoggingHandler in handlerClasses) + + runner.num_retries = 0 + runner.ignore_docker_for_reuse = False + runner.intermediate_output_ttl = 0 + runner.secret_store = cwltool.secrets.SecretStore() + runner.label = mock.MagicMock() + runner.label.return_value = '[container testjob]' + + runner.api.containers().get().execute.return_value = { + "state":"Complete", + "output": "abc+123", + "exit_code": 1, + "log": "def+234" + } + + col().open.return_value = [] + + loadingContext, runtimeContext = self.helper(runner) + + arvjob = arvados_cwl.ArvadosContainer(runner, + runtimeContext, + mock.MagicMock(), + {}, + None, + [], + [], + "testjob") + arvjob.output_callback = mock.MagicMock() + arvjob.collect_outputs = mock.MagicMock() + arvjob.successCodes = [0] + arvjob.outdir = "/var/spool/cwl" + arvjob.output_ttl = 3600 + arvjob.collect_outputs.return_value = {"out": "stuff"} + + arvjob.done({ + "state": "Final", + "log_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz1", + "output_uuid": "zzzzz-4zz18-zzzzzzzzzzzzzz2", + "uuid": "zzzzz-xvhdp-zzzzzzzzzzzzzzz", + "container_uuid": "zzzzz-8i9sb-zzzzzzzzzzzzzzz", + "modified_at": "2017-05-26T12:01:22Z" + }) + + runtime_status_update.assert_called_with( + 'error', + 'arvados.cwl-runner: [container testjob] (zzzzz-xvhdp-zzzzzzzzzzzzzzz) error log:', + ' ** log is empty **' + ) + arvjob.output_callback.assert_called_with({"out": "stuff"}, "permanentFail") + # 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") @@ -474,7 +603,6 @@ class TestContainer(unittest.TestCase): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 runner.secret_store = cwltool.secrets.SecretStore() @@ -549,7 +677,7 @@ class TestContainer(unittest.TestCase): 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz', 'output_path': '/var/spool/cwl', 'output_ttl': 0, - 'container_image': 'arvados/jobs', + 'container_image': '99999999999999999999999999999994+99', 'command': ['ls', '/var/spool/cwl'], 'cwd': '/var/spool/cwl', 'scheduling_parameters': {}, @@ -564,7 +692,6 @@ class TestContainer(unittest.TestCase): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 runner.secret_store = cwltool.secrets.SecretStore() @@ -642,7 +769,7 @@ class TestContainer(unittest.TestCase): 'owner_uuid': 'zzzzz-8i9sb-zzzzzzzzzzzzzzz', 'output_path': '/var/spool/cwl', 'output_ttl': 0, - 'container_image': 'arvados/jobs', + 'container_image': '99999999999999999999999999999993+99', 'command': ['md5sum', 'example.conf'], 'cwd': '/var/spool/cwl', 'scheduling_parameters': {}, @@ -662,7 +789,6 @@ class TestContainer(unittest.TestCase): arv_docker_clear_cache() runner = mock.MagicMock() - runner.project_uuid = "zzzzz-8i9sb-zzzzzzzzzzzzzzz" runner.ignore_docker_for_reuse = False runner.intermediate_output_ttl = 0 runner.secret_store = cwltool.secrets.SecretStore()