Fix three pre-publish issues found in readiness audit

- create_task.py list-tasks: crash on null credentials.provider
  (`creds.get("provider", {})` returns None when key exists but is null;
  switch to `(creds.get("provider") or {})`)

- sitecustomize.py: write hook_status.json after each module, not only
  when both have loaded; S3-only users (B2 module never imported) now
  get a status file from verify instead of "No status file found"

- README: add filesystem find + sqlite3 DB query to the emergency
  recovery section so users can locate their clone path when middlewared
  is down and midclt is unavailable
This commit is contained in:
2026-06-16 14:37:00 +00:00
parent 4730de75dd
commit 52828d3457
3 changed files with 17 additions and 3 deletions
+16
View File
@@ -292,6 +292,22 @@ touch /mnt/tank/truenas-truecloud-patch/disabled
systemctl restart middlewared
```
If you don't remember where you cloned the repo (midclt won't work while middlewared is
down), find the path two ways:
```bash
# Option 1 — search the filesystem:
find /mnt -name "recover.sh" -path "*/truenas-truecloud-patch/*" 2>/dev/null
# Option 2 — query the TrueNAS database directly:
sqlite3 /data/freenas-v1.db \
"SELECT script FROM initshutdownscript WHERE comment = 'TrueCloud provider patch (S3/B2)';"
```
The `script` column shows the full path to `patch/apply.sh`; your clone root is one
level up (strip `/patch/apply.sh` from the end). Then run the `touch` command above
with that path.
If middlewared **still** won't start after the kill switch is set, the problem
is unrelated to this patch. Check:
+1 -1
View File
@@ -141,7 +141,7 @@ def cmd_list_tasks(client, _args):
print("─" * 60)
for t in sorted(tasks, key=lambda x: x["id"]):
creds = t.get("credentials") or {}
ptype = creds.get("provider", {}).get("type", "?")
ptype = (creds.get("provider") or {}).get("type", "?")
enabled = "yes" if t.get("enabled") else "no"
print(f"{t['id']:>4} {enabled:<8} {ptype:<14} {t.get('description', '')}")
-2
View File
@@ -219,8 +219,6 @@ def _record_status(fullname: str, ok: bool, detail: str = "") -> None:
if fullname in _hook_status:
return # idempotent: first call wins
_hook_status[fullname] = {"ok": ok, "detail": detail}
if len(_hook_status) < len(_Finder._targets):
return # wait for all patches; _Finder._targets is the canonical count
payload = {
"patched_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),