mesh/delivery delivered
mesh-build-node was unassigned from ace on 2026-10-23 and its ace.typescript key stayed in mesh-build-node_toolchains at v26.12.0: nothing deletes it, so the indexer, the builder and the checker took it for a builder and every Node fingerprint said 'builders disagree'. The three now read mesh-controller_assignments and pass a toolchain report on only while its build module has an open assignment on its node; builders that run different versions still disagree.
113 lines
5.0 KiB
Go
113 lines
5.0 KiB
Go
package main
|
|
|
|
// The same in mesh-delivery-indexer, mesh-delivery-builder and mesh-delivery-checker, as standing.go is (issue 1940).
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery/buildrun"
|
|
)
|
|
|
|
func gateReport(t *testing.T, node, version string) (string, []byte) {
|
|
t.Helper()
|
|
key, err := delivery.ToolchainKey(node, "typescript")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
value, err := delivery.EncodeToolchain(delivery.Toolchain{Language: "typescript", Version: version, Recipe: "f59571cf4be75adf", At: time.Unix(0, 0).UTC()})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return key, value
|
|
}
|
|
|
|
func agreedOf(set *buildrun.ToolchainSet) (string, string) {
|
|
tc, none := delivery.AgreedToolchain(set.Entries("typescript"))
|
|
return tc.Version, none
|
|
}
|
|
|
|
// The live case of issue 1940: mesh-build-node was unassigned from ace, whose report stayed at v26.12.0 while the
|
|
// three nodes it runs on report v26.12.1. Without the assignments every report counts (and they disagree); with them
|
|
// ace's is left out and the three agree; assigned to ace again, its report counts again and the guard refuses again.
|
|
func TestAnUnassignedBuildNodesReportIsNoBuilder(t *testing.T) {
|
|
set := &buildrun.ToolchainSet{}
|
|
var said []string
|
|
gate := newToolchainGate([]string{buildrun.GoBuildModule, buildrun.NodeBuildModule}, set.Put, func(f string, a ...any) { said = append(said, f) })
|
|
for node, version := range map[string]string{"ace": "v26.12.0", "g14": "v26.12.1", "novox": "v26.12.1", "shanks": "v26.12.1"} {
|
|
key, value := gateReport(t, node, version)
|
|
gate.Toolchain(buildrun.NodeBuildModule, key, value, false)
|
|
}
|
|
if _, none := agreedOf(set); !strings.HasPrefix(none, "builders disagree") {
|
|
t.Fatalf("before the assignments are read every report counts, so they disagree; got %q", none)
|
|
}
|
|
for _, node := range []string{"g14", "novox", "shanks"} {
|
|
gate.Assignment(node+"."+buildrun.NodeBuildModule, []byte(`{"node":"`+node+`","module":"mesh-build-node"}`), false)
|
|
}
|
|
gate.Assignment("ace.mesh-build-node", []byte(`{"node":"ace","module":"mesh-build-node","ended":"2026-10-23T08:00:00Z"}`), false)
|
|
gate.Assignment("ace.mesh-build-go", []byte(`{"node":"ace","module":"mesh-build-go"}`), false) // another module's: no matter
|
|
if !gate.Settled() {
|
|
t.Fatal("settling the assignments left ace's report out, so what was passed on changed")
|
|
}
|
|
if v, none := agreedOf(set); v != "v26.12.1" || none != "" {
|
|
t.Fatalf("the assigned nodes agree on v26.12.1; got %q, %q", v, none)
|
|
}
|
|
if len(said) != 1 {
|
|
t.Fatalf("ace's report is said left out once; said %v", said)
|
|
}
|
|
// ace reports again while unassigned (an old value seen again): still left out.
|
|
key, value := gateReport(t, "ace", "v26.12.0")
|
|
if gate.Toolchain(buildrun.NodeBuildModule, key, value, false) {
|
|
t.Fatal("an unassigned node's report changed what was passed on")
|
|
}
|
|
// Assigned to ace again: its report counts, and the builders really disagree: the guard stays.
|
|
if !gate.Assignment("ace.mesh-build-node", []byte(`{"node":"ace","module":"mesh-build-node"}`), false) {
|
|
t.Fatal("assigning ace again passed nothing on")
|
|
}
|
|
if _, none := agreedOf(set); !strings.HasPrefix(none, "builders disagree: v26.12.0 recipe f59571cf4be75adf on ace") {
|
|
t.Fatalf("builders that run different versions still disagree; got %q", none)
|
|
}
|
|
// The assignment's key deleted, or a pending one written: no assignment stands, and ace's report goes again.
|
|
if !gate.Assignment("ace.mesh-build-node", []byte(`{"node":"ace","module":"mesh-build-node","state":"pending"}`), false) {
|
|
t.Fatal("a pending assignment is no assignment on the machine")
|
|
}
|
|
if v, none := agreedOf(set); v != "v26.12.1" || none != "" {
|
|
t.Fatalf("got %q, %q", v, none)
|
|
}
|
|
gate.Assignment("ace.mesh-build-node", nil, true)
|
|
if v, _ := agreedOf(set); v != "v26.12.1" {
|
|
t.Fatalf("got %q", v)
|
|
}
|
|
// A report deleted from the bucket goes whether its node is assigned or not.
|
|
key, _ = gateReport(t, "g14", "v26.12.1")
|
|
if !gate.Toolchain(buildrun.NodeBuildModule, key, nil, true) {
|
|
t.Fatal("deleting g14's report passed nothing on")
|
|
}
|
|
if n := len(set.Entries("typescript")); n != 2 {
|
|
t.Fatalf("novox and shanks are left; got %d entries", n)
|
|
}
|
|
}
|
|
|
|
// A newly assigned build node that has not reported yet changes nothing; its report counts as it comes.
|
|
func TestANewlyAssignedNodeCountsFromItsReport(t *testing.T) {
|
|
set := &buildrun.ToolchainSet{}
|
|
gate := newToolchainGate([]string{buildrun.NodeBuildModule}, set.Put, func(string, ...any) {})
|
|
gate.Settled()
|
|
if gate.Assignment("g14.mesh-build-node", []byte(`{}`), false) {
|
|
t.Fatal("an assignment with no report passed something on")
|
|
}
|
|
key, value := gateReport(t, "g14", "v26.12.1")
|
|
if !gate.Toolchain(buildrun.NodeBuildModule, key, value, false) {
|
|
t.Fatal("an assigned node's report was not passed on")
|
|
}
|
|
key, value = gateReport(t, "ace", "v26.12.0")
|
|
if gate.Toolchain(buildrun.NodeBuildModule, key, value, false) {
|
|
t.Fatal("a report of a node with no assignment key was passed on after the assignments settled")
|
|
}
|
|
if v, none := agreedOf(set); v != "v26.12.1" || none != "" {
|
|
t.Fatalf("got %q, %q", v, none)
|
|
}
|
|
}
|