Repository: nats-io/nats-server (go)

Issue:
metric cluster_name for leaf node is not correct
### Observed behavior

Endpoint for leaf node metrics, in my case curl http://127.0.0.1:5888/varz returns the value of the cluster_name metric, which does not correspond to the name of the cluster to which this leaf node is connected, but coincides with the value of the server_name metric.
```
# curl http://127.0.0.1:5888/varz
{
  "server_id": "***********************************************",
  "server_name": "sandbox-v3",
  "version": "2.10.18",
  "proto": 1,
  "git_commit": "57d23ac",
  "go": "go1.22.5",
  "host": "localhost",
  "port": 5222,
  "auth_required": true,
  "max_connections": 65536,
  "ping_interval": 120000000000,
  "ping_max": 2,
  "http_host": "localhost",
  "http_port": 5888,
  "http_base_path": "",
  "https_port": 0,
  "auth_timeout": 2,
  "max_control_line": 4096,
  "max_payload": 1048576,
  "max_pending": 67108864,
  "cluster": {
     "name": "sandbox-v3"
  },
  "gateway": {},
  "leaf": {
    "remotes": [
      {
        "local_account": "************************************************************",
        "urls": [
          "sandbox-nats-vm01.****************:7422",
          "sandbox-nats-vm02.****************:7422",
          "sandbox-nats-vm03.****************:7422"
        ]
      },
      {
        "local_account": "*************************************************************",
        "urls": [
          "sandbox-nats-vm01:7422",
          "sandbox-nats-vm02:7422",
          "sandbox-nats-vm03:7422"
        ]
      }
    ]
  },
```
At the same time, the same metric from the server node returns the value of the cluster_name with the actual value of the cluster name specified in the configuration file:
```
# curl http://sandbox-nats-vm01.**************:4888/varz
{
  "server_id": "***********************************************",
  "server_name": "node-01",
  "version": "2.10.18",
  "proto": 1,
  "git_commit": "57d23ac",
  "go": "go1.22.5",
  "host": "sandbox-nats-vm01.**************",
  "port": 4222,
  "auth_required": true,
  "connect_urls": [
    "sandbox-nats-vm01.**************:4222",
    "sandbox-nats-vm02.**************:4222",
    "sandbox-nats-vm03.**************:4222"
  ],
  "max_connections": 65536,
  "ping_interval": 120000000000,
  "ping_max": 2,
  "http_host": "sandbox-nats-vm01.*****************",
  "http_port": 4888,
  "http_base_path": "",
  "https_port": 0,
  "auth_timeout": 2,
  "max_control_line": 4096,
  "max_payload": 2097152,
  "max_pending": 67108864,
  "cluster": {
    "name": "sandbox-nats-cluster",
    "addr": "sandbox-nats-vm01.******************",
    "cluster_port": 6222,
    "auth_timeout": 2,
    "urls": [
      "sandbox-nats-vm01.***************:6222",
      "sandbox-nats-vm02***************:6222",
      "sandbox-nats-vm03.***************:6222"
    ],
    "tls_timeout": 2,
    "pool_size": 3
  },
```
The mismatch of the values ​​of the cluster_node metric for the modes of the leaf node and the server node makes it impossible to use this metric in monitoring for all types of nodes simultaneously.

### Expected behavior

If this is possible, then I would like to receive the current value of the cluster_name metric, corresponding to the cluster name for further identification of all nodes, one way or another participating in the work of this cluster

### Server and client version

# nats-server -v
nats-server: v2.10.18
# nats --version
0.0.35

### Host environment

Cluster. 3 nodes of:
Debian GNU/Linux 11 (bullseye)
AMD Ryzen 7 7700 8-Core Processor (family: 0x19, model: 0x61, stepping: 0x2)
64Gb RAM

Client:
Debian GNU/Linux 11 (bullseye)
AMD Ryzen 9 3900 12-Core Processor
128Gb RAM

### Steps to reproduce

_No response_

Pull request:
[FIXED] LeafNode: Don't report cluster name in varz/banner when none defined
In situation where a Leaf server has no cluster specified, we internally use the server name as the cluster name. We may need it in the protocol so that the hub can suppress some messages to avoid duplicates.

We were however still reporting a cluster name in `/varz` and in the banner on startup. This PR fixes both.

Resolves #5913

Signed-off-by: Ivan Kozlovic ivan@synadia.com


Patch:
diff --git a/server/monitor.go b/server/monitor.go
index 6ee36614..6d7b5872 100644
--- a/server/monitor.go
+++ b/server/monitor.go
@@ -1592,6 +1592,11 @@ func (s *Server) createVarz(pcpu float64, rss int64) *Varz {
 		TrustedOperatorsJwt:   opts.operatorJWT,
 		TrustedOperatorsClaim: opts.TrustedOperators,
 	}
