You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by ji...@apache.org on 2020/11/18 19:04:26 UTC
[helix] 17/18: Add REST API for cluster topology (#1416)
This is an automated email from the ASF dual-hosted git repository.
jiajunwang pushed a commit to branch helix-0.9.x
in repository https://gitbox.apache.org/repos/asf/helix.git
commit 790e158788295295455e934ec588ffd87f5eac94
Author: Meng Zhang <mn...@linkedin.com>
AuthorDate: Tue Oct 6 22:09:52 2020 -0700
Add REST API for cluster topology (#1416)
This commit provides a few REST endpoints for user to retrieve cluster topology information. The APIs are added in ClusterAccessor.
---
.../server/resources/helix/ClusterAccessor.java | 26 ++++++
.../helix/rest/server/AbstractTestClass.java | 5 +-
.../helix/rest/server/TestClusterAccessor.java | 97 ++++++++++++++++++++++
3 files changed, 126 insertions(+), 2 deletions(-)
diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
index ccbe12a..d0a4997 100644
--- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
+++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/helix/ClusterAccessor.java
@@ -300,6 +300,32 @@ public class ClusterAccessor extends AbstractHelixResource {
return OK(objectMapper.writeValueAsString(clusterTopology));
}
+ @GET
+ @Path("{clusterId}/topologymap")
+ public Response getClusterTopologyMap(@PathParam("clusterId") String clusterId) {
+ HelixAdmin admin = getHelixAdmin();
+ Map<String, List<String>> topologyMap;
+ try {
+ topologyMap = admin.getClusterTopology(clusterId).getTopologyMap();
+ } catch (HelixException ex) {
+ return badRequest(ex.getMessage());
+ }
+ return JSONRepresentation(topologyMap);
+ }
+
+ @GET
+ @Path("{clusterId}/faultzonemap")
+ public Response getClusterFaultZoneMap(@PathParam("clusterId") String clusterId) {
+ HelixAdmin admin = getHelixAdmin();
+ Map<String, List<String>> faultZoneMap;
+ try {
+ faultZoneMap = admin.getClusterTopology(clusterId).getFaultZoneMap();
+ } catch (HelixException ex) {
+ return badRequest(ex.getMessage());
+ }
+ return JSONRepresentation(faultZoneMap);
+ }
+
@POST
@Path("{clusterId}/configs")
public Response updateClusterConfig(
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java b/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java
index 347be89..5e73c37 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/AbstractTestClass.java
@@ -473,8 +473,9 @@ public class AbstractTestClass extends JerseyTestNg.ContainerPerClassTest {
final Response response = webTarget.request().get();
Assert.assertEquals(response.getStatus(), expectedReturnStatus);
- // NOT_FOUND will throw text based html
- if (expectedReturnStatus != Response.Status.NOT_FOUND.getStatusCode()) {
+ // NOT_FOUND and BAD_REQUEST will throw text based html
+ if (expectedReturnStatus != Response.Status.NOT_FOUND.getStatusCode()
+ && expectedReturnStatus != Response.Status.BAD_REQUEST.getStatusCode()) {
Assert.assertEquals(response.getMediaType().getType(), "application");
} else {
Assert.assertEquals(response.getMediaType().getType(), "text");
diff --git a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
index 3dfb883..06a02c1 100644
--- a/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
+++ b/helix-rest/src/test/java/org/apache/helix/rest/server/TestClusterAccessor.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -121,6 +122,102 @@ public class TestClusterAccessor extends AbstractTestClass {
}
@Test(dependsOnMethods = "testGetClusterTopology")
+ public void testGetClusterTopologyAndFaultZoneMap() throws IOException {
+ System.out.println("Start test :" + TestHelper.getTestMethodName());
+ String topologyMapUrlBase = "clusters/TestCluster_1/topologymap/";
+ String faultZoneUrlBase = "clusters/TestCluster_1/faultzonemap/";
+
+ // test invalid case where instance config and cluster topology have not been set.
+ get(topologyMapUrlBase, null, Response.Status.BAD_REQUEST.getStatusCode(), true);
+ get(faultZoneUrlBase, null, Response.Status.BAD_REQUEST.getStatusCode(), true);
+
+ String cluster = "TestCluster_1";
+ for (int i = 0; i < 5; i++) {
+ String instance = cluster + "localhost_129" + String.valueOf(18 + i);
+ HelixDataAccessor helixDataAccessor = new ZKHelixDataAccessor(cluster, _baseAccessor);
+ InstanceConfig instanceConfig =
+ helixDataAccessor.getProperty(helixDataAccessor.keyBuilder().instanceConfig(instance));
+ instanceConfig.setDomain("helixZoneId=zone0,instance=" + instance);
+ helixDataAccessor
+ .setProperty(helixDataAccessor.keyBuilder().instanceConfig(instance), instanceConfig);
+ }
+
+ for (int i = 0; i < 5; i++) {
+ String instance = cluster + "localhost_129" + String.valueOf(23 + i);
+ HelixDataAccessor helixDataAccessor = new ZKHelixDataAccessor(cluster, _baseAccessor);
+ InstanceConfig instanceConfig =
+ helixDataAccessor.getProperty(helixDataAccessor.keyBuilder().instanceConfig(instance));
+ instanceConfig.setDomain("helixZoneId=zone1,instance=" + instance);
+ helixDataAccessor
+ .setProperty(helixDataAccessor.keyBuilder().instanceConfig(instance), instanceConfig);
+ }
+
+ // test invalid case where instance config is set, but cluster topology has not been set.
+ get(topologyMapUrlBase, null, Response.Status.BAD_REQUEST.getStatusCode(), true);
+ get(faultZoneUrlBase, null, Response.Status.BAD_REQUEST.getStatusCode(), true);
+
+ ClusterConfig configDelta = new ClusterConfig(cluster);
+ configDelta.getRecord().setSimpleField("TOPOLOGY", "/helixZoneId/instance");
+ updateClusterConfigFromRest(cluster, configDelta, Command.update);
+
+ //get valid cluster topology map
+ String topologyMapDef = get(topologyMapUrlBase, null, Response.Status.OK.getStatusCode(), true);
+ Map<String, Object> topologyMap =
+ OBJECT_MAPPER.readValue(topologyMapDef, new TypeReference<HashMap<String, Object>>() {
+ });
+ Assert.assertEquals(topologyMap.size(), 2);
+ Assert.assertTrue(topologyMap.get("/helixZoneId:zone0") instanceof List);
+ List<String> instances = (List<String>) topologyMap.get("/helixZoneId:zone0");
+ Assert.assertEquals(instances.size(), 5);
+ Assert.assertTrue(instances.containsAll(new HashSet<>(Arrays
+ .asList("/instance:TestCluster_1localhost_12918",
+ "/instance:TestCluster_1localhost_12919",
+ "/instance:TestCluster_1localhost_12920",
+ "/instance:TestCluster_1localhost_12921",
+ "/instance:TestCluster_1localhost_12922"))));
+
+ Assert.assertTrue(topologyMap.get("/helixZoneId:zone1") instanceof List);
+ instances = (List<String>) topologyMap.get("/helixZoneId:zone1");
+ Assert.assertEquals(instances.size(), 5);
+ Assert.assertTrue(instances.containsAll(new HashSet<>(Arrays
+ .asList("/instance:TestCluster_1localhost_12923",
+ "/instance:TestCluster_1localhost_12924",
+ "/instance:TestCluster_1localhost_12925",
+ "/instance:TestCluster_1localhost_12926",
+ "/instance:TestCluster_1localhost_12927"))));
+
+ configDelta = new ClusterConfig(cluster);
+ configDelta.getRecord().setSimpleField("FAULT_ZONE_TYPE", "helixZoneId");
+ updateClusterConfigFromRest(cluster, configDelta, Command.update);
+
+ //get valid cluster fault zone map
+ String faultZoneMapDef = get(faultZoneUrlBase, null, Response.Status.OK.getStatusCode(), true);
+ Map<String, Object> faultZoneMap =
+ OBJECT_MAPPER.readValue(faultZoneMapDef, new TypeReference<HashMap<String, Object>>() {
+ });
+ Assert.assertEquals(faultZoneMap.size(), 2);
+ Assert.assertTrue(faultZoneMap.get("/helixZoneId:zone0") instanceof List);
+ instances = (List<String>) faultZoneMap.get("/helixZoneId:zone0");
+ Assert.assertEquals(instances.size(), 5);
+ Assert.assertTrue(instances.containsAll(new HashSet<>(Arrays
+ .asList("/instance:TestCluster_1localhost_12918",
+ "/instance:TestCluster_1localhost_12919",
+ "/instance:TestCluster_1localhost_12920",
+ "/instance:TestCluster_1localhost_12921",
+ "/instance:TestCluster_1localhost_12922"))));
+
+ Assert.assertTrue(faultZoneMap.get("/helixZoneId:zone1") instanceof List);
+ instances = (List<String>) faultZoneMap.get("/helixZoneId:zone1");
+ Assert.assertEquals(instances.size(), 5);
+ Assert.assertTrue(instances.containsAll(new HashSet<>(Arrays
+ .asList("/instance:TestCluster_1localhost_12923",
+ "/instance:TestCluster_1localhost_12924",
+ "/instance:TestCluster_1localhost_12925",
+ "/instance:TestCluster_1localhost_12926",
+ "/instance:TestCluster_1localhost_12927"))));
+ }
+
+ @Test(dependsOnMethods = "testGetClusterTopologyAndFaultZoneMap")
public void testAddConfigFields() throws IOException {
System.out.println("Start test :" + TestHelper.getTestMethodName());
String cluster = _clusters.iterator().next();