Files
mesh-host/internal/link/enginepackages.go
T
jschoubben 5906e18028 Node-engine: a composition, its codec, digest and keys come from the node-engine client; the candidate answer and its limits from the seat's contract; the declared packages are the engine's own (H-2, H-3, ADR 0634 §1; issue 1966)
The engine's Composed keeps the catalogue's Plan and the declaration types and goes through nodeengineclient's codec; the fingerprint key part is meshcatalogclient's. The candidate answer is nodeengine.CandidateAnswer; the have still holds it through an interim JSON bridge (link.interimAnswers) until the Have group moves.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-05 15:10:45 +01:00

113 lines
3.8 KiB
Go

package link
import (
"context"
"encoding/json"
"errors"
"fmt"
nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
"github.com/novox/mesh-host/internal/compositions"
"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 ("mesh-host." + nodeengine.StatePackages).
const PackagesBucket = compositions.PackagesBucket
// 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 *nodeengine.PackagesByNodeValue `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: "mesh-host." + nodeengine.StatePackages}
if keyErr == nil && key != nil {
var d nodeengine.PackagesByNodeValue
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
}