mesh-delivery-contracts: the registry image seats gain holds and windows; git, mesh-artifact-sweep and mesh-artifact-store join as seat-list files (D-5, D-28, ADR 0635 §2; issue 1966)
This commit is contained in:
@@ -3,6 +3,13 @@
|
||||
// Package containerimageregistry is the contract of the seat "container-image-registry", defined whole (seats/container-image-registry/seat.json): The mesh's container images, in the one artifact store: the OCI distribution protocol's read side, an image's tags and mirroring an outside image.
|
||||
package containerimageregistry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "container-image-registry"
|
||||
|
||||
@@ -61,6 +68,85 @@ const SubjectHeld = "mesh.seat.container-image-registry.event.held"
|
||||
// SubjectReleased is where released is published.
|
||||
const SubjectReleased = "mesh.seat.container-image-registry.event.released"
|
||||
|
||||
const (
|
||||
StateHolds = "holds"
|
||||
StateWindows = "windows"
|
||||
)
|
||||
|
||||
// HoldsBySeatNameDigestKey is the key of one value of the state holds under the form "<seat>.<name>.<digest>".
|
||||
func HoldsBySeatNameDigestKey(seat, name, digest string) string {
|
||||
return seat + "." + name + "." + digest
|
||||
}
|
||||
|
||||
// ParseHoldsBySeatNameDigestKey reads the parts of a key of the form "<seat>.<name>.<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseHoldsBySeatNameDigestKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeHoldsBySeatNameDigestValue reads one value of the state holds under the form "<seat>.<name>.<digest>", refusing a field HoldsBySeatNameDigestValue does not know and any text after the value.
|
||||
func DecodeHoldsBySeatNameDigestValue(raw []byte) (HoldsBySeatNameDigestValue, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// WindowsByCallerKey is the key of one value of the state windows under the form "<caller>".
|
||||
func WindowsByCallerKey(caller string) string { return caller }
|
||||
|
||||
// ParseWindowsByCallerKey reads the parts of a key of the form "<caller>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseWindowsByCallerKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeWindowsByCallerValue reads one value of the state windows under the form "<caller>", refusing a field WindowsByCallerValue does not know and any text after the value.
|
||||
func DecodeWindowsByCallerValue(raw []byte) (WindowsByCallerValue, error) {
|
||||
var v WindowsByCallerValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Artifact is one published thing, as every registry event and answer names it.
|
||||
type Artifact struct {
|
||||
// the source commit it was built from
|
||||
@@ -401,3 +487,42 @@ type HeldEvent = Hold
|
||||
|
||||
// ReleasedEvent is the body of the event released.
|
||||
type ReleasedEvent = Hold
|
||||
|
||||
// HoldsBySeatNameDigestValue is one value of the state holds under the key <seat>.<name>.<digest>.
|
||||
type HoldsBySeatNameDigestValue struct {
|
||||
By string `json:"by"`
|
||||
Digest string `json:"digest"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Seat string `json:"seat"`
|
||||
// when (RFC 3339 time)
|
||||
Since string `json:"since"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValueDroppedItem is a type of the contract.
|
||||
type WindowsByCallerValueDroppedItem struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Node string `json:"node"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValue is one value of the state windows under the key <caller>.
|
||||
type WindowsByCallerValue struct {
|
||||
// when it last closed (RFC 3339 time)
|
||||
Closed string `json:"closed,omitempty"`
|
||||
// retired nodes dropped by hand
|
||||
Dropped []WindowsByCallerValueDroppedItem `json:"dropped,omitempty"`
|
||||
// the nodes holding it open
|
||||
Holding []string `json:"holding,omitempty"`
|
||||
LastHolding []string `json:"last_holding,omitempty"`
|
||||
// the caller whose window it is
|
||||
Name string `json:"name"`
|
||||
Open bool `json:"open,omitempty"`
|
||||
// node to when it began to hold
|
||||
Since map[string]string `json:"since,omitempty"`
|
||||
// of each node holding the window, why it holds it
|
||||
Why map[string]string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
@@ -29,6 +29,14 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v HeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.held$")},
|
||||
{name: "released on the seat's subject", subject: "mesh.seat.container-image-registry.event.released", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}",
|
||||
decode: func(b []byte) (any, error) { var v ReleasedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.container-image-registry\\.event\\.released$")},
|
||||
{name: "a hold", subject: "", given: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"container-image-registry\",\"since\":\"2026-11-05T10:00:00Z\"}", wire: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"container-image-registry\",\"since\":\"2026-11-05T10:00:00Z\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "an open window", subject: "", given: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}", wire: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}",
|
||||
decode: func(b []byte) (any, error) { var v WindowsByCallerValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish opens a transfer", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a field the reader does not know is dropped", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"later\":{\"x\":1},\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"image\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
// Package ociartifactregistry is the contract of the seat "oci-artifact-registry", defined whole (seats/oci-artifact-registry/seat.json): The mesh's bundles and archives, by digest, in the one artifact store: the OCI distribution protocol's read side.
|
||||
package ociartifactregistry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Seat is the seat's name.
|
||||
const Seat = "oci-artifact-registry"
|
||||
|
||||
@@ -59,6 +66,85 @@ const SubjectHeld = "mesh.seat.oci-artifact-registry.event.held"
|
||||
// SubjectReleased is where released is published.
|
||||
const SubjectReleased = "mesh.seat.oci-artifact-registry.event.released"
|
||||
|
||||
const (
|
||||
StateHolds = "holds"
|
||||
StateWindows = "windows"
|
||||
)
|
||||
|
||||
// HoldsBySeatNameDigestKey is the key of one value of the state holds under the form "<seat>.<name>.<digest>".
|
||||
func HoldsBySeatNameDigestKey(seat, name, digest string) string {
|
||||
return seat + "." + name + "." + digest
|
||||
}
|
||||
|
||||
// ParseHoldsBySeatNameDigestKey reads the parts of a key of the form "<seat>.<name>.<digest>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseHoldsBySeatNameDigestKey(key string) (string, string, string, bool) {
|
||||
rest := key
|
||||
v0, after0, found0 := strings.Cut(rest, ".")
|
||||
if !found0 || v0 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after0
|
||||
v2, after2, found2 := strings.Cut(rest, ".")
|
||||
if !found2 || v2 == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
rest = after2
|
||||
if rest == "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
v4 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", "", "", false
|
||||
}
|
||||
return v0, v2, v4, true
|
||||
}
|
||||
|
||||
// DecodeHoldsBySeatNameDigestValue reads one value of the state holds under the form "<seat>.<name>.<digest>", refusing a field HoldsBySeatNameDigestValue does not know and any text after the value.
|
||||
func DecodeHoldsBySeatNameDigestValue(raw []byte) (HoldsBySeatNameDigestValue, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// WindowsByCallerKey is the key of one value of the state windows under the form "<caller>".
|
||||
func WindowsByCallerKey(caller string) string { return caller }
|
||||
|
||||
// ParseWindowsByCallerKey reads the parts of a key of the form "<caller>", in the order of the form; the last result is false for a key of another form.
|
||||
func ParseWindowsByCallerKey(key string) (string, bool) {
|
||||
rest := key
|
||||
if rest == "" {
|
||||
return "", false
|
||||
}
|
||||
v0 := rest
|
||||
rest = ""
|
||||
if rest != "" {
|
||||
return "", false
|
||||
}
|
||||
return v0, true
|
||||
}
|
||||
|
||||
// DecodeWindowsByCallerValue reads one value of the state windows under the form "<caller>", refusing a field WindowsByCallerValue does not know and any text after the value.
|
||||
func DecodeWindowsByCallerValue(raw []byte) (WindowsByCallerValue, error) {
|
||||
var v WindowsByCallerValue
|
||||
dec := json.NewDecoder(bytes.NewReader(raw))
|
||||
dec.DisallowUnknownFields()
|
||||
if err := dec.Decode(&v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if dec.More() {
|
||||
return v, errors.New("text after the value")
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Artifact is one published thing, as every registry event and answer names it.
|
||||
type Artifact struct {
|
||||
// the source commit it was built from
|
||||
@@ -359,3 +445,42 @@ type HeldEvent = Hold
|
||||
|
||||
// ReleasedEvent is the body of the event released.
|
||||
type ReleasedEvent = Hold
|
||||
|
||||
// HoldsBySeatNameDigestValue is one value of the state holds under the key <seat>.<name>.<digest>.
|
||||
type HoldsBySeatNameDigestValue struct {
|
||||
By string `json:"by"`
|
||||
Digest string `json:"digest"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Seat string `json:"seat"`
|
||||
// when (RFC 3339 time)
|
||||
Since string `json:"since"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValueDroppedItem is a type of the contract.
|
||||
type WindowsByCallerValueDroppedItem struct {
|
||||
// when (RFC 3339 time)
|
||||
At string `json:"at,omitempty"`
|
||||
By string `json:"by,omitempty"`
|
||||
Node string `json:"node"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// WindowsByCallerValue is one value of the state windows under the key <caller>.
|
||||
type WindowsByCallerValue struct {
|
||||
// when it last closed (RFC 3339 time)
|
||||
Closed string `json:"closed,omitempty"`
|
||||
// retired nodes dropped by hand
|
||||
Dropped []WindowsByCallerValueDroppedItem `json:"dropped,omitempty"`
|
||||
// the nodes holding it open
|
||||
Holding []string `json:"holding,omitempty"`
|
||||
LastHolding []string `json:"last_holding,omitempty"`
|
||||
// the caller whose window it is
|
||||
Name string `json:"name"`
|
||||
Open bool `json:"open,omitempty"`
|
||||
// node to when it began to hold
|
||||
Since map[string]string `json:"since,omitempty"`
|
||||
// of each node holding the window, why it holds it
|
||||
Why map[string]string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
@@ -29,6 +29,14 @@ func TestFixtures(t *testing.T) {
|
||||
decode: func(b []byte) (any, error) { var v HeldEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.held$")},
|
||||
{name: "released on the seat's subject", subject: "mesh.seat.oci-artifact-registry.event.released", given: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}", wire: "{\"at\":\"2026-11-04T12:00:00Z\",\"by\":\"mesh-artifact-sweep\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"holder\":\"distribution\",\"name\":\"mesh-sdk\",\"reason\":\"kept by the sweep's rule\"}",
|
||||
decode: func(b []byte) (any, error) { var v ReleasedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.oci-artifact-registry\\.event\\.released$")},
|
||||
{name: "a hold", subject: "", given: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"oci-artifact-registry\",\"since\":\"2026-11-05T10:00:00Z\"}", wire: "{\"by\":\"operator@shanks\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"base\",\"name\":\"base\",\"seat\":\"oci-artifact-registry\",\"since\":\"2026-11-05T10:00:00Z\"}",
|
||||
decode: func(b []byte) (any, error) {
|
||||
var v HoldsBySeatNameDigestValue
|
||||
err := json.Unmarshal(b, &v)
|
||||
return v, err
|
||||
}, subjectIs: nil},
|
||||
{name: "an open window", subject: "", given: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}", wire: "{\"holding\":[\"ace\"],\"name\":\"mesh-delivery-builder\",\"open\":true,\"since\":{\"ace\":\"2026-11-05T10:00:00Z\"}}",
|
||||
decode: func(b []byte) (any, error) { var v WindowsByCallerValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "publish opens a transfer", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
decode: func(b []byte) (any, error) { var v PublishInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
|
||||
{name: "a field the reader does not know is dropped", subject: "", given: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"later\":{\"x\":1},\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}", wire: "{\"commit\":\"cccccccccccccccccccccccccccccccccccccccc\",\"digest\":\"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"fingerprint\":\"f1\",\"kind\":\"bundle\",\"name\":\"mesh-sdk\",\"parts\":[{\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"size\":1048576}],\"transfer\":\"t1\",\"version\":\"0.1.19\"}",
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"name": "a hold",
|
||||
"of": "state:holds/<seat>.<name>.<digest>",
|
||||
"given": {
|
||||
"seat": "container-image-registry",
|
||||
"name": "base",
|
||||
"kind": "base",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"by": "operator@shanks",
|
||||
"since": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an open window",
|
||||
"of": "state:windows/<caller>",
|
||||
"given": {
|
||||
"name": "mesh-delivery-builder",
|
||||
"open": true,
|
||||
"holding": [
|
||||
"ace"
|
||||
],
|
||||
"since": {
|
||||
"ace": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -340,5 +340,140 @@
|
||||
"held"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "holds",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the bundles, archives and copied bases kept from retention's removal",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<seat>.<name>.<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "one hold: an untagged bundle, archive or copied base kept from retention's removal",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat",
|
||||
"name",
|
||||
"kind",
|
||||
"digest",
|
||||
"by",
|
||||
"since"
|
||||
]
|
||||
},
|
||||
"description": "one hold"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the direct-write windows, one per caller",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<caller>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "the direct-write window of one caller: open while a node holds it open",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the caller whose window it is"
|
||||
},
|
||||
"open": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the nodes holding it open"
|
||||
},
|
||||
"since": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"description": "node to when it began to hold"
|
||||
},
|
||||
"closed": {
|
||||
"type": "string",
|
||||
"description": "when it last closed (RFC 3339 time)"
|
||||
},
|
||||
"last_holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dropped": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"node": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"description": "retired nodes dropped by hand"
|
||||
},
|
||||
"why": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "of each node holding the window, why it holds it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"description": "one caller's window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "git",
|
||||
"scope": "mesh",
|
||||
"grain": "seat-list",
|
||||
"decision": "novox/hq ADR 0111",
|
||||
"delivers": "git"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-artifact-store",
|
||||
"scope": "mesh",
|
||||
"grain": "seat-list",
|
||||
"decision": "novox/hq ADR 0075"
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"format": 1,
|
||||
"seat": "mesh-artifact-sweep",
|
||||
"scope": "mesh",
|
||||
"grain": "seat-list",
|
||||
"decision": "novox/hq ADR 0571",
|
||||
"verbs": [
|
||||
{
|
||||
"name": "artifacts",
|
||||
"description": "Every artifact the mesh made that lives in its artifact store, each with its repository, digest, kind (image or archive) and state: kept, and why (a want names it, or one of the five newest builds of its module in the builder's log), eligible (made, kept for no reason, not yet let go), or unrecorded (in the store and named by nothing the mesh made: never removed). Reads only.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"collected": {
|
||||
"description": "\"true\": list the collected references too (counted always)",
|
||||
"enum": [
|
||||
"true",
|
||||
"false"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"repository": {
|
||||
"description": "only this repository's references, as \u003cmodule\u003e/\u003cartifact\u003e (the counts stay whole)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "held",
|
||||
"description": "Whether the artifact store holds each reference given, asked with one HEAD each: held, gone (the store answered 404) or unknown with why. The builder asks it before it reuses a done key. Reads only.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"references": {
|
||||
"description": "the artifacts' references, artifact-store://\u003cmodule\u003e/\u003cartifact\u003e/blobs/sha256:\u003chex\u003e: a list, or one text separated by commas",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"references"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "collect",
|
||||
"description": "The artifact store's sweep, now: what is eligible is let go of. Without confirm a dry run: what would be let go, nothing held or deleted. With confirm and why: every kept archive held first, then each eligible artifact let go of, oldest first, an index with the platform manifests no kept index names, each deletion recorded in the hand-act log. Bounded by most (500 by default, at most 5000) and 45 seconds; what is left is said. Never touches a digest the mesh did not make.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"confirm": {
|
||||
"description": "\"true\": let go of what is eligible (needs why); without it nothing is done",
|
||||
"enum": [
|
||||
"true",
|
||||
"false"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"most": {
|
||||
"description": "let go of at most this many (500 by default, at most 5000)",
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"description": "with confirm: why, required and recorded",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "mirrors",
|
||||
"description": "The bases the mesh copied into its artifact store, each kept (a kept build stood on it), eligible (only builds no longer kept stood on it) or unrecorded (no build the builder's log holds stood on it: never removed), by what the builder's log says each build stood on. Records nothing by hand.",
|
||||
"input": {
|
||||
"properties": {},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "images",
|
||||
"description": "Every container image one machine's want and have name, each with the resources naming it: what the machine keeps when it prunes its images. Reads only.",
|
||||
"input": {
|
||||
"properties": {
|
||||
"node": {
|
||||
"description": "the machine's name",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "artifacts-gone"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"name": "a hold",
|
||||
"of": "state:holds/<seat>.<name>.<digest>",
|
||||
"given": {
|
||||
"seat": "oci-artifact-registry",
|
||||
"name": "base",
|
||||
"kind": "base",
|
||||
"digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"by": "operator@shanks",
|
||||
"since": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "an open window",
|
||||
"of": "state:windows/<caller>",
|
||||
"given": {
|
||||
"name": "mesh-delivery-builder",
|
||||
"open": true,
|
||||
"holding": [
|
||||
"ace"
|
||||
],
|
||||
"since": {
|
||||
"ace": "2026-11-05T10:00:00Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -243,5 +243,140 @@
|
||||
"held"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "holds",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the bundles, archives and copied bases kept from retention's removal",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<seat>.<name>.<digest>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "one hold: an untagged bundle, archive or copied base kept from retention's removal",
|
||||
"properties": {
|
||||
"seat": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"digest": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"since": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"seat",
|
||||
"name",
|
||||
"kind",
|
||||
"digest",
|
||||
"by",
|
||||
"since"
|
||||
]
|
||||
},
|
||||
"description": "one hold"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"template": "state",
|
||||
"history": 1,
|
||||
"description": "the direct-write windows, one per caller",
|
||||
"key-forms": [
|
||||
{
|
||||
"key": "<caller>",
|
||||
"value": {
|
||||
"type": "object",
|
||||
"description": "the direct-write window of one caller: open while a node holds it open",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "the caller whose window it is"
|
||||
},
|
||||
"open": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "the nodes holding it open"
|
||||
},
|
||||
"since": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
},
|
||||
"description": "node to when it began to hold"
|
||||
},
|
||||
"closed": {
|
||||
"type": "string",
|
||||
"description": "when it last closed (RFC 3339 time)"
|
||||
},
|
||||
"last_holding": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dropped": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"node": {
|
||||
"type": "string"
|
||||
},
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"why": {
|
||||
"type": "string"
|
||||
},
|
||||
"at": {
|
||||
"type": "string",
|
||||
"description": "when (RFC 3339 time)"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"description": "retired nodes dropped by hand"
|
||||
},
|
||||
"why": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "of each node holding the window, why it holds it"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"description": "one caller's window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -41,6 +41,49 @@ export const heldSubject = "mesh.seat.container-image-registry.event.held";
|
||||
|
||||
export const releasedSubject = "mesh.seat.container-image-registry.event.released";
|
||||
|
||||
export const states = {
|
||||
holds: "holds",
|
||||
windows: "windows",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state holds under the form "<seat>.<name>.<digest>". */
|
||||
export function holdsBySeatNameDigestKey(seat: string, name: string, digest: string): string {
|
||||
return seat + "." + name + "." + digest;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<seat>.<name>.<digest>", or undefined for a key of another form. */
|
||||
export function parseHoldsBySeatNameDigestKey(key: string): { seat: string; name: string; digest: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const seat = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
const at2 = rest.indexOf(".");
|
||||
if (at2 <= 0) return undefined;
|
||||
const name = rest.slice(0, at2);
|
||||
rest = rest.slice(at2 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const digest = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { seat, name, digest };
|
||||
}
|
||||
|
||||
/** The key of one value of the state windows under the form "<caller>". */
|
||||
export function windowsByCallerKey(caller: string): string {
|
||||
return caller;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<caller>", or undefined for a key of another form. */
|
||||
export function parseWindowsByCallerKey(key: string): { caller: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const caller = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { caller };
|
||||
}
|
||||
|
||||
export type Artifact = registry.Artifact;
|
||||
export type KeptEvent = registry.KeptEvent;
|
||||
export type Outcome = registry.Outcome;
|
||||
@@ -188,3 +231,41 @@ export type HeldEvent = Hold;
|
||||
/** is the body of the event released. */
|
||||
export type ReleasedEvent = Hold;
|
||||
|
||||
/** is one value of the state holds under the key <seat>.<name>.<digest>. */
|
||||
export interface HoldsBySeatNameDigestValue {
|
||||
"by": string;
|
||||
"digest": string;
|
||||
"kind": string;
|
||||
"name": string;
|
||||
"seat": string;
|
||||
/** when (RFC 3339 time) */
|
||||
"since": string;
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
export interface WindowsByCallerValueDroppedItem {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"by"?: string;
|
||||
"node": string;
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
/** is one value of the state windows under the key <caller>. */
|
||||
export interface WindowsByCallerValue {
|
||||
/** when it last closed (RFC 3339 time) */
|
||||
"closed"?: string;
|
||||
/** retired nodes dropped by hand */
|
||||
"dropped"?: WindowsByCallerValueDroppedItem[];
|
||||
/** the nodes holding it open */
|
||||
"holding"?: string[];
|
||||
"last_holding"?: string[];
|
||||
/** the caller whose window it is */
|
||||
"name": string;
|
||||
"open"?: boolean;
|
||||
/** node to when it began to hold */
|
||||
"since"?: Record<string, string>;
|
||||
/** of each node holding the window, why it holds it */
|
||||
"why"?: Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,49 @@ export const heldSubject = "mesh.seat.oci-artifact-registry.event.held";
|
||||
|
||||
export const releasedSubject = "mesh.seat.oci-artifact-registry.event.released";
|
||||
|
||||
export const states = {
|
||||
holds: "holds",
|
||||
windows: "windows",
|
||||
} as const;
|
||||
|
||||
/** The key of one value of the state holds under the form "<seat>.<name>.<digest>". */
|
||||
export function holdsBySeatNameDigestKey(seat: string, name: string, digest: string): string {
|
||||
return seat + "." + name + "." + digest;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<seat>.<name>.<digest>", or undefined for a key of another form. */
|
||||
export function parseHoldsBySeatNameDigestKey(key: string): { seat: string; name: string; digest: string } | undefined {
|
||||
let rest = key;
|
||||
const at0 = rest.indexOf(".");
|
||||
if (at0 <= 0) return undefined;
|
||||
const seat = rest.slice(0, at0);
|
||||
rest = rest.slice(at0 + 1);
|
||||
const at2 = rest.indexOf(".");
|
||||
if (at2 <= 0) return undefined;
|
||||
const name = rest.slice(0, at2);
|
||||
rest = rest.slice(at2 + 1);
|
||||
if (rest === "") return undefined;
|
||||
const digest = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { seat, name, digest };
|
||||
}
|
||||
|
||||
/** The key of one value of the state windows under the form "<caller>". */
|
||||
export function windowsByCallerKey(caller: string): string {
|
||||
return caller;
|
||||
}
|
||||
|
||||
/** The parts of a key of the form "<caller>", or undefined for a key of another form. */
|
||||
export function parseWindowsByCallerKey(key: string): { caller: string } | undefined {
|
||||
let rest = key;
|
||||
if (rest === "") return undefined;
|
||||
const caller = rest;
|
||||
rest = "";
|
||||
if (rest !== "") return undefined;
|
||||
return { caller };
|
||||
}
|
||||
|
||||
export type Artifact = registry.Artifact;
|
||||
export type KeptEvent = registry.KeptEvent;
|
||||
export type Outcome = registry.Outcome;
|
||||
@@ -147,3 +190,41 @@ export type HeldEvent = Hold;
|
||||
/** is the body of the event released. */
|
||||
export type ReleasedEvent = Hold;
|
||||
|
||||
/** is one value of the state holds under the key <seat>.<name>.<digest>. */
|
||||
export interface HoldsBySeatNameDigestValue {
|
||||
"by": string;
|
||||
"digest": string;
|
||||
"kind": string;
|
||||
"name": string;
|
||||
"seat": string;
|
||||
/** when (RFC 3339 time) */
|
||||
"since": string;
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
export interface WindowsByCallerValueDroppedItem {
|
||||
/** when (RFC 3339 time) */
|
||||
"at"?: string;
|
||||
"by"?: string;
|
||||
"node": string;
|
||||
"why"?: string;
|
||||
}
|
||||
|
||||
/** is one value of the state windows under the key <caller>. */
|
||||
export interface WindowsByCallerValue {
|
||||
/** when it last closed (RFC 3339 time) */
|
||||
"closed"?: string;
|
||||
/** retired nodes dropped by hand */
|
||||
"dropped"?: WindowsByCallerValueDroppedItem[];
|
||||
/** the nodes holding it open */
|
||||
"holding"?: string[];
|
||||
"last_holding"?: string[];
|
||||
/** the caller whose window it is */
|
||||
"name": string;
|
||||
"open"?: boolean;
|
||||
/** node to when it began to hold */
|
||||
"since"?: Record<string, string>;
|
||||
/** of each node holding the window, why it holds it */
|
||||
"why"?: Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user