Files

85 lines
3.3 KiB
Go

package nodeengineclient
import (
"encoding/json"
"fmt"
"time"
seat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
"git.novox.be/novox/mesh-sdk/go/keys"
)
// A node's own health report (the state health, one key per node, written by that node's engine alone) and the data items
// a module declares as the engine placed them (the state data, `<node>.<module>`): their layouts are the seat's contract
// (seat.HealthByNodeValue, seat.DataByNodeModuleValue); this file is their codec, shared by the engine and its readers.
// RootContract is the statement contract from which an engine judges an account's root: a report whose contract is lower
// judged nothing about root, and a reader must not read its empty root verdicts as healthy (HealthByNodeValue.Contract).
const RootContract = 3
// EncodeHealth is h as JSON at the current version; it refuses a report naming no node, and a fall back to the old login
// that names another node, no login fallen back from, no reason or no time.
func EncodeHealth(h seat.HealthByNodeValue) ([]byte, error) {
h.V = keys.Version
if h.Node == "" {
return nil, fmt.Errorf("node-engine: a node's health report names its node")
}
if f := h.Fallback; f != nil && (f.Node != h.Node || f.From == "" || f.Why == "" || f.At.IsZero()) {
return nil, fmt.Errorf("node-engine: a node's fall back to its old login names its node %s, the login fallen back from, why and when", h.Node)
}
return json.Marshal(h)
}
// DecodeHealth reads a report; it refuses another version (keys.ErrUnknownVersion) and a field the contract does not know.
func DecodeHealth(raw []byte) (seat.HealthByNodeValue, error) {
if err := keys.CheckVersion(raw); err != nil {
return seat.HealthByNodeValue{}, err
}
h, err := seat.DecodeHealthByNodeValue(raw)
if err != nil {
return seat.HealthByNodeValue{}, fmt.Errorf("node-engine: a node's health report is not readable: %w", err)
}
return h, nil
}
// SameHealth says two reports say the same, whenever each was written.
func SameHealth(a, b seat.HealthByNodeValue) bool {
a.At, b.At = time.Time{}, time.Time{}
return sameJSON(a, b)
}
// DataKey is a module's key on a node in the state data, `<node>.<module>`, its module part escaped as a have's is.
func DataKey(node, module string) (string, error) { return HaveKey(node, module) }
// DataNodeKeys is the pattern of every data key of a node, `<node>.*`.
func DataNodeKeys(node string) (string, error) {
if err := keys.CheckName("node", node); err != nil {
return "", err
}
return node + ".*", nil
}
// EncodeData is d as JSON at the current version; it refuses a node or module the key refuses.
func EncodeData(d seat.DataByNodeModuleValue) ([]byte, error) {
d.V = keys.Version
if _, err := DataKey(d.Node, d.Module); err != nil {
return nil, err
}
if d.Items == nil {
d.Items = []seat.PlacedDataItem{}
}
return json.Marshal(d)
}
// DecodeData reads a module's data items; it refuses another version (keys.ErrUnknownVersion).
func DecodeData(raw []byte) (seat.DataByNodeModuleValue, error) {
if err := keys.CheckVersion(raw); err != nil {
return seat.DataByNodeModuleValue{}, err
}
d, err := seat.DecodeDataByNodeModuleValue(raw)
if err != nil {
return seat.DataByNodeModuleValue{}, fmt.Errorf("node-engine: a module's data items are not readable: %w", err)
}
return d, nil
}