Safe removal (R-1044), found on Tester 1 G4: the drive gate leaves a safely removed drive alone (no false RETURNED/ABSENT, no mail); presence = backing device, not the agent's state
gates / gates (push) Successful in 46s

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-15 10:25:08 +02:00
parent a09cab124a
commit 37ffa3beb5
5 changed files with 87 additions and 14 deletions
+36 -14
View File
@@ -325,21 +325,13 @@ func (s *Server) reconcileEjectedDrives(ctx context.Context, agent *agentapi.Cli
if sp.EjectedAt == "" {
continue
}
var disk *agentapi.DiskInfo
for i := range disks {
if (sp.EjectDurableID != "" && disks[i].DurableID == sp.EjectDurableID) || (sp.EjectDurableID == "" && disks[i].GuestPath == sp.Path) {
disk = &disks[i]
}
}
attached := disk != nil && disk.State == "attached"
if !sp.EjectSeenGone {
if !attached {
_ = s.settings.SetEjectSeenGone(sp.Path)
s.logger.Printf("[INFO] [media] safely removed drive %s: its disk has left the box", sp.Path)
}
step, disk := ejectStep(sp, disks)
switch step {
case ejectWaitGone, ejectWaitBack:
continue
}
if !attached {
case ejectGone:
_ = s.settings.SetEjectSeenGone(sp.Path)
s.logger.Printf("[INFO] [media] safely removed drive %s: its disk has left the box", sp.Path)
continue
}
if !disk.BoundUnderParent && disk.MountPath != "" {
@@ -420,3 +412,33 @@ var plexClaim = func(code string) (int, error) {
resp.Body.Close()
return resp.StatusCode, nil
}
// The safe-removal states (R-1044).
const (
ejectWaitGone = iota // removed, the disk is still in the box
ejectGone // the disk has just been seen gone
ejectWaitBack // gone, not back yet
ejectBack // back: re-attach and clear
)
// ejectStep is the PURE decision of reconcileEjectedDrives. A disk is PHYSICALLY present when the agent reports a backing
// device: its `state` stays „attached" for a registered disk that is gone (measured on Tester 1, Part G4: Media D pulled,
// state attached, backing_device "", total_bytes 0). Pinned by TestEjectStep_G4.
func ejectStep(sp settings.StoragePath, disks []agentapi.DiskInfo) (int, *agentapi.DiskInfo) {
var disk *agentapi.DiskInfo
for i := range disks {
if (sp.EjectDurableID != "" && disks[i].DurableID == sp.EjectDurableID) || (sp.EjectDurableID == "" && disks[i].GuestPath == sp.Path) {
disk = &disks[i]
}
}
present := disk != nil && disk.BackingDevice != ""
switch {
case !sp.EjectSeenGone && present:
return ejectWaitGone, disk
case !sp.EjectSeenGone:
return ejectGone, disk
case !present:
return ejectWaitBack, disk
}
return ejectBack, disk
}