fix: a require-only consumer of a parameterless provision still asks (and is minted a credential)
A consumer that requires a provision whose serves names no consumer key (redis-cache, amqp) contributes no payload, but it still ASKS for it. ContributionsFrom keyed 'asks' on contributions alone, so such a consumer's grant got From='' — read as withdrawn — and the provider never created its account. redis-cache consumers (e.g. baserow) were silently unprovisioned, tolerated only by their embedded fallback. A module asks iff it still requires the provision, whether or not it hands anything up. Regression test added. Found by the lavinmq AMQP provider bed (given:[] for a require-only amqp consumer); fix lab-proven green there. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -333,6 +333,30 @@ func TestAConsumersOwnContributionsAreStillThere(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestARequireOnlyConsumerOfAParameterlessProvisionStillAsks(t *testing.T) {
|
||||
// A consumer that requires a parameterless provision (one whose serves names no consumer key —
|
||||
// redis-cache, amqp) contributes no payload, but it still ASKS for the provision and must be
|
||||
// granted a credential. Keying "asks" on contributions alone gave its grant From="" — read as
|
||||
// withdrawn — so the provider never created the account and the consumer authenticated nowhere.
|
||||
r := Resolution{
|
||||
Node: "laptop",
|
||||
Modules: []Manifest{{
|
||||
Module: "amqp-ping",
|
||||
Requires: []string{"amqp"},
|
||||
}},
|
||||
}
|
||||
values, asks, err := r.ContributionsFrom("amqp", "amqp-ping", SettingsBy{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !asks {
|
||||
t.Fatal("a require-only consumer was treated as not asking; its grant would be withdrawn and it would never be provisioned")
|
||||
}
|
||||
if values == nil {
|
||||
t.Fatalf("asks is true but values is nil; expected an empty contribution, got %v", values)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAConsumerThatStoppedAskingIsWithdrawn(t *testing.T) {
|
||||
// Withdrawal is how a credential is taken away. The provisioner removes what nobody asks for,
|
||||
// and it can only do that if the mesh stops asking — a consumer that was unassigned would
|
||||
|
||||
@@ -637,6 +637,21 @@ func (r Resolution) ContributionsFrom(requirement, module string, settings Setti
|
||||
return g.Values, true, nil
|
||||
}
|
||||
}
|
||||
// It contributes no payload — but a require-only consumer of a parameterless provision (one whose
|
||||
// `serves` names no consumer-supplied key: `redis-cache`, `amqp`) still ASKS for it and must be
|
||||
// granted a credential. Keying "asks" on contributions alone marked those grants withdrawn
|
||||
// (From=""), so the provider never created the account and the consumer authenticated nowhere. A
|
||||
// module asks iff it still requires the provision, whether or not it hands anything up with it.
|
||||
for _, m := range r.Modules {
|
||||
if m.Module != module {
|
||||
continue
|
||||
}
|
||||
for _, req := range m.Requires {
|
||||
if req == requirement {
|
||||
return map[string]any{}, true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nothing on that machine asks for this any more. Said as "not found" rather than as an
|
||||
// error: it is how a credential is withdrawn, and the provider removes what nobody asks for.
|
||||
return nil, false, nil
|
||||
|
||||
Reference in New Issue
Block a user