diff --git a/rest/admin_api.go b/rest/admin_api.go index 8e841f0608..d4be499190 100644 --- a/rest/admin_api.go +++ b/rest/admin_api.go @@ -512,15 +512,17 @@ func (h *handler) handleGetConfig() error { return err } - replications, err := database.SGReplicateMgr.GetReplications() - if err != nil { - return err - } + if database.SGReplicateMgr != nil { + replications, err := database.SGReplicateMgr.GetReplications() + if err != nil { + return err + } - dbConfig.Replications = make(map[string]*db.ReplicationConfig, len(replications)) + dbConfig.Replications = make(map[string]*db.ReplicationConfig, len(replications)) - for replicationName, replicationConfig := range replications { - dbConfig.Replications[replicationName] = replicationConfig.ReplicationConfig.Redacted(h.ctx()) + for replicationName, replicationConfig := range replications { + dbConfig.Replications[replicationName] = replicationConfig.ReplicationConfig.Redacted(h.ctx()) + } } } diff --git a/rest/api_test.go b/rest/api_test.go index 7c463f3aa8..8321fd8cc3 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -28,6 +28,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "testing" "time" @@ -3710,3 +3711,39 @@ func TestUnsupportedServerConfigOptions(t *testing.T) { }) } } + +func TestGetConfigAfterFailToStartOnlineProcess(t *testing.T) { + rt := NewRestTester(t, &RestTesterConfig{ + PersistentConfig: true, + }) + defer rt.Close() + + dbConfig := rt.NewDbConfig() + dbConfig.StartOffline = base.Ptr(true) + resp := rt.CreateDatabase("db", dbConfig) + RequireStatus(t, resp, http.StatusCreated) + + // create invalid user to cause StartOnlineProcesses error + rt.GetDatabase().Options.ConfigPrincipals.Users = map[string]*auth.PrincipalConfig{ + "alice": { + JWTChannels: base.SetOf("asdf"), + }, + } + + // Directly set the database state to DBStarting to simulate regular startup. + // This direct atomic manipulation is required in this test to simulate a state transition. + // This is safe in the context of this test. + atomic.StoreUint32(&rt.GetDatabase().State, db.DBStarting) + rt.WaitForDBState(db.RunStateString[db.DBStarting]) + + // Can't trigger error case from REST API - call directly + rt.ServerContext().asyncDatabaseOnline(base.NewNonCancelCtx(), rt.GetDatabase(), nil, rt.ServerContext().GetDbVersion("db")) + + // Error should cause db to stay offline. + rt.WaitForDBState(db.RunStateString[db.DBOffline]) + require.Equal(t, int64(1), rt.GetDatabase().DbStats.Database().TotalOnlineFatalErrors.Value()) + + // Original bug will trigger panic here on this endpoint + resp = rt.SendAdminRequest(http.MethodGet, "/_config?include_runtime=true", "") + RequireStatus(t, resp, http.StatusOK) +}