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);
 

Reply via email to