Files
mesh-delivery/modules/mesh-delivery-indexer/internal/index/bus_state.go
T

102 lines
3.7 KiB
Go

package index
import (
"fmt"
"slices"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/views"
)
// BusStateTool is the indexer's tool the bus module's keeper takes its first view from (design 25 § 12.2; DLV-I11):
// every trunk module whose accepted manifest asks for bus state, then each manifest-accepted after its revision.
const BusStateTool = "indexer_bus_state"
// BusStateModule is one module of BusStateAnswer: the digest of its trunk's accepted manifest, the verdicts state's
// revision of that verdict (the one its manifest-accepted carries, ADR 0622 §2), and its bus-state entries whole.
type BusStateModule struct {
Module string `json:"module"`
Digest string `json:"digest"`
Revision uint64 `json:"revision"`
BusState []catalogue.BusStateEntry `json:"bus-state"`
}
// BusStateAnswer is indexer_bus_state's answer: Revision is the verdicts state's mark, read before anything else, so
// every manifest-accepted above it is a change after this answer (ADR 0615 §4); Modules in module order.
type BusStateAnswer struct {
Revision uint64 `json:"revision"`
Modules []BusStateModule `json:"modules"`
}
// BusStateOf answers indexer_bus_state from the indexer's own states: its trunk modules, the judge's verdicts and the
// manifests it keeps, and the marks the views family says them up to. A module refused or waiting, or whose manifest
// asks for no bus state, is left out; a value that cannot be read is an error, never a module silently left out.
func BusStateOf(modules, verdicts, manifests, marks views.Source) (BusStateAnswer, error) {
out := BusStateAnswer{Modules: []BusStateModule{}}
if marks != nil {
if e, ok, err := marks.Get(VerdictsState); err != nil {
return BusStateAnswer{}, fmt.Errorf("the mark of %s cannot be read: %w", VerdictsState, err)
} else if ok {
m, err := views.DecodeMark(e.Value)
if err != nil {
return BusStateAnswer{}, fmt.Errorf("the mark of %s: %w", VerdictsState, err)
}
out.Revision = m.Revision
}
}
names, err := modules.Keys()
if err != nil {
return BusStateAnswer{}, fmt.Errorf("the modules state cannot be listed: %w", err)
}
slices.Sort(names)
for _, name := range names {
e, ok, err := modules.Get(name)
if err != nil {
return BusStateAnswer{}, fmt.Errorf("modules.%s cannot be read: %w", name, err)
}
if !ok {
continue
}
mod, err := meshcatalogclient.DecodeModule(e.Value)
if err != nil || mod.Manifest == "" {
continue // no manifest held for it, so no verdict
}
key, err := meshcatalogclient.VerdictsKey(mod.Manifest)
if err != nil {
continue
}
ve, ok, err := verdicts.Get(key)
if err != nil {
return BusStateAnswer{}, fmt.Errorf("the verdict on %s cannot be read: %w", name, err)
}
if !ok {
continue
}
v, err := meshcatalogclient.DecodeVerdict(ve.Value)
if err != nil || v.State != meshcatalogclient.VerdictAccepted {
continue
}
me, ok, err := manifests.Get(mod.Manifest)
if err != nil {
return BusStateAnswer{}, fmt.Errorf("the manifest of %s cannot be read: %w", name, err)
}
if !ok {
continue
}
kept, err := meshcatalogclient.DecodeManifest(me.Value)
if err != nil {
return BusStateAnswer{}, fmt.Errorf("the manifest of %s: %w", name, err)
}
m, err := catalogue.Decode([]byte(kept.Manifest))
if err != nil {
return BusStateAnswer{}, fmt.Errorf("the accepted manifest of %s does not read: %w", name, err)
}
if len(m.BusState) == 0 {
continue
}
out.Modules = append(out.Modules, BusStateModule{Module: name, Digest: mod.Manifest, Revision: ve.Revision, BusState: m.BusState})
}
return out, nil
}