166 lines
4.6 KiB
Go
166 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
"git.novox.be/novox/mesh-sdk/go/buscalls"
|
|
"git.novox.be/novox/mesh-sdk/go/process"
|
|
)
|
|
|
|
// busStates is the module's states as this process reaches them (its bus account's `state`).
|
|
type busStates struct{ c *process.Conn }
|
|
|
|
func (b busStates) Put(state, key string, v any) error {
|
|
_, err := b.c.State(state).Put(key, v)
|
|
return err
|
|
}
|
|
func (b busStates) Delete(state, key string) error { return b.c.State(state).Delete(key) }
|
|
func (b busStates) Keys(state string) ([]string, error) { return b.c.State(state).Keys() }
|
|
func (b busStates) Get(state, key string) (json.RawMessage, bool, error) {
|
|
e, err := b.c.State(state).Get(key)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
if e == nil {
|
|
return nil, false, nil
|
|
}
|
|
return e.Value, true, nil
|
|
}
|
|
|
|
// JoinBus gives the machine this process's events and states, as its own bus account says. Without the
|
|
// account or the bus the machine keeps none, and what it changes is said as a warning (§18: with the bus down
|
|
// the apply converges from the config file it last got). It answers how to leave the bus.
|
|
func (m *Machine) JoinBus() func() {
|
|
acct, err := process.ReadBrokerAccount(os.Getenv)
|
|
if err != nil {
|
|
m.Warnings = append(m.Warnings, err.Error())
|
|
return func() {}
|
|
}
|
|
c, err := process.Connect(acct)
|
|
if err != nil {
|
|
m.Warnings = append(m.Warnings, err.Error())
|
|
return func() {}
|
|
}
|
|
m.Emit = func(kind string, body any) error { return c.Emit(kind, body, stdio.EmitOptions{}) }
|
|
m.States = busStates{c}
|
|
m.Findings = meshconditionsclient.FindingWriter{State: c.State(StateFindings)}
|
|
m.Call = c.Call
|
|
return c.Close
|
|
}
|
|
|
|
// toolStates is the module's states as the tools bundle reaches them, through the tool runner.
|
|
type toolStates struct{}
|
|
|
|
func (toolStates) Put(state, key string, v any) error {
|
|
_, err := stdio.State(state).Put(key, v)
|
|
return err
|
|
}
|
|
func (toolStates) Delete(state, key string) error { return stdio.State(state).Delete(key) }
|
|
func (toolStates) Keys(state string) ([]string, error) { return stdio.State(state).Keys() }
|
|
func (toolStates) Get(state, key string) (json.RawMessage, bool, error) {
|
|
e, err := stdio.State(state).Get(key)
|
|
if err != nil || e == nil {
|
|
return nil, false, err
|
|
}
|
|
return e.Value, true, nil
|
|
}
|
|
|
|
// eventHub hands the module's events, heard once through the tool runner, to the hand acts waiting for one.
|
|
type eventHub struct {
|
|
mu sync.Mutex
|
|
started bool
|
|
waiters map[int]hubWaiter
|
|
next int
|
|
}
|
|
|
|
type hubWaiter struct {
|
|
kinds []string
|
|
ch chan map[string]any
|
|
}
|
|
|
|
func (h *eventHub) await(kinds []string, bound time.Duration) (map[string]any, bool) {
|
|
h.mu.Lock()
|
|
if h.waiters == nil {
|
|
h.waiters = map[int]hubWaiter{}
|
|
}
|
|
h.next++
|
|
id, w := h.next, hubWaiter{kinds, make(chan map[string]any, 1)}
|
|
h.waiters[id] = w
|
|
first := !h.started
|
|
h.started = true
|
|
h.mu.Unlock()
|
|
defer func() { h.mu.Lock(); delete(h.waiters, id); h.mu.Unlock() }()
|
|
if first {
|
|
_ = stdio.Subscribe("#", func(e stdio.Envelope) error {
|
|
var body map[string]any
|
|
_ = json.Unmarshal(e.Body, &body)
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
for _, w := range h.waiters {
|
|
for _, k := range w.kinds {
|
|
if e.Key == k || strings.HasSuffix(e.Key, "."+k) {
|
|
select {
|
|
case w.ch <- body:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
select {
|
|
case ev := <-w.ch:
|
|
return ev, true
|
|
case <-time.After(bound):
|
|
return nil, false
|
|
}
|
|
}
|
|
|
|
// busLedger is the ledger `snapshots` through the tool runner.
|
|
type busLedger struct{}
|
|
|
|
func (busLedger) Apply(instance, operation string, body any, call string) error {
|
|
raw, err := json.Marshal(body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = stdio.BusState(buscalls.LedgerApply, buscalls.ApplyRequest{Ledger: LedgerName, Instance: instance,
|
|
Operation: operation, Body: raw, Call: call})
|
|
return err
|
|
}
|
|
|
|
func (busLedger) Entries() ([]LedgerEntry, error) {
|
|
raw, err := stdio.BusState(buscalls.LedgerQuery, buscalls.QueryRequest{Ledger: LedgerName, Index: "by-dataset-kind"})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var q buscalls.QueryAnswer
|
|
if err := json.Unmarshal(raw, &q); err != nil {
|
|
return nil, err
|
|
}
|
|
var out []LedgerEntry
|
|
for _, ie := range q.Entries {
|
|
raw, err := stdio.BusState(buscalls.LedgerGet, buscalls.InstanceRequest{Ledger: LedgerName, Instance: ie.Instance})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var st buscalls.InstanceState
|
|
if err := json.Unmarshal(raw, &st); err != nil {
|
|
return nil, err
|
|
}
|
|
var e LedgerEntry
|
|
if err := json.Unmarshal(st.State, &e); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, e)
|
|
}
|
|
return out, nil
|
|
}
|