Files
mesh-catalog/modules/distribution/cmd/distribution-tools/contract.go
T
jschoubben 85fd46b4cc CAT-D2: distribution's verbs and events speak the two seats' contracts from mesh-contracts
The verbs answered the holder's own shapes (missing and ended, a part named digest, versions as bare strings, held as
the publish's event) while the seat definitions, now generated into mesh-contracts, name lacks and held, part,
versions of artifacts, tags with digests and a hold's own body. A build module using mesh-sdk's parts helper would
have read no lacking part and sent nothing. The bundle now takes and answers the contract's types and says each
event in its body; an image's manifest is the part whose digest is the publish's, as the contract names no media
type, and a hold on the image seat is of a copied base.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 13:07:14 +01:00

72 lines
3.0 KiB
Go

package main
// The seats' contracts (mesh-contracts, generated from the seat definitions; ADR 0620): every verb's input and answer,
// and every event's body, as the two seats distribution holds define them. The verbs common to both seats have one
// shape in every registry seat, so containerimageregistry's types serve oci-artifact-registry's too. The holder's own
// types stay inside; what crosses the bus is the contract's.
import (
"encoding/base64"
"fmt"
"time"
reg "git.novox.be/novox/mesh-contracts/go/seats/containerimageregistry"
"distribution/internal/holder"
)
func publishOf(in reg.PublishInput) holder.PublishRequest {
r := holder.PublishRequest{Transfer: in.Transfer, Name: in.Name, Version: in.Version, Tag: in.Tag, Kind: in.Kind,
Fingerprint: in.Fingerprint, Commit: in.Commit, Digest: in.Digest}
for _, p := range in.Parts {
r.Parts = append(r.Parts, holder.Part{Digest: p.Digest, Size: p.Size})
}
return r
}
func publishAnswer(a holder.PublishAnswer) reg.PublishAnswer {
return reg.PublishAnswer{Transfer: a.Transfer, Lacks: a.Missing, Held: a.Ended && len(a.Missing) == 0}
}
func partOf(in reg.PublishPartInput) (holder.PartRequest, error) {
data, err := base64.StdEncoding.DecodeString(in.Data)
if err != nil {
return holder.PartRequest{}, fmt.Errorf("publish-part: data is not base64: %w", err)
}
return holder.PartRequest{Transfer: in.Transfer, Digest: in.Part, Sequence: int(in.Sequence), Last: in.Last, Data: data}, nil
}
func partAnswer(in reg.PublishPartInput, a holder.PublishAnswer) reg.PublishPartAnswer {
return reg.PublishPartAnswer{Transfer: in.Transfer, Part: in.Part, Received: in.Sequence + 1, Complete: a.Ended}
}
// versionsAnswer is `versions`: each version with the digest the store holds under it.
func versionsAnswer(v *holder.Verbs, kind string, a holder.VersionsAnswer) reg.VersionsAnswer {
out := reg.VersionsAnswer{Name: a.Name, Versions: []reg.Artifact{}}
for _, ver := range a.Versions {
d, _ := v.Catalog.Digest(a.Name, ver)
out.Versions = append(out.Versions, reg.Artifact{Name: a.Name, Version: ver, Digest: d, Kind: kind})
}
return out
}
func tagsAnswer(v *holder.Verbs, a holder.VersionsAnswer) reg.TagsAnswer {
out := reg.TagsAnswer{Name: a.Name, Tags: []reg.TagsAnswerTagsItem{}}
for _, t := range a.Versions {
d, _ := v.Catalog.Digest(a.Name, t)
out.Tags = append(out.Tags, reg.TagsAnswerTagsItem{Tag: t, Digest: d})
}
return out
}
// eventBody is an event's body as the seat's contract defines it: an Outcome for published, refused and removed, a
// Hold for held and released.
func eventBody(event string, e holder.Event) any {
switch event {
case holder.EventHeld, holder.EventReleased:
return reg.Hold{Name: e.Name, Digest: e.Digest, Holder: e.Holder, By: e.By, Reason: e.Reason, At: time.Now().UTC().Format(time.RFC3339)}
}
return reg.Outcome{Name: e.Name, Version: e.Version, Kind: e.Kind, Tag: e.Tag, Digest: e.Digest, Fingerprint: e.Fingerprint,
Commit: e.Commit, Holder: e.Holder, Transfer: e.Transfer, Reference: e.Reference, Reason: e.Reason}
}