diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index 0fc99700..ceec0ad6 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -348,6 +348,9 @@ func (sys *System) getRecord(app core.App) (*core.Record, error) { record, err := app.FindRecordById("systems", sys.Id) if err != nil || record == nil { _ = sys.manager.RemoveSystem(sys.Id) + if err == nil { + err = fmt.Errorf("system record %s not found", sys.Id) + } return nil, err } return record, nil @@ -377,10 +380,16 @@ func (sys *System) HasUser(app core.App, user *core.Record) bool { // setDown marks a system as down in the database. // It takes the original error that caused the system to go down and returns any error // encountered during the process of updating the system status. +// It is a no-op if the system's context has been cancelled. func (sys *System) setDown(originalError error) error { if sys.Status == down || sys.Status == paused { return nil } + // the updater can race shutdown, and the app may already be disposed by the + // time we get here, so don't touch the database once the context is cancelled + if sys.ctx != nil && sys.ctx.Err() != nil { + return sys.ctx.Err() + } record, err := sys.getRecord(sys.manager.hub) if err != nil { return err diff --git a/internal/hub/systems/system_test.go b/internal/hub/systems/system_test.go index 4fbff5b4..ab720c7a 100644 --- a/internal/hub/systems/system_test.go +++ b/internal/hub/systems/system_test.go @@ -3,6 +3,7 @@ package systems import ( + "context" "testing" "github.com/henrygd/beszel/internal/entities/system" @@ -157,3 +158,17 @@ func TestCombinedData_MigrateDeprecatedFields(t *testing.T) { } }) } + +func TestSetDownAfterContextCancelled(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + // manager is nil on purpose: setDown must bail out before touching the app + sys := &System{Status: up, ctx: ctx} + + if err := sys.setDown(nil); err != context.Canceled { + t.Fatalf("expected context.Canceled, got %v", err) + } + if sys.Status != up { + t.Fatalf("status should be untouched, got %q", sys.Status) + } +}