package host import ( "context" "crypto/rand" "encoding/hex" "errors" "sync" "github.com/carlosframework/platform/internal/hostbox" execlease "github.com/carlosframework/platform/internal/hostexec" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/rootparticipation" "github.com/carlosframework/platform/internal/rootstate" ) var openClaimExecutionAuthority = func() (rootstate.Store, error) { return rootstate.Open(rootparticipation.Namespace, true, false) } type claimExecutionRuntime struct { parks *servingParkRuntime broker *execlease.RootBroker state rootstate.Store mu sync.Mutex cancel context.CancelFunc done chan struct{} closed bool root *ClaimRootHTTP } func newClaimExecutionRuntime(h ClaimRootHTTP, state rootstate.Store) (*claimExecutionRuntime, error) { boot, err := execlease.BootID() if err != nil { return nil, err } var id [32]byte if _, err = rand.Read(id[:]); err != nil { return nil, err } ledger := rootparticipation.Ledger{State: state, VerifyLaunches: rootparticipation.VerifyLaunchCensus} runtime := &claimExecutionRuntime{state: state, root: &h} broker, err := execlease.NewRootBroker(execlease.RootBrokerConfig{ State: state, Remote: h.Stop.Coordinator.Evidence, Clock: execlease.SystemClock{}, BootID: boot, Identity: hex.EncodeToString(id[:]), Authority: func(ctx context.Context, q execlease.ExecutionRequest, peer execlease.ControllerProcess) (execlease.ExecutionAuthority, error) { a, err := runtime.root.executionAuthorityWithMigration(ctx, state, q, peer) if errors.Is(err, hostbox.ErrChanged) || errors.Is(err, hostbox.ErrTypedInventory) || errors.Is(err, hostguard.ErrFenced) || errors.Is(err, hostguard.ErrStampMismatch) || errors.Is(err, hostguard.ErrRetiredStamp) { err = errors.Join(execlease.ErrInactive, err) } return a, err }, Alive: execlease.ControllerAlive, Cleanup: func(ctx context.Context, r execlease.ExecutionRecord) error { return runtime.root.cleanupExecution(ctx, r) }, Settle: func(ctx context.Context, r execlease.ExecutionRecord) error { return runtime.root.settleExecution(ctx, r) }, Transition: func(ctx context.Context, r execlease.ExecutionRecord, q execlease.ExecutionRequest, a execlease.ExecutionAuthority) error { return runtime.root.transitionExecution(ctx, r, q, a) }, Exclude: ledger.Acquire, Recovered: func(ctx context.Context) error { return rootparticipation.WithLaunchExclusion(ctx, ledger.RecoverAcquisition) }, }) if err != nil { return nil, err } runtime.broker = broker return runtime, nil } func (r *claimExecutionRuntime) Start(ctx context.Context) { r.mu.Lock() defer r.mu.Unlock() if r.closed || r.done != nil { return } worker, cancel := context.WithCancel(ctx) r.cancel, r.done = cancel, make(chan struct{}) go func() { defer close(r.done); r.broker.Run(worker) }() } func (r *claimExecutionRuntime) Close(ctx context.Context) error { r.mu.Lock() defer r.mu.Unlock() if r.closed { return nil } if r.cancel != nil { r.cancel() select { case <-r.done: case <-ctx.Done(): return ctx.Err() } } if err := r.parks.Close(ctx); err != nil { return err } err := r.broker.Close(ctx) if ctx.Err() != nil { return errors.Join(err, ctx.Err()) } r.closed = true return errors.Join(err, r.state.Close()) }