Repository: nats-io/nats-server
Pull request #3663 (2022-11-23 00:28:13)

Add a system account responder for IDZ along with STATSZ etc.
This will avoid write locks and any non-static data.

Signed-off-by: Derek Collison <derek@nats.io>

/cc @nats-io/core



Patch:
diff --git a/server/events.go b/server/events.go
index cdad49c1..6c61442f 100644
--- a/server/events.go
+++ b/server/events.go
@@ -163,6 +163,13 @@ type accNumConnsReq struct {
 	Account string     `json:"acc"`
 }
 
+// ServerID is basic static info for a server.
+type ServerID struct {
+	Name string `json:"name"`
+	Host string `json:"host"`
+	ID   string `json:"id"`
+}
+
 // ServerInfo identifies remote servers.
 type ServerInfo struct {
 	Name      string    `json:"name"`
@@ -881,6 +888,7 @@ func (s *Server) initEventTracking() {
 		s.Errorf("Error setting up internal tracking: %v", err)
 	}
 	monSrvc := map[string]msgHandler{
+		"IDZ":    s.idzReq,
 		"STATSZ": s.statszReq,
 		"VARZ": func(sub *subscription, c *client, _ *Account, subject, reply string, msg []byte) {
 			optz := &VarzEventOptions{}
@@ -1574,6 +1582,19 @@ func (s *Server) statszReq(sub *subscription, c *client, _ *Account, subject, re
 	s.mu.Unlock()
 }
 
+// idzReq is for a request for basic static server info.
+// Try to not hold the write lock or dynamically create data.
+func (s *Server) idzReq(sub *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+	id := &ServerID{
+		Name: s.info.Name,
+		Host: s.info.Host,
+		ID:   s.info.ID,
+	}
+	s.sendInternalMsg(reply, _EMPTY_, nil, &id)
+}
+
 var errSkipZreq = errors.New("filtered response")
 
 const (


Test patch:
diff --git a/server/events_test.go b/server/events_test.go
index 605e687f..61ce61b2 100644
--- a/server/events_test.go
+++ b/server/events_test.go
@@ -1664,7 +1664,7 @@ func TestSystemAccountWithGateways(t *testing.T) {
 
 	// If this tests fails with wrong number after 10 seconds we may have
 	// added a new inititial subscription for the eventing system.
-	checkExpectedSubs(t, 45, sa)
+	checkExpectedSubs(t, 47, sa)
 
 	// Create a client on B and see if we receive the event
 	urlb := fmt.Sprintf("nats://%s:%d", ob.Host, ob.Port)
diff --git a/server/monitor_test.go b/server/monitor_test.go
index b3cfac6d..0e666bb6 100644
--- a/server/monitor_test.go
+++ b/server/monitor_test.go
@@ -2250,15 +2250,15 @@ func TestServerIDs(t *testing.T) {
 
 	for mode := 0; mode < 2; mode++ {
 		v := pollVarz(t, s, mode, murl+"varz", nil)
-		if v.ID == "" {
+		if v.ID == _EMPTY_ {
 			t.Fatal("Varz ID is empty")
 		}
 		c := pollConz(t, s, mode, murl+"connz", nil)
-		if c.ID == "" {
+		if c.ID == _EMPTY_ {
 			t.Fatal("Connz ID is empty")
 		}
 		r := pollRoutez(t, s, mode, murl+"routez", nil)
-		if r.ID == "" {
+		if r.ID == _EMPTY_ {
 			t.Fatal("Routez ID is empty")
 		}
 		if v.ID != c.ID || v.ID != r.ID {
@@ -3933,7 +3933,7 @@ func TestMonitorAccountz(t *testing.T) {
 	body = string(readBody(t, fmt.Sprintf("http://127.0.0.1:%d%s?acc=$SYS", s.MonitorAddr().Port, AccountzPath)))
 	require_Contains(t, body, `"account_detail": {`)
 	require_Contains(t, body, `"account_name": "$SYS",`)
-	require_Contains(t, body, `"subscriptions": 40,`)
+	require_Contains(t, body, `"subscriptions": 42,`)
 	require_Contains(t, body, `"is_system": true,`)
 	require_Contains(t, body, `"system_account": "$SYS"`)
 
@@ -4569,3 +4569,30 @@ func TestMonitorWebsocket(t *testing.T) {
 		}
 	}
 }
+
+func TestServerIDZRequest(t *testing.T) {
+	conf := createConfFile(t, []byte(`
+		listen: 127.0.0.1:-1
+		server_name: TEST22
+		# For access to system account.
+		accounts { $SYS { users = [ { user: "admin", pass: "s3cr3t!" } ] } }
+	`))
+	defer removeFile(t, conf)
+
+	s, _ := RunServerWithConfig(conf)
+	defer s.Shutdown()
+
+	nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("admin", "s3cr3t!"))
+	require_NoError(t, err)
+
+	subject := fmt.Sprintf(serverPingReqSubj, "IDZ")
+	resp, err := nc.Request(subject, nil, time.Second)
+	require_NoError(t, err)
+
+	var sid ServerID
+	err = json.Unmarshal(resp.Data, &sid)
+	require_NoError(t, err)
+
+	require_True(t, sid.Name == "TEST22")
+	require_True(t, strings.HasPrefix(sid.ID, "N"))
+}