Files
mesh-host/internal/link/enginepackages.go
T
jschoubben 0d1bbe967d Answer the node seat's packages from the engine's own key alone
The controller's declared-packages mirror ended with the controller (issue 1126) and is deleted after group B's
delivery, so the verb no longer reads or compares it.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:22:54 +01:00

112 lines
3.7 KiB
Go

package link
import (
"context"
"encoding/json"
"errors"
"fmt"
"git.novox.be/novox/mesh-sdk/go/delivery"
"github.com/nats-io/nats.go"
)
// ---- the packages this node's compositions declare, in the bus's own storage (novox/hq ADR 0565 §6) ---------------
// PackagesBucket is the node-engine's declared state packages, created by the grants part from the engine's manifest as
// its states health and data are: one key, the node's name, written by that node's engine alone, read by pacman as
// mesh-host.packages (delivery.EnginePackagesRead).
const PackagesBucket = "mesh-host_" + delivery.EnginePackagesState
// packagesStore binds PackagesBucket over the link open now: bound, never created.
func (l *natsLink) packagesStore() (nats.KeyValue, error) {
kv, err := l.js.KeyValue(PackagesBucket)
if err != nil {
return nil, fmt.Errorf("the node-engine's state %s cannot be bound (it is created from the node-engine's "+
"manifest; written again at the next composition that changes, or by the node seat's packages with write): %w",
PackagesBucket, err)
}
return kv, nil
}
// packagesStore binds PackagesBucket over a link made of a connection the caller holds (OverNATS), as natsLink does.
func (b OverNATS) packagesStore() (nats.KeyValue, error) {
kv, err := b.JS.KeyValue(PackagesBucket)
if err != nil {
return nil, fmt.Errorf("the node-engine's state %s cannot be bound: %w", PackagesBucket, err)
}
return kv, nil
}
// EnginePackages is this node's key of PackagesBucket over whichever link the session has open
// (selfcompose.PackagesStore); with none open every read and write is refused, and nothing is kept anywhere else.
type EnginePackages struct{ Session *Session }
func (p EnginePackages) kv() (nats.KeyValue, error) {
p.Session.init()
p.Session.mu.Lock()
bus := p.Session.bus
p.Session.mu.Unlock()
l, ok := bus.(interface{ packagesStore() (nats.KeyValue, error) })
if bus == nil || !ok {
return nil, ErrNoLink
}
return l.packagesStore()
}
// Get is the key's value, nil when it has none.
func (p EnginePackages) Get(_ context.Context, key string) ([]byte, error) {
kv, err := p.kv()
if err != nil {
return nil, err
}
e, err := kv.Get(key)
if errors.Is(err, nats.ErrKeyNotFound) {
return nil, nil
}
if err != nil {
return nil, err
}
return e.Value(), nil
}
// Put writes the key.
func (p EnginePackages) Put(_ context.Context, key string, value []byte) error {
kv, err := p.kv()
if err != nil {
return err
}
_, err = kv.Put(key, value)
return err
}
// PackagesView is the node seat's verb packages' answer (ADR 0565 §6): this node's key of the engine's state packages.
// The controller's mirror it was once compared with ended with the controller (issue 1126).
type PackagesView struct {
Node string `json:"node"`
// Key is the node's key as the bus holds it (Source); null when the engine has written none.
Source string `json:"source"`
Key *delivery.DeclaredPackages `json:"key"`
KeyError string `json:"key_error,omitempty"`
// Wrote says, for write, whether the key was written again (false: it already held what the engine composed).
Wrote *bool `json:"wrote,omitempty"`
Note string `json:"note,omitempty"`
}
// PackagesAnswer is the verb's answer from the key as read (nil: no key), with its read's error.
func PackagesAnswer(node string, key []byte, keyErr error) PackagesView {
v := PackagesView{Node: node, Source: delivery.EnginePackagesRead}
if keyErr == nil && key != nil {
var d delivery.DeclaredPackages
if keyErr = json.Unmarshal(key, &d); keyErr == nil {
v.Key = &d
}
}
switch {
case keyErr != nil:
v.KeyError = keyErr.Error()
case v.Key == nil:
v.Note = "the engine has written no key for this node yet"
}
return v
}