This is an automated email from the ASF dual-hosted git repository. jxue pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/helix.git
commit ebffa4693d5d2097ec6105698f2863a7ad1eda69 Author: narendly <[email protected]> AuthorDate: Mon Feb 25 18:11:17 2019 -0800 [HELIX-803] REST: Support GET of maintenance history History entries regarding cluster maintenance mode will be recorded in the HISTORY ZNode as part of the auto-exit of maintenance mode project. In order to make it accessible programmatically, we are going to support a REST endpoint that fetches maintenance history entries. Changelist: 1. Add an endpoint for retrieving maintenance history 2. Add integration tests for retrieving controller leadership history and maintenance history --- .../org/apache/helix/model/ControllerHistory.java | 2 +- .../rest/server/resources/AbstractResource.java | 1 + .../server/resources/helix/ClusterAccessor.java | 52 ++++++++++++---- .../helix/rest/server/TestClusterAccessor.java | 70 ++++++++++++++++++++-- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java b/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java index 4d87ffc..955a5bf 100644 --- a/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java +++ b/helix-core/src/main/java/org/apache/helix/model/ControllerHistory.java @@ -62,7 +62,7 @@ public class ControllerHistory extends HelixProperty { EXIT } - public enum HistoryUpdaterType { + public enum HistoryType { CONTROLLER_LEADERSHIP, MAINTENANCE } diff --git a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/AbstractResource.java b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/AbstractResource.java index 3b7d995..f5734a2 100644 --- a/helix-rest/src/main/java/org/apache/helix/rest/server/resources/AbstractResource.java +++ b/helix-rest/src/main/java/org/apache/helix/rest/server/resources/AbstractResource.java @@ -46,6 +46,7 @@ public class AbstractResource { id, disabled, history, + maintenanceHistory, count, error } 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 e901d9c..febe65b 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 @@ -324,20 +324,16 @@ public class ClusterAccessor extends AbstractHelixResource { @GET @Path("{clusterId}/controller/history") - public Response getClusterControllerHistory(@PathParam("clusterId") String clusterId) { - HelixDataAccessor dataAccessor = getDataAccssor(clusterId); - Map<String, Object> controllerHistory = new HashMap<>(); - controllerHistory.put(Properties.id.name(), clusterId); - - ControllerHistory history = - dataAccessor.getProperty(dataAccessor.keyBuilder().controllerLeaderHistory()); - if (history != null) { - controllerHistory.put(Properties.history.name(), history.getHistoryList()); - } else { - controllerHistory.put(Properties.history.name(), Collections.emptyList()); - } + public Response getClusterControllerLeadershipHistory(@PathParam("clusterId") String clusterId) { + return JSONRepresentation(getControllerHistory(clusterId, + ControllerHistory.HistoryType.CONTROLLER_LEADERSHIP)); + } - return JSONRepresentation(controllerHistory); + @GET + @Path("{clusterId}/controller/maintenanceHistory") + public Response getClusterMaintenanceHistory(@PathParam("clusterId") String clusterId) { + return JSONRepresentation( + getControllerHistory(clusterId, ControllerHistory.HistoryType.MAINTENANCE)); } @GET @@ -397,4 +393,34 @@ public class ClusterAccessor extends AbstractHelixResource { } return false; } + + /** + * Reads HISTORY ZNode from the metadata store and generates a Map object that contains the + * pertinent history entries depending on the history type. + * @param clusterId + * @param historyType + * @return + */ + private Map<String, Object> getControllerHistory(String clusterId, + ControllerHistory.HistoryType historyType) { + HelixDataAccessor dataAccessor = getDataAccssor(clusterId); + Map<String, Object> history = new HashMap<>(); + history.put(Properties.id.name(), clusterId); + + ControllerHistory historyRecord = + dataAccessor.getProperty(dataAccessor.keyBuilder().controllerLeaderHistory()); + + switch (historyType) { + case CONTROLLER_LEADERSHIP: + history.put(Properties.history.name(), + historyRecord != null ? historyRecord.getHistoryList() : Collections.emptyList()); + break; + case MAINTENANCE: + history.put(Properties.maintenanceHistory.name(), + historyRecord != null ? historyRecord.getMaintenanceHistoryList() + : Collections.emptyList()); + break; + } + return history; + } } 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 763d3e2..46be8c2 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 @@ -41,10 +41,12 @@ import org.apache.helix.model.ClusterConfig; import org.apache.helix.model.MaintenanceSignal; import org.apache.helix.rest.common.HelixRestNamespace; import org.apache.helix.rest.server.auditlog.AuditLog; +import org.apache.helix.rest.server.resources.AbstractResource; import org.apache.helix.rest.server.resources.AbstractResource.Command; import org.apache.helix.rest.server.resources.helix.ClusterAccessor; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -188,12 +190,12 @@ public class TestClusterAccessor extends AbstractTestClass { // create an existing cluster should fail. _auditLogger.clearupLogs(); String cluster = _clusters.iterator().next(); - put("clusters/" + cluster, null, Entity.entity(new String(), MediaType.APPLICATION_JSON_TYPE), + put("clusters/" + cluster, null, Entity.entity("", MediaType.APPLICATION_JSON_TYPE), Response.Status.CREATED.getStatusCode()); // create a new cluster cluster = "NewCluster"; - put("clusters/" + cluster, null, Entity.entity(new String(), MediaType.APPLICATION_JSON_TYPE), + put("clusters/" + cluster, null, Entity.entity("", MediaType.APPLICATION_JSON_TYPE), Response.Status.CREATED.getStatusCode()); // verify the cluster has been created. @@ -214,7 +216,7 @@ public class TestClusterAccessor extends AbstractTestClass { String cluster = _clusters.iterator().next(); _auditLogger.clearupLogs(); post("clusters/" + cluster, ImmutableMap.of("command", "disable"), - Entity.entity(new String(), MediaType.APPLICATION_JSON_TYPE), + Entity.entity("", MediaType.APPLICATION_JSON_TYPE), Response.Status.OK.getStatusCode()); PropertyKey.Builder keyBuilder = new PropertyKey.Builder(cluster); @@ -223,7 +225,7 @@ public class TestClusterAccessor extends AbstractTestClass { // enable a cluster. post("clusters/" + cluster, ImmutableMap.of("command", "enable"), - Entity.entity(new String(), MediaType.APPLICATION_JSON_TYPE), + Entity.entity("", MediaType.APPLICATION_JSON_TYPE), Response.Status.OK.getStatusCode()); // verify the cluster is paused. @@ -258,6 +260,66 @@ public class TestClusterAccessor extends AbstractTestClass { Assert.assertNull(accessor.getProperty(accessor.keyBuilder().maintenance())); } + @Test + public void testGetControllerLeadershipHistory() throws IOException { + System.out.println("Start test :" + TestHelper.getTestMethodName()); + String cluster = _clusters.iterator().next(); + + // Get the leader controller name for the cluster + String leader = + get("clusters/" + cluster + "/controller", Response.Status.OK.getStatusCode(), true); + Map<String, String> leaderMap = + OBJECT_MAPPER.readValue(leader, new TypeReference<HashMap<String, String>>() { + }); + Assert.assertNotNull(leaderMap, "Controller leader cannot be null!"); + leader = leaderMap.get("controller"); + Assert.assertNotNull(leader, "Leader name cannot be null!"); + + // Get the controller leadership history JSON's last entry + String leadershipHistory = get("clusters/" + cluster + "/controller/history", + Response.Status.OK.getStatusCode(), true); + Map<String, Object> leadershipHistoryMap = + OBJECT_MAPPER.readValue(leadershipHistory, new TypeReference<HashMap<String, Object>>() { + }); + Assert.assertNotNull(leadershipHistoryMap, "Leadership history cannot be null!"); + Object leadershipHistoryList = + leadershipHistoryMap.get(AbstractResource.Properties.history.name()); + Assert.assertNotNull(leadershipHistoryList); + List<?> list = (List<?>) leadershipHistoryList; + Assert.assertTrue(list.size() > 0); + String lastLeaderEntry = (String) list.get(list.size() - 1); + + // Check that the last entry contains the current leader name + Assert.assertTrue(lastLeaderEntry.contains(leader)); + } + + @Test + public void testGetMaintenanceHistory() throws IOException { + System.out.println("Start test :" + TestHelper.getTestMethodName()); + String cluster = _clusters.iterator().next(); + String reason = TestHelper.getTestMethodName(); + + // Enable maintenance mode + post("clusters/" + cluster, ImmutableMap.of("command", "enableMaintenanceMode"), + Entity.entity(reason, MediaType.APPLICATION_JSON_TYPE), Response.Status.OK.getStatusCode()); + + // Get the maintenance history JSON's last entry + String maintenanceHistory = get("clusters/" + cluster + "/controller/maintenanceHistory", + Response.Status.OK.getStatusCode(), true); + Map<String, Object> maintenanceHistoryMap = + OBJECT_MAPPER.readValue(maintenanceHistory, new TypeReference<HashMap<String, Object>>() { + }); + Object maintenanceHistoryList = + maintenanceHistoryMap.get(AbstractResource.Properties.maintenanceHistory.name()); + Assert.assertNotNull(maintenanceHistoryList); + List<?> list = (List<?>) maintenanceHistoryList; + Assert.assertTrue(list.size() > 0); + String lastMaintenanceEntry = (String) list.get(list.size() - 1); + + // Check that the last entry contains the reason string + Assert.assertTrue(lastMaintenanceEntry.contains(reason)); + } + private ClusterConfig getClusterConfigFromRest(String cluster) throws IOException { String body = get("clusters/" + cluster + "/configs", Response.Status.OK.getStatusCode(), true);
