96 lines
3.6 KiB
Go
96 lines
3.6 KiB
Go
// mesh-provisioner: the provisioner's server, holder of the mesh-provisioner seat once for the mesh (novox/hq ADR
|
|
// 0349 clause 3, design 57 P4). A Go bundle served by a runtime of its own, on its own bus account (`runs-as`, ADR
|
|
// 0259 §8), keeping four buckets: mesh-provisioner_registry (which domain answers which kind of placeholder, its
|
|
// alone), and mesh-provisioner_needs, _applied and _collect, which each machine's provisioner client writes for its
|
|
// own machine (the controller grants the client those keys alone, issue 670). stdout is the MCP channel; what this
|
|
// module says, it says on stderr.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// Seat is the role this module holds, once for the mesh, and Verb its one verb.
|
|
const (
|
|
Seat = "mesh-provisioner"
|
|
Verb = "provisioning"
|
|
)
|
|
|
|
func logf(format string, a ...any) { fmt.Fprintf(os.Stderr, format+"\n", a...) }
|
|
|
|
// runtimeStore is the bus, through the runtime that launched this bundle.
|
|
type runtimeStore struct{}
|
|
|
|
func (runtimeStore) Get(state, key string) (*Entry, error) {
|
|
e, err := stdio.State(state).Get(key)
|
|
if err != nil || e == nil {
|
|
return nil, err
|
|
}
|
|
return &Entry{Key: e.Key, Value: e.Value, Revision: e.Revision}, nil
|
|
}
|
|
|
|
func (runtimeStore) Put(state, key string, value any) error {
|
|
_, err := stdio.State(state).Put(key, value)
|
|
return err
|
|
}
|
|
|
|
func (runtimeStore) Delete(state, key string) error { return stdio.State(state).Delete(key) }
|
|
|
|
func (runtimeStore) Keys(state string) ([]string, error) { return stdio.State(state).Keys() }
|
|
|
|
func main() {
|
|
go publishing(runtimeStore{})
|
|
if err := stdio.Serve("", Tools(runtimeStore{})); err != nil {
|
|
logf("%v", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// publishing writes the registry once the runtime answers, and again every hour, so a registry emptied by hand or
|
|
// a bucket made again is written back without a restart. The runtime answers only once Serve runs, so the first
|
|
// pass is tried again until it lands.
|
|
func publishing(s Store) {
|
|
for wait := 2 * time.Second; ; {
|
|
time.Sleep(wait)
|
|
written, deleted, err := Publish(s)
|
|
switch {
|
|
case err != nil:
|
|
logf("[mesh-provisioner] the registry is not written yet (%v); trying again in %s", err, wait)
|
|
wait = min(wait*2, time.Minute)
|
|
continue
|
|
case len(written) > 0 || len(deleted) > 0:
|
|
logf("[mesh-provisioner] the registry: wrote %v, deleted %v", written, deleted)
|
|
}
|
|
wait = time.Hour
|
|
}
|
|
}
|
|
|
|
// Tools is the seat's verb as the runtime serves it.
|
|
func Tools(s Store) []stdio.Tool {
|
|
return []stdio.Tool{{
|
|
Name: Seat + "." + Verb,
|
|
Description: "Read what the provisioner keeps (novox/hq ADR 0349 clause 3): the registry (which domain " +
|
|
"answers which kind of placeholder, the default), the needs each module on each node recorded, what is " +
|
|
"applied where, or the items of every collection, narrowed by node and module, and collect by " +
|
|
"collection. Reads only: each node's provisioner client writes its own node's needs, applied and items.",
|
|
Input: map[string]any{"type": "object", "properties": map[string]any{
|
|
"what": map[string]any{"type": "string", "enum": []string{Registry, Needs, Applied, Collect},
|
|
"description": "registry when not given"},
|
|
"node": map[string]any{"type": "string", "description": "a node"},
|
|
"module": map[string]any{"type": "string", "description": "a module: for collect, a contributor"},
|
|
"collection": map[string]any{"type": "string", "description": "for collect: a collection"},
|
|
}},
|
|
Run: func(args map[string]any) (any, error) {
|
|
q, err := queryOf(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Do(s, q)
|
|
},
|
|
}}
|
|
}
|