v0.116.0: observability pass — always-on debug ring + leveled sweep + agent tab + self-log pull — MinAgent: 0.81.0

Capture layer: LogBuffer always exists; logger = MultiWriter(LevelFilterWriter
(stdout, logging.level), ring) so DEBUG detail exists remotely without a config
flip while docker logs keep respecting the level. New internal/logx leveled
helpers. Report ACK gains controller_log_requested (additive); next report
ships controller_log_tail (128KB, consume-once, app-tail wire byte-compatible).
Debug page: Vezérlő|Ügynök tabs; agent tab proxies agent /debug/logs with the
pre-0.83 notice on typed 404. Sweep: netstorage_job phases, netprobe, handler
validation refusals + orphan WARN, SupportsWithSource gate line, agentapi
per-call DEBUG, migrate phase lines, tier2/offbox unswallowed persists.
Red-proofs: filter-disabled, drain-removed, dropped-phase-line all FAIL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-11 16:45:57 +02:00
parent 647116480a
commit 26a43708b7
25 changed files with 797 additions and 31 deletions
+10 -2
View File
@@ -9,6 +9,7 @@ import (
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
"gitea.dooplex.hu/admin/felhom-controller/internal/logx"
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
)
@@ -79,20 +80,24 @@ func (s *Server) handleNetStorageAdd(w http.ResponseWriter, r *http.Request) {
}
name := strings.TrimSpace(req.Name)
if !mountNameRe.MatchString(name) {
logx.Debugf(s.logger, "[web] netstorage add refused by validation: name %q", name)
writeDiskJSON(w, http.StatusBadRequest, false, "érvénytelen név (csak betűk, számok, _ és -)", nil)
return
}
proto := strings.ToLower(strings.TrimSpace(req.Protocol))
if proto != "nfs" && proto != "smb" {
logx.Debugf(s.logger, "[web] netstorage add %q refused by validation: protocol %q", name, proto)
writeDiskJSON(w, http.StatusBadRequest, false, "protokoll: nfs vagy smb", nil)
return
}
server, export := strings.TrimSpace(req.Server), strings.TrimSpace(req.Export)
if server == "" || export == "" {
logx.Debugf(s.logger, "[web] netstorage add %q refused by validation: empty server/export", name)
writeDiskJSON(w, http.StatusBadRequest, false, "a szerver és a megosztás kötelező", nil)
return
}
if proto == "smb" && (req.Username == "" || req.Password == "") {
logx.Debugf(s.logger, "[web] netstorage add %q refused by validation: smb credentials missing", name)
writeDiskJSON(w, http.StatusBadRequest, false, "SMB-hez felhasználónév és jelszó szükséges", nil)
return
}
@@ -114,8 +119,8 @@ func (s *Server) handleNetStorageAdd(w http.ResponseWriter, r *http.Request) {
// instead of failing mid-pipeline in `verifying` with a misleading rollback. Runs BEFORE the
// single-flight claim (a refused add must not consume the slot). SupportUnknown passes: a down
// agent speaks through the existing agent-error paths, never as "too old".
support := s.netFeatures.Supports(r.Context(), agent, agentapi.FeatureNetstorageVerify)
s.logger.Printf("[DEBUG] [web] netstorage add %q capability gate: %s=%s", name, agentapi.FeatureNetstorageVerify, support)
support, supSource := s.netFeatures.SupportsWithSource(r.Context(), agent, agentapi.FeatureNetstorageVerify)
s.logger.Printf("[DEBUG] [web] netstorage add %q capability gate: %s=%s (source=%s)", name, agentapi.FeatureNetstorageVerify, support, supSource)
if support == agentapi.SupportNo {
s.logger.Printf("[WARN] [web] netstorage add %q refused: agent predates %s (probe 404)", name, agentapi.FeatureNetstorageVerify)
writeDiskJSON(w, http.StatusPreconditionFailed, false, netAddOutdatedMsg, map[string]any{"code": "agent_outdated"})
@@ -199,6 +204,9 @@ func (s *Server) networkStorageItems(ctx context.Context) []networkStorageItem {
}
}
sort.Strings(orphans)
if len(orphans) > 0 {
logx.Warnf(s.logger, "[web] netstorage: %d orphan agent-side share(s) with no registry entry: %v", len(orphans), orphans)
}
for _, name := range orphans {
m := live[name]
items = append(items, networkStorageItem{