mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
feat(install): updates refresh systemd units; new installs run the newest release (#729)
Updates that move HEAD now install changed systemd units through a root-owned helper (/usr/local/sbin/ledmatrix-refresh-units, two literal sudo lines), with a backup restored on rollback; a refresh that fails part-way puts the old units back. Devices without the new sudo rule keep updating and are told to re-run the installer once. The one-shot installer now checks out the newest vX.Y.Z release (LEDMATRIX_CHANNEL=beta keeps main) and never moves an existing checkout backwards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,6 +80,8 @@ class FakeHost:
|
||||
self.nrestarts = 0
|
||||
self.heartbeat = heartbeat
|
||||
self.display_started_at = -1000.0 # the pre-update display, long running
|
||||
self.unit_restores = [] # (argv, commit checked out, restarts so far)
|
||||
self.restore_ok = True
|
||||
|
||||
def broken(self, kind):
|
||||
if self.running_head is None:
|
||||
@@ -103,6 +105,10 @@ class FakeHost:
|
||||
if self.pip:
|
||||
return self.pip(args, self)
|
||||
return done(args, rc=0 if self.pip_ok else 1)
|
||||
if args[:3] == ['sudo', '-n', av.REFRESH_UNITS_PATH]:
|
||||
self.unit_restores.append((list(args), git(self.repo, 'rev-parse', 'HEAD'),
|
||||
len(self.restarts)))
|
||||
return done(args, rc=0 if self.restore_ok else 1)
|
||||
if args[:4] == ['sudo', '-n', 'systemctl', 'restart']:
|
||||
if self.restart_failures:
|
||||
self.restart_failures -= 1
|
||||
@@ -405,3 +411,45 @@ def test_the_heartbeat_location_and_freshness_match_the_display():
|
||||
# window it has to stay healthy for.
|
||||
assert av.HEARTBEAT_FRESH_SECONDS + av.POLL_SECONDS < av.STABLE_SECONDS
|
||||
assert av.HEARTBEAT_FRESH_SECONDS > display_watchdog.BEAT_INTERVAL_SECONDS * 2
|
||||
|
||||
|
||||
# -- systemd units the update installed ------------------------------------------
|
||||
|
||||
def test_a_rollback_restores_the_units_the_update_installed(tmp_path):
|
||||
"""The update installed new units (web_interface/unit_refresh.py); the
|
||||
rollback puts the old ones back before restarting onto the old code."""
|
||||
code, result, host, head, old, new = check(tmp_path, 'display_down', units_refreshed=True)
|
||||
assert result['status'] == 'rolled_back' and head == old
|
||||
assert len(host.unit_restores) == 1
|
||||
argv, commit, restarts_before = host.unit_restores[0]
|
||||
assert argv == ['sudo', '-n', av.REFRESH_UNITS_PATH, '--restore']
|
||||
assert commit == old, 'restored after the code was rolled back'
|
||||
assert restarts_before == 2, 'restored before the services restart onto the old code'
|
||||
assert host.restarts[-2:] == [('ledmatrix.service', old), ('ledmatrix-web.service', old)]
|
||||
assert result['detail'] is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize('pending', [{}, {'units_refreshed': False}])
|
||||
def test_a_rollback_leaves_units_alone_when_the_update_did_not_change_them(tmp_path, pending):
|
||||
# {} is what an updater from before this change writes.
|
||||
code, result, host, head, old, new = check(tmp_path, 'display_down', **pending)
|
||||
assert result['status'] == 'rolled_back' and host.unit_restores == []
|
||||
|
||||
|
||||
def test_a_healthy_update_keeps_its_new_units(tmp_path):
|
||||
code, result, host, head, old, new = check(tmp_path, units_refreshed=True)
|
||||
assert result['status'] == 'success' and host.unit_restores == []
|
||||
|
||||
|
||||
def test_a_failed_unit_restore_is_reported_but_the_rollback_stands(tmp_path):
|
||||
repo, old, new = updated_repo(tmp_path)
|
||||
av.write_pending(av.pending_path(repo), {'status': 'pending', 'old_head': old, 'new_head': new,
|
||||
'display_was_active': True, 'dependency_failures': [],
|
||||
'units_refreshed': True})
|
||||
host = FakeHost(repo, new, 'display_down')
|
||||
host.restore_ok = False
|
||||
host.verifier().verify()
|
||||
result = av.read_pending(av.pending_path(repo))
|
||||
assert result['status'] == 'rolled_back'
|
||||
assert git(repo, 'rev-parse', 'HEAD') == old
|
||||
assert 'install_service.sh' in result['detail']
|
||||
|
||||
Reference in New Issue
Block a user