Files
mesh-catalog/modules/distribution/cmd/store-tools/view.go
T
jochen 434b52614d distribution: say what the store holds and what the records name, so collection is no longer blind (hq ADR 0251)
The store had no working tools: its TypeScript client was never built, and nothing could count what
the store holds that no record names. A Go bundle lists the store's files through its own container,
reads each manifest through its door, and sets that beside the controller's records. Collection is
asked of the controller, which decides and records; the store's tools never delete. The image.pushed
event was declared and never emitted, and nothing consumes it, so it goes.
2026-10-08 11:59:39 +02:00

130 lines
3.2 KiB
Go

package main
import (
"sort"
)
// View is the store at one moment: its files, and every manifest's content that could be read.
type View struct {
L *Listing
// M is each manifest read, by Key.
M map[string]*Manifest
// Unread is each manifest that could not be read, by Key, with why.
Unread map[string]string
}
// Marks are the blobs one manifest keeps from the store's collector: its own content, its
// configuration and its layers, and for an index the manifests it lists. A manifest that could not be
// read marks only itself here, and the answers that rest on it say so.
func (v *View) Marks(repo, digest string) []string {
out := []string{digest}
if m := v.M[Key(repo, digest)]; m != nil {
out = append(out, m.Refs...)
out = append(out, m.Children...)
}
return out
}
// Marked is every blob some manifest marks, leaving out the manifests named in without — the
// collector's own mark, and the mark it would make once those are gone.
func (v *View) Marked(without map[string]bool) map[string]bool {
marked := map[string]bool{}
for name, r := range v.L.Repos {
for d := range r.Revisions {
if without[Key(name, d)] {
continue
}
for _, b := range v.Marks(name, d) {
marked[b] = true
}
}
}
return marked
}
// Bytes is the size of these blobs, as the store holds them; a blob the store does not hold counts
// nothing.
func (v *View) Bytes(blobs map[string]bool) int64 {
var n int64
for b := range blobs {
n += v.L.Blobs[b]
}
return n
}
// Total is the size of every blob the store holds.
func (v *View) Total() int64 {
var n int64
for _, s := range v.L.Blobs {
n += s
}
return n
}
// Unmarked is the bytes and count of the blobs no manifest marks, once the manifests in without are
// gone: what the nightly collector frees.
func (v *View) Unmarked(without map[string]bool) (int64, int) {
marked := v.Marked(without)
var n int64
count := 0
for b, s := range v.L.Blobs {
if !marked[b] {
n += s
count++
}
}
return n, count
}
// RepoBlobs are the blobs one repository's manifests mark.
func (v *View) RepoBlobs(name string) map[string]bool {
out := map[string]bool{}
if r := v.L.Repos[name]; r != nil {
for d := range r.Revisions {
for _, b := range v.Marks(name, d) {
out[b] = true
}
}
}
return out
}
// Shared are the blobs that the manifests of more than one repository mark.
func (v *View) Shared() map[string]bool {
seen := map[string]string{}
shared := map[string]bool{}
for _, name := range v.L.RepoNames() {
for b := range v.RepoBlobs(name) {
if first, ok := seen[b]; ok && first != name {
shared[b] = true
} else {
seen[b] = name
}
}
}
return shared
}
// tagsOf is a repository's tags, in order, with what each names.
func tagsOf(r *Repo) []map[string]string {
names := make([]string, 0, len(r.Tags))
for t := range r.Tags {
names = append(names, t)
}
sort.Strings(names)
out := make([]map[string]string, 0, len(names))
for _, t := range names {
out = append(out, map[string]string{"tag": t, "digest": r.Tags[t]})
}
return out
}
// taggedDigests are the manifests some tag names, in one repository.
func taggedDigests(r *Repo) map[string]bool {
out := map[string]bool{}
for _, d := range r.Tags {
out[d] = true
}
return out
}