Answer an unreadable assignment as not yet, never a refusal (issue 928)
This commit is contained in:
@@ -143,8 +143,8 @@ func (d Declared) May(caller, name string) (bool, string) {
|
||||
}
|
||||
raw, err := d.Read(node + "." + module)
|
||||
if err != nil {
|
||||
return false, fmt.Sprintf("the assignment %s.%s could not be read, so what it names is not known: %v", node,
|
||||
module, err)
|
||||
return false, fmt.Sprintf(notYetPrefix+"the assignment %s.%s could not be read, so what it names is not known: %v",
|
||||
node, module, err)
|
||||
}
|
||||
if raw == nil {
|
||||
return false, fmt.Sprintf("%s is not on %s: neither assigned there nor required by a module there", module,
|
||||
@@ -173,3 +173,8 @@ func (d Declared) May(caller, name string) (bool, string) {
|
||||
}
|
||||
return false, fmt.Sprintf("%s's plan on %s names no secret %s", module, node, secret)
|
||||
}
|
||||
|
||||
// notYetPrefix begins a reason that is not a refusal (issue 928): what the rule needs could not be read just now, a
|
||||
// bus error rather than an answer. The vault says "holds no secret named ... yet", which the client waits on and asks
|
||||
// again, and records no refusal.
|
||||
const notYetPrefix = "not yet: "
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
||||
)
|
||||
|
||||
// Issue 928: an assignment the vault could not read (the controller was being pushed, "no responders") is not a
|
||||
// refusal the client keeps for ten minutes: it is "not yet", the answer the client waits on and asks again.
|
||||
func TestAnAssignmentThatCannotBeReadIsNotYetAndNotARefusal(t *testing.T) {
|
||||
down := errors.New("nats: no responders available for request")
|
||||
read := func(string) ([]byte, error) { return nil, down }
|
||||
v := newVault(t, Declared{Read: read})
|
||||
asker, _ := seal.GenerateKeyPair()
|
||||
for _, name := range []string{"own/g14/claude-code/token", "provision/novox/gitea-read/g14/claude-code"} {
|
||||
_, err := v.Get("provisioner.g14", name, asker.PublicKey)
|
||||
if err == nil {
|
||||
t.Fatalf("%s: answered while the assignment cannot be read and the vault holds nothing", name)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "holds no secret named") || !strings.Contains(err.Error(), "no responders") {
|
||||
t.Errorf("%s: the answer is %q, not the not-yet the client waits on, with the reason", name, err)
|
||||
}
|
||||
}
|
||||
hs, _ := v.Store.Handed(10)
|
||||
for _, h := range hs {
|
||||
if h.Outcome == "refused" {
|
||||
t.Errorf("a read error was recorded as a refusal: %+v", h)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A pair row asked for while the consumer's assignment cannot be read is not yet either, and a row whose
|
||||
// assignment reads as absent stays "not on".
|
||||
func TestAPairRowWhoseAssignmentCannotBeReadIsNotYet(t *testing.T) {
|
||||
down := errors.New("nats: no responders available for request")
|
||||
parts := []string{"pair", "novox", "db", "g14", "claude-code"}
|
||||
why := pairRowRefusal(func(string) ([]byte, error) { return nil, down }, "g14", parts)
|
||||
if !strings.HasPrefix(why, notYetPrefix) || !strings.Contains(why, "no responders") {
|
||||
t.Errorf("a read error answered %q, want a not-yet carrying the reason", why)
|
||||
}
|
||||
why = pairRowRefusal(func(string) ([]byte, error) { return nil, nil }, "g14", parts)
|
||||
if strings.HasPrefix(why, notYetPrefix) || !strings.Contains(why, "is not on g14") {
|
||||
t.Errorf("an absent assignment answered %q, want the refusal 'not on'", why)
|
||||
}
|
||||
}
|
||||
@@ -780,7 +780,10 @@ func pairRowRefusal(read AssignmentReader, node string, parts []string) string {
|
||||
return "the vault cannot read the assignments, so it cannot tell what " + node + "'s plans name"
|
||||
}
|
||||
raw, err := read(consumer + "." + module)
|
||||
if err != nil || raw == nil {
|
||||
if err != nil {
|
||||
return fmt.Sprintf(notYetPrefix+"the assignment %s.%s could not be read: %v", consumer, module, err)
|
||||
}
|
||||
if raw == nil {
|
||||
return fmt.Sprintf("%s is not on %s", module, consumer)
|
||||
}
|
||||
var a plannedProvisions
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -237,6 +238,11 @@ func (v *Vault) Get(caller, name, publicKey string) (Answer, error) {
|
||||
return Answer{}, fmt.Errorf("%s may not ask for %s: %s", caller, name, why)
|
||||
}
|
||||
} else if ok, why := askers.May(caller, name); !ok {
|
||||
if reason, notYet := strings.CutPrefix(why, notYetPrefix); notYet {
|
||||
// Not a refusal (issue 928): the client waits and asks again, and nothing is recorded against it.
|
||||
v.log("not yet %s to %s: %s", name, caller, reason)
|
||||
return Answer{}, fmt.Errorf("the vault holds no secret named %s yet: %s", name, reason)
|
||||
}
|
||||
v.log("refused %s to %s: %s", name, caller, why)
|
||||
_ = v.recordHanding(Handing{To: caller, Name: name, Outcome: "refused", Why: why})
|
||||
return Answer{}, fmt.Errorf("%s may not ask for %s: %s", caller, name, why)
|
||||
|
||||
Reference in New Issue
Block a user