package main import ( "context" "time" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/buildstep" "git.novox.be/novox/mesh-sdk/go/delivery" ) // **The module pulls its seat's asks** (novox/hq ADR 0525 §2-§6, ADR 0536 §4; issue 1887 A3node), as mesh-build-go // does: the seat node-build-node, delivery first, then check, then recheck, at most the node's build capacity at once // (its setting `capacity`); each ask runs with the step bodies its tools run, behind the same guard, and ends on the key // .. The tools stay as the hand path (ADR 0525 §8). // Seat is the node seat this module holds. const Seat = "node-build-node" func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps { byName := map[string]buildstep.StepFunc{} for _, s := range steps { run := s.Run byName[s.Name] = func(ctx context.Context, args map[string]any) (any, error) { defer m.holdArg(args)() return run(ctx, args) } } return buildstep.AskSteps{ Fetch: byName["build_node_fetch"], Build: byName["build_node_build"], Test: byName["build_node_test"], Toolchain: func(ctx context.Context) (delivery.ToolchainUsed, error) { installed, err := m.Probe(ctx) if err != nil { return delivery.ToolchainUsed{}, err } return delivery.ToolchainUsed{Version: installed, Recipe: m.recipe().Version()}, nil }, Release: func(ws string) error { return m.spaces().Release(ws) }, Logf: m.Say, } } // Puller is the module's pull of its seat's asks, on source (the runtime's seat pull when nil). func (m *Module) Puller(steps []StepTool, source buildstep.AskSource) *buildstep.Puller { if source == nil { source = buildstep.SeatAsks{Language: delivery.AskNode} } capacity := m.Capacity // The capacity's own change wakes the puller (issue 1887): the runtime's watch of the config value as mesh-config // resolved it; the settings file is read only when that watch cannot start. A test's Capacity is its own. var watch func(string, func(stdio.ProvisionChange) error) error // And the CPU bounds it (issue 1806's follow-up): the configured capacity is an upper bound, the node's build slice // gives floor(its quota's cores / the module's cores-per-step) at once, read live; a test judges by its own Capacity. // And the node's build hours (a node may build only in a window): outside them it takes // nothing new. var cores func() float64 var hours func() (*buildstep.BuildWindow, string) if capacity == nil { hours = buildstep.NodeBuildHours(m.Settings, m.Node, m.Say) capacity = buildstep.NodeCapacity(m.Settings, m.Node, m.Say) watch = buildstep.WatchConfigServed cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[delivery.AskNode], m.Say) } ask := m.askSteps(steps) var load func(string) ([]byte, error) if m.ReadPauses.Get != nil { load = func(key string) ([]byte, error) { return m.ReadPauses.Get(key) } } return &buildstep.Puller{Source: source, Language: delivery.AskNode, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load, Capacity: capacity, WatchConfig: watch, CoresPerStep: cores, BuildHours: hours, Logf: m.Say, Run: func(ctx context.Context, a delivery.StepAsk) (any, error) { if err := m.Prepare(ctx); err != nil { return nil, err } if err := m.room(); err != nil { return nil, err } return buildstep.RunAsk(ctx, ask, a) }} } // SeatTools are the seat's verbs on this node (ADR 0525 §10). func (m *Module) SeatTools(p *buildstep.Puller) []stdio.Tool { return buildstep.SeatTools(Seat, "Node", p) } // StepEndTool ends one step left running (issue 1890); the module's steps state is the one it reads and writes. const StepEndTool = "build_node_step_end" // endLeftRunning is the module's start sweep (issue 1890): it ends the steps of its node that a previous process left // running (buildstep.EndStaleAtStart; every step names its owner). main runs it once at the start, and the module's test // runs it through the conformance kit's left-running case. func (m *Module) endLeftRunning(ctx context.Context, scan buildstep.Scan, booted time.Time, logf func(string, ...any)) { buildstep.EndStaleAtStart(ctx, scan, m.Node, booted, logf) } // stepScan is the module's steps state as buildstep reads it. func (m *Module) stepScan() buildstep.Scan { return buildstep.ScanOf(stdio.State(delivery.StepsState)) }