+	// If this is a leaf without cluster, reset the cluster name (that is otherwise
+	// set to the server name).
+	if s.leafNoCluster {
+		varz.Cluster.Name = _EMPTY_
+	}
 	if len(opts.Routes) > 0 {
 		varz.Cluster.URLs = urlsToStrings(opts.Routes)
 	}
diff --git a/server/server.go b/server/server.go
index 5b3408c7..31fa376b 100644
--- a/server/server.go
+++ b/server/server.go
@@ -229,6 +229,7 @@ type Server struct {
 	leafRemoteAccounts sync.Map
 	leafNodeEnabled    bool
 	leafDisableConnect bool // Used in test only
+	leafNoCluster      bool // Indicate that this server has only remotes and no cluster defined
 
 	quitCh           chan struct{}
 	startupComplete  chan struct{}
@@ -750,6 +751,7 @@ func NewServer(opts *Options) (*Server, error) {
 	// If we have solicited leafnodes but no clustering and no clustername.
 	// However we may need a stable clustername so use the server name.
 	if len(opts.LeafNode.Remotes) > 0 && opts.Cluster.Port == 0 && opts.Cluster.Name == _EMPTY_ {
+		s.leafNoCluster = true
 		opts.Cluster.Name = opts.ServerName
 	}
 
@@ -2178,7 +2180,17 @@ func (s *Server) Start() {
 
 	// Snapshot server options.
 	opts := s.getOpts()
-	clusterName := s.ClusterName()
+
+	// Capture if this server is a leaf that has no cluster, so we don't
+	// display the cluster name if that is the case.
+	s.mu.RLock()
+	leafNoCluster := s.leafNoCluster
+	s.mu.RUnlock()
+
+	var clusterName string
+	if !leafNoCluster {
+		clusterName = s.ClusterName()
+	}
 
 	s.Noticef("  Version:  %s", VERSION)
 	s.Noticef("  Git:      [%s]", gc)


Test patch:
diff --git a/server/leafnode_test.go b/server/leafnode_test.go
index 14088926..eeba7737 100644
--- a/server/leafnode_test.go
+++ b/server/leafnode_test.go
@@ -7968,3 +7968,38 @@ func TestLeafNodeServerKickClient(t *testing.T) {
 	require_True(t, lid != ln.Cid)
 	require_True(t, ln.Start.After(disconnectTime))
 }
+
+func TestLeafNodeBannerNoClusterNameIfNoCluster(t *testing.T) {
+	u, err := url.Parse("nats://127.0.0.1:1234")
+	require_NoError(t, err)
+
+	opts := DefaultOptions()
+	opts.ServerName = "LEAF_SERVER"
+	opts.Cluster.Name = _EMPTY_
+	opts.Cluster.Port = 0
+	opts.LeafNode.Remotes = []*RemoteLeafOpts{{URLs: []*url.URL{u}}}
+	opts.NoLog = false
+
+	s, err := NewServer(opts)
+	require_NoError(t, err)
+	defer func() {
+		s.Shutdown()
+		s.WaitForShutdown()
+	}()
+	l := &captureNoticeLogger{}
+	s.SetLogger(l, false, false)
+	s.Start()
+
+	if !s.ReadyForConnections(time.Second) {
+		t.Fatal("Server not ready!")
+	}
+
+	l.Lock()
+	for _, n := range l.notices {
+		if strings.Contains(n, "Cluster: ") {
+			l.Unlock()
+			t.Fatalf("Cluster name should not be displayed, got %q", n)
+		}
+	}
+	l.Unlock()
+}
diff --git a/server/monitor_test.go b/server/monitor_test.go
index 8be9e7d8..a3c09136 100644
--- a/server/monitor_test.go
+++ b/server/monitor_test.go
@@ -3138,6 +3138,14 @@ func TestMonitorLeafNode(t *testing.T) {
 	for mode := 0; mode < 2; mode++ {
 		check := func(t *testing.T, v *Varz) {
 			t.Helper()
+			// Issue 5913. When we have solicited leafnodes but no clustering
+			// and no clustername, we may need a stable clustername so we use
+			// the server name as cluster name. However, we should not expose
+			// it in /varz.
+			if v.Cluster.Name != _EMPTY_ {
+				t.Fatalf("mode=%v - unexpected cluster name: %s", mode, v.Cluster.Name)
+			}
+			// Check rest is as expected.
 			if !reflect.DeepEqual(v.LeafNode, expected) {
 				t.Fatalf("mode=%v - expected %+v, got %+v", mode, expected, v.LeafNode)
 			}