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:
@@ -73,6 +73,11 @@ func NewServer(cfg *config.Config, dataDir string, logger *log.Logger, version s
|
||||
return s
|
||||
}
|
||||
|
||||
// isDebug returns true if logging level is "debug".
|
||||
func (s *Server) isDebug() bool {
|
||||
return s.cfg != nil && s.cfg.Logging.Level == "debug"
|
||||
}
|
||||
|
||||
func (s *Server) loadTemplates() {
|
||||
s.tmpl = template.Must(
|
||||
template.New("").Funcs(template.FuncMap{
|
||||
@@ -311,6 +316,9 @@ func (s *Server) processHubRestore(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] Setup: hub restore — pulling recovery from %s for customer %s", hubURL, customerID)
|
||||
}
|
||||
recovery, err := report.PullRecovery(hubURL, customerID, password)
|
||||
if err != nil {
|
||||
var msg string
|
||||
@@ -328,6 +336,10 @@ func (s *Server) processHubRestore(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] Setup: hub recovery received — hasInfra=%v, configLen=%d", recovery.HasInfraBackup, len(recovery.ConfigYAML))
|
||||
}
|
||||
|
||||
// Store recovery data in state for restore execution
|
||||
s.state.SelectedBackup = &SelectedBackup{
|
||||
Source: "hub",
|
||||
@@ -392,6 +404,9 @@ func (s *Server) processFreshHub(w http.ResponseWriter, r *http.Request) {
|
||||
s.logger.Printf("[INFO] Setup: config downloaded (%d bytes), writing config...", len(configYAML))
|
||||
|
||||
// Write config and finish setup
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] Setup: writing fresh config (%d bytes)", len(configYAML))
|
||||
}
|
||||
s.state.SetFormField("retrieval_password", password)
|
||||
if err := s.writeFreshConfig(configYAML, password); err != nil {
|
||||
s.logger.Printf("[ERROR] Setup: writeFreshConfig failed: %v", err)
|
||||
@@ -447,6 +462,9 @@ func (s *Server) processManual(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Generate controller.yaml
|
||||
configYAML := s.generateManualConfig()
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] Setup: generated manual config (%d bytes) for customer %s", len(configYAML), customerID)
|
||||
}
|
||||
if err := s.writeFreshConfig(configYAML, ""); err != nil {
|
||||
csrf := ensureCSRFToken(w, r)
|
||||
data := map[string]interface{}{
|
||||
|
||||
@@ -45,7 +45,7 @@ type lsblkDevice struct {
|
||||
}
|
||||
|
||||
// ScanDrivesForInfraBackups scans all block devices for .felhom-infra-backup/ directories.
|
||||
func ScanDrivesForInfraBackups(logger *log.Logger) ([]DriveBackup, error) {
|
||||
func ScanDrivesForInfraBackups(logger *log.Logger, debug bool) ([]DriveBackup, error) {
|
||||
logger.Printf("[INFO] Setup: scanning drives for infra backups...")
|
||||
|
||||
// Read currently mounted filesystems
|
||||
@@ -68,6 +68,10 @@ func ScanDrivesForInfraBackups(logger *log.Logger) ([]DriveBackup, error) {
|
||||
return nil, fmt.Errorf("parsing lsblk: %w", err)
|
||||
}
|
||||
|
||||
if debug {
|
||||
logger.Printf("[DEBUG] Setup scan: lsblk returned %d block devices", len(lsblk.Blockdevices))
|
||||
}
|
||||
|
||||
var results []DriveBackup
|
||||
|
||||
// Flatten all partitions
|
||||
@@ -83,6 +87,10 @@ func ScanDrivesForInfraBackups(logger *log.Logger) ([]DriveBackup, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
logger.Printf("[DEBUG] Setup scan: found %d partitions to check, %d root devices to skip", len(partitions), len(rootDevices))
|
||||
}
|
||||
|
||||
for _, part := range partitions {
|
||||
// Skip partitions without filesystem
|
||||
if part.FSType == nil || *part.FSType == "" || *part.FSType == "swap" {
|
||||
@@ -256,7 +264,7 @@ func countValid(results []DriveBackup) int {
|
||||
|
||||
// runDriveScan runs the scan asynchronously and stores results on the Server.
|
||||
func (s *Server) runDriveScan() {
|
||||
results, err := ScanDrivesForInfraBackups(s.logger)
|
||||
results, err := ScanDrivesForInfraBackups(s.logger, s.isDebug())
|
||||
|
||||
s.scanMu.Lock()
|
||||
defer s.scanMu.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user