Grant each provisioner client a read of its node's own have, so it refills when the node's own composition moves (issue 1945)
mesh/delivery checking
mesh/delivery-group group fix/r6-node-composition held: a member is held: novox/mesh-sdk@10b6e202320f (merged without a passing check: only a person decides wh…
mesh/check checking 3dadc46ee6cc on ba4d15657343 | novox/mesh-controller#458 in group fix/r6-node-composition

This commit is contained in:
2026-10-25 01:58:27 +02:00
parent d8aa565fee
commit 3dadc46ee6
3 changed files with 41 additions and 0 deletions
+5
View File
@@ -3,6 +3,7 @@ package broker
import (
"errors"
"fmt"
"git.novox.be/novox/mesh-sdk/go/delivery"
"strings"
"github.com/nats-io/nats.go"
@@ -131,6 +132,10 @@ func provisionerGrants(p Principal) (pub, sub []string, err error) {
pub = append(pub, "$KV."+bucket+"."+p.Node+".>")
}
pub = append(pub, wholeBucketRead(ProvisionerRegistryBucket)...)
// Its own node's own have, `<node>.node`, read and never written, and no other key of the bucket: it fills every
// module again when the node-engine moves to a new composition of the node's own (issue 1945), since an apply can
// leave a filled file as the composition had it.
pub = append(pub, readGrants(delivery.HaveBucket, p.Node+".node")...)
pub = append(pub, "prov.applied."+p.Node+".>")
token := CallerToken(p.Username())
if token == "" {
+29
View File
@@ -0,0 +1,29 @@
package broker
import (
"slices"
"testing"
)
// The provisioner client reads its own node's own have, `<node>.node` in node-engine_have, and no other key of it: it
// fills every module again when the node-engine moves to a new composition of the node's own (issue 1945, live on
// 2026-10-25: the files a composition had written empty were refilled by nothing).
func TestTheProvisionerReadsItsNodesOwnHaveAlone(t *testing.T) {
p, err := PermissionsFor(Principal{Kind: KindProvisioner, Node: "g14", PasswordHash: "x"})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{"$JS.API.STREAM.INFO.KV_node-engine_have",
"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.g14.node",
"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.g14.node"} {
if !slices.Contains(p.Publish, want) {
t.Errorf("the provisioner may not %s", want)
}
}
for _, s := range p.Publish {
if s == "$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.g14.>" ||
s == "$JS.API.DIRECT.GET.KV_node-engine_have.>" || s == "$KV.node-engine_have.g14.node" {
t.Errorf("the provisioner is granted %s", s)
}
}
}
+7
View File
@@ -93,6 +93,13 @@ func TestTheProvisionerClientIsGrantedItsMachinesKeysAndNothingElse(t *testing.T
"$JS.API.CONSUMER.CREATE.KV_mesh-provisioner_registry.>",
"$JS.API.CONSUMER.DELETE.KV_mesh-provisioner_registry.>",
"$JS.FC.KV_mesh-provisioner_registry.>")
// Its node's own have, one key, read (issue 1945).
wantPub = append(wantPub,
"$JS.API.STREAM.INFO.KV_node-engine_have",
"$JS.API.DIRECT.GET.KV_node-engine_have.$KV.node-engine_have.g14.node",
"$JS.API.CONSUMER.CREATE.KV_node-engine_have.*.$KV.node-engine_have.g14.node",
"$JS.API.CONSUMER.DELETE.KV_node-engine_have.>",
"$JS.FC.KV_node-engine_have.>")
wantPub = append(wantPub, "prov.applied.g14.>", "mesh.seat.mesh-vault.call.get.provisioner~g14",
"prov.changed.g14.>")
slices.Sort(wantPub)