100 lines
4.8 KiB
Go
100 lines
4.8 KiB
Go
package buscompose
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/health"
|
|
)
|
|
|
|
// ControllerLeftoverConsumers are the ended controller's durable consumers left on the bus, one leftover of the module
|
|
// mesh-delivery placed nowhere (issue 1126, milestone section 3), and mesh-catalog's of the controller's built-before,
|
|
// which its manifest no longer consumes and no part composes; each `<stream>.<consumer>`.
|
|
var ControllerLeftoverConsumers = []string{
|
|
"EVENTS." + ControllerName, "EVENTS." + MergesConsumerName, "EVENTS.novox_mesh-delivery", "EVENTS.novox_mesh-catalog",
|
|
"CONTROL." + ControllerName, DeadLetterNoticesStream + "." + ControllerName,
|
|
}
|
|
|
|
// ControllerLeftoverConsumersOn are ControllerLeftoverConsumers and, for each node given, the consumer the controller
|
|
// made for the node's engine (`EVENTS.<node>_mesh-host`, of restic's backed-up): the engine hears that event without a
|
|
// durable consumer, and the grants part composes none for it, so nothing reads it (issue 1126).
|
|
func ControllerLeftoverConsumersOn(nodes []string) []string {
|
|
out := slices.Clone(ControllerLeftoverConsumers)
|
|
for _, n := range nodes {
|
|
if safeSubject.MatchString(n) {
|
|
out = append(out, "EVENTS."+n+"_"+EngineModule)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ControllerLeftoverBuckets are the ended controller's buckets, each by its local name (`KV_mesh-controller_<name>`).
|
|
var ControllerLeftoverBuckets = []string{"asked", "assignments", "builds", "calls", "checks-running", "composed",
|
|
"condition-history", "conditions", "declared-config", "declared-packages", "delivery-merges", "grants", "hand-acts",
|
|
"lease", "predefined", "rollouts", "states-announced"}
|
|
|
|
// ControllerLeftoverStreams are the ended controller's streams, by their names: CONTROL, ASSIGNMENTS, each of its buckets'
|
|
// and, for each node given, the declaration files' object bucket the controller wrote (ADR 0486 §2).
|
|
func ControllerLeftoverStreams(nodes []string) []string {
|
|
out := []string{"CONTROL", AssignmentsStream}
|
|
for _, b := range ControllerLeftoverBuckets {
|
|
out = append(out, "KV_"+BucketName(catalogue.EndedControllerName, b))
|
|
}
|
|
for _, n := range nodes {
|
|
if safeSubject.MatchString(n) {
|
|
out = append(out, "OBJ_"+DeclarationFilesBucket+n)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// BusRemovalGrants are the whole authority of an account whose bus declares removals (ADR 0564 Limits 4, ADR 0590 clause
|
|
// 6), each removal its grant: the node-engine's acts bucket read (STREAM.INFO with a subject filter lists what a removal
|
|
// would take, so the tools read before they remove) and its keys removed, which nothing else may empty when it fills; and
|
|
// the ended controller's leftovers, each consumer and each stream read and deleted by its name, for the nodes given (the
|
|
// declaration files' object buckets). A removal outside the set is refused, as the indexer's judge refuses it in a
|
|
// manifest (novox/hq ADR 0578 §1).
|
|
func BusRemovalGrants(removes, nodes []string) ([]string, error) {
|
|
var out []string
|
|
for _, r := range removes {
|
|
switch r {
|
|
case catalogue.RemovesEngineActs:
|
|
out = append(out, "$JS.API.STREAM.INFO.KV_"+EngineActsBucket, "$KV."+EngineActsBucket+".>")
|
|
case catalogue.RemovesControllerLeftovers:
|
|
for _, c := range ControllerLeftoverConsumersOn(nodes) {
|
|
out = append(out, "$JS.API.CONSUMER.INFO."+c, "$JS.API.CONSUMER.DELETE."+c)
|
|
}
|
|
for _, s := range ControllerLeftoverStreams(nodes) {
|
|
out = append(out, "$JS.API.STREAM.INFO."+s, "$JS.API.STREAM.DELETE."+s)
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("a bus account removes %q, which is not one of %s (novox/hq ADR 0564 Limits 4, ADR 0590)", r,
|
|
strings.Join([]string{catalogue.RemovesEngineActs, catalogue.RemovesControllerLeftovers}, ", "))
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// WatchmanHears are the core subjects the watchman subscribes, with no durable consumer (ADR 0564 clauses 4, 5 and
|
|
// Limits 3, 11): the power seat's words (S1, ADR 0211), the providers' failing and recovered words (S8), each tool
|
|
// runner's beat (S11), the self-check's checked (S10), a consumer deleted and the bus module's slow consumers and closed
|
|
// connections (S9), the provisioner clients' reports (S21), the dead letters kept, and the lease write's events. Never the bus's API audit
|
|
// (`$JS.EVENT.ADVISORY.API`): it carries every API call's request and answer in the account, the vault's hashes among
|
|
// them (the lead, 2026-10-29).
|
|
func WatchmanHears() []string {
|
|
return []string{
|
|
"mesh.mod.power.event.*",
|
|
"mesh.mod.*.event.provisioner.failing",
|
|
"mesh.mod.*.event.provisioner.recovered",
|
|
"mesh.control.*.tools-alive",
|
|
"mesh.seat.mesh-self-check.event." + health.EventChecked,
|
|
"$JS.EVENT.ADVISORY.CONSUMER.DELETED.>",
|
|
"mesh.mod.nats.event.bus.slow-consumer",
|
|
"mesh.mod.nats.event.bus.connection-closed",
|
|
ProvAppliedSubjects,
|
|
deadLetterPrefix + ">",
|
|
}
|
|
}
|