The builder's published package reached the bus library through buildstep, whose KeptBuildsOf took mesh-sdk's buscalls.ObjectInfo, and buscalls now imports nats.go for its seat chunks. buildstep's layouts, keys and codecs are read by modules that never touch the bus (its own guard), so the kept build of one object is judged from the object's own fields (KeptBuildOf, SortKept) and the build module lists its store itself. The guard now also refuses a bus library reached through an import, the kind that let this in. Builds: design 47 G (join 11 area 4: builds TestThePackageReachesNoBus, buildstep's bus-free guard) Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
149 lines
6.0 KiB
Go
149 lines
6.0 KiB
Go
package buildstep
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// **The artifact store's holder takes its own new build even when its seat is the one broken** (novox/hq design 47,
|
|
// lines 1888-1906 at b33ac325; test 60): when the seat oci-artifact-registry refuses or does not answer the store of a
|
|
// trunk build of a module that claims that seat, the build module keeps that build on the bus, one object per build
|
|
// named by its digest, and the node-engine composing the holder asks the build seats' verb kept-artifact where it is.
|
|
// The judgement the three build modules share is here (ADR 0634 §2): what may be kept, the kept builds a node holds,
|
|
// and the verb's answer with its three refusals. The store and the verb's serving are each build module's own.
|
|
|
|
// StoreSeat is the seat whose holder's builds a build node keeps.
|
|
const StoreSeat = "oci-artifact-registry"
|
|
|
|
// The refusals of kept-artifact, the words of the part build-step's enum.
|
|
const (
|
|
KeptRefusedClaimsNoStore = "claims no " + StoreSeat
|
|
KeptRefusedNotKept = "not kept"
|
|
KeptRefusedDigestDiffers = "digest differs"
|
|
)
|
|
|
|
// CatalogModuleVerb is the seat mesh-catalog's verb that answers one module's manifest, as a manifest invokes it.
|
|
const CatalogModuleVerb = "seat:mesh-catalog.module"
|
|
|
|
// KeptFor is what a build keeps a refused artifact for: the module, which claims the store's seat, and its build's
|
|
// fingerprint, which kept-artifact is asked by.
|
|
type KeptFor struct{ Module, Fp string }
|
|
|
|
// KeepOf is the keep of a build, or nil when nothing may be kept: only a trunk build (publish) of a module whose manifest
|
|
// at the built commit claims the store's seat, named by its fingerprint.
|
|
func KeepOf(publish bool, manifest catalogue.Manifest, fp string) *KeptFor {
|
|
if !publish || fp == "" || !manifest.ClaimsSeat(StoreSeat) {
|
|
return nil
|
|
}
|
|
return &KeptFor{Module: manifest.Module, Fp: fp}
|
|
}
|
|
|
|
// KeptBuild is one kept build: the object's description names its module, fingerprint and the node that kept it.
|
|
type KeptBuild struct {
|
|
Module string `json:"module"`
|
|
Fp string `json:"fp"`
|
|
Node string `json:"node"`
|
|
Digest string `json:"digest"`
|
|
Bytes uint64 `json:"bytes"`
|
|
LastUsed time.Time `json:"last-used"`
|
|
}
|
|
|
|
// KeptObject is the object name (the hex digest of "sha256:<hex>") and description a build is kept under on node.
|
|
func KeptObject(k KeptFor, node, digest string) (name, description string) {
|
|
desc, _ := json.Marshal(KeptBuild{Module: k.Module, Fp: k.Fp, Node: node})
|
|
return strings.TrimPrefix(digest, "sha256:"), string(desc)
|
|
}
|
|
|
|
// KeptBuildOf is the build node keeps under one object of a store, by its name (the digest), description, size and last change,
|
|
// or false when the object names none of node's. The object's form is the build module's store's: this package reads no bus.
|
|
func KeptBuildOf(name, description string, size uint64, modified time.Time, node string) (KeptBuild, bool) {
|
|
var k KeptBuild
|
|
if json.Unmarshal([]byte(description), &k) != nil || k.Node != node {
|
|
return KeptBuild{}, false
|
|
}
|
|
k.Digest, k.Bytes, k.LastUsed = name, size, modified.UTC()
|
|
return k, true
|
|
}
|
|
|
|
// SortKept orders kept builds by digest, the order kept-artifact's readers list them in.
|
|
func SortKept(kept []KeptBuild) {
|
|
slices.SortFunc(kept, func(a, b KeptBuild) int { return strings.Compare(a.Digest, b.Digest) })
|
|
}
|
|
|
|
// ClaimsStoreOf reads the seat mesh-catalog's answer of one module (CatalogModuleVerb): whether its manifest claims the
|
|
// store's seat; a module the catalog holds no manifest of claims nothing.
|
|
func ClaimsStoreOf(module string, raw []byte) (bool, error) {
|
|
var a struct {
|
|
Module struct {
|
|
Manifest json.RawMessage `json:"manifest"`
|
|
} `json:"module"`
|
|
}
|
|
if err := json.Unmarshal(raw, &a); err != nil {
|
|
return false, fmt.Errorf("%s answered what is no module: %w", CatalogModuleVerb, err)
|
|
}
|
|
if len(a.Module.Manifest) == 0 {
|
|
return false, nil
|
|
}
|
|
man, err := catalogue.Decode(a.Module.Manifest)
|
|
if err != nil {
|
|
return false, fmt.Errorf("the manifest of %s: %w", module, err)
|
|
}
|
|
return man.ClaimsSeat(StoreSeat), nil
|
|
}
|
|
|
|
// KeptArtifactAnswer is the verb kept-artifact's answer, the part build-step's layout.
|
|
type KeptArtifactAnswer struct {
|
|
Module string `json:"module"`
|
|
Fp string `json:"fp"`
|
|
Digest string `json:"digest,omitempty"`
|
|
Size int64 `json:"size,omitempty"`
|
|
Store string `json:"store,omitempty"`
|
|
Object string `json:"object,omitempty"`
|
|
Refused string `json:"refused,omitempty"`
|
|
Words string `json:"words,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// KeptArtifact answers kept-artifact for module, fp and the hex digest from the builds this node keeps, in store (the
|
|
// store's name on the bus); claims asks the seat mesh-catalog whether a module claims the store's seat, only when no
|
|
// build of it is kept here at that digest.
|
|
func KeptArtifact(module, fp, digest string, kept []KeptBuild, store string, claims func(module string) (bool, error)) KeptArtifactAnswer {
|
|
a := KeptArtifactAnswer{Module: module, Fp: fp}
|
|
var other *KeptBuild
|
|
for i, k := range kept {
|
|
if k.Module != module || k.Fp != fp {
|
|
continue
|
|
}
|
|
if k.Digest == digest {
|
|
a.Digest, a.Size, a.Object = k.Digest, int64(k.Bytes), k.Digest
|
|
if store == "" {
|
|
a.Error = "this module runs with no name of its own (MESH_MODULE), so its store's name on the bus is unknown"
|
|
return a
|
|
}
|
|
a.Store = store
|
|
return a
|
|
}
|
|
other = &kept[i]
|
|
}
|
|
ok, err := claims(module)
|
|
switch {
|
|
case err != nil:
|
|
a.Error = "asking whether " + module + " claims " + StoreSeat + ": " + err.Error()
|
|
case !ok:
|
|
a.Refused = KeptRefusedClaimsNoStore
|
|
a.Words = fmt.Sprintf("%s claims no %s: a build node keeps builds for that seat's holder alone", module, StoreSeat)
|
|
case other != nil:
|
|
a.Refused, a.Digest = KeptRefusedDigestDiffers, other.Digest
|
|
a.Words = fmt.Sprintf("this node keeps the build of %s at %s under the digest %s, not %s", module, fp, other.Digest, digest)
|
|
default:
|
|
a.Refused = KeptRefusedNotKept
|
|
a.Words = fmt.Sprintf("this node keeps no build of %s at %s (never kept, or swept)", module, fp)
|
|
}
|
|
return a
|
|
}
|