v0.24.0 — Pre-testing observability: debug logging, diagnostic dump, startup self-test
- Add [DEBUG] logging across all modules (backup, storage, sync, selfupdate, monitor, notify, report, assets, setup) gated behind logging.level: "debug" - Add /api/debug/dump endpoint returning full controller state JSON (debug only) - Add startup self-test validating 9 subsystems (Docker, dirs, storage, hub, restic repos, metrics DB) with pass/warn/fail summary - New packages: internal/selftest, internal/util - Constructor/signature changes: debug bool params, logger params on RunHealthCheck and BuildReport, smart watchdog probe logging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/util"
|
||||
)
|
||||
|
||||
// FormatAndMount formats a disk/partition and mounts it.
|
||||
@@ -27,8 +29,14 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
progress <- FormatProgress{Step: "error", Message: msg, Error: errStr, Percent: 0}
|
||||
return fmt.Errorf("%s: %w", msg, err)
|
||||
}
|
||||
dbg := func(format string, args ...interface{}) {
|
||||
if req.Logger != nil && req.Debug {
|
||||
req.Logger.Printf("[DEBUG] FormatAndMount: "+format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
mountPath := "/mnt/" + req.MountName
|
||||
dbg("starting: device=%s mountName=%s createPartition=%v", req.DevicePath, req.MountName, req.CreatePartition)
|
||||
|
||||
// --- Step 1: Validate ---
|
||||
send("validating", "Eszköz ellenőrzése...", 5)
|
||||
@@ -78,10 +86,14 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
if req.CreatePartition {
|
||||
// Wipe existing partition table and filesystem signatures first
|
||||
// H18: Log wipefs errors instead of silently discarding them.
|
||||
dbg("step wipefs: wipefs -a %s", HostDevicePath(req.DevicePath))
|
||||
send("partitioning", fmt.Sprintf("wipefs -a %s ...", HostDevicePath(req.DevicePath)), 12)
|
||||
if err := exec.Command("wipefs", "-a", HostDevicePath(req.DevicePath)).Run(); err != nil {
|
||||
// Non-fatal: some systems don't have wipefs; continue anyway
|
||||
dbg("wipefs failed (non-fatal): %v", err)
|
||||
send("partitioning", fmt.Sprintf("[WARN] wipefs sikertelen %s: %v (folytatás)", req.DevicePath, err), 13)
|
||||
} else {
|
||||
dbg("wipefs completed successfully")
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
@@ -89,12 +101,16 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
// ",," = start=default, size=default(fill disk), type=default(Linux filesystem GUID).
|
||||
// --force: overwrite even if device appears busy.
|
||||
// --wipe always: wipe filesystem signatures from newly created partitions.
|
||||
dbg("step sfdisk: sfdisk --force --wipe always %s", HostDevicePath(req.DevicePath))
|
||||
send("partitioning", fmt.Sprintf("sfdisk --force --wipe always %s ...", HostDevicePath(req.DevicePath)), 15)
|
||||
sfdiskInput := "label: gpt\n,,\n"
|
||||
cmd := exec.Command("sfdisk", "--force", "--wipe", "always", HostDevicePath(req.DevicePath))
|
||||
cmd.Stdin = strings.NewReader(sfdiskInput)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
dbg("sfdisk failed: %s", util.TruncateStr(string(out), 500))
|
||||
return "", fail("partitioning", "Partícionálás sikertelen: "+string(out), err)
|
||||
} else {
|
||||
dbg("sfdisk output: %s", util.TruncateStr(string(out), 500))
|
||||
}
|
||||
|
||||
_ = exec.Command("partprobe", HostDevicePath(req.DevicePath)).Run()
|
||||
@@ -119,14 +135,17 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
fsLabel = fsLabel[:16]
|
||||
}
|
||||
|
||||
dbg("step mkfs.ext4: mkfs.ext4 -L %s -F %s", fsLabel, HostDevicePath(partDev))
|
||||
send("formatting", fmt.Sprintf("mkfs.ext4 -L %s -F %s ...", fsLabel, HostDevicePath(partDev)), 30)
|
||||
mkfsCmd := exec.Command("mkfs.ext4", "-L", fsLabel, "-F", HostDevicePath(partDev))
|
||||
var mkfsOut bytes.Buffer
|
||||
mkfsCmd.Stdout = &mkfsOut
|
||||
mkfsCmd.Stderr = &mkfsOut
|
||||
if err := mkfsCmd.Run(); err != nil {
|
||||
dbg("mkfs.ext4 failed: %s", util.TruncateStr(mkfsOut.String(), 500))
|
||||
return "", fail("formatting", "Formázás sikertelen: "+mkfsOut.String(), err)
|
||||
}
|
||||
dbg("mkfs.ext4 output: %s", util.TruncateStr(mkfsOut.String(), 500))
|
||||
|
||||
send("formatting", "Formázás kész", 60)
|
||||
|
||||
@@ -135,12 +154,15 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
return "", fail("mounting", "Csatlakoztatási mappa nem hozható létre: "+mountPath, err)
|
||||
}
|
||||
|
||||
dbg("step blkid: blkid -s UUID -o value %s", HostDevicePath(partDev))
|
||||
send("mounting", fmt.Sprintf("UUID lekérése: blkid %s ...", HostDevicePath(partDev)), 65)
|
||||
uuidOut, err := exec.Command("blkid", "-s", "UUID", "-o", "value", HostDevicePath(partDev)).Output()
|
||||
if err != nil {
|
||||
dbg("blkid UUID failed: %v", err)
|
||||
return "", fail("mounting", "UUID lekérése sikertelen", err)
|
||||
}
|
||||
uuid := strings.TrimSpace(string(uuidOut))
|
||||
dbg("blkid returned UUID=%q", uuid)
|
||||
if uuid == "" {
|
||||
return "", fail("mounting", "UUID üres a formázás után", fmt.Errorf("empty UUID"))
|
||||
}
|
||||
@@ -148,28 +170,36 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
// Backup fstab (non-fatal)
|
||||
_ = BackupFstab(FstabPath)
|
||||
|
||||
dbg("step fstab: appending UUID=%s mountPath=%s fstype=ext4", uuid, mountPath)
|
||||
if err := AppendFstabEntry(FstabPath, uuid, mountPath, "ext4", "defaults,nofail,noatime"); err != nil {
|
||||
dbg("fstab append failed: %v", err)
|
||||
return "", fail("mounting", "fstab bejegyzés hozzáadása sikertelen", err)
|
||||
}
|
||||
dbg("fstab entry added successfully")
|
||||
|
||||
// Mount by device path explicitly — container's /etc/fstab != host fstab,
|
||||
// so "mount /mnt/hdd_1" (fstab lookup) won't work.
|
||||
dbg("step mount: mount -t ext4 -o defaults,noatime %s %s", HostDevicePath(partDev), mountPath)
|
||||
send("mounting", fmt.Sprintf("mount -t ext4 %s %s ...", HostDevicePath(partDev), mountPath), 70)
|
||||
if out, err := exec.Command("mount", "-t", "ext4", "-o", "defaults,noatime",
|
||||
HostDevicePath(partDev), mountPath).CombinedOutput(); err != nil {
|
||||
dbg("mount failed: %s", util.TruncateStr(string(out), 500))
|
||||
// H19: Roll back fstab entry to prevent orphaned entry that hangs system on reboot.
|
||||
_ = RemoveFstabEntry(FstabPath, uuid)
|
||||
return "", fail("mounting", "Csatlakoztatás sikertelen: "+string(out), err)
|
||||
}
|
||||
|
||||
// Verify mount actually worked (don't just trust exit code)
|
||||
dbg("step verify: findmnt -n -o SOURCE --target %s", mountPath)
|
||||
verifyOut, verifyErr := exec.Command("findmnt", "-n", "-o", "SOURCE", "--target", mountPath).Output()
|
||||
if verifyErr != nil || strings.TrimSpace(string(verifyOut)) == "" {
|
||||
dbg("mount verification failed: findmnt returned %q err=%v", string(verifyOut), verifyErr)
|
||||
// H19: Also roll back fstab if mount verify fails.
|
||||
_ = RemoveFstabEntry(FstabPath, uuid)
|
||||
return "", fail("mounting", "A csatlakoztatás nem ellenőrizhető: mount sikerült, de a meghajtó nem látható",
|
||||
fmt.Errorf("mount point %s not found after mount", mountPath))
|
||||
}
|
||||
dbg("mount verified: findmnt source=%q", strings.TrimSpace(string(verifyOut)))
|
||||
|
||||
send("mounting", "Csatlakoztatva: "+mountPath, 80)
|
||||
|
||||
@@ -185,6 +215,7 @@ func FormatAndMount(req FormatRequest, progress chan<- FormatProgress) (string,
|
||||
}
|
||||
}
|
||||
|
||||
dbg("format and mount completed successfully: %s", mountPath)
|
||||
send("done", "Meghajtó sikeresen inicializálva: "+mountPath, 100)
|
||||
|
||||
return mountPath, nil
|
||||
|
||||
Reference in New Issue
Block a user