Compose each machine in the merge gate with its engine's contract, so moved modules are handed there as on the mesh (issue 969)
This commit is contained in:
@@ -287,6 +287,12 @@ func gatherFacts(ctx context.Context, open *stores, busVersion string) (snapshot
|
|||||||
return snapshot.Facts{}, err
|
return snapshot.Facts{}, err
|
||||||
}
|
}
|
||||||
m.Architecture, m.Kernel = reported.Architecture, reported.Kernel
|
m.Architecture, m.Kernel = reported.Architecture, reported.Kernel
|
||||||
|
// Whether its node-engine takes handed files is its contract (issue 969).
|
||||||
|
if stated, had, err := inv.HealthOf(ctx, n.Name); err != nil {
|
||||||
|
return snapshot.Facts{}, err
|
||||||
|
} else if had {
|
||||||
|
m.Contract = stated.Contract
|
||||||
|
}
|
||||||
outward, err := inv.OutwardLinksOf(ctx, n.Name)
|
outward, err := inv.OutwardLinksOf(ctx, n.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return snapshot.Facts{}, err
|
return snapshot.Facts{}, err
|
||||||
|
|||||||
@@ -1017,6 +1017,13 @@ func raiseFromFacts(ctx context.Context, open *stores, f snapshot.Facts, shelf m
|
|||||||
"kernel": m.Kernel, "capabilities": caps}); err != nil {
|
"kernel": m.Kernel, "capabilities": caps}); err != nil {
|
||||||
return notes, err
|
return notes, err
|
||||||
}
|
}
|
||||||
|
// What its node-engine takes, as the mesh composes it today (issue 969): without the statement the gate
|
||||||
|
// composes a moved module the old way on a machine the mesh hands it to.
|
||||||
|
if stated, ok := standInHealth(m, f.Taken); ok {
|
||||||
|
if _, err := inv.RecordHealth(ctx, stated); err != nil {
|
||||||
|
return notes, err
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, record := range []func(context.Context, string, string) error{inv.RecordSealingKey, inv.RecordOverlayKey} {
|
for _, record := range []func(context.Context, string, string) error{inv.RecordSealingKey, inv.RecordOverlayKey} {
|
||||||
key, err := standInKey()
|
key, err := standInKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1355,3 +1362,12 @@ func firstOr(items, otherwise []string) string {
|
|||||||
}
|
}
|
||||||
return "it says nothing more"
|
return "it says nothing more"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// standInHealth is the statement the gate gives a machine of the snapshot so that it composes as the mesh does:
|
||||||
|
// only its contract, which says whether it takes handed files (issue 969). False for a snapshot that carries none.
|
||||||
|
func standInHealth(m snapshot.Machine, taken time.Time) (inventory.NodeHealth, bool) {
|
||||||
|
if m.Contract == 0 {
|
||||||
|
return inventory.NodeHealth{}, false
|
||||||
|
}
|
||||||
|
return inventory.NodeHealth{Node: m.Name, Contract: m.Contract, SaidAt: taken, HeardAt: taken}, true
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
snapshot "github.com/novox/mesh-controller/internal/facts"
|
||||||
|
"github.com/novox/mesh-controller/internal/link"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Issue 969: the gate composes a machine as the mesh does, so a moved module is handed where the machine takes
|
||||||
|
// handed files and composed the old way where it does not.
|
||||||
|
func TestIssue969TheGateGivesAMachineItsContractSoItTakesHandedFiles(t *testing.T) {
|
||||||
|
taken := time.Date(2026, 10, 16, 12, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
stated, ok := standInHealth(snapshot.Machine{Name: "f35", Contract: link.HandedContract}, taken)
|
||||||
|
if !ok || !takesHanded(stated, ok) {
|
||||||
|
t.Fatalf("a machine whose engine takes handed files must be given that in the gate: %+v, %v", stated, ok)
|
||||||
|
}
|
||||||
|
old, ok := standInHealth(snapshot.Machine{Name: "e12", Contract: link.HandedContract - 1}, taken)
|
||||||
|
if !ok || takesHanded(old, ok) {
|
||||||
|
t.Fatalf("a machine whose engine is older must not take handed files: %+v, %v", old, ok)
|
||||||
|
}
|
||||||
|
none, ok := standInHealth(snapshot.Machine{Name: "d01"}, taken)
|
||||||
|
if ok || takesHanded(none, ok) {
|
||||||
|
t.Fatalf("a snapshot that carries no contract states nothing: %+v, %v", none, ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1126,7 +1126,13 @@ func engineTakesHanded(ctx context.Context, inv *inventory.Inventory, node strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return had && stated.Contract >= link.HandedContract, nil
|
return takesHanded(stated, had), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// takesHanded is whether a machine's newest statement (had: it ever stated one) is of an engine that writes a
|
||||||
|
// filled file on the provisioner client's word alone.
|
||||||
|
func takesHanded(stated inventory.NodeHealth, had bool) bool {
|
||||||
|
return had && stated.Contract >= link.HandedContract
|
||||||
}
|
}
|
||||||
|
|
||||||
// provisionerAccount is the account the provisioner's client runs as on this machine (novox/hq ADR 0349 §4): the user
|
// provisionerAccount is the account the provisioner's client runs as on this machine (novox/hq ADR 0349 §4): the user
|
||||||
|
|||||||
@@ -112,6 +112,10 @@ type Machine struct {
|
|||||||
// NodeEngine and NodeTools are the builds it runs.
|
// NodeEngine and NodeTools are the builds it runs.
|
||||||
NodeEngine string `json:"node-engine,omitempty"`
|
NodeEngine string `json:"node-engine,omitempty"`
|
||||||
NodeTools string `json:"node-tools,omitempty"`
|
NodeTools string `json:"node-tools,omitempty"`
|
||||||
|
// Contract is the contract of the node-engine's newest health statement (0 when it never stated one): what
|
||||||
|
// says whether the machine takes handed files (novox/hq issue 969). A check that composed it without would
|
||||||
|
// judge a moved module the old way where the mesh hands it.
|
||||||
|
Contract int `json:"contract,omitempty"`
|
||||||
// Capabilities are what it reported it can do; the detail kept only where it is a version.
|
// Capabilities are what it reported it can do; the detail kept only where it is a version.
|
||||||
Capabilities []Capability `json:"capabilities,omitempty"`
|
Capabilities []Capability `json:"capabilities,omitempty"`
|
||||||
// Site, Hub, Public: where it is on the private network — its site (a pseudonym), whether it is the
|
// Site, Hub, Public: where it is on the private network — its site (a pseudonym), whether it is the
|
||||||
|
|||||||
Reference in New Issue
Block a user