GIT12: the forge module's package registry checks a publish against the builder's reservation, asked by the package's name
The holder asks the seat mesh-builds' verb reserved with the publish's name and version in place of builder_versions, and stores only a version reserved for an artifact of the publish's digest; the manifest invokes the verb and takes no builder package. Builds: design 47 G §381, §478, §504 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -450,12 +450,12 @@ func TestToolsAreTheOnesTheReadmeNamesAndSayWhatTheyDo(t *testing.T) {
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// The builder's question is its published package, taken by its series (ADR 0634 §1).
|
||||
if m.Packages["git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"] != "0.1" {
|
||||
t.Errorf("packages %v: want builds of series 0.1", m.Packages)
|
||||
// The reservation is asked through the seat mesh-builds, so the manifest takes no package of the builder (design 47 §381).
|
||||
if len(m.Packages) != 0 {
|
||||
t.Errorf("packages %v: want none", m.Packages)
|
||||
}
|
||||
// Both package seats are claimed serving the four verbs this bundle answers as <seat>.<verb>, over its own incoming
|
||||
// and transfers, asking the builder's numbering (design 51 *Publishing*).
|
||||
// and transfers, asking the builder's reservation (design 51 *Publishing*, design 47 §381).
|
||||
claimed := map[string]string{}
|
||||
for _, c := range m.Claims {
|
||||
claimed[c.Name] = strings.Join(c.Serves, ",")
|
||||
@@ -488,7 +488,7 @@ func TestToolsAreTheOnesTheReadmeNamesAndSayWhatTheyDo(t *testing.T) {
|
||||
t.Errorf("transfers has options (a lifetime): %s", req)
|
||||
}
|
||||
// The retention asks the builder's versions; the forge's other bundles ask the catalog half and record a re-read.
|
||||
if strings.Join(m.Invokes, ",") != "seat:mesh-catalog.revision,seat:mesh-catalog.registrations,seat:mesh-hand-acts.hand-act,mesh-delivery-builder.builder_versions" {
|
||||
if strings.Join(m.Invokes, ",") != "seat:mesh-catalog.revision,seat:mesh-catalog.registrations,seat:mesh-hand-acts.hand-act,seat:mesh-builds.reserved" {
|
||||
t.Errorf("the manifest invokes %v", m.Invokes)
|
||||
}
|
||||
if len(m.Tools) != 0 {
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -274,11 +273,11 @@ func recordAct(q meshhandactsclient.Request) error {
|
||||
// boundCap is incoming's cap from its binding, read once per bundle.
|
||||
var boundCap = parts.BusCap(packageseats.IncomingObjects)
|
||||
|
||||
// busHolder is the holder over the runtime's bus state, the builder's numbering and the forge.
|
||||
// busHolder is the holder over the runtime's bus state, the builder's reservation and the forge.
|
||||
func busHolder(forge func() (*Forge, error)) (*packageseats.Holder, error) {
|
||||
return &packageseats.Holder{Objects: parts.BusObjects(packageseats.IncomingObjects),
|
||||
Transfers: parts.BusTransfers[packageseats.Opening](packageseats.TransfersState),
|
||||
Builder: builds.BuilderVersions{Ask: stdio.Ask}, Store: seatStore{forge}, Say: say, Finding: finding,
|
||||
Builder: packageseats.Reserved{Ask: stdio.Ask}, Store: seatStore{forge}, Say: say, Finding: finding,
|
||||
Cap: boundCap, Record: recordAct, Idle: parts.BusArm(packageseats.IdleState)}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ package main
|
||||
// the built package-registry bundle, played over its real stdin and stdout as the node's tool runtime plays it, its
|
||||
// `incoming` an object store and its `transfers` and `findings` key-value buckets of a nats-server of this test alone
|
||||
// (so the publish in parts runs through mesh-sdk's parts.BusObjects and BusTransfers, ruling 4 of the lead's 14:0x), the
|
||||
// builder's builder_versions answered on mesh/ask, and the forge an
|
||||
// builder's reserved answered on mesh/ask, and the forge an
|
||||
// httptest server that takes reads only with the tools' token and writes only with the packages account's.
|
||||
//
|
||||
// A Go package the builder numbered goes up in two chunks, is uploaded whole as the packages account and says
|
||||
@@ -37,7 +37,6 @@ import (
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
||||
"git.novox.be/novox/mesh-sdk/go/testbus"
|
||||
|
||||
"gitea/internal/packageseats"
|
||||
@@ -117,11 +116,15 @@ func (p *player) handle(m wire) {
|
||||
}
|
||||
_ = json.Unmarshal(m.Params, &a)
|
||||
switch {
|
||||
case m.Method == "mesh/ask" && a.Key == builds.BuilderVersionsAddress:
|
||||
var q struct{ Name, Version, Digest string }
|
||||
case m.Method == "mesh/ask" && a.Key == packageseats.Address:
|
||||
var q struct{ Name, Version string }
|
||||
_ = json.Unmarshal(a.Body, &q)
|
||||
d, ok := p.numbered[q.Name+"@"+q.Version]
|
||||
p.answer(m.ID, map[string]any{"recorded": ok && d == q.Digest, "why": "not numbered with that digest"}, nil)
|
||||
if !ok {
|
||||
p.answer(m.ID, nil, errors.New("no version of "+q.Name+" is reserved"))
|
||||
break
|
||||
}
|
||||
p.answer(m.ID, map[string]any{"module": "m", "version": q.Version, "artifacts": []map[string]any{{"artifact": "m:a", "digest": d}}}, nil)
|
||||
case m.Method == "mesh/seat.publish":
|
||||
p.answer(m.ID, map[string]any{}, p.nc.Publish(a.Subject, a.Body))
|
||||
case m.Method == "mesh/publish":
|
||||
@@ -395,12 +398,12 @@ func TestThePackageSeatsWhole(t *testing.T) {
|
||||
t.Errorf("an ended transfer is still kept: %v", keys)
|
||||
}
|
||||
|
||||
// One the builder did not number: refused before the forge sees it, said, and the seat's finding written.
|
||||
// One the builder did not reserve: refused before the forge sees it, said, and the seat's finding written.
|
||||
other := bigZip(t, "git.example/acme/sdk@v0.1.16")
|
||||
od := "sha256:" + sha(other)
|
||||
transfer, _ = publish("0.1.16", od, len(other))
|
||||
_ = send(transfer, od, other)
|
||||
if o := event("refused"); !strings.Contains(fmt.Sprint(o["reason"]), "did not number") {
|
||||
if o := event("refused"); !strings.Contains(fmt.Sprint(o["reason"]), "no version of git.example/acme/sdk is reserved") {
|
||||
t.Errorf("refused says %v", o)
|
||||
}
|
||||
findings, _ := js.KeyValue(ctx, "gitea_findings")
|
||||
|
||||
@@ -8,8 +8,8 @@ require (
|
||||
git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-contracts/provisioning/go/clients/meshprovisionerclient v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/gopackageregistry v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/contracts/go/seats/npmpackageregistry v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-delivery/sdk v0.0.0-00010101000000-000000000000
|
||||
git.novox.be/novox/mesh-sdk/go v0.0.0-00010101000000-000000000000
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
|
||||
@@ -108,7 +108,7 @@ type Version struct {
|
||||
Published time.Time
|
||||
}
|
||||
|
||||
// Builder answers whether the builder numbered a version with a digest (the builder package's builds.BuilderVersions).
|
||||
// Builder answers whether the builder reserved a version with a digest (Reserved).
|
||||
type Builder interface {
|
||||
Recorded(name, version, digest string) (ok bool, why string, err error)
|
||||
}
|
||||
@@ -165,14 +165,14 @@ func key(seat, transfer string) string { return seat + "_" + transfer }
|
||||
func (h *Holder) receiver() *parts.Receiver[Opening] {
|
||||
return &parts.Receiver[Opening]{Objects: h.Objects, Transfers: h.Transfers, Cap: IncomingCap, Bound: h.Cap,
|
||||
MaxChunk: MaxChunk, Idle: TransferIdle, Arm: h.Idle, Full: IncomingFullPct, Now: h.now,
|
||||
// The builder's numbering is asked before anything is uploaded (ADR 0605 §4).
|
||||
// The builder's reservation is asked before anything is uploaded (ADR 0605 §4, design 47 §381).
|
||||
Check: func(t Transfer) error {
|
||||
ok, why, err := h.Builder.Recorded(t.Shape.Name, t.Shape.Version, t.Shape.Digest)
|
||||
if err != nil {
|
||||
return errors.New("builder_versions did not answer, so nothing is stored: " + err.Error())
|
||||
return errors.New("the reservation could not be read, so nothing is stored: " + err.Error())
|
||||
}
|
||||
if !ok {
|
||||
return errors.New("the builder did not number it: " + why)
|
||||
return errors.New("the builder did not reserve it: " + why)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -252,7 +252,7 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) {
|
||||
numbered bool
|
||||
data []byte
|
||||
want string
|
||||
}{{"the builder did not number it", false, file, "did not number"}} {
|
||||
}{{"the builder did not reserve it", false, file, "did not reserve"}} {
|
||||
b := newBed(c.numbered)
|
||||
if _, err := b.h.Publish(NpmSeat, nodeBuild, pub("x", "0.1.0", file)); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package packageseats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds"
|
||||
)
|
||||
|
||||
// Reserved is the holder's check of a publish against the builder's reservation (novox/hq design 47 §381, §478, §504): it asks the
|
||||
// seat mesh-builds' verb reserved with the publish's name and version, a package's name in its ecosystem, and accepts the publish
|
||||
// only when that version is the one reserved and an artifact of it has the publish's digest. It reads no catalog and a publish gains
|
||||
// no field. It is the Builder the holder's Check asks.
|
||||
type Reserved struct {
|
||||
// Ask is the runtime's request and reply over the bus (mesh-sdk stdio.Ask).
|
||||
Ask func(address string, body any) (json.RawMessage, error)
|
||||
}
|
||||
|
||||
// Address is the seat verb asked, as the manifest's `invokes` names it.
|
||||
const Address = "seat:" + meshbuilds.Seat + "." + meshbuilds.VerbReserved
|
||||
|
||||
// Recorded answers whether name@version with digest is reserved, and why not when it is not. An answer that cannot be read is an
|
||||
// error, never a yes; a refusal by the builder (a name no entry carries, a version it did not reserve) is a no that names it.
|
||||
func (r Reserved) Recorded(name, version, digest string) (bool, string, error) {
|
||||
raw, err := r.Ask(Address, meshbuilds.ReservedInput{Name: name, Version: version})
|
||||
if err != nil {
|
||||
return false, "", fmt.Errorf("%s did not answer for %s@%s: %w", Address, name, version, err)
|
||||
}
|
||||
var a meshbuilds.ReservedAnswer
|
||||
if err := json.Unmarshal(raw, &a); err != nil || a.Version == "" {
|
||||
return false, "", fmt.Errorf("%s answered no reservation for %s@%s: %s", Address, name, version, raw)
|
||||
}
|
||||
if a.Version != version {
|
||||
return false, fmt.Sprintf("%s reserved %s for %s, not %s", a.Module, a.Version, name, version), nil
|
||||
}
|
||||
for _, art := range a.Artifacts {
|
||||
if art.Digest == digest {
|
||||
return true, "", nil
|
||||
}
|
||||
}
|
||||
return false, fmt.Sprintf("%s reserved %s@%s for another digest than %s", a.Module, name, version, digest), nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package packageseats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// A publish is checked against the builder's reservation by the seat mesh-builds' verb reserved, asked with the package's name
|
||||
// (design 47 §381, §478).
|
||||
func TestReservedChecksAPublishAgainstTheReservation(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
answer string
|
||||
err error
|
||||
ok bool
|
||||
why string // part of the reason, or of the error
|
||||
}{
|
||||
{name: "the reserved version and digest", answer: `{"module":"mesh-sdk","version":"0.1.19","artifacts":[{"artifact":"mesh-sdk:ts","digest":"sha256:aa"}]}`, ok: true},
|
||||
{name: "another artifact of it carries the digest", answer: `{"module":"mesh-sdk","version":"0.1.19","artifacts":[{"artifact":"mesh-sdk:go"},{"artifact":"mesh-sdk:ts","digest":"sha256:aa"}]}`, ok: true},
|
||||
{name: "another digest", answer: `{"module":"mesh-sdk","version":"0.1.19","artifacts":[{"artifact":"mesh-sdk:ts","digest":"sha256:bb"}]}`, why: "another digest than sha256:aa"},
|
||||
{name: "another version reserved", answer: `{"module":"mesh-sdk","version":"0.1.20","artifacts":[]}`, why: "reserved 0.1.20 for @novox/mesh-sdk, not 0.1.19"},
|
||||
{name: "the builder refused the name", err: errors.New("no entry carries @novox/mesh-sdk"), why: "no entry carries @novox/mesh-sdk"},
|
||||
{name: "an answer that is none", answer: `{"error":"x"}`, why: "answered no reservation"},
|
||||
} {
|
||||
asked := ""
|
||||
r := Reserved{Ask: func(address string, body any) (json.RawMessage, error) {
|
||||
b, _ := json.Marshal(body)
|
||||
asked = address + " " + string(b)
|
||||
return json.RawMessage(c.answer), c.err
|
||||
}}
|
||||
ok, why, err := r.Recorded("@novox/mesh-sdk", "0.1.19", "sha256:aa")
|
||||
if asked != `seat:mesh-builds.reserved {"name":"@novox/mesh-sdk","version":"0.1.19"}` {
|
||||
t.Fatalf("%s: asked %s", c.name, asked)
|
||||
}
|
||||
if c.ok {
|
||||
if !ok || err != nil {
|
||||
t.Errorf("%s: not accepted: %v %v", c.name, why, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ok || !strings.Contains(why+fmtErr(err), c.why) {
|
||||
t.Errorf("%s: ok=%v why=%q err=%v, want %q", c.name, ok, why, err, c.why)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fmtErr(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"module": "gitea",
|
||||
"version": "1",
|
||||
"packages": {
|
||||
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds": "0.1"
|
||||
},
|
||||
"moved": true,
|
||||
"requires": [
|
||||
"postgres-database",
|
||||
@@ -651,7 +648,7 @@
|
||||
"seat:mesh-catalog.revision",
|
||||
"seat:mesh-catalog.registrations",
|
||||
"seat:mesh-hand-acts.hand-act",
|
||||
"mesh-delivery-builder.builder_versions"
|
||||
"seat:mesh-builds.reserved"
|
||||
],
|
||||
"consumes": [
|
||||
"mesh-catalog.catalog-revision",
|
||||
|
||||
Reference in New Issue
Block a user