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 }