fix: refresh mutations and defer busy quit
This commit is contained in:
@@ -153,6 +153,18 @@ func (a *Application) dispatch(intent Intent, publish func(Result)) Result {
|
||||
}
|
||||
defer unlock()
|
||||
}
|
||||
// A different process may have changed the persisted Active Profile since
|
||||
// this Application was constructed. Reload only after the global mutation
|
||||
// lock is held, before selecting a Profile or invoking a Target. Load is
|
||||
// deliberately separate from Store.Mutate so no Profile-store lock is nested.
|
||||
fresh, err := a.store.Load()
|
||||
if err != nil {
|
||||
return Result{Snapshot: a.Snapshot(), Err: fmt.Errorf("reload Profile state for mutation: %w", err)}
|
||||
}
|
||||
a.state = fresh
|
||||
if intent.Kind == SetTargetDesired || intent.Kind == ActivateProfile {
|
||||
a.refreshInspections()
|
||||
}
|
||||
if intent.Kind == SetTargetDesired {
|
||||
return a.setTargetDesired(intent)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"proxyctl/internal/app"
|
||||
"proxyctl/internal/dockerclient"
|
||||
"proxyctl/internal/operationlock"
|
||||
"proxyctl/internal/profile"
|
||||
"proxyctl/internal/shell"
|
||||
"proxyctl/internal/target"
|
||||
@@ -56,6 +57,52 @@ func (t *coordinatedTarget) SetDesired(_ context.Context, enabled bool, active *
|
||||
return target.OperationResult{Inspection: target.Inspection{State: t.state, Evidence: "matches " + active.Name}}
|
||||
}
|
||||
|
||||
func TestSecondApplicationUsesPersistedActiveProfileChangedByFirstApplication(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
storePath := filepath.Join(root, "profiles.json")
|
||||
bootstrap, err := app.New(profile.NewFileStore(storePath))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, name := range []string{"old", "new"} {
|
||||
result := bootstrap.Dispatch(app.Intent{Kind: app.CreateProfile, Profile: profile.Profile{Name: name, Mode: profile.SingleMode, SingleURL: "http://" + name + ".example:8080"}})
|
||||
if result.Err != nil {
|
||||
t.Fatalf("create %s Profile: %v", name, result.Err)
|
||||
}
|
||||
}
|
||||
if result := bootstrap.Dispatch(app.Intent{Kind: app.ActivateProfile, Name: "old"}); result.Err != nil {
|
||||
t.Fatalf("activate old Profile: %v", result.Err)
|
||||
}
|
||||
|
||||
firstTarget := &coordinatedTarget{id: "shell", state: target.Disabled}
|
||||
secondTarget := &coordinatedTarget{id: "shell", state: target.Disabled}
|
||||
first, err := app.NewWithTargetsAndLock(profile.NewFileStore(storePath), []target.Target{firstTarget}, operationlock.New(filepath.Join(root, "operation.lock")))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := app.NewWithTargetsAndLock(profile.NewFileStore(storePath), []target.Target{secondTarget}, operationlock.New(filepath.Join(root, "operation.lock")))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := second.Snapshot().ActiveProfile; got != "old" {
|
||||
t.Fatalf("second Application starting Active Profile = %q, want old", got)
|
||||
}
|
||||
|
||||
if result := first.Dispatch(app.Intent{Kind: app.ActivateProfile, Name: "new"}); result.Err != nil {
|
||||
t.Fatalf("first Application activates new Profile: %v", result.Err)
|
||||
}
|
||||
result := second.Dispatch(app.Intent{Kind: app.SetTargetDesired, TargetID: "shell", Enabled: true})
|
||||
if result.Err != nil {
|
||||
t.Fatalf("second Application enables Target: %v", result.Err)
|
||||
}
|
||||
if secondTarget.appliedName != "new" {
|
||||
t.Fatalf("second Application applied stale Profile %q, want freshly persisted Active Profile new", secondTarget.appliedName)
|
||||
}
|
||||
if result.Snapshot.ActiveProfile != "new" {
|
||||
t.Fatalf("second Application snapshot Active Profile = %q, want new", result.Snapshot.ActiveProfile)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveProfileSwitchCoordinatesEnabledTargetsWithoutRollback(t *testing.T) {
|
||||
first := &coordinatedTarget{id: "first", state: target.Disabled}
|
||||
second := &coordinatedTarget{id: "second", state: target.Disabled, failName: "new"}
|
||||
|
||||
@@ -104,8 +104,8 @@ func TestApplicationReloadsStateAfterCommittedDurabilityError(t *testing.T) {
|
||||
if !errors.As(result.Err, &committed) {
|
||||
t.Fatalf("mutation error = %v, want committed durability error", result.Err)
|
||||
}
|
||||
if store.loadCalls != 2 {
|
||||
t.Fatalf("Load calls = %d, want startup plus post-commit reload", store.loadCalls)
|
||||
if store.loadCalls != 3 {
|
||||
t.Fatalf("Load calls = %d, want startup, pre-mutation, and post-commit reloads", store.loadCalls)
|
||||
}
|
||||
if len(result.Snapshot.Profiles) != 1 || result.Snapshot.Profiles[0].Name != "committed" {
|
||||
t.Fatalf("Application snapshot after committed error = %#v", result.Snapshot)
|
||||
|
||||
Reference in New Issue
Block a user