Issue a bus process's account when its module is assigned or gains it (issue 1226)

This commit is contained in:
2026-10-21 19:21:11 +02:00
parent deac3e232c
commit 6bcfbeb5b2
3 changed files with 88 additions and 2 deletions
+21 -2
View File
@@ -408,12 +408,21 @@ func issueOnAssign(ctx context.Context, open *stores, node, module string) strin
return ""
}
user := broker.Principal{Kind: broker.KindModule, Node: node, Module: module}.Username()
if _, reads := m.OwnSecrets["broker"]; !reads {
_, reads := m.OwnSecrets["broker"]
if !reads && !runsTheProvisionerClient(m) {
// A module whose bus accounts are its processes' alone has none of its own: only the processes that
// are not issued yet are issued (issue 1226).
return processAccountsOnAssign(ctx, inv, m, node)
}
if !reads {
// A module running the provisioner client alone is minted the client's account (issue 917).
user = broker.Principal{Kind: broker.KindProvisioner, Node: node, Module: module}.Username()
}
if _, minted, err := inv.BusUserHash(ctx, user); err != nil || minted {
if _, minted, err := inv.BusUserHash(ctx, user); err != nil {
return ""
} else if minted {
// Its own account stands and is kept; a process it gained is not issued yet (issue 1226).
return processAccountsOnAssign(ctx, inv, m, node)
}
busAddress, err := broker.BusAddress()
if err == nil {
@@ -426,6 +435,16 @@ func issueOnAssign(ctx context.Context, open *stores, node, module string) strin
return fmt.Sprintf("its bus credential is issued and sealed to %s, and arrives with the push", node)
}
// processAccountsOnAssign issues the process accounts of an assigned module not issued yet, and says what it did.
func processAccountsOnAssign(ctx context.Context, inv *inventory.Inventory, m catalogue.Manifest, node string) string {
line, err := issueMissingProcessAccounts(ctx, inv, m, node)
if err != nil {
return fmt.Sprintf("its process accounts are not issued (%v): `module issue %s --node %s` first — "+
"`push %s` refuses to send %s until it is", err, m.Module, node, node, m.Module)
}
return line
}
// refusedMovingTheController refuses assigning or unassigning the controller's module through a verb (review of
// novox/hq ADR 0272): the node it is assigned to is the control-node, and the control-node's operator account is
// the controller's terminal, so whoever moves the module chooses the terminal. At the terminal alone, as every
+3
View File
@@ -768,6 +768,9 @@ func takeIn(ctx context.Context, inv *inventory.Inventory, result link.BuildResu
}
return manifest, kept, err
}
// A bus process the new definition gains gets its account now, on each machine assigned the module, so the
// next push composes a real account (issue 1226).
issueGainedProcessAccounts(ctx, inv, manifest)
// Which repository it is registered from, by the forge's own id (novox/hq ADR 0266).
if err := inv.SetSourceIdentity(ctx, manifest.Module, repoID); err != nil {
return manifest, kept, err
@@ -0,0 +1,64 @@
//go:build integration
package main
import (
"path/filepath"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"github.com/novox/mesh-controller/internal/broker"
)
// 1226/AC2: given an assigned module whose new definition adds a bus process, when the delivery records it,
// that process's account is issued and the next compose carries a real account; and (AC1) before it, the
// composition refuses the module naming issue; (AC3) a later record does not replace the account.
func TestAGainedProcessIsIssuedWhenTheDeliveryRecordsIt(t *testing.T) {
open := aMesh(t)
ctx := t.Context()
inv := open.inventory
dir := t.TempDir()
if err := busCertificate([]string{"--into", dir}); err != nil {
t.Fatal(err)
}
t.Setenv(broker.AddressVar, "127.0.0.1:4222")
t.Setenv(broker.CertificateVar, filepath.Join(dir, "tls.crt"))
// Assigned without the process, then a definition that gains it.
plain, err := catalogue.ParseManifest([]byte(`{"module":"mesh-mail","version":"1",
"own-secrets":{"broker-send":{"path":"/var/lib/mesh-mail/broker-send","owner":"mesh-mail-send"}},
"resources":[{"id":"account","type":"user","name":"mesh-mail-send"}]}`))
if err != nil {
t.Fatal(err)
}
register(t, open, plain)
if _, err := assign(ctx, open, "laptop", "mesh-mail"); err != nil {
t.Fatal(err)
}
gained := mailSenderWithProcess()
register(t, open, gained)
// 1226/AC1 on the store: nothing issued, so the compose refuses, naming issue.
if _, err := prepareSend(ctx, open, "laptop"); err == nil || !strings.Contains(err.Error(), "mesh-controller.issue laptop mesh-mail") {
t.Fatalf("1226/AC1: the composition did not refuse the unissued account naming issue: %v", err)
}
issueGainedProcessAccounts(ctx, inv, gained)
hash, held, err := inv.BusUserHash(ctx, "laptop.mesh-mail.send")
if err != nil || !held {
t.Fatalf("1226/AC2: the gained process's account was not issued: held %v, %v", held, err)
}
if _, err := prepareSend(ctx, open, "laptop"); err != nil {
t.Fatalf("1226/AC2: the next compose does not carry the account: %v", err)
}
// 1226/AC3: recording the definition again, and composing, replaces nothing.
issueGainedProcessAccounts(ctx, inv, gained)
again, _, err := inv.BusUserHash(ctx, "laptop.mesh-mail.send")
if err != nil || again != hash {
t.Fatalf("1226/AC3: the issued account was replaced (%v)", err)
}
}