The assign act and the assignments' watch both checked whether an account was minted and then minted it, and minting overwrites the hash: interleaved, the bus kept one caller's hash and the node was sealed the other's password, the crash loop this branch fixes. Both now mint through one statement that inserts the row only when none stands; the caller whose insert wins seals, the other seals nothing. issueMissingProcessAccounts mints the same way. A watch opened again after one ended handed the standing assignments as the start's look, so one that came to stand in the gap got no bus composition or grant when its account already stood: only the process's first watch is the start's look now. The start's look reads the catalogue once and ends with one composition of the bus's node and the grants; a controller not holding the lease is told so and tries again rather than dropping the key. Tests folded to stay within the budget: the assignment tests are three table tests, the process account and mayIssue tests folded into one of them, and the concurrent mint is a case of the inventory's rotation test. Research 072: the grants piece (memberships and credentials heard from placement).
133 lines
4.6 KiB
Go
133 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/inventory"
|
|
)
|
|
|
|
// processUser is the bus user of one process of a module on a node (novox/hq ADR 0436 §1).
|
|
func processUser(node, module, process string) string {
|
|
return broker.Principal{Kind: broker.KindProcess, Node: node, Module: module, Process: process}.Username()
|
|
}
|
|
|
|
// processAccountWarning is the warning line for the own secret broker-<process id> of a process that declares bus
|
|
// when nobody issued its account (novox/hq ADR 0436 §2, issue 1226): "" for any other secret and for an issued
|
|
// account. The send goes on with the stand-in until the composition leaves the module off (issue 1753), so the
|
|
// line is the only loud thing about it.
|
|
func processAccountWarning(m catalogue.Manifest, node, secret string, minted []string) string {
|
|
id, isProcess := strings.CutPrefix(secret, "broker-")
|
|
if !isProcess {
|
|
return ""
|
|
}
|
|
if _, declared := m.ProcessBuses()[id]; !declared {
|
|
return ""
|
|
}
|
|
user := processUser(node, m.Module, id)
|
|
for _, u := range minted {
|
|
if u == user {
|
|
return ""
|
|
}
|
|
}
|
|
return fmt.Sprintf("%s/%s process %s: its bus account is not issued — mesh-controller.issue %s %s",
|
|
node, m.Module, id, node, m.Module)
|
|
}
|
|
|
|
// sayWarning writes a line where the compose's other warnings go: the act's answer when it carries a writer,
|
|
// else the controller's journal.
|
|
func sayWarning(ctx context.Context, line string) {
|
|
var w io.Writer = os.Stderr
|
|
if lw, ok := ctx.Value(leftOutKey{}).(io.Writer); ok {
|
|
w = lw
|
|
}
|
|
fmt.Fprintln(w, line)
|
|
}
|
|
|
|
// processAccountsMissing is each process of m that declares bus and whose account is not minted yet on node.
|
|
func processAccountsMissing(m catalogue.Manifest, node string, minted []string) []broker.ProcessPrincipal {
|
|
have := map[string]bool{}
|
|
for _, u := range minted {
|
|
have[u] = true
|
|
}
|
|
var out []broker.ProcessPrincipal
|
|
for _, pr := range inventory.ProcessesOf(m) {
|
|
if !have[processUser(node, m.Module, pr.Process)] {
|
|
out = append(out, pr)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// issueMissingProcessAccounts issues the accounts of m's bus processes on node that are not minted, and no
|
|
// other: an account already issued is rotated on purpose, never by a later delivery or assignment (issue 1226).
|
|
// It says what it issued, one line, and nothing when nothing was missing.
|
|
func issueMissingProcessAccounts(ctx context.Context, inv *inventory.Inventory, m catalogue.Manifest, node string) (string, error) {
|
|
if len(m.ProcessBuses()) == 0 || mayIssue(m) != nil {
|
|
return "", nil
|
|
}
|
|
// Only the missing: never a second password over one another caller minted between this look and the mint.
|
|
ctx = onlyIfAbsent(ctx)
|
|
minted, err := inv.BusMinted(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
missing := processAccountsMissing(m, node, minted)
|
|
if len(missing) == 0 {
|
|
return "", nil
|
|
}
|
|
known, err := broker.FromEnvironment()
|
|
if err != nil {
|
|
return "", fmt.Errorf("cannot deliver a credential without knowing where the bus is: %w", err)
|
|
}
|
|
reachable, err := brokerReachableAt(ctx, inv, known, node)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
busAddress, err := broker.BusAddress()
|
|
if err != nil {
|
|
busAddress = ""
|
|
}
|
|
var ids []string
|
|
for _, pr := range missing {
|
|
if err := issueProcess(ctx, inv, m, pr, node, busAddress, known, reachable); err != nil {
|
|
return "", err
|
|
}
|
|
ids = append(ids, pr.Process)
|
|
}
|
|
return fmt.Sprintf("its process account(s) %s issued and sealed to %s, and arrive with the push",
|
|
strings.Join(ids, ", "), node), nil
|
|
}
|
|
|
|
// issueGainedProcessAccounts runs after a module's new definition is registered: on each machine assigned it, the
|
|
// accounts of its bus processes not yet minted are issued, so a process a delivery adds is not pushed with a
|
|
// random stand-in (issue 1226). Never fatal: the build worked and the module is registered, so a failure is said,
|
|
// and the push refuses the module until `mesh-controller.issue` is run.
|
|
func issueGainedProcessAccounts(ctx context.Context, inv *inventory.Inventory, m catalogue.Manifest) {
|
|
if len(m.ProcessBuses()) == 0 {
|
|
return
|
|
}
|
|
nodes, err := inv.Running(ctx, m.Module)
|
|
if err != nil {
|
|
fmt.Printf("%s: its machines are not read to issue its process accounts: %v\n", m.Module, err)
|
|
return
|
|
}
|
|
for _, node := range nodes {
|
|
line, err := issueMissingProcessAccounts(ctx, inv, m, node)
|
|
if err != nil {
|
|
fmt.Printf("%s on %s: its process accounts are not issued (%v): `mesh-controller.issue %s %s` first\n",
|
|
m.Module, node, err, node, m.Module)
|
|
continue
|
|
}
|
|
if line != "" {
|
|
fmt.Printf("%s on %s: %s\n", m.Module, node, line)
|
|
}
|
|
}
|
|
}
|