Files
mesh-catalog/modules/distribution/internal/holder/platforms.go
T
jschoubben b88c6dac5b CAT-AS1: distribution removes an index's platforms with it, but those a kept index names
The sweep asks remove now instead of deleting over the store's door, so
what its door did for an index is the holder's: a removal for retention
removes the platform manifests no tagged or held index of the repository
names, and refuses a manifest such an index names.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 16:22:48 +01:00

118 lines
3.9 KiB
Go

package holder
// **An index's platform manifests go with it, unless a kept index names them** (CAT-AS1: what the artifact sweep did
// over the store's door, ADR 0257, is the holder's now that the sweep asks `remove`). A removal for retention of an
// index removes first every platform manifest it names that no other kept index of the repository names, then the
// index; a kept index is one a tag names or one held (`hold`). A manifest a kept index names is never removed for
// retention, so holding an index keeps its platforms too.
import (
"encoding/json"
"fmt"
"net/http"
"strings"
)
// spared is every manifest of name a kept index keeps on seat, but the one being removed (except): each tagged
// manifest and held digest, and the platform manifests each of those names.
func (v *Verbs) spared(seat, name, except string) (map[string]string, error) {
keepers := map[string]string{} // digest -> why it is kept
tags, err := v.Catalog.Versions(name)
if err != nil {
return nil, err
}
for _, t := range tags {
d, err := v.Catalog.Digest(name, t)
if err != nil {
return nil, err
}
if d != "" {
keepers[d] = "the tag " + t
}
}
holds, err := v.Holds.List()
if err != nil {
return nil, err
}
for _, h := range holds {
if h.Seat == seat && h.Name == name {
keepers[h.Digest] = "the hold of " + h.Digest
}
}
out := map[string]string{}
for d, why := range keepers {
if d == except {
continue
}
out[d] = why
children, err := v.Catalog.Platforms(name, d)
if err != nil {
return nil, fmt.Errorf("reading the kept %s@%s: %w", name, d, err)
}
for _, c := range children {
out[c] = "the index " + d + ", kept by " + why
}
}
return out, nil
}
// removeForRetention removes name@digest and, for an index, the platform manifests no kept index names; a manifest a
// kept index names is refused. Any read that fails removes nothing.
func (v *Verbs) removeForRetention(seat, name, digest string) error {
children, err := v.Catalog.Platforms(name, digest)
if err != nil {
return fmt.Errorf("remove: the store cannot read %s@%s, so nothing is removed: %w", name, digest, err)
}
spared, err := v.spared(seat, name, digest)
if err != nil {
return fmt.Errorf("remove: which manifests a kept index of %s names cannot be read, so nothing is removed: %w", name, err)
}
if why, kept := spared[digest]; kept {
return fmt.Errorf("remove: refused: %s@%s is kept by %s", name, digest, why)
}
for _, c := range children {
if _, kept := spared[c]; kept {
continue
}
if err := v.Catalog.RemoveDigest(name, c); err != nil {
return fmt.Errorf("remove: the store did not remove %s@%s, a platform of %s: %w", name, c, digest, err)
}
}
if err := v.Catalog.RemoveDigest(name, digest); err != nil {
return fmt.Errorf("remove: the store did not remove %s@%s: %w", name, digest, err)
}
return nil
}
// Platforms is the manifests an index names, by digest: none for a manifest that is not an index, or one the store
// does not hold.
func (o *OCI) Platforms(name, digest string) ([]string, error) {
resp, b, err := o.do(http.MethodGet, "/v2/"+name+"/manifests/"+digest, "", nil,
"application/vnd.oci.image.index.v1+json", "application/vnd.docker.distribution.manifest.list.v2+json",
mediaManifest, "application/vnd.docker.distribution.manifest.v2+json")
if err != nil {
return nil, err
}
if resp.StatusCode == http.StatusNotFound {
return nil, nil
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("the store answered %d for %s@%s", resp.StatusCode, name, digest)
}
var doc struct {
Manifests []struct {
Digest string `json:"digest"`
} `json:"manifests"`
}
if err := json.Unmarshal(b, &doc); err != nil {
return nil, fmt.Errorf("%s@%s is not a manifest: %w", name, digest, err)
}
var out []string
for _, m := range doc.Manifests {
if strings.HasPrefix(m.Digest, "sha256:") {
out = append(out, m.Digest)
}
}
return out, nil
}