package host import ( "context" "errors" "log" "path/filepath" "time" execlease "github.com/carlosframework/platform/internal/hostexec" "github.com/carlosframework/platform/internal/rootparticipation" ) // Recovery constructs only root's local readers and settlement machinery. // It never starts the monitor, contacts instance-control, or calls Execute. func recoverRootExecution(ctx context.Context, dataDir, guardDir string) (result error) { ledger, required, err := rootparticipation.OpenForUpdate() if err != nil { return err } if !required { if ledger.State != nil { ledger.State.Close() } return execlease.RecoverPending(ctx, dataDir, guardDir) } ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() census, err := ledger.RecoveryCensus() ledger.State.Close() if err != nil { return err } if census.NeverEnrolled { return nil } store, err := openDeployStore(ctx) if err != nil { return err } reg, err := openServiceRegistry(filepath.Join(dataDir, "registry.db")) if err != nil { return err } defer reg.Close() // FS-R4: an enrolled census keeps retained root state under maintenance // even if claims are now off; the no-census branch above stays separate. ctx = context.WithValue(ctx, claimEntryKey{}, claimEntry{FlagRead: true, Enabled: true, Store: store}) ctx = withClaimRuntimeResources(ctx, ClaimRuntimeResources{Mode: "instance-control", Registry: reg, DataDir: dataDir, OptDir: filepath.Dir(filepath.Dir(guardDir)), UnitsDir: "/etc/systemd/system"}) ctx, err = ensureProductionClaimAssembly(ctx) if err != nil { return err } entry, _ := ctx.Value(claimEntryKey{}).(claimEntry) defer func() { cleanup, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() result = errors.Join(result, entry.Assembly.Close(cleanup)) }() handler, err := NewProductionRootClaimStop(ctx) if err != nil { return err } runtime := handler.(ClaimRootHTTP).executionRuntime defer func() { cleanup, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() result = errors.Join(result, runtime.Close(cleanup)) }() handler, err = WithProductionClaimLaunch(ctx, handler) if err != nil { return err } root := handler.(ClaimRootWithLaunch).Root return recoverRootExecutionBeforeUpdate(ctx, runtime.broker, root) } func recoverRootExecutionBeforeUpdate(ctx context.Context, broker *execlease.RootBroker, root ClaimRootHTTP) error { if err := broker.Recover(ctx); err != nil { return err } if err := root.RecoverPending(ctx); err != nil { log.Print(err) } return nil }