v0.5.0: TrueNAS alert when an update is available
Raises a real alert in the TrueNAS UI bell -- not a log line nobody reads. On by default, checked once a day. install.sh --no-update-alerts turns it off. It does not nag --------------- A release whose CHANGELOG contains only a "### Docs" section changed no code and raises nothing. Anything else raises INFO; a "### Security" section raises WARNING. The CHANGELOG's own section headings are the signal, and a security fix anywhere in the range escalates the whole span -- a docs-only release sitting on top of a security fix still reports as security rather than hiding it. Why an AlertSource and not midclt ---------------------------------- TrueNAS cannot raise an alert from the CLI. midclt exposes only alert.dismiss, alert.list, alert.list_categories, alert.list_policies and alert.restore -- alert CREATION is internal to middlewared, and none of its ~60 one-shot classes is generic enough to reuse. Registering an AlertSource is the only way. It is also the least invasive thing this patch does. The providers and nested modules both APPEND CODE TO STOCK middleware files; the alert source ADDS ONE FILE and modifies none. It is the native mechanism -- the same one every built-in TrueNAS alert uses -- and TrueNAS polls it itself, so there is no cron job and no systemd timer. - Fail-safe: every error path returns None; it cannot take middlewared down. - Read-only: `git ls-remote` plus an HTTPS fetch of the CHANGELOG. It never writes to .git, so it cannot leave root-owned objects behind the way a `git fetch` from middlewared (running as root) would. - Removed by uninstall.sh (mw_patch.revert_all). - It only tells you; it never updates anything. Verified against the real repo and remote, with middlewared stubbed: on v0.4.1, only a README-only v0.4.2 available -> NO ALERT on v0.4.0, v0.4.1 fixed real bugs -> INFO on v0.3.2, v0.3.3 was the password fix -> SECURITY / WARNING 139 tests, ruff and shellcheck -S style clean.
This commit is contained in:
+40
-1
@@ -32,7 +32,7 @@
|
||||
# Derive PATCH_DIR from this script's location (parent of the patch/ directory).
|
||||
PATCH_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
LOG="$PATCH_DIR/apply.log"
|
||||
VERSION="0.4.2"
|
||||
VERSION="0.5.0"
|
||||
|
||||
# Rotate log at 512 KB to avoid unbounded growth on a system volume.
|
||||
# Keep two prior generations (.1 and .2) so the last three boots are always available.
|
||||
@@ -578,6 +578,40 @@ else:
|
||||
# that is inactive (superseded, or opt-in and off) is ok -- reporting a disabled
|
||||
# opt-in feature as FAIL would make `create_task.py verify` fail on a default
|
||||
# install. `active` says whether the module is doing anything.
|
||||
# ── update-available alert ────────────────────────────────────────────────────
|
||||
# Dropped into middlewared/alert/source/, where middlewared discovers and polls it
|
||||
# natively — no cron, no timer. It only raises an alert for releases that actually
|
||||
# changed something: a docs-only release is ignored (see tools/release_notes.py).
|
||||
alert_ok = False
|
||||
alert_detail = ''
|
||||
_patch_dir = os.path.dirname(os.path.dirname(nested_src))
|
||||
alert_src = os.path.join(os.path.dirname(nested_src), 'alert_source.py')
|
||||
alert_dst = os.path.join(mw_dir, 'alert', 'source', 'truecloud_patch_update.py')
|
||||
|
||||
if os.path.exists(os.path.join(_patch_dir, 'update_alerts_disabled')):
|
||||
alert_detail = 'disabled (update_alerts_disabled)'
|
||||
try:
|
||||
os.unlink(alert_dst)
|
||||
print('OK: Removed update alert (disabled).')
|
||||
except OSError:
|
||||
pass
|
||||
elif not os.path.exists(alert_src):
|
||||
alert_detail = 'alert_source.py not found'
|
||||
print(f'WARNING: {alert_src} missing — no update alert.')
|
||||
else:
|
||||
try:
|
||||
with open(alert_src, encoding='utf-8') as fh:
|
||||
_body = fh.read()
|
||||
# PATCH_DIR is baked in: the alert source must find the repo it belongs to.
|
||||
with open(alert_dst, 'w', encoding='utf-8') as fh:
|
||||
fh.write(_body.replace('@PATCH_DIR@', _patch_dir))
|
||||
alert_ok = True
|
||||
alert_detail = 'update alert installed'
|
||||
print(f'OK: Installed update alert → {alert_dst}')
|
||||
except Exception as e:
|
||||
alert_detail = f'not applied: {e}'
|
||||
print(f'WARNING: could not install update alert: {e}')
|
||||
|
||||
patches = {
|
||||
'providers': {
|
||||
'ok': (not providers_needed) or bool(b2_ok and restic_ok),
|
||||
@@ -589,6 +623,11 @@ patches = {
|
||||
'active': nested_needed,
|
||||
'detail': nested_detail,
|
||||
},
|
||||
'update_alert': {
|
||||
'ok': True, # never a failure: it is a convenience, not a patch
|
||||
'active': alert_ok,
|
||||
'detail': alert_detail,
|
||||
},
|
||||
}
|
||||
payload = {'patched_at': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), 'patches': patches}
|
||||
tmp = status_path + '.tmp'
|
||||
|
||||
Reference in New Issue
Block a user