mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
app, component: Add repair method
- Allows apps and component to implement custom repair methods. - Default implementation asks relevant components to repair, and then if needed, requests re-run setup for the app. - Component.repair will return True by default, indicating that setup should be re-run. Signed-off-by: James Valleroy <jvalleroy@mailbox.org> [sunil: Minor docstring styling fixes] [sunil: Improve tests for repair] Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
ddc9b434a7
commit
f487565b2c
@ -211,7 +211,6 @@ class App:
|
||||
"""Update the status of all follower components.
|
||||
|
||||
Do not query or update the status of the leader components.
|
||||
|
||||
"""
|
||||
for component in self.components.values():
|
||||
if not component.is_leader:
|
||||
@ -230,7 +229,6 @@ class App:
|
||||
and then supplementing the results with any app level diagnostic tests.
|
||||
|
||||
Also see :meth:`.has_diagnostics`.
|
||||
|
||||
"""
|
||||
results = []
|
||||
for component in self.components.values():
|
||||
@ -255,7 +253,6 @@ class App:
|
||||
it is assumed that it is for implementing diagnostic tests and this
|
||||
method returns True for such an app. Override this method if this
|
||||
default behavior does not fit the needs.
|
||||
|
||||
"""
|
||||
# App implements some diagnostics
|
||||
if self.__class__.diagnose is not App.diagnose:
|
||||
@ -268,6 +265,40 @@ class App:
|
||||
|
||||
return False
|
||||
|
||||
def repair(self, failed_checks: _list_type) -> bool:
|
||||
"""Try to fix failed diagnostics.
|
||||
|
||||
The default implementation asks relevant components to repair, and then
|
||||
requests re-run setup for the app.
|
||||
|
||||
failed_checks is a list of DiagnosticChecks that had failed or resulted
|
||||
in a warning. The list will be split up by component_id, and passed to
|
||||
the appropriate components. Remaining failed diagnostics do not have a
|
||||
related component, and should be handled by the app.
|
||||
|
||||
Returns whether the app setup should be re-run.
|
||||
"""
|
||||
should_rerun_setup = False
|
||||
|
||||
# Group the failed_checks by component
|
||||
components_failed_checks = collections.defaultdict(list)
|
||||
for failed_check in failed_checks:
|
||||
if failed_check.component_id:
|
||||
components_failed_checks[failed_check.component_id].append(
|
||||
failed_check)
|
||||
else:
|
||||
# There is a failed check with no related component.
|
||||
should_rerun_setup = True
|
||||
|
||||
# Repair each component that has failed checks
|
||||
for component_id, component in self.components.items():
|
||||
if components_failed_checks[component_id]:
|
||||
result = component.repair(
|
||||
components_failed_checks[component_id])
|
||||
should_rerun_setup = should_rerun_setup or result
|
||||
|
||||
return should_rerun_setup
|
||||
|
||||
|
||||
class Component:
|
||||
"""Interface for an app component.
|
||||
@ -319,7 +350,6 @@ class Component:
|
||||
enumeration from 'failed', 'passed', 'error', 'warning' and 'not_done'.
|
||||
|
||||
Also see :meth:`.has_diagnostics`.
|
||||
|
||||
"""
|
||||
return []
|
||||
|
||||
@ -333,10 +363,25 @@ class Component:
|
||||
is assumed that it is for implementing diagnostic tests and this method
|
||||
returns True for such a component. Override this method if this default
|
||||
behavior does not fit the needs.
|
||||
|
||||
"""
|
||||
return self.__class__.diagnose is not Component.diagnose
|
||||
|
||||
def repair(self, failed_checks: list) -> bool:
|
||||
"""Try to fix failed diagnostics.
|
||||
|
||||
The default implementation only requests re-run setup for the app.
|
||||
|
||||
Returns whether the app setup should be re-run by the caller.
|
||||
|
||||
This method should be overridden by components that implement
|
||||
diagnose(), if there is a known way to fix failed checks. The return
|
||||
value can be changed to False to avoid causing a re-run setup.
|
||||
|
||||
failed_checks is a list of DiagnosticChecks related to this component
|
||||
that had failed or warning result.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
class FollowerComponent(Component):
|
||||
"""Interface for an app component that follows other components.
|
||||
|
||||
@ -279,6 +279,38 @@ def test_app_has_diagnostics(app_with_components):
|
||||
assert app.has_diagnostics()
|
||||
|
||||
|
||||
@patch('plinth.setup.run_setup_on_app')
|
||||
def test_app_repair(_run_setup_on_app, app_with_components):
|
||||
"""Test running repair on an app."""
|
||||
component = app_with_components.get_component('test-follower-1')
|
||||
component.repair = Mock(return_value=True)
|
||||
|
||||
check1 = DiagnosticCheck('check1', 'check1', Result.FAILED, {})
|
||||
check2 = DiagnosticCheck('check2', 'check2', Result.WARNING, {})
|
||||
check3 = DiagnosticCheck('check3', 'check3', Result.FAILED, {},
|
||||
'test-follower-1')
|
||||
should_rerun_setup = app_with_components.repair([])
|
||||
assert not should_rerun_setup
|
||||
|
||||
should_rerun_setup = app_with_components.repair([check1])
|
||||
assert should_rerun_setup
|
||||
|
||||
should_rerun_setup = app_with_components.repair([check2])
|
||||
assert should_rerun_setup
|
||||
|
||||
should_rerun_setup = app_with_components.repair([check1, check2])
|
||||
assert should_rerun_setup
|
||||
component.repair.assert_not_called()
|
||||
|
||||
should_rerun_setup = app_with_components.repair([check3])
|
||||
assert should_rerun_setup
|
||||
assert component.repair.mock_calls == [call([check3])]
|
||||
|
||||
component.repair = Mock(return_value=False)
|
||||
should_rerun_setup = app_with_components.repair([check3])
|
||||
assert not should_rerun_setup
|
||||
|
||||
|
||||
def test_component_initialization():
|
||||
"""Test that component is initialized properly."""
|
||||
with pytest.raises(ValueError):
|
||||
@ -340,6 +372,13 @@ def test_component_has_diagnostics():
|
||||
assert not component.has_diagnostics()
|
||||
|
||||
|
||||
@patch('plinth.setup.run_setup_on_app')
|
||||
def test_component_repair(_run_setup_on_app):
|
||||
"""Test running repair on component."""
|
||||
component = Component('test-component')
|
||||
assert component.repair(['test-check'])
|
||||
|
||||
|
||||
def test_follower_component_initialization():
|
||||
"""Test that follower component is initialized properly."""
|
||||
component = FollowerComponent('test-follower-1')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user