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 <sunil@medhas.org>
This commit is contained in:
Sunil Mohan Adapa 2025-07-17 13:18:28 -07:00
parent 38810e566b
commit cc0a02ad1c
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
3 changed files with 25 additions and 29 deletions

View File

@ -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('/')

View File

@ -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)

View File

@ -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'