Ask mesh-sdk's Plan.Hands wherever the vault and the provisioner client judge a module handed, and refuse in the catalogue check any line that takes a plan existing for it, so a plan of provisions alone never again withholds a module's secrets (issue 774)

This commit is contained in:
2026-10-31 00:00:46 +01:00
parent ca48943b92
commit 3c3d018dbc
11 changed files with 148 additions and 27 deletions
@@ -870,4 +870,11 @@ func TestAPlanOfProvisionsAloneFetchesNoTemplates(t *testing.T) {
if targets, wrong := fillTargets(Plan{}, texts); wrong != "" || len(targets) != 0 {
t.Fatalf("filled %v: %s", targets, wrong)
}
// And such a plan hands its module to nobody (catalogue.Plan.Hands): read as no plan, never refused as one that
// names no source, and the module's files stay its node's.
routeOnly := `{"node":"g14","module":"grafana","provisioning":{"templates":{"digest":"","source":""},"files":null,` +
`"provisions":[{"provision":"route","provider":"g14","provider-module":"route-proxy"}]}}`
if module, _, err := planOf("g14", "g14.grafana", []byte(routeOnly)); module != "grafana" || !errors.Is(err, errNoPlan) {
t.Fatalf("a plan of provisions alone reads as %s: %v, want no plan", module, err)
}
}
@@ -77,7 +77,7 @@ func takeBinds(s *state, key string, value []byte, gone bool) bool {
return was != nil
}
b := binding{node: a.Node, module: a.Module}
if a.Provisioning != nil {
if a.Provisioning != nil { // binds, not handing: a plan of provisions alone still binds its consumer
b.provisions = a.Provisioning.Provisions
}
s.binds[key] = b
@@ -178,7 +178,14 @@ func planOf(node, key string, value []byte) (string, Plan, error) {
return "", Plan{}, fmt.Errorf("the assignment %s names node %q and module %q, not its key's", key, a.Node,
a.Module)
}
if len(a.Provisioning) == 0 || string(a.Provisioning) == "null" {
if len(a.Provisioning) == 0 || string(a.Provisioning) == "null" { // no plan at all, not handing
return a.Module, Plan{}, errNoPlan
}
// **A plan that hands nothing is no plan of this client's** (mesh-sdk catalogue.Plan.Hands, issue 774): the seat keys
// a provision its provider's helper answers for a module not handed (grafana's route), and that plan names
// provisions alone. Read leniently for the rule; a plan that hands is read strictly below.
var rule catalogue.Plan
if err := json.Unmarshal(a.Provisioning, &rule); err == nil && !rule.Hands() {
return a.Module, Plan{}, errNoPlan
}
var p Plan