The store is found by the seats' holder and its listen registry; list says, per repository, each manifest's digest, whether it is an index and what it names, its tags, its size and whether it is held, so the sweep reads the store through the seat. E15 now asks that nothing is provided or served. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
110 lines
3.6 KiB
Go
110 lines
3.6 KiB
Go
package holder
|
|
|
|
// **`list`: what the store holds, through the seat** (novox/hq ADR 0632 §3). For one repository, or every repository a
|
|
// seat holds, each manifest's digest, whether it is an index and the manifests it names, the tags naming it, its size
|
|
// and whether a hold keeps it. Read from the store's own files (files.go), so a manifest no tag names is listed too. The
|
|
// two seats share one store and split it by what a manifest keeps: a manifest keeping one blob under an empty
|
|
// configuration is a bundle or an archive, oci-artifact-registry's, named by that blob as its references name it;
|
|
// every other manifest, an index included, is container-image-registry's.
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sort"
|
|
|
|
reg "git.novox.be/novox/mesh-contracts/go/seats/containerimageregistry"
|
|
)
|
|
|
|
// VerbList is the seats' verb list.
|
|
const VerbList = reg.VerbList
|
|
|
|
// StoreFiles is the store's own files as list reads them: Files in the program, a test's stand-in.
|
|
type StoreFiles interface {
|
|
List(ctx context.Context) (*Listing, error)
|
|
Manifests(ctx context.Context, l *Listing) (map[string]*Manifest, map[string]string)
|
|
}
|
|
|
|
// List is `list` on seat: the repositories it holds, or name alone; a repository the seat holds nothing of is not named,
|
|
// and name not held is an empty answer. A manifest whose content cannot be read fails the answer, by name: a listing
|
|
// missing one would read as the store not holding it.
|
|
func (v *Verbs) List(ctx context.Context, seat, caller, name string) ([]reg.Repository, error) {
|
|
if err := v.Callers.Allowed(VerbList, caller); err != nil {
|
|
return nil, err
|
|
}
|
|
if seat != ImageSeat && seat != ArtifactSeat {
|
|
return nil, fmt.Errorf("list: %s is no seat of distribution's", seat)
|
|
}
|
|
if v.Files == nil {
|
|
return nil, errors.New("list: this process reads no store files")
|
|
}
|
|
l, err := v.Files.List(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list: the store's files cannot be listed: %w", err)
|
|
}
|
|
if name != "" {
|
|
r, held := l.Repos[name]
|
|
l = &Listing{Blobs: l.Blobs, Repos: map[string]*Repo{}}
|
|
if held {
|
|
l.Repos[name] = r
|
|
}
|
|
}
|
|
docs, unread := v.Files.Manifests(ctx, l)
|
|
if len(unread) > 0 {
|
|
keys := make([]string, 0, len(unread))
|
|
for k := range unread {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
return nil, fmt.Errorf("list: %d manifests cannot be read, the first %s: %s", len(keys), keys[0], unread[keys[0]])
|
|
}
|
|
holds, err := v.Holds.List()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list: the holds cannot be read: %w", err)
|
|
}
|
|
held := map[string]bool{}
|
|
for _, h := range holds {
|
|
held[h.Key()] = true
|
|
}
|
|
out := []reg.Repository{}
|
|
for _, repo := range l.RepoNames() {
|
|
r := l.Repos[repo]
|
|
tagsAt := map[string][]string{}
|
|
for t, d := range r.Tags {
|
|
tagsAt[d] = append(tagsAt[d], t)
|
|
}
|
|
digests := make([]string, 0, len(r.Revisions))
|
|
for d := range r.Revisions {
|
|
digests = append(digests, d)
|
|
}
|
|
sort.Strings(digests)
|
|
var manifests []reg.Listed
|
|
for _, d := range digests {
|
|
m := docs[Key(repo, d)]
|
|
artifact := ""
|
|
if m.Holder() {
|
|
artifact = m.Layers[0]
|
|
}
|
|
if (artifact != "") != (seat == ArtifactSeat) {
|
|
continue
|
|
}
|
|
e := reg.Listed{Digest: d, Index: len(m.Children) > 0, Platforms: m.Children, Artifact: artifact,
|
|
Tags: tagsAt[d], Size: l.Blobs[d]}
|
|
for _, b := range m.Refs {
|
|
e.Size += l.Blobs[b]
|
|
}
|
|
sort.Strings(e.Tags)
|
|
by := d
|
|
if artifact != "" {
|
|
by = artifact // a bundle or an archive is held by the blob its references name (HoldOne)
|
|
}
|
|
e.Held = held[Hold{Seat: seat, Name: repo, Digest: by}.Key()]
|
|
manifests = append(manifests, e)
|
|
}
|
|
if len(manifests) > 0 {
|
|
out = append(out, reg.Repository{Name: repo, Manifests: manifests})
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|