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 <[email protected]> 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();
