This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 786a308e7 fix(cluster): apply the configured cluster to detail and 
brokers (#6196)
786a308e7 is described below

commit 786a308e7f2126a5a87ecdaf87ab4e9fadb42e9b
Author: zmuxuny <[email protected]>
AuthorDate: Mon Oct 12 00:18:30 2026 -0700

    fix(cluster): apply the configured cluster to detail and brokers (#6196)
    
    `discoverClusters(instanceId)` already narrowed the topology to the cluster 
configured for the instance (`RocketMQClusterProvider.java:88-91`), but the 
other two instance-scoped reads ignored that restriction. 
`refreshClusterDetail(clusterId, instanceId)` (`:139-176`) looked the requested 
id up in the shared `clusterAddrTable` and fetched its broker runtime stats, 
and `discoverBrokers` (`:184-196`) listed every name in `brokerAddrTable`, so 
an instance configured for cluster A on a sh [...]
    
    Nine new instances — seven tests plus a two-case parameterized one — assert 
the status codes, the broker-name sets, and `verify(admin, 
never()).fetchBrokerRuntimeStats(...)` for the rejected detail read. 
`globalDetailStillExposesOtherClusterTest` and 
`unscopedInstanceStillExposesBothClustersTest` are the controls that fail if 
the restriction is widened past instance scope; 
`scopedBrokerLookupAcceptsMemberBrokerTest` and 
`scopedBrokerListDoesNotFallBackWhenClusterIsAbsentTest` pin the  [...]
---
 .../provider/apache/RocketMQClusterProvider.java   |  25 ++++-
 .../apache/RocketMQClusterProviderTest.java        | 115 +++++++++++++++++++++
 2 files changed, 137 insertions(+), 3 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
index adfef1aa2..77f57d395 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProvider.java
@@ -144,6 +144,12 @@ public class RocketMQClusterProvider implements 
ClusterProvider {
             return null;
         }
 
+        String configuredCluster = StringUtils.hasText(instanceId)
+                ? runtimeAdminClientResolver.configuredClusterName(instanceId) 
: null;
+        if (configuredCluster != null && !configuredCluster.equals(clusterId)) 
{
+            return null;
+        }
+
         try {
             return executeAdmin(instanceId, namesrvAddr, admin -> {
                 ClusterInfo clusterInfo = admin.examineBrokerClusterInfo();
@@ -183,14 +189,27 @@ public class RocketMQClusterProvider implements 
ClusterProvider {
 
     @Override
     public List<BrokerVO> discoverBrokers(String instanceId, String 
brokerName) {
+        String configuredCluster = 
runtimeAdminClientResolver.configuredClusterName(instanceId);
         return runtimeAdminClientResolver.execute(instanceId, admin -> {
             ClusterInfo info = admin.examineBrokerClusterInfo();
             if (info == null || info.getBrokerAddrTable() == null)
                 throw new BusinessException(503, "Broker topology is 
unavailable");
             Map<String, BrokerData> table = info.getBrokerAddrTable();
-            if (brokerName != null && !table.containsKey(brokerName))
-                throw new BusinessException(404, "Broker not found in 
Instance: " + brokerName);
-            Set<String> names = brokerName == null ? new 
java.util.TreeSet<>(table.keySet()) : Set.of(brokerName);
+            Set<String> names = new TreeSet<>(table.keySet());
+            if (configuredCluster != null) {
+                Map<String, Set<String>> clusters = info.getClusterAddrTable();
+                if (clusters == null || clusters.containsKey(configuredCluster)
+                        && clusters.get(configuredCluster) == null) {
+                    throw new BusinessException(503, "Broker cluster 
membership is unavailable");
+                }
+                names.retainAll(clusters.getOrDefault(configuredCluster, 
Set.of()));
+            }
+            if (brokerName != null) {
+                if (!names.contains(brokerName)) {
+                    throw new BusinessException(404, "Broker not found in 
Instance: " + brokerName);
+                }
+                names = Set.of(brokerName);
+            }
             return buildBrokerList(admin, names, table);
         });
     }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
index b10f378aa..2d8b62859 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/provider/apache/RocketMQClusterProviderTest.java
@@ -36,10 +36,13 @@ import 
org.apache.rocketmq.studio.instance.InstanceRepository;
 import org.apache.rocketmq.studio.instance.InstanceVO;
 import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 
@@ -383,6 +386,118 @@ class RocketMQClusterProviderTest {
         verify(adminFactory, never()).execute(eq("10.0.0.2:9876"), isNull(), 
any());
     }
 
+    @Test
+    void scopedDetailDoesNotReadAnotherClusterTest() throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, 
"DefaultCluster");
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThat(provider.refreshClusterDetail("OtherCluster", 
"selected")).isNull();
+        verify(admin, never()).fetchBrokerRuntimeStats("10.0.0.12:10911");
+        assertThat(provider.refreshClusterDetail("DefaultCluster", 
"selected")).isNotNull();
+    }
+
+    @Test
+    void scopedBrokerListIncludesOnlySelectedClusterTest() throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, 
"DefaultCluster");
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThat(provider.discoverBrokers("selected", null))
+                
.extracting(org.apache.rocketmq.studio.cluster.broker.BrokerVO::getName)
+                .containsExactly("broker-a");
+        verify(admin, never()).fetchBrokerRuntimeStats("10.0.0.12:10911");
+    }
+
+    @Test
+    void scopedBrokerLookupAcceptsMemberBrokerTest() throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, 
"DefaultCluster");
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThat(provider.discoverBrokers("selected", "broker-a"))
+                
.extracting(org.apache.rocketmq.studio.cluster.broker.BrokerVO::getName)
+                .containsExactly("broker-a");
+        verify(admin, never()).fetchBrokerRuntimeStats("10.0.0.12:10911");
+    }
+
+    @Test
+    void globalDetailStillExposesOtherClusterTest() throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = newProvider(admin);
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThat(provider.refreshClusterDetail("OtherCluster")).isNotNull();
+    }
+
+    @Test
+    void scopedBrokerLookupRejectsOtherClusterTest() throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, 
"DefaultCluster");
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThatThrownBy(() -> provider.discoverBrokers("selected", 
"broker-b"))
+                .isInstanceOfSatisfying(BusinessException.class, error -> 
assertThat(error.getCode()).isEqualTo(404));
+        verify(admin, never()).fetchBrokerRuntimeStats("10.0.0.12:10911");
+    }
+
+    @Test
+    void unscopedInstanceStillExposesBothClustersTest() throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, null);
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThat(provider.refreshClusterDetail("OtherCluster", 
"selected")).isNotNull();
+        assertThat(provider.discoverBrokers("selected", null))
+                
.extracting(org.apache.rocketmq.studio.cluster.broker.BrokerVO::getName)
+                .containsExactly("broker-a", "broker-b");
+    }
+
+    @Test
+    void scopedBrokerListDoesNotFallBackWhenClusterIsAbsentTest() throws 
Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, 
"MissingCluster");
+        when(admin.examineBrokerClusterInfo()).thenReturn(sharedTopology());
+
+        assertThat(provider.discoverBrokers("selected", null)).isEmpty();
+        verify(admin, never()).fetchBrokerRuntimeStats(anyString());
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = {true, false})
+    void scopedBrokerListRejectsMissingMembershipTest(boolean missingTable) 
throws Exception {
+        DefaultMQAdminExt admin = mock(DefaultMQAdminExt.class);
+        RocketMQClusterProvider provider = scopedProvider(admin, 
"DefaultCluster");
+        ClusterInfo topology = sharedTopology();
+        if (missingTable) {
+            topology.setClusterAddrTable(null);
+        } else {
+            topology.getClusterAddrTable().put("DefaultCluster", null);
+        }
+        when(admin.examineBrokerClusterInfo()).thenReturn(topology);
+
+        assertThatThrownBy(() -> provider.discoverBrokers("selected", null))
+                .isInstanceOfSatisfying(BusinessException.class, error -> 
assertThat(error.getCode()).isEqualTo(503));
+        verify(admin, never()).fetchBrokerRuntimeStats(anyString());
+    }
+
+    private RocketMQClusterProvider scopedProvider(DefaultMQAdminExt admin, 
String cluster) {
+        RuntimeAdminClientResolver runtime = 
mock(RuntimeAdminClientResolver.class);
+        
org.mockito.Mockito.lenient().when(runtime.resolveEndpoint("selected")).thenReturn("10.0.0.1:9876");
+        
org.mockito.Mockito.lenient().when(runtime.configuredClusterName("selected")).thenReturn(cluster);
+        when(runtime.execute(eq("selected"), any())).thenAnswer(call ->
+                
call.<MqAdminExtFactory.AdminAction<Object>>getArgument(1).apply(admin));
+        return new RocketMQClusterProvider(mock(MqAdminExtFactory.class), new 
RocketMQProperties(), runtime);
+    }
+
+    private ClusterInfo sharedTopology() {
+        ClusterInfo info = clusterInfo();
+        info.getClusterAddrTable().put("OtherCluster", Set.of("broker-b"));
+        info.getBrokerAddrTable().put("broker-b", new 
BrokerData("OtherCluster", "broker-b",
+                new HashMap<>(Map.of(0L, "10.0.0.12:10911"))));
+        return info;
+    }
+
     private RocketMQClusterProvider 
newAuthenticatedInstanceProvider(MqAdminExtFactory adminFactory,
                                                                       
DefaultMQAdminExt adminExt) {
         InstanceRepository instanceRepository = mock(InstanceRepository.class);

Reply via email to