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 07168eee784751661c298f888321c1493df1a46a Author: narendly <[email protected]> AuthorDate: Mon Feb 25 18:12:41 2019 -0800 [HELIX-804] REST: Support customFields in enabling/disabling maintenance mode To improve operability around maintenance mode, we want to allow users to specify a reason or customFields (KV mappings) when they enable/disable maintenance mode. Changelist: 1. Modify logic in ClusterAccessor so that the user could pass in customFields QueryParam in a JSON string 2. Add an integration test that verifies the fields have been set --- .../server/resources/helix/ClusterAccessor.java | 24 ++++++----- .../helix/rest/server/TestClusterAccessor.java | 48 +++++++++++++++++----- 2 files changed, 52 insertions(+), 20 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 febe65b..f24076a 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 @@ -49,6 +49,7 @@ import org.apache.helix.model.Message; import org.apache.helix.model.StateModelDefinition; import org.apache.helix.model.builder.HelixConfigScopeBuilder; import org.apache.helix.tools.ClusterSetup; +import org.codehaus.jackson.type.TypeReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -156,7 +157,7 @@ public class ClusterAccessor extends AbstractHelixResource { @Path("{clusterId}") public Response updateCluster(@PathParam("clusterId") String clusterId, @QueryParam("command") String commandStr, @QueryParam("superCluster") String superCluster, - String content) { + @QueryParam("customFields") String customFields, String content) { Command command; try { command = getCommand(commandStr); @@ -206,22 +207,25 @@ public class ClusterAccessor extends AbstractHelixResource { return serverError(ex); } break; + case enableMaintenanceMode: - try { - helixAdmin.enableMaintenanceMode(clusterId, true, content); - } catch (Exception ex) { - _logger.error("Failed to enable maintenance mode " + clusterId); - return serverError(ex); - } - break; case disableMaintenanceMode: try { - helixAdmin.enableMaintenanceMode(clusterId, false); + Map<String, String> customFieldsMap = new HashMap<>(); + if (customFields != null && !customFields.isEmpty()) { + customFieldsMap = OBJECT_MAPPER.readValue(customFields, + new TypeReference<HashMap<String, String>>() { + }); + } + helixAdmin.manuallyEnableMaintenanceMode(clusterId, + command == Command.enableMaintenanceMode, content, customFieldsMap); } catch (Exception ex) { - _logger.error("Failed to disable maintenance mode " + clusterId); + _logger.error("Failed to disable maintenance mode for cluster {}. Exception: {}", clusterId, + ex); return serverError(ex); } break; + default: return badRequest("Unsupported command " + command); } 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 46be8c2..b2a14cf 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 @@ -22,6 +22,7 @@ package org.apache.helix.rest.server; import com.google.common.collect.ImmutableMap; import com.sun.research.ws.wadl.HTTPMethods; import java.io.IOException; +import java.net.URLEncoder; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -52,7 +53,6 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class TestClusterAccessor extends AbstractTestClass { - ObjectMapper _mapper = new ObjectMapper(); @BeforeClass public void beforeClass() { @@ -68,12 +68,12 @@ public class TestClusterAccessor extends AbstractTestClass { _auditLogger.clearupLogs(); String body = get("clusters", Response.Status.OK.getStatusCode(), true); - JsonNode node = _mapper.readTree(body); + JsonNode node = OBJECT_MAPPER.readTree(body); String clustersStr = node.get(ClusterAccessor.ClusterProperties.clusters.name()).toString(); Assert.assertNotNull(clustersStr); - Set<String> clusters = _mapper.readValue(clustersStr, - _mapper.getTypeFactory().constructCollectionType(Set.class, String.class)); + Set<String> clusters = OBJECT_MAPPER.readValue(clustersStr, + OBJECT_MAPPER.getTypeFactory().constructCollectionType(Set.class, String.class)); Assert.assertEquals(clusters, _clusters, "clusters from response: " + clusters + " vs clusters actually: " + _clusters); @@ -255,7 +255,7 @@ public class TestClusterAccessor extends AbstractTestClass { Assert.assertNotNull(signal); Assert.assertEquals(reason, signal.getReason()); post("clusters/" + cluster, ImmutableMap.of("command", "disableMaintenanceMode"), - Entity.entity(new String(), MediaType.APPLICATION_JSON_TYPE), + Entity.entity("", MediaType.APPLICATION_JSON_TYPE), Response.Status.OK.getStatusCode()); Assert.assertNull(accessor.getProperty(accessor.keyBuilder().maintenance())); } @@ -304,13 +304,12 @@ public class TestClusterAccessor extends AbstractTestClass { 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); + 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()); + Object maintenanceHistoryList = maintenanceHistoryMap.get(AbstractResource.Properties.maintenanceHistory.name()); Assert.assertNotNull(maintenanceHistoryList); List<?> list = (List<?>) maintenanceHistoryList; Assert.assertTrue(list.size() > 0); @@ -320,6 +319,35 @@ public class TestClusterAccessor extends AbstractTestClass { Assert.assertTrue(lastMaintenanceEntry.contains(reason)); } + @Test(dependsOnMethods = "testEnableDisableMaintenanceMode") + public void testEnableDisableMaintenanceModeWithCustomFields() { + System.out.println("Start test :" + TestHelper.getTestMethodName()); + String cluster = _clusters.iterator().next(); + String reason = "Test reason"; + HelixDataAccessor accessor = new ZKHelixDataAccessor(cluster, _baseAccessor); + + String customFields = "{\"key1\":\"value1\",\"key2\":\"value2\"}"; + // Note that URLEncoder.encode has to be used due to a Jersey bug + // See https://github.com/Mercateo/rest-schemagen/issues/51 + post("clusters/" + cluster, + ImmutableMap.of("command", "enableMaintenanceMode", "customFields", + URLEncoder.encode(customFields)), + Entity.entity(reason, MediaType.APPLICATION_JSON_TYPE), Response.Status.OK.getStatusCode()); + + MaintenanceSignal signal = accessor.getProperty(accessor.keyBuilder().maintenance()); + Assert.assertNotNull(signal); + Assert.assertEquals(reason, signal.getReason()); + Assert.assertEquals(signal.getTriggeringEntity(), MaintenanceSignal.TriggeringEntity.USER); + Map<String, String> simpleFields = signal.getRecord().getSimpleFields(); + Assert.assertEquals(simpleFields.get("key1"), "value1"); + Assert.assertEquals(simpleFields.get("key2"), "value2"); + + post("clusters/" + cluster, ImmutableMap.of("command", "disableMaintenanceMode"), + Entity.entity("", MediaType.APPLICATION_JSON_TYPE), Response.Status.OK.getStatusCode()); + Assert.assertFalse( + accessor.getBaseDataAccessor().exists(accessor.keyBuilder().maintenance().getPath(), 0)); + } + private ClusterConfig getClusterConfigFromRest(String cluster) throws IOException { String body = get("clusters/" + cluster + "/configs", Response.Status.OK.getStatusCode(), true); @@ -337,7 +365,7 @@ public class TestClusterAccessor extends AbstractTestClass { Command command) throws IOException { _auditLogger.clearupLogs(); Entity entity = Entity - .entity(_mapper.writeValueAsString(newConfig.getRecord()), MediaType.APPLICATION_JSON_TYPE); + .entity(OBJECT_MAPPER.writeValueAsString(newConfig.getRecord()), MediaType.APPLICATION_JSON_TYPE); post("clusters/" + cluster + "/configs", ImmutableMap.of("command", command.name()), entity, Response.Status.OK.getStatusCode());
