The group's module paths are placeholders the re-pin rewrites. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
110 lines
3.7 KiB
Go
110 lines
3.7 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-delivery/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
|
|
}
|