From cc0a02ad1cd7b9e7f1d4f091326cfa8bfcf3ac89 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 17 Jul 2025 13:18:28 -0700 Subject: [PATCH] config: Allow overriding target path in dropin config component - To be used when configuration has to change based on the package version. Signed-off-by: Sunil Mohan Adapa --- plinth/config.py | 12 +++++------- plinth/privileged/config.py | 23 ++++++++++------------- plinth/tests/test_config.py | 19 ++++++++++--------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/plinth/config.py b/plinth/config.py index 1b7f6c8f0..2cc0d1dc3 100644 --- a/plinth/config.py +++ b/plinth/config.py @@ -108,14 +108,12 @@ class DropinConfigs(app_module.FollowerComponent): return results - @staticmethod - def get_target_path(path): + def get_target_path(self, path): """Return Path object for a target path.""" - target = pathlib.Path(DropinConfigs.ROOT) - target /= DropinConfigs.DROPIN_CONFIG_ROOT.lstrip('/') + target = pathlib.Path(self.ROOT) + target /= self.DROPIN_CONFIG_ROOT.lstrip('/') return target / path.lstrip('/') - @staticmethod - def get_etc_path(path): + def get_etc_path(self, path): """Return Path object for etc path.""" - return pathlib.Path(DropinConfigs.ROOT) / path.lstrip('/') + return pathlib.Path(self.ROOT) / path.lstrip('/') diff --git a/plinth/privileged/config.py b/plinth/privileged/config.py index 57bd922a6..403b82e4b 100644 --- a/plinth/privileged/config.py +++ b/plinth/privileged/config.py @@ -10,7 +10,7 @@ from plinth import module_loader from plinth.actions import privileged -def _assert_managed_dropin_config(app_id: str, path: str): +def _get_managed_dropin_config(app_id: str, path: str): """Check that this is a path managed by the specified app.""" module_path = module_loader.get_module_import_path(app_id) module = importlib.import_module(module_path) @@ -25,7 +25,7 @@ def _assert_managed_dropin_config(app_id: str, path: str): components = app.get_components_of_type(DropinConfigs) for component in components: if path in component.etc_paths: - return + return component raise AssertionError('Not a managed drop-in config') @@ -37,10 +37,9 @@ def dropin_is_valid(app_id: str, path: str, copy_only: bool, Optionally, drop the link if it is invalid. """ - _assert_managed_dropin_config(app_id, path) - from plinth.config import DropinConfigs - etc_path = DropinConfigs.get_etc_path(path) - target = DropinConfigs.get_target_path(path) + component = _get_managed_dropin_config(app_id, path) + etc_path = component.get_etc_path(path) + target = component.get_target_path(path) if etc_path.exists() or etc_path.is_symlink(): if (not copy_only and etc_path.is_symlink() and etc_path.readlink() == target): @@ -59,10 +58,9 @@ def dropin_is_valid(app_id: str, path: str, copy_only: bool, @privileged def dropin_link(app_id: str, path: str, copy_only: bool): """Create a symlink from /etc/ to /usr/share/freedombox/etc.""" - _assert_managed_dropin_config(app_id, path) - from plinth.config import DropinConfigs - target = DropinConfigs.get_target_path(path) - etc_path = DropinConfigs.get_etc_path(path) + component = _get_managed_dropin_config(app_id, path) + target = component.get_target_path(path) + etc_path = component.get_etc_path(path) etc_path.parent.mkdir(parents=True, exist_ok=True) if copy_only: shutil.copyfile(target, etc_path) @@ -73,7 +71,6 @@ def dropin_link(app_id: str, path: str, copy_only: bool): @privileged def dropin_unlink(app_id: str, path: str, missing_ok: bool = False): """Remove a symlink in /etc/.""" - _assert_managed_dropin_config(app_id, path) - from plinth.config import DropinConfigs - etc_path = DropinConfigs.get_etc_path(path) + component = _get_managed_dropin_config(app_id, path) + etc_path = component.get_etc_path(path) etc_path.unlink(missing_ok=missing_ok) diff --git a/plinth/tests/test_config.py b/plinth/tests/test_config.py index f0dc12d82..86e235410 100644 --- a/plinth/tests/test_config.py +++ b/plinth/tests/test_config.py @@ -31,9 +31,10 @@ def fixture_dropin_configs(): @pytest.fixture(autouse=True) -def fixture_assert_dropin_config(): +def fixture_assert_dropin_config(dropin_configs): """Mock asserting dropin config path.""" - with patch('plinth.privileged.config._assert_managed_dropin_config'): + with patch('plinth.privileged.config._get_managed_dropin_config') as mock: + mock.return_value = dropin_configs yield @@ -95,7 +96,7 @@ def test_dropin_configs_enable_disable_symlinks(dropin_configs, tmp_path): # Enable when a file already exists dropin_configs.disable() - etc_path = DropinConfigs.get_etc_path('/etc/test/path1') + etc_path = dropin_configs.get_etc_path('/etc/test/path1') etc_path.touch() dropin_configs.enable() _assert_symlinks(dropin_configs, tmp_path, should_exist=True) @@ -108,7 +109,7 @@ def test_dropin_configs_enable_disable_symlinks(dropin_configs, tmp_path): # When symlink already exists to correct location dropin_configs.disable() - target_path = DropinConfigs.get_target_path('/etc/test/path1') + target_path = dropin_configs.get_target_path('/etc/test/path1') etc_path.symlink_to(target_path) dropin_configs.enable() _assert_symlinks(dropin_configs, tmp_path, should_exist=True) @@ -119,7 +120,7 @@ def test_dropin_configs_enable_disable_copy_only(dropin_configs, tmp_path): with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path): dropin_configs.copy_only = True for path in ['/etc/test/path1', '/etc/path2']: - target = DropinConfigs.get_target_path(path) + target = dropin_configs.get_target_path(path) target.parent.mkdir(parents=True, exist_ok=True) target.write_text('test-config-content') @@ -135,7 +136,7 @@ def test_dropin_configs_enable_disable_copy_only(dropin_configs, tmp_path): # Enable when a file already exists with wrong content dropin_configs.disable() - etc_path = DropinConfigs.get_etc_path('/etc/test/path1') + etc_path = dropin_configs.get_etc_path('/etc/test/path1') etc_path.write_text('x-invalid-content') dropin_configs.enable() _assert_symlinks(dropin_configs, tmp_path, should_exist=True, @@ -182,7 +183,7 @@ def test_dropin_config_diagnose_symlinks(dropin_configs, tmp_path): # A file exists instead of symlink dropin_configs.disable() - etc_path = DropinConfigs.get_etc_path('/etc/test/path1') + etc_path = dropin_configs.get_etc_path('/etc/test/path1') etc_path.touch() results = dropin_configs.diagnose() assert results[0].result == 'failed' @@ -204,7 +205,7 @@ def test_dropin_config_diagnose_copy_only(dropin_configs, tmp_path): with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path): dropin_configs.copy_only = True for path in ['/etc/test/path1', '/etc/path2']: - target = DropinConfigs.get_target_path(path) + target = dropin_configs.get_target_path(path) target.parent.mkdir(parents=True, exist_ok=True) target.write_text('test-config-content') @@ -221,7 +222,7 @@ def test_dropin_config_diagnose_copy_only(dropin_configs, tmp_path): # A symlink exists instead of a copied file dropin_configs.disable() - etc_path = DropinConfigs.get_etc_path('/etc/test/path1') + etc_path = dropin_configs.get_etc_path('/etc/test/path1') etc_path.symlink_to('/blah') results = dropin_configs.diagnose() assert results[0].result == 'failed'