172 lines
7.0 KiB
Go
172 lines
7.0 KiB
Go
// Package membership is what the mesh issues an assignment to serve and to reach (novox/hq ADR 0160), as a
|
|
// process reads it from the bus: the Membership shape, the one subject it is published on, the stream that
|
|
// holds it, and the bus address helpers a reader needs. The controller composes it and the route proxy reads
|
|
// it; both use this one definition, so there is no second copy to drift (novox/hq issue 1633).
|
|
package membership
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
)
|
|
|
|
// AssignmentsStream holds every assignment's membership, the newest per subject.
|
|
const AssignmentsStream = "ASSIGNMENTS"
|
|
|
|
// Subject is the one address a runtime derives for itself: where its own membership is published, from
|
|
// the two names its credential carries. Everything else is in the membership.
|
|
func Subject(node, module string) string {
|
|
return "mesh.assignment." + node + "." + module
|
|
}
|
|
|
|
// DirectGetSubject is the subject-addressed direct get of a membership from its stream: the one request an
|
|
// account may make of the stream.
|
|
func DirectGetSubject(node, module string) string {
|
|
return "$JS.API.DIRECT.GET." + AssignmentsStream + "." + Subject(node, module)
|
|
}
|
|
|
|
// Membership is one assignment's subjects: what this instance of a module on this machine serves,
|
|
// and what it may reach.
|
|
type Membership struct {
|
|
Node string `json:"node"`
|
|
Module string `json:"module"`
|
|
// Serves is every address a tool of this instance answers on. `{tool}` stands for the tool's
|
|
// own name. An address with a queue is shared with the module's other instances.
|
|
Serves []Served `json:"serves"`
|
|
// Seats is every verb of a seat this instance holds, at the subject the seat's callers use.
|
|
Seats []SeatServed `json:"seats,omitempty"`
|
|
// Emits is where an event of this module lands; `{event}` stands for the event's name.
|
|
Emits string `json:"emits"`
|
|
// Reaches is each tool this module may call, `<module>.<tool>`, to the subjects that reach it.
|
|
Reaches map[string][]string `json:"reaches,omitempty"`
|
|
// Tools is where this instance answers what it serves.
|
|
Tools string `json:"tools"`
|
|
// Receives is what this assignment is given for each requirement it receives, by requirement (novox/hq
|
|
// ADR 0167): kept as JSON because the catalogue owns the shape of a contribution.
|
|
Receives map[string]json.RawMessage `json:"receives,omitempty"`
|
|
// Mesh is every machine's address on the private network (novox/hq ADR 0167).
|
|
Mesh []string `json:"mesh,omitempty"`
|
|
// State is every bucket this module's code may reach, and whether it may write it (novox/hq ADR 0201).
|
|
State []StateIssued `json:"state,omitempty"`
|
|
// Logs is every log this module's code may reach (novox/hq ADR 0297).
|
|
Logs []LogIssued `json:"logs,omitempty"`
|
|
// SeatTraffic is what this module's code may submit, say, hear, take, ask, answer and read on seats
|
|
// that name their caller or their kind (novox/hq ADR 0259 §3).
|
|
SeatTraffic *SeatTraffic `json:"seat-traffic,omitempty"`
|
|
// Streams are this module's own streams, Hears the module streams it hears (novox/hq ADR 0363).
|
|
Streams []StreamIssued `json:"streams,omitempty"`
|
|
Hears []HeardIssued `json:"hears,omitempty"`
|
|
// Waits are the event families a session may wait on through the mesh MCP server (novox/hq ADR 0367 §2).
|
|
Waits []WaitIssued `json:"waits,omitempty"`
|
|
}
|
|
|
|
// Served is one address a tool is answered on.
|
|
type Served struct {
|
|
Subject string `json:"subject"`
|
|
Queue string `json:"queue,omitempty"`
|
|
}
|
|
|
|
// SeatServed is one verb of a held seat, where its callers ask.
|
|
type SeatServed struct {
|
|
Seat string `json:"seat"`
|
|
Verb string `json:"verb"`
|
|
Subject string `json:"subject"`
|
|
}
|
|
|
|
// StateIssued is one bucket an assignment may reach, by the name its module uses for it (novox/hq ADR 0201).
|
|
// Key, when set, is the one key it may reach (novox/hq ADR 0260): this machine's.
|
|
//
|
|
// PerNode says the state is kept one bucket per node (novox/hq ADR 0593 §6: `composed-<node>`): Bucket is then the
|
|
// buckets' prefix, each bucket the prefix and a node's name, and a key's first part names its node and so its bucket.
|
|
// The runtime reads it as one state (as process.NodeFamily does): a key from its node's bucket, the keys and a watch over
|
|
// every bucket of the family, one created later included. Read only: PerNode is never issued with Writes.
|
|
type StateIssued struct {
|
|
Name string `json:"name"`
|
|
Bucket string `json:"bucket"`
|
|
Writes bool `json:"writes,omitempty"`
|
|
Key string `json:"key,omitempty"`
|
|
PerNode bool `json:"per-node,omitempty"`
|
|
}
|
|
|
|
// LogIssued is one log an assignment may reach, by the name its module uses for it (novox/hq ADR 0297).
|
|
type LogIssued struct {
|
|
Name string `json:"name"`
|
|
Stream string `json:"stream"`
|
|
Subject string `json:"subject"`
|
|
Writes bool `json:"writes"`
|
|
}
|
|
|
|
// Worker is one durable consumer a holder pulls a seat's work from.
|
|
type Worker struct {
|
|
Stream string
|
|
Consumer string
|
|
Filter string
|
|
}
|
|
|
|
// SeatTraffic is one module's seat traffic beyond tools. Publish and Subscribe are subject patterns, in the
|
|
// server's wildcards.
|
|
type SeatTraffic struct {
|
|
Publish []string `json:"publish,omitempty"`
|
|
Subscribe []string `json:"subscribe,omitempty"`
|
|
Answers []string `json:"answers,omitempty"`
|
|
Workers []Worker `json:"workers,omitempty"`
|
|
Records []string `json:"records,omitempty"`
|
|
Kinds []KindHeld `json:"kinds,omitempty"`
|
|
}
|
|
|
|
// KindHeld is one kind of a kinded bench and who holds it.
|
|
type KindHeld struct {
|
|
Seat string `json:"seat"`
|
|
Kind string `json:"kind"`
|
|
Module string `json:"module"`
|
|
Node string `json:"node"`
|
|
Capabilities []string `json:"capabilities,omitempty"`
|
|
}
|
|
|
|
// StreamIssued is one of a module's own streams as its membership names it (ADR 0363 §2).
|
|
type StreamIssued struct {
|
|
Stream string `json:"stream"`
|
|
Events []string `json:"events"`
|
|
Subject string `json:"subject"`
|
|
}
|
|
|
|
// HeardIssued is one module stream an assignment hears (ADR 0363 §8).
|
|
type HeardIssued struct {
|
|
Stream string `json:"stream"`
|
|
Consumer string `json:"consumer"`
|
|
}
|
|
|
|
// WaitIssued is one family as a membership issues it: the name a session asks for, and its subject.
|
|
type WaitIssued struct {
|
|
Event string `json:"event"`
|
|
Subject string `json:"subject"`
|
|
}
|
|
|
|
// CredentialIn reads the credential out of a bus address, `[scheme://]user:password@host:port`, splitting on
|
|
// the last at-sign so a password containing one stays whole, and returns the address without it.
|
|
func CredentialIn(address string) (user, password, bare string) {
|
|
at := strings.LastIndex(address, "@")
|
|
if at < 0 {
|
|
return "", "", address
|
|
}
|
|
scheme := ""
|
|
rest := address[:at]
|
|
if i := strings.Index(rest, "://"); i >= 0 {
|
|
scheme, rest = rest[:i+3], rest[i+3:]
|
|
}
|
|
user, password, _ = strings.Cut(rest, ":")
|
|
return user, password, scheme + address[at+1:]
|
|
}
|
|
|
|
// BareAddress is a bus address with any credential stripped, for something that only needs to know
|
|
// whether a server is answering there.
|
|
func BareAddress(address string) string {
|
|
_, _, bare := CredentialIn(address)
|
|
if bare == "" {
|
|
return address
|
|
}
|
|
if strings.Contains(bare, "://") {
|
|
return bare
|
|
}
|
|
return "nats://" + bare
|
|
}
|