Files
mesh-delivery/modules/mesh-delivery-view/internal/view/read.go
T
jschoubben f2379dfeea
mesh/merge-gate head f2379dfeea2d merged into trunk 113f7aa93385 — pass: builds mesh-delivery-view → shanks; no bus step; every machine composes with the c…
mesh/repo-check head f2379dfeea2d merged into trunk 113f7aa93385 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/check failed: g14: fetch of novox/mesh-delivery#18: build_go_fetch on g14: nats: no responders available for request | f2379dfeea2d on 113f7aa933…
Add builder_build and builder_builds read tools to the view, so the builder's keys can be checked live (issue 1723)
2026-10-22 09:54:46 +02:00

193 lines
5.9 KiB
Go

package view
// The read tools of the indexer's modules (novox/hq issue 1562): what the indexer stored under modules.<m>, answered as
// stored. The new-form fingerprint (FP2, None2, Inputs2), the reason it has none, and the path and toolchains it was computed
// with are only there. The view reads the bucket through its reads; the reads write nothing.
import (
"encoding/json"
"fmt"
"sort"
"strings"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// The read tools, named as the module is: indexer_ and the verb.
const (
ModuleReadTool = "indexer_module"
ModulesReadTool = "indexer_modules"
)
// StateRead reads the indexer's modules state, as the view's reads grant it: its keys and a key's stored value (nil for a key with none).
type StateRead struct {
Keys func() ([]string, error)
Get func(key string) (json.RawMessage, error)
}
// Reader answers the read tools from a modules state.
type Reader struct {
State StateRead // the indexer's modules
Builds StateRead // the builder's builds
}
// Module is the stored value of modules.<module>, exactly as the indexer wrote it.
func (r Reader) Module(module string) (json.RawMessage, error) {
module = strings.TrimSpace(module)
key, err := delivery.ModulesKey(module)
if err != nil {
return nil, err
}
if r.State.Get == nil {
return nil, fmt.Errorf("the modules state cannot be read here")
}
raw, err := r.State.Get(key)
if err != nil {
return nil, err
}
if len(raw) == 0 {
return nil, fmt.Errorf("no module %s in the modules state", module)
}
return raw, nil
}
// Modules is every key of the modules state with its stored value; with none2 only the modules that have no new-form
// fingerprint and say why (a None2 reason), which is what the fingerprint's check reads live.
func (r Reader) Modules(none2 bool) (map[string]json.RawMessage, error) {
if r.State.Keys == nil || r.State.Get == nil {
return nil, fmt.Errorf("the modules state cannot be read here")
}
keys, err := r.State.Keys()
if err != nil {
return nil, err
}
sort.Strings(keys)
out := map[string]json.RawMessage{}
for _, k := range keys {
raw, err := r.State.Get(k)
if err != nil {
return nil, err
}
if len(raw) == 0 {
continue
}
if none2 {
var m struct {
None2 string `json:"none2"`
}
if json.Unmarshal(raw, &m) != nil || m.None2 == "" {
continue
}
}
out[k] = raw
}
return out, nil
}
// The read tools of the builder's builds (novox/hq issue 1723): what the builder stored under `<module>.<fingerprint hex>`
// in mesh-delivery-builder_builds, answered as stored, so its states, nodes (in the step) and digests can be checked live.
const (
BuilderBuildReadTool = "builder_build"
BuilderBuildsReadTool = "builder_builds"
)
// BuilderRow is one key of the builder's builds: the key, the module and the fingerprint it parses to, and the stored value
// as the builder wrote it (state, by, step, manifest, artifacts with digests, toolchains, error).
type BuilderRow struct {
Key string `json:"key"`
Module string `json:"module"`
Fingerprint string `json:"fingerprint"`
Build json.RawMessage `json:"build"`
}
func (r Reader) builderRow(key string) (BuilderRow, bool, error) {
module, fp, err := delivery.ParseBuilderBuildKey(key)
if err != nil {
return BuilderRow{}, false, nil // a key that is no build key is not a build
}
raw, err := r.Builds.Get(key)
if err != nil {
return BuilderRow{}, false, err
}
if len(raw) == 0 {
return BuilderRow{}, false, nil
}
return BuilderRow{Key: key, Module: module, Fingerprint: fp, Build: raw}, true, nil
}
// BuilderBuilds lists the builder's keys, each as stored, sorted by key; module and state, when given, keep only the builds
// of that module or in that state.
func (r Reader) BuilderBuilds(module, state string) ([]BuilderRow, error) {
if r.Builds.Keys == nil || r.Builds.Get == nil {
return nil, fmt.Errorf("the builder's builds cannot be read here")
}
keys, err := r.Builds.Keys()
if err != nil {
return nil, err
}
sort.Strings(keys)
rows := []BuilderRow{}
for _, k := range keys {
row, ok, err := r.builderRow(k)
if err != nil {
return nil, err
}
if !ok || (module != "" && row.Module != module) {
continue
}
if state != "" {
var b struct {
State string `json:"state"`
}
if json.Unmarshal(row.Build, &b) != nil || b.State != state {
continue
}
}
rows = append(rows, row)
}
return rows, nil
}
// BuilderBuild is the build of module at the new-form fingerprint fp2; with an empty fp2 it is the build at the fingerprint
// the indexer holds for the module now (its fp2), joined through modules.<module>; commit, when given, must be the commit
// the indexer holds that fp2 for, or the answer says which commit it holds (the fingerprint of another commit is not guessed).
func (r Reader) BuilderBuild(module, fp2, commit string) (BuilderRow, error) {
module = strings.TrimSpace(module)
if r.Builds.Get == nil {
return BuilderRow{}, fmt.Errorf("the builder's builds cannot be read here")
}
if fp2 == "" {
raw, err := r.Module(module)
if err != nil {
return BuilderRow{}, err
}
var m struct {
FP2 string `json:"fp2"`
None2 string `json:"none2"`
Commit string `json:"commit"`
}
if err := json.Unmarshal(raw, &m); err != nil {
return BuilderRow{}, fmt.Errorf("the indexer's value of %s is not readable: %w", module, err)
}
if commit != "" && m.Commit != commit {
return BuilderRow{}, fmt.Errorf("the indexer holds %s at commit %s, not %s, so its fingerprint is not the one of that commit", module, m.Commit, commit)
}
if m.FP2 == "" {
return BuilderRow{}, fmt.Errorf("the indexer holds no new-form fingerprint of %s: %s", module, m.None2)
}
fp2 = m.FP2
}
key, err := delivery.BuilderBuildKey(module, fp2)
if err != nil {
return BuilderRow{}, err
}
row, ok, err := r.builderRow(key)
if err != nil {
return BuilderRow{}, err
}
if !ok {
return BuilderRow{}, fmt.Errorf("the builder holds no build %s", key)
}
return row, nil
}