Move no module whose fingerprint is unchanged, and let a key's build stand on the held build-agent build of the same digests at the switch (issue 1851, A9)

This commit is contained in:
2026-10-22 19:05:04 +02:00
parent 7743bd0d4b
commit 9da79ef8c3
8 changed files with 208 additions and 189 deletions
+47 -1
View File
@@ -2,6 +2,8 @@ package inventory
import (
"context"
"encoding/json"
"strings"
"github.com/novox/mesh-controller/internal/chain"
)
@@ -43,6 +45,11 @@ func (i *Inventory) sourceRows(ctx context.Context, module string) (map[string][
// standing is, of a module's builds newest first, the index of the build that stands for the build at
// `from`: the oldest of the unbroken run of builds behind it with its source fingerprint.
//
// **Across the switch to the new form** (novox/hq ADR 0453 §6, ADR 0499; R4e A9): a build of the new form (`src2:`, taken
// from the builder's key) continues the run of the build before it of the old form (`src1:` or none, build-agent's)
// when every artifact of the two has the same digest: the fingerprints cannot be compared, the programs can, and equal
// digests are no move. Past it the run goes on by the old fingerprint.
func standing(builds []sourceRow, from int) int {
at := from
fp := builds[from].fingerprint
@@ -50,14 +57,53 @@ func standing(builds []sourceRow, from int) int {
return at
}
for j := from + 1; j < len(builds); j++ {
if builds[j].fingerprint != fp || builds[j].failedGate {
if builds[j].failedGate {
break
}
if builds[j].fingerprint != fp {
if !newForm(fp) || newForm(builds[j].fingerprint) || !sameDigests(builds[at].made, builds[j].made) {
break
}
fp = builds[j].fingerprint
}
at = j
}
return at
}
// newForm says a source fingerprint is of the new form (ADR 0453 §6).
func newForm(fp string) bool { return strings.HasPrefix(fp, "src2:") }
// sameDigests says two builds' artifacts, as recorded (JSON of []Artifact), are the same programs: the same names, each
// with the same digest, wherever each was pushed. A build naming an artifact with no digest is never the same.
func sameDigests(a, b []byte) bool {
digests := func(raw []byte) map[string]string {
var made []Artifact
if json.Unmarshal(raw, &made) != nil || len(made) == 0 {
return nil
}
out := map[string]string{}
for _, m := range made {
d := digestIn(m.Reference)
if d == "" {
return nil
}
out[m.Name] = d
}
return out
}
da, db := digests(a), digests(b)
if da == nil || db == nil || len(da) != len(db) {
return false
}
for name, d := range da {
if db[name] != d {
return false
}
}
return true
}
// StandingBuild is the build that stands for a module's build (novox/hq issue 280): the oldest build of
// the unbroken run of builds with its source fingerprint, at or before it — itself when its fingerprint
// is empty or it starts the run. Answers its id and the manifest it was recorded with; empty when the
@@ -0,0 +1,49 @@
package inventory
import (
"encoding/json"
"testing"
)
func madeOf(t *testing.T, refs map[string]string) []byte {
t.Helper()
var made []Artifact
for name, ref := range refs {
made = append(made, Artifact{Name: name, Kind: "bundle", Reference: ref})
}
raw, err := json.Marshal(made)
if err != nil {
t.Fatal(err)
}
return raw
}
// R4e A9 (ADR 0453 §6, ADR 0499): at the switch, a build taken from the builder's key (`src2:`) whose every artifact has
// the digest of the build-agent build before it (`src1:`) is no move: it stands on that build, wherever each was pushed.
// One digest that differs is a move, as is a build of the old form after one of the new.
func TestAKeysBuildWithTheOldBuildsDigestsIsNoMove(t *testing.T) {
const d1, d2 = "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"sha256:2222222222222222222222222222222222222222222222222222222222222222"
old := sourceRow{id: "build-1", fingerprint: "src1:aa", made: madeOf(t, map[string]string{
"tools": "artifact-store://x/tools/blobs/" + d1, "img": "artifact-store://x/img@" + d2})}
key := sourceRow{id: "key-bb-1", fingerprint: "src2:bb", made: madeOf(t, map[string]string{
"tools": "artifact-store://x/tools/blobs/" + d1, "img": "registry.internal:5000/x/img@" + d2})}
if at := standing([]sourceRow{key, old}, 0); at != 1 {
t.Fatalf("the key's build with the same digests stands on %d, want the old build", at)
}
moved := key
moved.made = madeOf(t, map[string]string{"tools": "artifact-store://x/tools/blobs/" + d2, "img": "x/img@" + d2})
if at := standing([]sourceRow{moved, old}, 0); at != 0 {
t.Fatalf("a key's build with another digest stands on %d, want itself (a move)", at)
}
// Two key builds of one fingerprint are one run, as two builds of one fingerprint always were.
again := key
again.id = "key-bb-2"
if at := standing([]sourceRow{again, key, old}, 0); at != 2 {
t.Fatalf("a run of one new fingerprint on an old build of the same digests stands on %d", at)
}
// An old build after a new one never continues the new one's run.
if at := standing([]sourceRow{old, key}, 0); at != 0 {
t.Fatalf("an old build continues a new one's run: %d", at)
}
}