145 lines
5.8 KiB
Go
145 lines
5.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"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 A3go). It holds the seat
|
|
// node-build-go on its node and pulls the seat's asks through its runtime (buildstep.SeatAsks): delivery first, then
|
|
// check, then recheck, at most the node's build capacity at once (its setting `capacity`, read at every pull). Each ask
|
|
// runs with the same step bodies its tools run, behind the same guard, and ends on the key <node>.<ask id> of its steps
|
|
// state. The tools stay as the hand path (ADR 0525 §8).
|
|
|
|
// Seat is the node seat this module holds.
|
|
const Seat = "node-build-go"
|
|
|
|
// KeptState is the checker's kept verdicts, which a check ask takes for the packages whose digest has one (issue
|
|
// 1887's design limit 1).
|
|
const KeptState = delivery.CheckerModule + ".kept"
|
|
|
|
// askSteps are the module's step bodies for an ask, each holding its workspace while it runs, as a tool's does.
|
|
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_go_fetch"], Build: byName["build_go_build"], Digest: byName["build_go_digest"], Test: byName["build_go_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
|
|
},
|
|
Kept: m.KeptLookup,
|
|
Has: m.Has,
|
|
Release: func(ws string) error { return m.spaces().Release(ws) },
|
|
Logf: m.Say,
|
|
}
|
|
}
|
|
|
|
// keptValue reads one key of the checker's kept state; nil when it is absent. Main sets it to the state's read; a test
|
|
// its own.
|
|
func (m *Module) keptValue(key string) (json.RawMessage, error) {
|
|
if m.ReadKept == nil {
|
|
return nil, nil
|
|
}
|
|
return m.ReadKept(key)
|
|
}
|
|
|
|
// KeptLookup is whether the checker kept a verdict for the level and digest, and how many tests it counted.
|
|
func (m *Module) KeptLookup(ctx context.Context, level, digest string) (int, bool, error) {
|
|
key, err := delivery.KeptKey(level, digest)
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
raw, err := m.keptValue(key)
|
|
if err != nil || raw == nil {
|
|
return 0, false, err
|
|
}
|
|
v, err := delivery.DecodeKept(raw)
|
|
if err != nil {
|
|
return 0, false, fmt.Errorf("%s holds what is not a kept verdict: %w", key, err)
|
|
}
|
|
return v.Tests, true, nil
|
|
}
|
|
|
|
// 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.AskGo}
|
|
}
|
|
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)
|
|
settings := ""
|
|
if capacity == nil {
|
|
hours = buildstep.NodeBuildHours(m.Settings, m.Node, m.Say)
|
|
capacity = buildstep.NodeCapacity(m.Settings, m.Node, m.Say)
|
|
settings = m.Settings
|
|
watch = buildstep.WatchConfigServed
|
|
cores = buildstep.NodeCoresPerStep(m.Settings, buildstep.DefaultCoresPerStep[delivery.AskGo], m.Say)
|
|
}
|
|
ask := m.askSteps(steps)
|
|
var load func(string) ([]byte, error)
|
|
if m.ReadPauses.Get != nil {
|
|
load = func(key string) ([]byte, error) {
|
|
raw, err := m.ReadPauses.Get(key)
|
|
return raw, err
|
|
}
|
|
}
|
|
return &buildstep.Puller{Source: source, Language: delivery.AskGo, Node: m.Node, Put: m.Steps, PausePut: m.Pauses, Load: load,
|
|
Capacity: capacity, SettingsFile: settings, WatchConfig: watch, CoresPerStep: cores, BuildHours: hours, Logf: m.Say, Has: m.Has,
|
|
Run: func(ctx context.Context, a delivery.StepAsk) (any, error) {
|
|
if err := m.Check(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := m.room(); err != nil {
|
|
return nil, err
|
|
}
|
|
if a.Compose != nil {
|
|
return m.compose(ctx, ask.Fetch, a)
|
|
}
|
|
return buildstep.RunAsk(ctx, ask, a)
|
|
}}
|
|
}
|
|
|
|
// SeatTools are the seat's verbs on this node (ADR 0525 §10): what it runs, and its pause and resume.
|
|
func (m *Module) SeatTools(p *buildstep.Puller) []stdio.Tool {
|
|
return buildstep.SeatTools(Seat, "Go", p)
|
|
}
|
|
|
|
// StepEndTool ends one step left running (issue 1890); the module's steps state is the one it reads and writes.
|
|
const StepEndTool = "build_go_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))
|
|
}
|