Act on the review of the wilm import
Six findings from a four-agent review of the imported work, plus the contract bump the import owed. Nothing here changes what the imported features do. The contract version was not bumped for a change that widens what a plugin may declare. The asset-panel vocabulary gained two tab groups (serviceprocess, reports), a datetime column format, a map.body key and a tabs section that renders as a table. 0.29.0 bumped for exactly this class of change when the optional group was added, so this is 0.31.0 with a ledger row. All four are optional and resolve server-side when absent, so a plugin declaring none of them is unaffected. Site Backups now answers honestly where it has nothing behind it. The overlay is a compose profile: a separate container writes sets onto a shared volume and the api container only reads them. A Windows/IIS install runs no such container and the default root resolves to a drive-letter path nothing creates, so Back Up Now either failed on PermissionError or reported a run queued that would never happen, under a banner reading "No verified backup yet" - which describes a backup that failed, not a feature that was never installed. The three write routes refuse with the same 404 shape the configuration-policy routes use for a disabled feature, the list and status routes report whether a store exists, and the page says so instead of offering buttons. Two listAll callers still read a response that is not there. listAll resolves to a plain array through fetchAllPages, so response.data.data throws: PC Types settings rendered empty, and in Configuration Policies the throw happened inside the shared try, so PC types, applications, the action catalog and monitors were all left unassigned by one bad read. The agent package upload cannot succeed on IIS as shipped. MAX_CONTENT_LENGTH went to 256 MB to admit the ~69 MB tray binary, but IIS request filtering caps a body at 30,000,000 bytes and answers 413 before waitress sees the request, so web.config needs the matching requestLimits. The backup image could not be built by anyone outside a TLS-intercepting network. Dockerfile.backup did a plain COPY of a gitignored corporate root CA, and a COPY of a missing file fails the build. The trailing glob makes it a no-op when the cert is absent. endpointstates.domain was the one new string column written without a clamp. Under STRICT_TRANS_TABLES an over-long value raises Data too long and aborts the whole check-in, so a single malformed report would stop a machine reporting telemetry and events entirely. Not fixed here, and left with upstream: the restore path finalizes its schema upgrade only from a browser poll loop, so closing the tab mid-restore leaves the data behind the code; it restores instance files from the newest snapshot rather than the set that was asked for; and it takes no safety dump before the destructive load. All three are in the Docker-only overlay, which this site does not run, and none can be validated here. Scope: product
This commit is contained in:
@@ -85,6 +85,30 @@ def _backups_root():
|
||||
return os.environ.get(BACKUPS_ROOT_ENV, DEFAULT_BACKUPS_ROOT)
|
||||
|
||||
|
||||
def _backups_configured():
|
||||
"""Whether this deployment has a backup store behind the feature.
|
||||
|
||||
The overlay is a compose profile: a third container writes the sets onto a
|
||||
shared volume, and the api container only ever reads them and drops a
|
||||
sentinel. A Windows/IIS install has no such container, and the default root
|
||||
resolves there to a drive-letter path nothing creates - so every button
|
||||
either 500s on PermissionError or reports 'queued' for a run that will
|
||||
never happen. Answer the caller honestly instead.
|
||||
"""
|
||||
return os.path.isdir(_backups_root())
|
||||
|
||||
|
||||
def _unavailable_response():
|
||||
# 404 + NOT_FOUND is the established shape for "this feature is not
|
||||
# enabled here" (the configuration-policy routes answer the same way), and
|
||||
# the UI already tells a 404 apart from a real failure.
|
||||
return error_response(
|
||||
ErrorCodes.NOT_FOUND,
|
||||
'Site Backups is not configured on this deployment. It needs the '
|
||||
'backup overlay and a readable backups root (%s).' % BACKUPS_ROOT_ENV,
|
||||
http_code=404)
|
||||
|
||||
|
||||
def _read_json(path):
|
||||
"""Load a JSON file, or None when it is absent or unreadable."""
|
||||
try:
|
||||
@@ -339,6 +363,7 @@ def list_backups():
|
||||
'retention': RETENTION,
|
||||
'lastgoodat': (laststgood or {}).get('takenat'),
|
||||
'count': len(sets),
|
||||
'configured': _backups_configured(),
|
||||
}
|
||||
return success_response(data=sets, meta=meta)
|
||||
|
||||
@@ -373,6 +398,9 @@ def trigger_backup():
|
||||
up. Returning isrunning=false with a clear message keeps the UI honest
|
||||
rather than pretending the run started inside this process.
|
||||
"""
|
||||
if not _backups_configured():
|
||||
return _unavailable_response()
|
||||
|
||||
root = _backups_root()
|
||||
try:
|
||||
os.makedirs(root, exist_ok=True)
|
||||
@@ -401,6 +429,9 @@ def restore_backup(snapshotid):
|
||||
The actual restore is performed by scripts/backup/restore.sh in the backup
|
||||
container; here we validate and record intent.
|
||||
"""
|
||||
if not _backups_configured():
|
||||
return _unavailable_response()
|
||||
|
||||
root = _backups_root()
|
||||
setdir = os.path.join(root, os.path.basename(snapshotid))
|
||||
if not os.path.isdir(setdir):
|
||||
@@ -451,6 +482,9 @@ def finalize_restore():
|
||||
Idempotent and safe to call when nothing is pending: if there is no
|
||||
.upgrade-requested sentinel it reports there was nothing to finalize.
|
||||
"""
|
||||
if not _backups_configured():
|
||||
return _unavailable_response()
|
||||
|
||||
root = _backups_root()
|
||||
sentinel = os.path.join(root, '.upgrade-requested')
|
||||
try:
|
||||
@@ -496,6 +530,7 @@ def get_status():
|
||||
value is None when that kind of run has never been requested.
|
||||
"""
|
||||
return success_response(data={
|
||||
'configured': _backups_configured(),
|
||||
'backup': _read_status('backup'),
|
||||
'restore': _read_status('restore'),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user