CAT-REG: distribution's references name the seat as their registry part
The reach came from MESH_REGISTRY_REACH, which nothing sets, so every published event answered /<name>@<digest>. The store answers the path; the holder puts the seat's name before it, as catalogue.InRegistrySeat reads it back (ADR 0605 §9; lead's ruling on A3n). Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -473,7 +473,7 @@ func tools(v *holder.Verbs, windows kept[holder.Window], callers func() (holder.
|
||||
|
||||
func main() {
|
||||
base, _ := url.Parse("http://127.0.0.1:5001")
|
||||
store := &holder.OCI{Base: base, Registry: os.Getenv("MESH_REGISTRY_REACH"), Upstream: &holder.Upstream{}}
|
||||
store := &holder.OCI{Base: base, Upstream: &holder.Upstream{}}
|
||||
h := &holder.Holder{Objects: parts.BusObjects(holder.IncomingObjects), Builder: delivery.BuilderVersions{Ask: stdio.Ask}, Store: store, Say: say, Finding: finding,
|
||||
Transfers: parts.BusTransfers[holder.Opening](holder.TransfersState)}
|
||||
v := &holder.Verbs{Holder: h, Catalog: store, Holds: kept[holder.Hold]{state: "holds", key: holder.Hold.Key}, Mirrors: mirrors{}}
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
)
|
||||
|
||||
type memObjects map[string][]byte
|
||||
@@ -64,7 +66,7 @@ type fakeStore struct {
|
||||
func (s *fakeStore) Has(_, d string) (bool, error) { return s.has[d], nil }
|
||||
func (s *fakeStore) Put(t PublishRequest, parts map[string][]byte) (string, error) {
|
||||
s.put = append(s.put, parts)
|
||||
return "registry.mesh/" + t.Name + "@" + t.Digest, nil
|
||||
return t.Name + "@" + t.Digest, nil
|
||||
}
|
||||
func (s *fakeStore) Held(string, string, string) (string, bool, error) { return "", false, nil }
|
||||
|
||||
@@ -118,7 +120,7 @@ func (c *fakeCatalog) Mirror(source, digest string) (string, error) {
|
||||
return c.Reference(src.Name(), digest), nil
|
||||
}
|
||||
func (c *fakeCatalog) Reference(name, digest string) string {
|
||||
return "registry.mesh/" + name + "@" + digest
|
||||
return name + "@" + digest
|
||||
}
|
||||
|
||||
type memMirrors map[string]MirrorRequest
|
||||
@@ -213,6 +215,11 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
if err != nil || !a.Ended || len(b.said) != 1 || b.said[0].event != EventPublished || b.said[0].body.Reference == "" {
|
||||
t.Fatalf("last chunk = %+v, %v; said %+v", a, err, b.said)
|
||||
}
|
||||
// The reference's registry part is the seat's name, as a node-engine reads it back (ADR 0605 §9); nothing set on
|
||||
// the process names a reach.
|
||||
if seat, path, ok := catalogue.InRegistrySeat(b.said[0].body.Reference); !ok || seat != ArtifactSeat || path != "m/program@"+part.Digest {
|
||||
t.Errorf("the published reference %q is not the seat's form", b.said[0].body.Reference)
|
||||
}
|
||||
if len(b.h.Objects.(memObjects)) != 0 || len(b.h.Transfers.(memTransfers)) != 0 {
|
||||
t.Error("the transfer's chunks or key stayed after its end")
|
||||
}
|
||||
@@ -426,14 +433,14 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
"manifests": []map[string]any{{"mediaType": mediaManifest, "digest": digestOf(img), "size": len(img)}}})
|
||||
up.manifests[repo+":"+digestOf(idx)] = idx
|
||||
base, _ := url.Parse(sts.URL)
|
||||
o := &OCI{Base: base, Registry: "registry.mesh:5000", Upstream: &Upstream{Scheme: "http"}}
|
||||
o := &OCI{Base: base, Upstream: &Upstream{Scheme: "http"}}
|
||||
name := "upstream/" + host + "/" + repo
|
||||
store.blobs[name+"@"+digestOf(config)] = config // held already: not fetched again
|
||||
ref, err := o.Mirror(host+"/"+repo+":2", digestOf(idx))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := "registry.mesh:5000/" + name + "@" + digestOf(idx); ref != want {
|
||||
if want := name + "@" + digestOf(idx); ref != want {
|
||||
t.Errorf("reference %s, want %s", ref, want)
|
||||
}
|
||||
if store.manifests[name+":"+digestOf(idx)] == nil || store.manifests[name+":"+digestOf(img)] == nil || store.blobs[name+"@"+digestOf(layer)] == nil {
|
||||
@@ -460,7 +467,7 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
b := newBed(true)
|
||||
d := "sha256:" + strings.Repeat("c", 64)
|
||||
ref, err := b.v.Mirror(ImageSeat, "novox.mesh-build-image", MirrorRequest{Source: "docker.io/library/nats:2", Digest: d})
|
||||
want := "registry.mesh/upstream/docker.io/library/nats@" + d
|
||||
want := "container-image-registry/upstream/docker.io/library/nats@" + d
|
||||
key := MirrorsKey("upstream/docker.io/library/nats", d)
|
||||
asked := b.v.Mirrors.(memMirrors)
|
||||
if err != nil || ref != want || len(b.said) != 0 || asked[key].By != "novox.mesh-build-image" {
|
||||
|
||||
@@ -298,5 +298,6 @@ func (o *OCI) Mirror(source, digest string) (string, error) {
|
||||
return o.Reference(name, digest), nil
|
||||
}
|
||||
|
||||
// Reference is the seat's answer for name at digest: `<registry>/<name>@<digest>`.
|
||||
func (o *OCI) Reference(name, digest string) string { return o.Registry + "/" + name + "@" + digest }
|
||||
// Reference is the path of the seat's answer for name at digest: `<name>@<digest>`, the seat's name put before it by
|
||||
// the holder (SeatReference).
|
||||
func (o *OCI) Reference(name, digest string) string { return name + "@" + digest }
|
||||
|
||||
@@ -12,12 +12,11 @@ import (
|
||||
)
|
||||
|
||||
// OCI is the artifact store as the OCI distribution protocol answers it on its node's loopback, written by
|
||||
// distribution's own process alone: the Store and the Catalog the verbs use. Registry is the reach the seat answers in a
|
||||
// reference (`<registry>/<module>/<artifact>@sha256:<digest>`, ADR 0605 §9).
|
||||
// distribution's own process alone: the Store and the Catalog the verbs use. It answers a reference's path,
|
||||
// `<name>@sha256:<digest>`; the holder puts the seat's name before it (SeatReference, ADR 0605 §9).
|
||||
type OCI struct {
|
||||
Base *url.URL
|
||||
Registry string
|
||||
Client *http.Client
|
||||
Base *url.URL
|
||||
Client *http.Client
|
||||
// Upstream reads the outside registries `mirror` copies from.
|
||||
Upstream *Upstream
|
||||
}
|
||||
@@ -162,7 +161,7 @@ func (o *OCI) Put(t PublishRequest, parts map[string][]byte) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return o.Registry + "/" + t.Name + "@" + d, nil
|
||||
return t.Name + "@" + d, nil
|
||||
default:
|
||||
if err := o.putBlob(t.Name, "sha256:"+sum([]byte(emptyConfig)), []byte(emptyConfig)); err != nil {
|
||||
return "", err
|
||||
@@ -184,7 +183,7 @@ func (o *OCI) Put(t PublishRequest, parts map[string][]byte) (string, error) {
|
||||
if _, err := o.putManifest(t.Name, ref, mediaManifest, m); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return o.Registry + "/" + t.Name + "@" + t.Digest, nil
|
||||
return t.Name + "@" + t.Digest, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +196,7 @@ func (o *OCI) Held(name, version, digest string) (string, bool, error) {
|
||||
if d != digest {
|
||||
return "", false, nil
|
||||
}
|
||||
return o.Registry + "/" + name + "@" + d, true, nil
|
||||
return name + "@" + d, true, nil
|
||||
}
|
||||
|
||||
// Versions are name's tags.
|
||||
|
||||
@@ -128,13 +128,18 @@ type Builder interface {
|
||||
type Store interface {
|
||||
// Has says whether the store holds a blob of name.
|
||||
Has(name, digest string) (bool, error)
|
||||
// Put stores a publish's parts under its name and version and answers the reference.
|
||||
// Put stores a publish's parts under its name and version and answers the reference's path (SeatReference).
|
||||
Put(r PublishRequest, parts map[string][]byte) (reference string, err error)
|
||||
// Held is the reference of name at version when the store already holds digest under it: a rebuild of a held
|
||||
// Held is the reference's path of name at version when the store already holds digest under it: a rebuild of a held
|
||||
// fingerprint answers it and stores nothing.
|
||||
Held(name, version, digest string) (reference string, ok bool, err error)
|
||||
}
|
||||
|
||||
// SeatReference is a reference in the form the seat answers (novox/hq ADR 0605 §9): the seat's name, then the path the
|
||||
// store answers (`<name>@sha256:<digest>`), as catalogue.InRegistrySeat reads it back. Nothing set on the process names
|
||||
// a reach: the seat's name is the registry part.
|
||||
func SeatReference(seat, path string) string { return seat + "/" + path }
|
||||
|
||||
// Say says an event on a seat's family `<seat>.events`.
|
||||
type Say func(seat, event string, body Event) error
|
||||
|
||||
@@ -194,7 +199,8 @@ func (h *Holder) receiver() *parts.Receiver[Opening] {
|
||||
return nil
|
||||
},
|
||||
Store: func(t Transfer, held map[string][]byte) error {
|
||||
ref, err := h.Store.Put(t.Shape.PublishRequest, held)
|
||||
path, err := h.Store.Put(t.Shape.PublishRequest, held)
|
||||
ref := SeatReference(t.Shape.Seat, path)
|
||||
if err != nil {
|
||||
reason := "the store did not take it: " + err.Error()
|
||||
return errors.Join(errors.New("refused: "+reason), h.refused(t, reason))
|
||||
@@ -238,7 +244,7 @@ func (h *Holder) Publish(seat, caller string, r PublishRequest) (PublishAnswer,
|
||||
if ref, ok, err := h.Store.Held(r.Name, r.Version, r.Digest); err != nil {
|
||||
return PublishAnswer{}, fmt.Errorf("publish: the store cannot say whether it holds %s@%s: %w", r.Name, r.Version, err)
|
||||
} else if ok {
|
||||
return PublishAnswer{Transfer: r.Transfer, Ended: true}, h.Say(seat, EventPublished, h.eventOf(seat, r, ref, ""))
|
||||
return PublishAnswer{Transfer: r.Transfer, Ended: true}, h.Say(seat, EventPublished, h.eventOf(seat, r, SeatReference(seat, ref), ""))
|
||||
}
|
||||
}
|
||||
wants := make([]parts.Want, 0, len(r.Parts))
|
||||
|
||||
@@ -268,7 +268,7 @@ func (v *Verbs) Mirror(seat, caller string, r MirrorRequest) (string, error) {
|
||||
if err := v.Mirrors.Ask(MirrorsKey(src.Name(), r.Digest), r); err != nil {
|
||||
return "", fmt.Errorf("mirror: the copy of %s@%s cannot be taken on: %w", r.Source, r.Digest, err)
|
||||
}
|
||||
return v.Catalog.Reference(src.Name(), r.Digest), nil
|
||||
return SeatReference(seat, v.Catalog.Reference(src.Name(), r.Digest)), nil
|
||||
}
|
||||
|
||||
// Mirrored runs one asked copy and says its outcome on the image seat's events: published with the reference, or
|
||||
@@ -281,7 +281,9 @@ func (v *Verbs) Mirrored(r MirrorRequest) error {
|
||||
}
|
||||
e := Event{Name: src.Name(), Kind: KindUpstream, Digest: r.Digest, Holder: Self, By: r.By, ID: r.ID}
|
||||
event := EventPublished
|
||||
if e.Reference, err = v.Catalog.Mirror(r.Source, r.Digest); err != nil {
|
||||
path, err := v.Catalog.Mirror(r.Source, r.Digest)
|
||||
e.Reference = SeatReference(ImageSeat, path)
|
||||
if err != nil {
|
||||
event, e.Reference, e.Reason = EventRefused, "", fmt.Sprintf("%s@%s was not copied: %v", r.Source, r.Digest, err)
|
||||
}
|
||||
if err := v.Say(ImageSeat, event, e); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user