Let a provider not ready yet wait and keep serving what it served, so a restarted gitea no longer stops serving its consumers until its keys load (issue 1954)
This commit is contained in:
+21
-1
@@ -215,6 +215,11 @@ func New(opts Options) *Provider {
|
||||
plans: map[string][]byte{}, provisions: map[string][]byte{}, said: map[string]string{}, poke: make(chan struct{}, 1)}
|
||||
}
|
||||
|
||||
// ErrNotReady is what a Make returns, wrapped or alone, when the provider cannot make a consumer's provision yet but
|
||||
// will without anyone's hand (its keys not read since a restart): the entry waits and keeps serving what it served.
|
||||
// Any other error is a failure, and its entry serves nothing.
|
||||
var ErrNotReady = errors.New("not ready yet")
|
||||
|
||||
// What a pass did for one bind.
|
||||
const (
|
||||
Answered = "answered"
|
||||
@@ -493,7 +498,22 @@ func (p *Provider) answer(ctx context.Context, e Entry, have []byte) Outcome {
|
||||
}
|
||||
}
|
||||
if p.opts.Make != nil {
|
||||
if err := p.opts.Make(ctx, Consumer{Entry: e, Credential: credential}); err != nil {
|
||||
if err := p.opts.Make(ctx, Consumer{Entry: e, Credential: credential}); errors.Is(err, ErrNotReady) {
|
||||
// **Not ready is no failure** (the regression of 2026-10-30: gitea after a restart, its keys not read yet,
|
||||
// stopped serving every consumer): the entry says waits with why, and keeps what the last entry served, so a
|
||||
// consumer goes on with it until the provider can make it again.
|
||||
why := "the provider is not ready to make it yet: " + redact(err.Error(), credential)
|
||||
waiting := e
|
||||
var prev Entry
|
||||
if len(have) > 0 && json.Unmarshal(have, &prev) == nil && prev.State == provisions.Provided {
|
||||
waiting.Values, waiting.VaultName, waiting.User = prev.Values, prev.VaultName, prev.User
|
||||
} else {
|
||||
waiting.Values, waiting.VaultName, waiting.User = nil, "", ""
|
||||
}
|
||||
waiting.State, waiting.Why = provisions.Waits, why
|
||||
p.write(ctx, waiting, have, Outcome{Key: key, Status: Waiting, Why: why})
|
||||
return Outcome{Key: key, Status: Waiting, Why: why}
|
||||
} else if err != nil {
|
||||
why := "the provider could not make it: " + redact(err.Error(), credential)
|
||||
// Said in its entry, which the holder of mesh-provisioner reads (ADR 0570 clause 3), with nothing served.
|
||||
failed := e
|
||||
|
||||
@@ -525,6 +525,33 @@ func TestAFailedMakeIsSaidWithoutTheCredential(t *testing.T) {
|
||||
strings.Contains(string(raw), "hunter2") {
|
||||
t.Fatalf("the failed entry is %s", raw)
|
||||
}
|
||||
|
||||
// A provider not ready yet (gitea after a restart, its keys not read; the regression of 2026-10-30) is no failure:
|
||||
// a first entry waits serving nothing, and once made, a later not ready keeps serving what it served.
|
||||
m.fail = fmt.Errorf("keys not read yet: %w", ErrNotReady)
|
||||
_ = bus.Delete(context.Background(), BucketOf("postgres"), "ace.letta.postgres-database")
|
||||
if out, _ := p.Pass(context.Background()); len(out) != 1 || out[0].Status != Waiting {
|
||||
t.Fatalf("not ready at first: %+v", out)
|
||||
}
|
||||
raw = bus.get(BucketOf("postgres"), "ace.letta.postgres-database")
|
||||
if err := json.Unmarshal(raw, &e); err != nil || e.State != "waits" || !strings.Contains(e.Why, "keys not read yet") || e.Values != nil {
|
||||
t.Fatalf("the first entry not ready is %s", raw)
|
||||
}
|
||||
m.fail = nil
|
||||
if out, _ := p.Pass(context.Background()); len(out) != 1 || out[0].Status != Answered {
|
||||
t.Fatalf("made: %+v", out)
|
||||
}
|
||||
var made Entry
|
||||
_ = json.Unmarshal(bus.get(BucketOf("postgres"), "ace.letta.postgres-database"), &made)
|
||||
m.fail = ErrNotReady
|
||||
if out, _ := p.Pass(context.Background()); len(out) != 1 || out[0].Status != Waiting {
|
||||
t.Fatalf("not ready after a restart: %+v", out)
|
||||
}
|
||||
raw = bus.get(BucketOf("postgres"), "ace.letta.postgres-database")
|
||||
if err := json.Unmarshal(raw, &e); err != nil || e.State != "waits" || e.VaultName != made.VaultName || e.User != made.User ||
|
||||
fmt.Sprint(e.Values) != fmt.Sprint(made.Values) || len(made.Values) == 0 {
|
||||
t.Fatalf("not ready after it served, the entry is %s, it served %+v", raw, made)
|
||||
}
|
||||
}
|
||||
|
||||
// Run watches the binds on the stand-in bus: a consumer that binds is answered, and one that unbinds is removed,
|
||||
|
||||
Reference in New Issue
Block a user