Take every catalogued module's done key once at the start, beside the updates-only watch, so a key written or interrupted across a restart is registered (issue 1945)
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -24,7 +25,7 @@ import (
|
|||||||
// carries the module's permissions at the new fingerprint: a new revision of the bus module's composition alone,
|
// carries the module's permissions at the new fingerprint: a new revision of the bus module's composition alone,
|
||||||
// written into its want at once by the assigner), and writes `grants.<module>.<fp>`. The assigner's sixth and seventh
|
// written into its want at once by the assigner), and writes `grants.<module>.<fp>`. The assigner's sixth and seventh
|
||||||
// rules then let a want name the fingerprint. **No timer**: woken by the keys, and a starting controller looks at every
|
// rules then let a want name the fingerprint. **No timer**: woken by the keys, and a starting controller looks at every
|
||||||
// module once (B5's start pass).
|
// module once (doneKeysAtStart).
|
||||||
|
|
||||||
// doneKeySteps are what taking a module's done key in does, each a step a test may stand in for.
|
// doneKeySteps are what taking a module's done key in does, each a step a test may stand in for.
|
||||||
type doneKeySteps struct {
|
type doneKeySteps struct {
|
||||||
@@ -261,4 +262,55 @@ func watchDoneKeys(ctx context.Context, open *stores) {
|
|||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
closeKeys()
|
closeKeys()
|
||||||
}()
|
}()
|
||||||
|
// And every module once now, after the watch is open so no write falls between the two.
|
||||||
|
inBackground(func() {
|
||||||
|
doneKeysAtStart(ctx, catalogedModules(open), func(module string) { takeDoneKeyAtStart(ctx, open, module) })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// doneKeysAtStart takes in the done key of every catalogued module once, one after another: the start's look the watch of
|
||||||
|
// new writes cannot give (it hears updates only). A done key written while no controller served, or whose take-in the
|
||||||
|
// controller this one replaced was stopped in, is taken here; without it the module stays in the node's own composition
|
||||||
|
// with no credential until its keys are written again (issue 1945, live on 2026-10-25 at 02:34). A take-in with nothing
|
||||||
|
// to do does nothing; a catalogue that cannot be read is said.
|
||||||
|
func doneKeysAtStart(ctx context.Context, modules func(context.Context) ([]string, error), take func(module string)) {
|
||||||
|
names, err := modules(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("done keys: the modules are not looked at once at the start: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, m := range names {
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
take(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// takeDoneKeyAtStart is one take-in of the start's look, under the module's lock; one that fails is handed to takeDoneKey,
|
||||||
|
// which tries again every doneKeyRetry, loudly, so a failing module never holds up the look at the others.
|
||||||
|
func takeDoneKeyAtStart(ctx context.Context, open *stores, module string) {
|
||||||
|
mu, _ := doneKeysMu.LoadOrStore(module, &sync.Mutex{})
|
||||||
|
mu.(*sync.Mutex).Lock()
|
||||||
|
err := takeDoneKeyOnce(ctx, open, module)
|
||||||
|
mu.(*sync.Mutex).Unlock()
|
||||||
|
if err != nil && !errors.Is(err, errKeyWaits) {
|
||||||
|
inBackground(func() { takeDoneKey(ctx, open, module) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// catalogedModules are the names of every module the catalogue holds.
|
||||||
|
func catalogedModules(open *stores) func(context.Context) ([]string, error) {
|
||||||
|
return func(ctx context.Context) ([]string, error) {
|
||||||
|
entries, err := open.inventory.Catalogued(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make([]string, 0, len(entries))
|
||||||
|
for _, e := range entries {
|
||||||
|
out = append(out, e.Manifest.Module)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A starting controller takes in the done key of every catalogued module once, beside the watch of new writes: a done key
|
||||||
|
// written while no controller served, or whose take-in the controller it replaced was stopped in, is otherwise never
|
||||||
|
// taken, and its module stays in the node's own composition with no credential (issue 1945, live on 2026-10-25 at 02:34:
|
||||||
|
// the controller replaced mid take-in left mesh-provisioner-client, gitea, postgres and many more unregistered at their
|
||||||
|
// new fingerprints, and the new one, watching updates only, never looked).
|
||||||
|
func TestAStartingControllerTakesEveryModulesDoneKeyOnce(t *testing.T) {
|
||||||
|
var took []string
|
||||||
|
doneKeysAtStart(context.Background(), func(context.Context) ([]string, error) {
|
||||||
|
return []string{"mesh-provisioner-client", "gitea", "postgres"}, nil
|
||||||
|
}, func(module string) { took = append(took, module) })
|
||||||
|
sort.Strings(took)
|
||||||
|
if want := []string{"gitea", "mesh-provisioner-client", "postgres"}; !reflect.DeepEqual(took, want) {
|
||||||
|
t.Fatalf("took %v, want %v", took, want)
|
||||||
|
}
|
||||||
|
// A catalogue that cannot be read takes nothing and is said, never fatal.
|
||||||
|
took = nil
|
||||||
|
doneKeysAtStart(context.Background(), func(context.Context) ([]string, error) { return nil, errors.New("down") },
|
||||||
|
func(module string) { took = append(took, module) })
|
||||||
|
if len(took) != 0 {
|
||||||
|
t.Fatalf("took %v from a catalogue that could not be read", took)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user