From 1412166efc85b5b766a1baaae18bc46374347147 Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Mon, 22 Jan 2024 18:40:07 +0100 Subject: [PATCH] device driver deprecation data: print nice error msg on malformed data In case of malformed device_driver_deprecation_data.json user could originally see raw traceback without having too much information what it actually means or how to fix it. That usually happens only when the file is manually modified on the machine. So in this case we inform user what file is problematic and how to restore the original file installed by our package. In case of upstream development, this msg could be seen also when new data is provided if: * data file is malformed * data file has a new format of data (still json expected) * etc. These issues however will be discovered prior the merge as the running tests will fail, so such a problematic file should never get part of the upstream. From that point, we will be expect that user has malformed / customized data file. So no need to handle all possible errors differently in this case. --- .../deviceanddriverdeprecationdataload.py | 36 ++++++++++++++----- .../tests/test_ddddload.py | 28 +++++++++++++++ 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/libraries/deviceanddriverdeprecationdataload.py b/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/libraries/deviceanddriverdeprecationdataload.py index f422c2c3b9..d4065a7232 100644 --- a/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/libraries/deviceanddriverdeprecationdataload.py +++ b/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/libraries/deviceanddriverdeprecationdataload.py @@ -1,6 +1,9 @@ +from leapp.exceptions import StopActorExecutionError from leapp.libraries.common import fetch +from leapp.libraries.common.rpms import get_leapp_packages, LeappComponents from leapp.libraries.stdlib import api from leapp.models import DeviceDriverDeprecationData, DeviceDriverDeprecationEntry +from leapp.models.fields import ModelViolationError def process(): @@ -22,12 +25,29 @@ def process(): docs_url='', docs_title='') - api.produce( - DeviceDriverDeprecationData( - entries=[ - DeviceDriverDeprecationEntry(**entry) - for entry in deprecation_data['data'] - if entry.get('device_type') in supported_device_types - ] + try: + api.produce( + DeviceDriverDeprecationData( + entries=[ + DeviceDriverDeprecationEntry(**entry) + for entry in deprecation_data['data'] + if entry.get('device_type') in supported_device_types + ] + ) ) - ) + except (ModelViolationError, ValueError, KeyError, AttributeError, TypeError) as err: + # All listed errors, we expect this to happen only when data is malformed + # or manually updated. Corrupted data in the upstream is discovered + # prior the merge thanks to the testing. So suggest just the restoration + # of the file. + msg = 'Invalid device and driver deprecation data: {}'.format(err) + hint = ( + 'This issue is usually caused by manual update of the {lp} file.' + ' The data inside is either incorrect or old. To restore the original' + ' {lp} file, remove it and reinstall the following packages: {rpms}' + .format( + lp='/etc/leapp/file/device_driver_deprecation_data.json', + rpms=', '.join(get_leapp_packages(component=LeappComponents.REPOSITORY)) + ) + ) + raise StopActorExecutionError(msg, details={'hint': hint}) diff --git a/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/tests/test_ddddload.py b/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/tests/test_ddddload.py index 69bcd09cfb..c3386745ae 100644 --- a/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/tests/test_ddddload.py +++ b/repos/system_upgrade/common/actors/loaddevicedriverdeprecationdata/tests/test_ddddload.py @@ -1,5 +1,9 @@ +import pytest + +from leapp.exceptions import StopActorExecutionError from leapp.libraries.actor import deviceanddriverdeprecationdataload as ddddload from leapp.libraries.common import fetch +from leapp.libraries.common.testutils import CurrentActorMocked TEST_DATA = { 'data': [ @@ -57,3 +61,27 @@ def load_data_asset_mock(*args, **kwargs): assert produced assert len(produced[0].entries) == 3 assert not any([e.device_type == 'unsupported' for e in produced[0].entries]) + + +@pytest.mark.parametrize('data', ( + {}, + {'foo': 'bar'}, + {'data': 1, 'foo': 'bar'}, + {'data': 'string', 'foo': 'bar'}, + {'data': {'foo': 1}, 'bar': 2}, + {'data': {'foo': 1, 'device_type': None}}, + {'data': {'foo': 1, 'device_type': 'cpu'}}, + {'data': {'driver_name': ['foo'], 'device_type': 'cpu'}}, +)) +def test_invalid_dddd_data(monkeypatch, data): + produced = [] + + def load_data_asset_mock(*args, **kwargs): + return data + + monkeypatch.setattr(fetch, 'load_data_asset', load_data_asset_mock) + monkeypatch.setattr(ddddload.api, 'current_actor', CurrentActorMocked()) + monkeypatch.setattr(ddddload.api, 'produce', lambda *v: produced.extend(v)) + with pytest.raises(StopActorExecutionError): + ddddload.process() + assert not produced