You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tubemq.apache.org by go...@apache.org on 2020/12/02 08:39:24 UTC

[incubator-tubemq] branch TUBEMQ-421 updated: [TUBEMQ-439] Add cluster info query (#334)

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

gosonzhang pushed a commit to branch TUBEMQ-421
in repository https://gitbox.apache.org/repos/asf/incubator-tubemq.git


The following commit(s) were added to refs/heads/TUBEMQ-421 by this push:
     new 37739df  [TUBEMQ-439] Add cluster info query (#334)
37739df is described below

commit 37739df8fd8ea213f74e0f94a82ace1b351a10a6
Author: EMsnap <zp...@connect.ust.hk>
AuthorDate: Wed Dec 2 16:39:16 2020 +0800

    [TUBEMQ-439] Add cluster info query (#334)
---
 .../controller/ManagerControllerAdvice.java        |   4 +-
 .../{TubeResult.java => TubeMQResult.java}         |  15 ++-
 .../controller/cluster/ClusterController.java      |   8 +-
 .../manager/controller/node/NodeController.java    |  54 +++++++++
 .../manager/controller/topic/TopicController.java  |  20 ++--
 .../org/apache/tubemq/manager/entry/NodeEntry.java |   2 +
 .../tubemq/manager/repository/NodeRepository.java  |   6 +
 .../apache/tubemq/manager/service/NodeService.java |  36 +++++-
 .../{TubeHttpConst.java => TubeMQHttpConst.java}   |   2 +-
 .../service/tube/TubeHttpClusterInfoList.java      |  97 ++++++++++++++++
 .../src/main/resources/application.properties      |   2 +-
 .../manager/controller/TestBusinessController.java |   8 +-
 .../manager/controller/TestClusterController.java  |   4 +-
 .../manager/controller/TestNodeController.java     | 128 +++++++++++++++++++++
 .../manager/repository/TestBusinessRepository.java |   2 +-
 15 files changed, 356 insertions(+), 32 deletions(-)

diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/ManagerControllerAdvice.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/ManagerControllerAdvice.java
index 5053834..1627b29 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/ManagerControllerAdvice.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/ManagerControllerAdvice.java
@@ -32,8 +32,8 @@ public class ManagerControllerAdvice {
      * @return
      */
     @ExceptionHandler(Exception.class)
-    public TubeResult handlingParameterException(Exception ex) {
-        TubeResult result = new TubeResult();
+    public TubeMQResult handlingParameterException(Exception ex) {
+        TubeMQResult result = new TubeMQResult();
         result.setErrMsg(ex.getClass().getName() + " " + ex.getMessage());
         result.setErrCode(-1);
         return result;
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/TubeResult.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/TubeMQResult.java
similarity index 71%
rename from tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/TubeResult.java
rename to tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/TubeMQResult.java
index 144d975..939d0a8 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/TubeResult.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/TubeMQResult.java
@@ -17,11 +17,22 @@
 
 package org.apache.tubemq.manager.controller;
 
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 @Data
-public class TubeResult {
-    private String errMsg;
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class TubeMQResult {
+    private String errMsg = "";
     private int errCode = 0;
     private boolean result = true;
+
+    public static TubeMQResult getErrorResult(String errorMsg) {
+        return TubeMQResult.builder().errCode(1)
+                .errMsg(errorMsg).result(false).build();
+    }
 }
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/cluster/ClusterController.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/cluster/ClusterController.java
index 58fe8f9..53e1aa3 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/cluster/ClusterController.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/cluster/ClusterController.java
@@ -18,7 +18,7 @@
 package org.apache.tubemq.manager.controller.cluster;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.tubemq.manager.service.TubeHttpConst.SCHEMA;
+import static org.apache.tubemq.manager.service.TubeMQHttpConst.SCHEMA;
 
 import com.google.gson.Gson;
 import java.net.URLEncoder;
@@ -32,7 +32,7 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
-import org.apache.tubemq.manager.controller.TubeResult;
+import org.apache.tubemq.manager.controller.TubeMQResult;
 import org.apache.tubemq.manager.entry.NodeEntry;
 import org.apache.tubemq.manager.repository.NodeRepository;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -71,7 +71,7 @@ public class ClusterController {
     private String queryMaster(String url) {
         log.info("start to request {}", url);
         HttpGet httpGet = new HttpGet(url);
-        TubeResult defaultResult = new TubeResult();
+        TubeMQResult defaultResult = new TubeMQResult();
         try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
             // return result json to response
             return EntityUtils.toString(response.getEntity());
@@ -116,7 +116,7 @@ public class ClusterController {
                     + "/" + TUBE_REQUEST_PATH + "?" + covertMapToQueryString(requestBody);
             return queryMaster(url);
         } else {
-            TubeResult result = new TubeResult();
+            TubeMQResult result = new TubeMQResult();
             result.setErrCode(-1);
             result.setResult(false);
             result.setErrMsg("token is not correct");
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/node/NodeController.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/node/NodeController.java
new file mode 100644
index 0000000..fe64b7b
--- /dev/null
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/node/NodeController.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tubemq.manager.controller.node;
+
+import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.tubemq.manager.controller.TubeMQResult;
+import org.apache.tubemq.manager.service.NodeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping(path = "/v1/node")
+@Slf4j
+public class NodeController {
+
+    public static final String NO_SUCH_METHOD = "no such method";
+    public static final String OP_QUERY = "op_query";
+    public static final String ADMIN_QUERY_CLUSTER_INFO = "admin_query_cluster_info";
+
+    private final Gson gson = new Gson();
+
+    @Autowired
+    NodeService nodeService;
+
+    @RequestMapping(value = "/query", method = RequestMethod.GET,
+            produces = MediaType.APPLICATION_JSON_VALUE)
+    public @ResponseBody String queryInfo(@RequestParam String type, @RequestParam String method,
+            @RequestParam(required = false) Integer clusterId) {
+
+        if (method.equals(ADMIN_QUERY_CLUSTER_INFO) && type.equals(OP_QUERY)) {
+            return nodeService.queryClusterInfo(clusterId);
+        }
+
+        return gson.toJson(TubeMQResult.getErrorResult(NO_SUCH_METHOD));
+    }
+
+}
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/topic/TopicController.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/topic/TopicController.java
index fdeac4e..983579f 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/topic/TopicController.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/controller/topic/TopicController.java
@@ -20,7 +20,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.tubemq.manager.controller.TubeResult;
+import org.apache.tubemq.manager.controller.TubeMQResult;
 import org.apache.tubemq.manager.entry.TopicEntry;
 import org.apache.tubemq.manager.entry.TopicStatus;
 import org.apache.tubemq.manager.exceptions.TubeMQManagerException;
@@ -54,7 +54,7 @@ public class TopicController {
      * @throws Exception - exception
      */
     @PostMapping("/add")
-    public TubeResult addTopic(@RequestBody TopicEntry entry) {
+    public TubeMQResult addTopic(@RequestBody TopicEntry entry) {
         // entry in adding status
         entry.setStatus(TopicStatus.ADDING.value());
         topicRepository.saveAndFlush(entry);
@@ -69,7 +69,7 @@ public class TopicController {
             }
             topicRepository.saveAndFlush(entry1);
         });
-        return new TubeResult();
+        return new TubeMQResult();
     }
 
     /**
@@ -79,8 +79,8 @@ public class TopicController {
      * @throws Exception
      */
     @PostMapping("/update")
-    public TubeResult updateTopic(@RequestBody TopicEntry entry) {
-        return new TubeResult();
+    public TubeMQResult updateTopic(@RequestBody TopicEntry entry) {
+        return new TubeMQResult();
     }
 
     /**
@@ -90,10 +90,10 @@ public class TopicController {
      * @throws Exception
      */
     @GetMapping("/check")
-    public TubeResult checkTopicByBusinessName(
+    public TubeMQResult checkTopicByBusinessName(
             @RequestParam String businessName) {
         List<TopicEntry> result = topicRepository.findAllByBusinessName(businessName);
-        return new TubeResult();
+        return new TubeMQResult();
     }
 
     /**
@@ -104,10 +104,10 @@ public class TopicController {
      * @throws Exception
      */
     @GetMapping("/get/{id}")
-    public TubeResult getBusinessByID(
+    public TubeMQResult getBusinessByID(
             @PathVariable Long id) {
         Optional<TopicEntry> businessEntry = topicRepository.findById(id);
-        TubeResult result = new TubeResult();
+        TubeMQResult result = new TubeMQResult();
         if (!businessEntry.isPresent()) {
             result.setErrCode(-1);
             result.setErrMsg("business not found");
@@ -120,7 +120,7 @@ public class TopicController {
      * @return
      */
     @GetMapping("/throwException")
-    public TubeResult throwException() {
+    public TubeMQResult throwException() {
         throw new TubeMQManagerException("exception for test");
     }
 }
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/entry/NodeEntry.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/entry/NodeEntry.java
index 54c4236..fb19232 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/entry/NodeEntry.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/entry/NodeEntry.java
@@ -48,4 +48,6 @@ public class NodeEntry {
     private int webPort;
 
     private int clusterId;
+
+    private String clusterName;
 }
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/repository/NodeRepository.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/repository/NodeRepository.java
index 4bf6ec7..7a0cbb0 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/repository/NodeRepository.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/repository/NodeRepository.java
@@ -21,8 +21,14 @@ import org.apache.tubemq.manager.entry.NodeEntry;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
+
 @Repository
 public interface NodeRepository extends JpaRepository<NodeEntry, Long> {
 
     NodeEntry findNodeEntryByClusterIdIsAndMasterIsTrue(int clusterId);
+
+    List<NodeEntry> findNodeEntriesByClusterIdIs(int clusterId);
+
+    List<NodeEntry> findAll();
 }
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/NodeService.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/NodeService.java
index 4e0db3e..2e819cc 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/NodeService.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/NodeService.java
@@ -18,11 +18,11 @@
 package org.apache.tubemq.manager.service;
 
 
-import static org.apache.tubemq.manager.service.TubeHttpConst.ADD_TUBE_TOPIC;
-import static org.apache.tubemq.manager.service.TubeHttpConst.BROKER_RUN_STATUS;
-import static org.apache.tubemq.manager.service.TubeHttpConst.RELOAD_BROKER;
-import static org.apache.tubemq.manager.service.TubeHttpConst.SCHEMA;
-import static org.apache.tubemq.manager.service.TubeHttpConst.TOPIC_CONFIG_INFO;
+import static org.apache.tubemq.manager.service.TubeMQHttpConst.ADD_TUBE_TOPIC;
+import static org.apache.tubemq.manager.service.TubeMQHttpConst.BROKER_RUN_STATUS;
+import static org.apache.tubemq.manager.service.TubeMQHttpConst.RELOAD_BROKER;
+import static org.apache.tubemq.manager.service.TubeMQHttpConst.SCHEMA;
+import static org.apache.tubemq.manager.service.TubeMQHttpConst.TOPIC_CONFIG_INFO;
 
 import com.google.gson.Gson;
 import java.io.IOException;
@@ -31,24 +31,30 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
+
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.tubemq.manager.controller.TubeMQResult;
 import org.apache.tubemq.manager.entry.NodeEntry;
 import org.apache.tubemq.manager.repository.NodeRepository;
 import org.apache.tubemq.manager.service.tube.TubeHttpBrokerInfoList;
+import org.apache.tubemq.manager.service.tube.TubeHttpClusterInfoList;
 import org.apache.tubemq.manager.service.tube.TubeHttpResponse;
 import org.apache.tubemq.manager.service.tube.TubeHttpTopicInfoList;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
 
 /**
  * node service to query broker/master/standby status of tube cluster.
  */
 @Slf4j
+@Component
 public class NodeService {
 
     private final CloseableHttpClient httpclient = HttpClients.createDefault();
@@ -266,6 +272,26 @@ public class NodeService {
         }
     }
 
+    public String queryClusterInfo(Integer clusterId) {
+        TubeHttpClusterInfoList clusterInfoList;
+        try {
+            // find all nodes by given clusterIds, show all nodes if clusterIds not provided
+            List<NodeEntry> nodeEntries = clusterId == null ?
+                    nodeRepository.findAll() : nodeRepository.findNodeEntriesByClusterIdIs(clusterId);
+            // divide all entries by clusterId
+            Map<Integer, List<NodeEntry>> nodeEntriesPerCluster =
+                    nodeEntries.parallelStream().collect(Collectors.groupingBy(NodeEntry::getClusterId));
+
+            clusterInfoList = TubeHttpClusterInfoList.getClusterInfoList(nodeEntriesPerCluster);
+        } catch (Exception e) {
+            log.error("query cluster info error", e);
+            return gson.toJson(TubeMQResult.getErrorResult(""));
+        }
+
+        return gson.toJson(clusterInfoList);
+    }
+
+
     public void close() throws IOException {
         httpclient.close();
     }
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/TubeHttpConst.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/TubeMQHttpConst.java
similarity index 97%
rename from tubemq-manager/src/main/java/org/apache/tubemq/manager/service/TubeHttpConst.java
rename to tubemq-manager/src/main/java/org/apache/tubemq/manager/service/TubeMQHttpConst.java
index 81a360e..d1d4949 100644
--- a/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/TubeHttpConst.java
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/TubeMQHttpConst.java
@@ -17,7 +17,7 @@
 
 package org.apache.tubemq.manager.service;
 
-public class TubeHttpConst {
+public class TubeMQHttpConst {
     public static final String SCHEMA = "http://";
     public static final String BROKER_RUN_STATUS =
             "/webapi.htm?type=op_query&method=admin_query_broker_run_status";
diff --git a/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/tube/TubeHttpClusterInfoList.java b/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/tube/TubeHttpClusterInfoList.java
new file mode 100644
index 0000000..9a36de9
--- /dev/null
+++ b/tubemq-manager/src/main/java/org/apache/tubemq/manager/service/tube/TubeHttpClusterInfoList.java
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tubemq.manager.service.tube;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.apache.tubemq.manager.controller.TubeMQResult;
+import org.apache.tubemq.manager.entry.NodeEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Data
+public class TubeHttpClusterInfoList extends TubeMQResult {
+
+    private List<ClusterData> data = new ArrayList<>();
+
+    @Data
+    @AllArgsConstructor
+    public static class ClusterData {
+
+        @Data
+        public static class ClusterInfo {
+
+            @Data
+            public static class BrokerInfo {
+                private int brokerId;
+                private String brokerIp;
+            }
+
+            private String master;
+            private List<String> standby = new ArrayList<>();
+            private List<BrokerInfo> broker = new ArrayList<>();
+
+        }
+
+        private int clusterId;
+        private String clusterName;
+        private ClusterInfo clusterInfo;
+
+    }
+
+
+    public static TubeHttpClusterInfoList getClusterInfoList(Map<Integer, List<NodeEntry>> nodeEntriesPerCluster) {
+        // for each cluster provide cluster information
+        TubeHttpClusterInfoList clusterInfoList = new TubeHttpClusterInfoList();
+        nodeEntriesPerCluster.forEach((id, entries) -> {
+                    ClusterData.ClusterInfo singleClusterInfo = getSingleClusterInfo(entries);
+                    ClusterData clusterData =
+                            new ClusterData(id, entries.get(0).getClusterName(), singleClusterInfo);
+                    clusterInfoList.getData().add(clusterData);
+                }
+        );
+        return clusterInfoList;
+    }
+
+    private static ClusterData.ClusterInfo getSingleClusterInfo(List<NodeEntry> entries) {
+
+        TubeHttpClusterInfoList.ClusterData.ClusterInfo clusterInfo =
+                new TubeHttpClusterInfoList.ClusterData.ClusterInfo();
+
+        entries.forEach(nodeEntry -> {
+            if (nodeEntry.isMaster()) {
+                clusterInfo.setMaster(nodeEntry.getIp());
+            }
+            if (nodeEntry.isBroker()) {
+                ClusterData.ClusterInfo.BrokerInfo brokerInfo =
+                        new ClusterData.ClusterInfo.BrokerInfo();
+                brokerInfo.setBrokerId((int) nodeEntry.getBrokerId());
+                brokerInfo.setBrokerIp(nodeEntry.getIp());
+                clusterInfo.getBroker().add(brokerInfo);
+            }
+            if (nodeEntry.isStandby()) {
+                clusterInfo.getStandby().add(nodeEntry.getIp());
+            }
+        });
+
+        return clusterInfo;
+    }
+
+
+}
diff --git a/tubemq-manager/src/main/resources/application.properties b/tubemq-manager/src/main/resources/application.properties
index dee51b7..9afbdab 100644
--- a/tubemq-manager/src/main/resources/application.properties
+++ b/tubemq-manager/src/main/resources/application.properties
@@ -14,4 +14,4 @@
 # limitations under the License.
 
 spring.jpa.hibernate.ddl-auto=update
-# configuration for manager
+# configuration for manager
\ No newline at end of file
diff --git a/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestBusinessController.java b/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestBusinessController.java
index 9a497cf..0dbec60 100644
--- a/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestBusinessController.java
+++ b/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestBusinessController.java
@@ -82,8 +82,8 @@ public class TestBusinessController {
         HttpHeaders headers = new HttpHeaders();
         HttpEntity<TopicEntry> request = new HttpEntity<>(entry, headers);
 
-        ResponseEntity<TubeResult> responseEntity =
-                client.postForEntity(uri, request, TubeResult.class);
+        ResponseEntity<TubeMQResult> responseEntity =
+                client.postForEntity(uri, request, TubeMQResult.class);
         assertThat(responseEntity.getStatusCode().is2xxSuccessful()).isEqualTo(true);
     }
 
@@ -91,8 +91,8 @@ public class TestBusinessController {
     public void testControllerException() throws Exception {
         final String baseUrl = "http://localhost:" + randomServerPort + "/business/throwException";
         URI uri = new URI(baseUrl);
-        ResponseEntity<TubeResult> responseEntity =
-                client.getForEntity(uri, TubeResult.class);
+        ResponseEntity<TubeMQResult> responseEntity =
+                client.getForEntity(uri, TubeMQResult.class);
         assertThat(Objects.requireNonNull(responseEntity.getBody()).getErrCode()).isEqualTo(-1);
         assertTrue(responseEntity.getBody().getErrMsg().contains("exception for test"));
     }
diff --git a/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestClusterController.java b/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestClusterController.java
index efd1cb1..bd893a7 100644
--- a/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestClusterController.java
+++ b/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestClusterController.java
@@ -62,7 +62,7 @@ public class TestClusterController {
         NodeEntry nodeEntry = new NodeEntry();
         nodeEntry.setMaster(true);
         nodeEntry.setIp("127.0.0.1");
-        nodeEntry.setWebPort(8080);
+        nodeEntry.setWebPort(8014);
         return nodeEntry;
     }
 
@@ -75,7 +75,7 @@ public class TestClusterController {
                 "/v1/cluster/query?method=admin_query_topic_info&type=op_query");
         MvcResult result = mockMvc.perform(request).andReturn();
         String resultStr = result.getResponse().getContentAsString();
-        TubeResult clusterResult = gson.fromJson(resultStr, TubeResult.class);
+        TubeMQResult clusterResult = gson.fromJson(resultStr, TubeMQResult.class);
         Assert.assertEquals(-1, clusterResult.getErrCode());
         Assert.assertTrue(clusterResult.getErrMsg().contains("NumberFormatException"));
     }
diff --git a/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestNodeController.java b/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestNodeController.java
new file mode 100644
index 0000000..0b8958e
--- /dev/null
+++ b/tubemq-manager/src/test/java/org/apache/tubemq/manager/controller/TestNodeController.java
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tubemq.manager.controller;
+
+import com.google.gson.Gson;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.tubemq.manager.entry.NodeEntry;
+import org.apache.tubemq.manager.repository.NodeRepository;
+import org.apache.tubemq.manager.service.NodeService;
+import org.assertj.core.util.Lists;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.RequestBuilder;
+
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+
+@Slf4j
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@AutoConfigureMockMvc
+public class TestNodeController {
+
+    @MockBean
+    private NodeRepository nodeRepository;
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    private List<NodeEntry> getOneNodeEntry() {
+        NodeEntry nodeEntry = new NodeEntry();
+        nodeEntry.setMaster(true);
+        nodeEntry.setIp("127.0.0.1");
+        nodeEntry.setWebPort(8014);
+        nodeEntry.setClusterId(0);
+        return Lists.newArrayList(nodeEntry);
+    }
+
+
+    private List<NodeEntry> getTwoNodeEntries() {
+        NodeEntry nodeEntry1 = new NodeEntry();
+        nodeEntry1.setMaster(true);
+        nodeEntry1.setIp("127.0.0.1");
+        nodeEntry1.setWebPort(8014);
+        nodeEntry1.setClusterId(1);
+
+        NodeEntry nodeEntry2 = new NodeEntry();
+        nodeEntry2.setMaster(true);
+        nodeEntry2.setIp("127.0.0.1");
+        nodeEntry2.setWebPort(8014);
+        nodeEntry2.setClusterId(2);
+
+        return Lists.newArrayList(nodeEntry1, nodeEntry2);
+    }
+
+    private String expectedOneEntry =
+            "{\"data\":[{\"clusterId\":0," +
+            "\"clusterInfo\":{\"master\":\"127.0.0.1\"," +
+            "\"standby\":[],\"broker\":[]}}],\"errMsg\":" +
+            "\"\",\"errCode\":0,\"result\":true}";
+
+
+    private String expectedTwoEntries =
+            "{\"data\":[{\"clusterId\":1,\"clusterInfo\":" +
+                    "{\"master\":\"127.0.0.1\",\"standby\"" +
+                    ":[],\"broker\":[]}},{\"clusterId\":2," +
+                    "\"clusterInfo\":{\"master\":\"127.0.0.1\"," +
+                    "\"standby\":[],\"broker\":[]}}]," +
+                    "\"errMsg\":\"\",\"errCode\":0,\"result\":true}";
+
+    @Test
+    public void testClusterInfo() throws Exception {
+        List<NodeEntry> nodeEntries = getOneNodeEntry();
+        when(nodeRepository.findNodeEntriesByClusterIdIs(any(Integer.class)))
+                .thenReturn(nodeEntries);
+        RequestBuilder request = get("/v1/node/query?method=admin_query_cluster_info&" +
+                "type=op_query&clusterId=1");
+        MvcResult result = mockMvc.perform(request).andReturn();
+        String resultStr = result.getResponse().getContentAsString();
+        Assert.assertEquals(resultStr, expectedOneEntry);
+        log.info("result json string is {}, response type is {}", resultStr,
+                result.getResponse().getContentType());
+    }
+
+
+    @Test
+    public void testClusterInfoTwoEntries() throws Exception {
+        List<NodeEntry> nodeEntries = getTwoNodeEntries();
+        when(nodeRepository.findNodeEntriesByClusterIdIs(any(Integer.class)))
+                .thenReturn(nodeEntries);
+        RequestBuilder request = get("/v1/node/query?method=admin_query_cluster_info&" +
+                "type=op_query&clusterId=1");
+        MvcResult result = mockMvc.perform(request).andReturn();
+        String resultStr = result.getResponse().getContentAsString();
+        Assert.assertEquals(resultStr, expectedTwoEntries);
+        log.info("result json string is {}, response type is {}", resultStr,
+                result.getResponse().getContentType());
+    }
+
+
+
+}
diff --git a/tubemq-manager/src/test/java/org/apache/tubemq/manager/repository/TestBusinessRepository.java b/tubemq-manager/src/test/java/org/apache/tubemq/manager/repository/TestBusinessRepository.java
index 7bd8c73..e7e7ba9 100644
--- a/tubemq-manager/src/test/java/org/apache/tubemq/manager/repository/TestBusinessRepository.java
+++ b/tubemq-manager/src/test/java/org/apache/tubemq/manager/repository/TestBusinessRepository.java
@@ -61,7 +61,7 @@ public class TestBusinessRepository {
             entityManager.persist(businessEntry);
             entityManager.flush();
         } catch (Exception ex) {
-            assertThat(ex.getMessage()).contains("size must be between");
+            assertThat(ex.getMessage()).contains("30");
         }
     }
 }