controller v0.68.0: storage lifecycle on intermediary model (H2/H3/M1/M3 + boot-id)

H2 decommission UI button (migrate / anyway); H3 one-click re-enroll of a
decommissioned drive; M1 default reassignment (auto-promote + block-if-none);
M3 migrate re-asserts 2775 setgid on userdata dirs; deterministic guest-reboot
recreate via agent boot_id (replaces the timed sample). Fixes the {path}/{where}
H1 JS bug. Non-hollow tests + companions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 19:26:02 +02:00
parent bc41acf4da
commit 42f69dadda
10 changed files with 293 additions and 46 deletions
@@ -429,9 +429,23 @@ func (s *Server) finalizeDecommission(ctx context.Context, where, migratedTo str
// target path for a migrate-then-decommission, or "" for decommission-anyway. The agent is injected so
// the orchestration is testable without a live agent.
func (s *Server) finalizeDecommissionWith(ctx context.Context, agent diskAgent, where, migratedTo string) error {
// M1: never leave zero default. If `where` is the default, promote another usable drive; if none
// exists, BLOCK before any side-effect (compute against the registry BEFORE SetDecommissioned, which
// flips IsDefault/Schedulable on `where`).
promote, mustBlock := defaultPromotionTarget(s.settings.GetStoragePaths(), where, migratedTo)
if mustBlock {
return fmt.Errorf("ez az egyetlen használható tárhely — a leszerelés megtagadva (előbb adj hozzá vagy állíts be másik alapértelmezett meghajtót)")
}
if err := s.settings.SetDecommissioned(where, migratedTo); err != nil {
return fmt.Errorf("nyilvántartás frissítése sikertelen: %w", err)
}
if promote != "" {
if derr := s.settings.SetDefaultStoragePath(promote); derr != nil {
s.logger.Printf("[WARN] [web] default reassignment to %s failed: %v", promote, derr)
} else {
s.logger.Printf("[INFO] [web] default drive reassigned %s → %s (M1)", where, promote)
}
}
// Registered path is the STABLE /mnt/felhom-drives/<name>; the agent decommissions the raw mount.
if _, err := agent.Decommission(ctx, agentWhere(where)); err != nil {
return fmt.Errorf("a meghajtó leszerelése sikertelen: %w", err)