B2b: decommission orchestration + missing-storage indicator + re-enroll fix (v0.65.0)
agentapi.Decommission + handleStorageDecommission (migrate-all-or-none, Change 2): migrate-then-decommission via the migration done-hook, or decommission-anyway (stop apps, keep HDD_PATH). 'Hiányzó tárhely' badge on dashboard/stacks/app card when an app's drive is decommissioned/disconnected/absent. Change 4: registerStoragePath clears the decommissioned marker on re-enroll (ClearDecommissioned had no callers). Non-hollow tests incl. mutation-proven Change-4 companion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -115,6 +115,11 @@ type MigrationJob struct {
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
FinishedAt time.Time `json:"finished_at,omitempty"`
|
||||
|
||||
// DecommissionOnDone: when set, the migration was started by the decommission flow — on successful
|
||||
// completion the done-hook soft-marks the source registry path + tells the agent to decommission it.
|
||||
// The engine itself does NOT decommission; it only fires the hook (the policy lives in the caller).
|
||||
DecommissionOnDone bool `json:"decommission_on_done,omitempty"`
|
||||
}
|
||||
|
||||
func (j *MigrationJob) clone() *MigrationJob {
|
||||
@@ -185,17 +190,27 @@ func (m *Manager) MigrationStatus() *MigrationJob {
|
||||
return m.migJob.clone()
|
||||
}
|
||||
|
||||
// SetMigrationDoneHook registers a callback fired (in the migration goroutine) when a migration
|
||||
// completes successfully. The decommission flow uses it to soft-mark + agent-decommission the source.
|
||||
func (m *Manager) SetMigrationDoneHook(fn func(*MigrationJob)) { m.migDoneHook = fn }
|
||||
|
||||
// MigrateAll moves the whole namespace off sourcePath onto targetPath.
|
||||
func (m *Manager) MigrateAll(ctx context.Context, sourcePath, targetPath string) (string, error) {
|
||||
return m.startMigration("all", sourcePath, "", targetPath)
|
||||
return m.startMigration("all", sourcePath, "", targetPath, false)
|
||||
}
|
||||
|
||||
// MigrateAllAndDecommission moves the whole namespace then (on success) fires the done-hook so the
|
||||
// caller decommissions the now-empty source drive.
|
||||
func (m *Manager) MigrateAllAndDecommission(ctx context.Context, sourcePath, targetPath string) (string, error) {
|
||||
return m.startMigration("all", sourcePath, "", targetPath, true)
|
||||
}
|
||||
|
||||
// MigrateApp moves a single app's data subtree onto targetPath.
|
||||
func (m *Manager) MigrateApp(ctx context.Context, appName, targetPath string) (string, error) {
|
||||
return m.startMigration("app", "", appName, targetPath)
|
||||
return m.startMigration("app", "", appName, targetPath, false)
|
||||
}
|
||||
|
||||
func (m *Manager) startMigration(scope, sourcePath, appName, targetPath string) (string, error) {
|
||||
func (m *Manager) startMigration(scope, sourcePath, appName, targetPath string, decommission bool) (string, error) {
|
||||
if err := m.acquireMigrating(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -207,10 +222,11 @@ func (m *Manager) startMigration(scope, sourcePath, appName, targetPath string)
|
||||
}()
|
||||
|
||||
j := &MigrationJob{
|
||||
Scope: scope,
|
||||
Target: filepath.Clean(targetPath),
|
||||
Units: map[string]*MigUnit{},
|
||||
StartedAt: time.Now().UTC(),
|
||||
Scope: scope,
|
||||
Target: filepath.Clean(targetPath),
|
||||
Units: map[string]*MigUnit{},
|
||||
StartedAt: time.Now().UTC(),
|
||||
DecommissionOnDone: decommission,
|
||||
}
|
||||
j.ID = "mig-" + j.StartedAt.Format("20060102-150405")
|
||||
|
||||
@@ -389,6 +405,9 @@ func (m *Manager) runMigration(ctx context.Context, j *MigrationJob) {
|
||||
}
|
||||
if j.Phase == PhaseDone {
|
||||
m.logger.Printf("[INFO] [migrate] %s complete: %s → %s (%d app(s))", j.ID, j.Source, j.Target, len(j.Apps))
|
||||
if m.migDoneHook != nil {
|
||||
m.migDoneHook(j.clone()) // decommission policy (soft-mark + agent) lives in the hook
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user