Merge remote-tracking branch 'origin/main' into fix/delivery-records-true
# Conflicts: # modules/mesh-delivery-view/go.mod # modules/mesh-delivery-view/go.sum
This commit is contained in:
@@ -67,7 +67,8 @@ type UsageAnswer struct {
|
||||
At time.Time `json:"at"`
|
||||
Nodes []NodeUsage `json:"nodes"`
|
||||
Backlog []BacklogRow `json:"backlog"`
|
||||
// TriesRunning are the tries the checker runs now; their asks are counted under check until the try ends and names them.
|
||||
// TriesRunning are the tries the checker runs now, each with its asks and where each stands (a checker that names its
|
||||
// asks as they are put, issue 1890; an older one names them only at the end, and they are counted under check until then).
|
||||
TriesRunning []TryRunning `json:"tries_running"`
|
||||
Notes []string `json:"notes,omitempty"`
|
||||
}
|
||||
@@ -77,6 +78,9 @@ type TryRunning struct {
|
||||
ID string `json:"id"`
|
||||
Repository string `json:"repository"`
|
||||
Started time.Time `json:"started,omitzero"`
|
||||
Asks []string `json:"asks,omitempty"`
|
||||
// Steps are the try's own words for each ask: asked, or the node that took it, its state and the phase its step says.
|
||||
Steps json.RawMessage `json:"steps,omitempty"`
|
||||
}
|
||||
|
||||
// NodeUsage is one node: each build seat it holds or is set to build for, whether it is eligible to build, and its CPU.
|
||||
@@ -101,7 +105,17 @@ type SeatUsage struct {
|
||||
// Capacity is the asks it runs at once; CapacityFrom where that came from.
|
||||
Capacity int `json:"capacity"`
|
||||
CapacityFrom string `json:"capacity_from"`
|
||||
Paused bool `json:"paused"`
|
||||
// Configured is mesh-config's capacity when the module says it judges by a lower one (capacity.<node> of its pauses
|
||||
// state: the configured capacity bounded by its node's build CPU, issue 1806's follow-up); Capacity is then the
|
||||
// module's and CapacityFrom its why.
|
||||
Configured int `json:"configured,omitempty"`
|
||||
// BuildHours is the node's build window as the module says it (HH:MM-HH:MM and its zone), absent when it builds at any
|
||||
// hour; OutsideHours says the module judged the time outside it, so it takes nothing new (the operator, 2026-10-26).
|
||||
BuildHours string `json:"build_hours,omitempty"`
|
||||
OutsideHours bool `json:"outside_hours,omitempty"`
|
||||
// BuildHoursFault is why the module cannot read its node's build-hours value, naming it: it then builds at any hour.
|
||||
BuildHoursFault string `json:"build_hours_fault,omitempty"`
|
||||
Paused bool `json:"paused"`
|
||||
// Eligible says it takes asks: it holds the seat, has a capacity above 0 and is not paused; WhyNot says why not.
|
||||
// EligibleKnown is false when this tool cannot tell (it cannot read mesh-config's capacities): Eligible is then
|
||||
// absent, never false, and WhyNot starts "not known: ".
|
||||
@@ -384,8 +398,9 @@ func (u UsageReader) Usage() UsageAnswer {
|
||||
}
|
||||
}
|
||||
|
||||
// Pauses: paused.<node> of each module.
|
||||
// Pauses: paused.<node> of each module; and capacity.<node>, the capacity the module says it judges by.
|
||||
paused := map[string]bool{} // <node>.<language>
|
||||
judged := map[string]delivery.Capacity{}
|
||||
for _, lang := range delivery.AskLanguages {
|
||||
module, _ := delivery.BuildModuleOf(lang)
|
||||
pr, ok := u.Pauses[lang]
|
||||
@@ -411,6 +426,13 @@ func (u UsageReader) Usage() UsageAnswer {
|
||||
paused[node+"."+lang] = p.Paused
|
||||
}
|
||||
}
|
||||
if key, err := delivery.CapacityKey(node); err == nil {
|
||||
if raw, err := pr.Get(key); err == nil && len(raw) > 0 {
|
||||
if c, err := delivery.DecodeCapacity(raw); err == nil {
|
||||
judged[node+"."+lang] = c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,6 +474,18 @@ func (u UsageReader) Usage() UsageAnswer {
|
||||
s.Capacity = (&buildrun.Places{Capacity: caps}).CapacityOf(node)
|
||||
s.CapacityFrom = fmt.Sprintf("the modules' default %q: mesh-config holds no resolved value for %s on %s", buildstep.DefaultCapacities, s.Module, node)
|
||||
}
|
||||
if c, ok := judged[node+"."+lang]; ok && s.Holds && unknown == "" && !isBad && c.Configured == s.Capacity {
|
||||
// The module running on the node says its build hours, and judges by less than mesh-config gives when its
|
||||
// node's build CPU bounds it or the time is outside its build hours.
|
||||
s.BuildHours, s.OutsideHours = c.BuildHours, c.OutsideHours
|
||||
if c.BuildHoursFault != "" {
|
||||
s.BuildHours, s.BuildHoursFault = "any hour", c.BuildHoursFault
|
||||
}
|
||||
if c.Effective != c.Configured {
|
||||
s.Configured, s.Capacity = s.Capacity, c.Effective
|
||||
s.CapacityFrom = fmt.Sprintf("%s, as %s on %s judges it (said %s)", c.Why, s.Module, node, c.At.UTC().Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
s.Paused = paused[node+"."+lang]
|
||||
s.EligibleKnown = true
|
||||
switch {
|
||||
@@ -466,6 +500,8 @@ func (u UsageReader) Usage() UsageAnswer {
|
||||
s.WhyNot = "not known: this tool cannot read its capacity (" + unknown + ")"
|
||||
case isBad:
|
||||
s.WhyNot = "its capacity on " + node + " cannot be read, so its puller uses 0"
|
||||
case s.OutsideHours:
|
||||
s.WhyNot = "outside its build hours " + s.BuildHours + " on " + node + ": it takes nothing new until they open"
|
||||
case s.Capacity == 0:
|
||||
s.WhyNot = "its capacity on " + node + " is 0"
|
||||
}
|
||||
@@ -517,6 +553,7 @@ func (u UsageReader) tries(now time.Time, ans *UsageAnswer, note func(string, ..
|
||||
want = append(want, k)
|
||||
}
|
||||
}
|
||||
unnamed := 0
|
||||
raws, err := u.Tries.values(want)
|
||||
if err != nil {
|
||||
note("the checker's tries cannot all be read: %v", err)
|
||||
@@ -526,11 +563,12 @@ func (u UsageReader) tries(now time.Time, ans *UsageAnswer, note func(string, ..
|
||||
continue
|
||||
}
|
||||
var t struct {
|
||||
ID string `json:"id"`
|
||||
Repository string `json:"repository"`
|
||||
State string `json:"state"`
|
||||
Started time.Time `json:"started"`
|
||||
Asks []string `json:"asks"`
|
||||
ID string `json:"id"`
|
||||
Repository string `json:"repository"`
|
||||
State string `json:"state"`
|
||||
Started time.Time `json:"started"`
|
||||
Asks []string `json:"asks"`
|
||||
Steps json.RawMessage `json:"steps"`
|
||||
}
|
||||
if json.Unmarshal(raws[i], &t) != nil {
|
||||
continue
|
||||
@@ -539,11 +577,14 @@ func (u UsageReader) tries(now time.Time, ans *UsageAnswer, note func(string, ..
|
||||
asks[a] = true
|
||||
}
|
||||
if t.State == "running" {
|
||||
ans.TriesRunning = append(ans.TriesRunning, TryRunning{ID: t.ID, Repository: t.Repository, Started: t.Started})
|
||||
ans.TriesRunning = append(ans.TriesRunning, TryRunning{ID: t.ID, Repository: t.Repository, Started: t.Started, Asks: t.Asks, Steps: t.Steps})
|
||||
if len(t.Asks) == 0 {
|
||||
unnamed++
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(ans.TriesRunning) > 0 {
|
||||
note("a running try's asks are counted under check until the try ends and names them")
|
||||
if unnamed > 0 {
|
||||
note("%d running tries name no ask yet (none put yet, or a checker that names them only at the end): their asks are counted under check until then", unnamed)
|
||||
}
|
||||
return asks
|
||||
}
|
||||
|
||||
@@ -191,4 +191,36 @@ func TestBuildUsageAnswersEveryNodesUseFromTheStates(t *testing.T) {
|
||||
if verdict["shanks"] != "not known" || verdict["ace"] != "not eligible" {
|
||||
t.Errorf("verdicts with mesh-config unreadable = %v, want shanks not known, ace not eligible (paused)", verdict)
|
||||
}
|
||||
|
||||
// Issue 1806's follow-up: the module on shanks says it judges by 1 of mesh-config's 2 (its build CPU bounds it); the
|
||||
// seat says the module's capacity and why. And issue 1890: a running try that names its asks is said with them.
|
||||
u = usageFixture()
|
||||
u.Pauses["go"] = states(map[string]string{"capacity.shanks": `{"v":1,"effective":1,"configured":2,"quota-from":"the cpu.max of /meshyield.slice","quota-cores":6,"cores-per-step":6,"by-cpu":1,"why":"1, bounded by the CPU","at":"2026-10-26T11:00:00Z"}`,
|
||||
"capacity.ace": `{"v":1,"effective":2,"configured":3,"why":"stale: mesh-config says 2 now"}`})
|
||||
u.Tries = states(map[string]string{"try-" + strconv.FormatInt(at("11:58").UnixNano(), 10): `{"id":"try-2","repository":"novox/mesh-delivery","head":"h","trunk":"t","state":"running","asks":["` + tryAsk0 + `"],"steps":[{"ask":"` + tryAsk0 + `","node":"shanks","state":"running","phase":"unit: tests of 3 packages"}]}`})
|
||||
bound := u.Usage()
|
||||
sc := seatOf(t, bound, "shanks", "node-build-go")
|
||||
if sc.Capacity != 1 || sc.Configured != 2 || !strings.HasPrefix(sc.CapacityFrom, "1, bounded by the CPU, as mesh-build-go on shanks judges it") {
|
||||
t.Errorf("a seat its CPU bounds: capacity %d of %d, from %q", sc.Capacity, sc.Configured, sc.CapacityFrom)
|
||||
}
|
||||
if sa := seatOf(t, bound, "ace", "node-build-go"); sa.Capacity != 2 || sa.Configured != 0 {
|
||||
t.Errorf("a module's word older than mesh-config's value is not taken: %+v", sa)
|
||||
}
|
||||
// The operator, 2026-10-26: outside its build hours the seat takes nothing new, and says so.
|
||||
u.Pauses["go"] = states(map[string]string{"capacity.shanks": `{"v":1,"effective":0,"configured":2,"build-hours":"18:00-06:00 CET","outside-hours":true,"why":"0, outside its build hours 18:00-06:00 CET until 18:00 (otherwise 2)","at":"2026-10-26T11:00:00Z"}`})
|
||||
if so := seatOf(t, u.Usage(), "shanks", "node-build-go"); so.Capacity != 0 || !so.OutsideHours || so.Eligible == nil || *so.Eligible ||
|
||||
so.WhyNot != "outside its build hours 18:00-06:00 CET on shanks: it takes nothing new until they open" {
|
||||
t.Errorf("a seat outside its build hours: %+v", so)
|
||||
}
|
||||
// A build-hours value the module cannot read: it builds at any hour, and the seat says the value and why.
|
||||
u.Pauses["go"] = states(map[string]string{"capacity.shanks": `{"v":1,"effective":2,"configured":2,"build-hours-fault":"the build hours \"shanks=18:00-6\": \"6\" is not a time HH:MM","why":"2; at any hour, because its build hours cannot be read","at":"2026-10-26T11:00:00Z"}`})
|
||||
if sf := seatOf(t, u.Usage(), "shanks", "node-build-go"); sf.BuildHours != "any hour" || !strings.Contains(sf.BuildHoursFault, "shanks=18:00-6") || sf.Eligible == nil || !*sf.Eligible {
|
||||
t.Errorf("a seat whose build hours cannot be read: %+v", sf)
|
||||
}
|
||||
if len(bound.TriesRunning) != 1 || len(bound.TriesRunning[0].Asks) != 1 || !strings.Contains(string(bound.TriesRunning[0].Steps), "unit: tests") ||
|
||||
strings.Contains(strings.Join(bound.Notes, " | "), "name no ask yet") {
|
||||
t.Errorf("a running try naming its asks: %+v, notes %v", bound.TriesRunning, bound.Notes)
|
||||
}
|
||||
}
|
||||
|
||||
var tryAsk0 = stepID(at("11:58"), "a00010")
|
||||
|
||||
Reference in New Issue
Block a user