package host import ( "errors" "fmt" "github.com/carlosframework/platform/internal/registry" "log" "path/filepath" "time" ) // Frozen from origin/main 34a4328f, internal/host/agent.go. Only names, // clock and readiness injection differ, so the oracle cannot drift with // the production legacy helpers. func fix5MainCheckedRestart(sys unitSystem, optDir string, getenv func(string) string, settled func(rt registry.Route), c rootRestartChecks) func(rt registry.Route) error { return func(rt registry.Route) error { before, err := sys.Show(rt.Unit) if err != nil { log.Printf("adopt: %s: cannot read %s (%v); restart not checked", rt.Host, rt.Unit, err) return sys.TryRestart(rt.Unit) } link := filepath.Join(optDir, "live", rt.Host) if before.ExecPath != link { log.Printf("adopt: binding: %s runs %s, but the unit stamp is on %s; restart not checked", rt.Unit, before.ExecPath, rt.Host) return sys.TryRestart(rt.Unit) } if before.ActiveState != "active" { return sys.TryRestart(rt.Unit) // stopped by an operator: stays a no-op } want, err := filepath.EvalSymlinks(link) if err != nil { return sys.TryRestart(rt.Unit) } if err := sys.TryRestart(rt.Unit); err != nil { return err } timeout := unitSettleTimeout(getenv) err = settleUnit(sys, rt.Unit, before.InvocationID, settleOpts{ Timeout: timeout, Poll: 500 * time.Millisecond, Hold: 5 * time.Second, WantDir: filepath.Dir(want), Ready: c.legacyReady(rt), Now: c.now, Sleep: c.sleep}) if errors.Is(err, errUnitStopped) { // An operator stopped it between the read above and now. Not a // verdict on the build, and a rollback restart would undo their // stop: same as a unit that was stopped before the restart. log.Printf("adopt: %s: %s was stopped during settle; not judged, not restarted", rt.Host, rt.Unit) return nil } if err == nil { log.Printf("adopt: %s: %s settled on %s", rt.Host, rt.Unit, filepath.Dir(want)) // D3 (proofobserve.go): a build that passed the settle check is a // build this box has run well. if settled != nil { settled(rt) } } return err } } // rollbackUnitRestart is D1's rollback restart (release.WithRollbackRestart): // the live link already names the restored build; replace whatever is // running (a deadline failure can leave the rejected build up) and check // the restored build comes up too. func fix5MainRollbackRestart(reg *registry.Registry, sys unitSystem, optDir string, getenv func(string) string, c rootRestartChecks) func(host string) error { return func(host string) error { rt, err := reg.Get(host) if err != nil || rt == nil || rt.Unit == "" { return nil } before, _ := sys.Show(rt.Unit) if err := sys.ResetFailed(rt.Unit); err != nil { log.Printf("adopt: %s: reset-failed %s: %v (continuing)", host, rt.Unit, err) } if err := sys.Restart(rt.Unit); err != nil { return err } want, err := filepath.EvalSymlinks(filepath.Join(optDir, "live", host)) if err != nil { return err } err = settleUnit(sys, rt.Unit, before.InvocationID, settleOpts{ Timeout: unitSettleTimeout(getenv), Poll: 500 * time.Millisecond, Hold: 5 * time.Second, WantDir: filepath.Dir(want), Ready: c.legacyReady(*rt), Now: c.now, Sleep: c.sleep}) if errors.Is(err, errUnitStopped) { // The restored build is what the link names; a stop an operator // made while it settled is theirs to keep, not a failed recovery. log.Printf("adopt: %s: %s was stopped during the rollback's settle; not judged, not restarted", host, rt.Unit) return nil } if err != nil { return fmt.Errorf("rollback did not come up either: %w", err) } return nil } }