diff --git a/server/events.go b/server/events.go index 2f709679..5d9f27e1 100644 --- a/server/events.go +++ b/server/events.go @@ -60,6 +60,7 @@ const ( serverDirectReqSubj = "$SYS.REQ.SERVER.%s.%s" serverPingReqSubj = "$SYS.REQ.SERVER.PING.%s" serverStatsPingReqSubj = "$SYS.REQ.SERVER.PING" // use $SYS.REQ.SERVER.PING.STATSZ instead + serverReloadReqSubj = "$SYS.REQ.SERVER.%s.RELOAD" // with server ID leafNodeConnectEventSubj = "$SYS.ACCOUNT.%s.LEAFNODE.CONNECT" // for internal use only remoteLatencyEventSubj = "$SYS.LATENCY.M2.%s" inboxRespSubj = "$SYS._INBOX.%s.%s" @@ -1207,6 +1208,12 @@ func (s *Server) initEventTracking() { if _, err := s.sysSubscribeInternal(accSubsSubj, s.noInlineCallback(s.debugSubscribers)); err != nil { s.Errorf("Error setting up internal debug service for subscribers: %v", err) } + + // Listen for requests to reload the server configuration. + subject = fmt.Sprintf(serverReloadReqSubj, s.info.ID) + if _, err := s.sysSubscribe(subject, s.noInlineCallback(s.reloadConfig)); err != nil { + s.Errorf("Error setting up internal tracking: %v", err) + } } // UserInfo returns basic information to a user about bound account and user permissions. @@ -2679,6 +2686,19 @@ func (s *Server) nsubsRequest(sub *subscription, c *client, _ *Account, subject, s.sendInternalMsgLocked(reply, _EMPTY_, nil, nsubs) } +// accountClaimUpdate will receive claim updates for accounts. +func (s *Server) reloadConfig(sub *subscription, c *client, _ *Account, subject, reply string, hdr, msg []byte) { + if !s.eventsRunning() { + return + } + + optz := &EventFilterOptions{} + s.zReq(c, reply, hdr, msg, optz, optz, func() (interface{}, error) { + // Reload the server config, as requested. + return nil, s.Reload() + }) +} + // Helper to grab account name for a client. func accForClient(c *client) string { if c.acc != nil { diff --git a/server/events_test.go b/server/events_test.go index afcb7ce0..ad116191 100644 --- a/server/events_test.go +++ b/server/events_test.go @@ -2535,6 +2535,73 @@ func TestServerEventsStatszSingleServer(t *testing.T) { checkSubsPending(t, sub, 1) } +func TestServerEventsReload(t *testing.T) { + conf := createConfFile(t, []byte(` + listen: "127.0.0.1:-1" + accounts: { + $SYS { users [{user: "admin", password: "p1d"}]} + test { users [{user: "foo", password: "bar"}]} + } + ping_interval: "100ms" + `)) + opts := LoadConfig(conf) + opts.Trace = true + opts.Debug = true + opts.TraceVerbose = true + s := RunServer(opts) + defer s.Shutdown() + subject := fmt.Sprintf("$SYS.REQ.SERVER.%s.RELOAD", s.info.ID) + + // Connect as a test user and make sure the reload endpoint is not + // accessible. + ncTest, _ := jsClientConnect(t, s, nats.UserInfo("foo", "bar")) + defer ncTest.Close() + testReply := ncTest.NewRespInbox() + sub, err := ncTest.SubscribeSync(testReply) + require_NoError(t, err) + err = ncTest.PublishRequest(subject, testReply, nil) + require_NoError(t, err) + _, err = sub.NextMsg(time.Second) + require_Error(t, err) + + require_True(t, s.getOpts().PingInterval == 100*time.Millisecond) + + // rewrite the config file with a different ping interval + err = os.WriteFile(conf, []byte(` + listen: "127.0.0.1:-1" + accounts: { + $SYS { users [{user: "admin", password: "p1d"}]} + test { users [{user: "foo", password: "bar"}]} + } + ping_interval: "200ms" + `), 0666) + require_NoError(t, err) + + // Connect as a system user and make sure if there is + // subscription interest that we will receive updates. + nc, _ := jsClientConnect(t, s, nats.UserInfo("admin", "p1d")) + defer nc.Close() + + // Request the server to reload and wait for the response. + // subject := fmt.Sprintf("$SYS.REQ.SERVER.%s.RELOAD", s.info.ID) + reply := nc.NewRespInbox() + sub, err = nc.SubscribeSync(reply) + require_NoError(t, err) + err = nc.PublishRequest(subject, reply, nil) + require_NoError(t, err) + msg, err := sub.NextMsg(time.Second) + require_NoError(t, err) + + apiResp := ServerAPIResponse{} + err = json.Unmarshal(msg.Data, &apiResp) + require_NoError(t, err) + + require_True(t, apiResp.Data.(string) == "OK") + + // See that the ping interval has changed. + require_True(t, s.getOpts().PingInterval == 200*time.Millisecond) +} + func Benchmark_GetHash(b *testing.B) { b.StopTimer() // Get 100 random names