F6 deeper half: poll agent /disks/format/status on the 15s client timeout (slow USB mkfs runs detached) — agentapi.FormatStatus + awaitAgentFormat; then mount+register. Found on the live leg.
This commit is contained in:
@@ -31,6 +31,7 @@ type diskAgent interface {
|
||||
Disks(ctx context.Context) (agentapi.DisksResponse, error)
|
||||
ListCandidates(ctx context.Context) (agentapi.CandidatesResult, error)
|
||||
FormatDisk(ctx context.Context, device, fstype string, confirmed bool, durableID string) (agentapi.FormatResult, error)
|
||||
FormatStatus(ctx context.Context) (agentapi.FormatStatusResult, error)
|
||||
AssignDisk(ctx context.Context, uuid, where, fstype, options string) error
|
||||
EjectDisk(ctx context.Context, where string) (agentapi.EjectResult, error)
|
||||
Decommission(ctx context.Context, where string) (agentapi.DecommissionResult, error)
|
||||
@@ -133,11 +134,18 @@ func (s *Server) runStorageInit(ctx context.Context, agent diskAgent, device, fs
|
||||
}
|
||||
return res, nil // STOP — no bypass; the UI surfaces Opsign.
|
||||
}
|
||||
if err != nil {
|
||||
if err != nil && !isAgentTimeout(err) {
|
||||
return storageInitResult{}, fmt.Errorf("formázás sikertelen: %w", err)
|
||||
}
|
||||
if !fr.Formatted {
|
||||
return storageInitResult{}, fmt.Errorf("az eszköz nem lett megformázva (%s)", fr.Reason)
|
||||
// A slow mkfs (e.g. a large USB) outran the agent client's 15 s timeout — the agent runs the
|
||||
// mkfs DETACHED and records the job, so we POLL GET /disks/format/status to the terminal
|
||||
// outcome (F6: "mkfs continues detached; poll the status") and only THEN continue to
|
||||
// mount+register. A real (non-timeout) format failure surfaced above already.
|
||||
fr, err = s.awaitAgentFormat(ctx, agent, device)
|
||||
if err != nil {
|
||||
return storageInitResult{}, err
|
||||
}
|
||||
}
|
||||
// 2. Resolve the NEW fs UUID. A freshly-formatted RAW device isn't in /disks (not enrolled yet), so
|
||||
// resolve via the raw-device scan too (Impl-2b) — the device now appears there with its new durable_id.
|
||||
@@ -196,6 +204,56 @@ func (s *Server) runStorageAttach(ctx context.Context, agent diskAgent, device,
|
||||
return storageInitResult{Registered: true, Where: stable}, nil
|
||||
}
|
||||
|
||||
// isAgentTimeout reports whether a FormatDisk error is the agent-client's 15 s timeout (the mkfs is
|
||||
// then running DETACHED agent-side) rather than a real format failure — so we poll the status instead
|
||||
// of surfacing it as a failure (F6). A confirm/refuse verdict is returned as a typed error and is
|
||||
// matched BEFORE this, so it never reaches here.
|
||||
func isAgentTimeout(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return true
|
||||
}
|
||||
s := err.Error()
|
||||
return strings.Contains(s, "Client.Timeout") || strings.Contains(s, "deadline exceeded") || strings.Contains(s, "awaiting headers")
|
||||
}
|
||||
|
||||
// awaitAgentFormat follows a mkfs that outran the agent-client timeout: the agent runs it detached and
|
||||
// records the job, so we poll GET /disks/format/status until it leaves `running` (F6). Returns a
|
||||
// Formatted result on a `done` job, or an error on `failed` / idle (no job) / a persistent poll error /
|
||||
// ctx expiry. Transient poll errors are tolerated up to a small budget (the agent may be busy).
|
||||
func (s *Server) awaitAgentFormat(ctx context.Context, agent diskAgent, device string) (agentapi.FormatResult, error) {
|
||||
t := time.NewTicker(2 * time.Second)
|
||||
defer t.Stop()
|
||||
consecutiveErrs := 0
|
||||
for {
|
||||
st, err := agent.FormatStatus(ctx)
|
||||
if err != nil {
|
||||
consecutiveErrs++
|
||||
if consecutiveErrs >= 5 {
|
||||
return agentapi.FormatResult{}, fmt.Errorf("a formázás állapota nem kérdezhető le: %w", err)
|
||||
}
|
||||
} else {
|
||||
consecutiveErrs = 0
|
||||
switch st.Phase {
|
||||
case "done": // the agent's format-job terminal phases (internal/localapi/formatjob.go)
|
||||
return agentapi.FormatResult{Device: device, Formatted: true}, nil
|
||||
case "failed":
|
||||
return agentapi.FormatResult{}, fmt.Errorf("formázás sikertelen: %s", st.Error)
|
||||
case "idle":
|
||||
return agentapi.FormatResult{}, fmt.Errorf("a formázás nem indult el az eszközön (%s)", device)
|
||||
// "running" (or an unexpected phase) → keep polling
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return agentapi.FormatResult{}, ctx.Err()
|
||||
case <-t.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reEnrollClearMarker un-retires a re-plugged decommissioned drive (Change 4): clears the soft marker
|
||||
// and restores Schedulable so its apps' "missing storage" indicator clears. Returns true if it acted.
|
||||
func (s *Server) reEnrollClearMarker(where string) (bool, error) {
|
||||
|
||||
Reference in New Issue
Block a user