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
The following commit(s) were added to refs/heads/master by this push:
new c5dbb64 Implementation of stateModelDef modification in REST 2.0
c5dbb64 is described below
commit c5dbb640a72a1afeb72e2113d0a1ae8a48f9e6b6
Author: Kai Sun <[email protected]>
AuthorDate: Tue Jul 16 18:36:42 2019 -0700
Implementation of stateModelDef modification in REST 2.0
Current implementation of Rest 2.0 does not support stateModelDef
modification. Here, we will implement
delete -- remove the stateModelDef with the input id.
put -- create new statemodeldef if no existing one with same input id
set -- replace the content of node with input id
We also add the following test cases:
Test delete model one; expect success
Test delete model one again; expect success
Create the deleted model one; expect success
Create the deleted model one again; expect failure as the same model id
exists
Set the model one with modified content; expect success
Read the model one; expect the content would be same as modified content
Set the model one to original content restore original state; expect success
---
.../server/resources/helix/ClusterAccessor.java | 95 ++++++++++++++++++++--
.../helix/rest/server/TestClusterAccessor.java | 76 ++++++++++++++++-
.../helix/rest/server/TestNamespacedAPIAccess.java | 4 +-
.../helix/rest/server/TestPerInstanceAccessor.java | 4 +-
.../helix/rest/server/TestResourceAccessor.java | 8 +-
.../apache/helix/rest/server/TestTaskAccessor.java | 2 +-
6 files changed, 168 insertions(+), 21 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 2266f97..edfc847 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
@@ -35,12 +35,14 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
import org.apache.helix.AccessOption;
import org.apache.helix.ConfigAccessor;
import org.apache.helix.HelixAdmin;
import org.apache.helix.HelixDataAccessor;
import org.apache.helix.HelixException;
import org.apache.helix.PropertyKey;
+import org.apache.helix.PropertyPathBuilder;
import org.apache.helix.ZNRecord;
import org.apache.helix.manager.zk.ZKUtil;
import org.apache.helix.manager.zk.client.HelixZkClient;
@@ -417,23 +419,98 @@ public class ClusterAccessor extends
AbstractHelixResource {
}
@GET
- @Path("{clusterId}/maintenance")
- public Response getClusterMaintenanceMode(@PathParam("clusterId") String
clusterId) {
- return JSONRepresentation(
- ImmutableMap.of(ClusterProperties.maintenance.name(),
getHelixAdmin().isInMaintenanceMode(clusterId)));
- }
-
- @GET
@Path("{clusterId}/statemodeldefs/{statemodel}")
public Response getClusterStateModelDefinition(@PathParam("clusterId")
String clusterId,
@PathParam("statemodel") String statemodel) {
HelixDataAccessor dataAccessor = getDataAccssor(clusterId);
- StateModelDefinition stateModelDef =
-
dataAccessor.getProperty(dataAccessor.keyBuilder().stateModelDef(statemodel));
+ StateModelDefinition stateModelDef =
dataAccessor.getProperty(dataAccessor.keyBuilder().stateModelDef(statemodel));
+ if (stateModelDef == null) {
+ return badRequest("Statemodel not found!");
+ }
return JSONRepresentation(stateModelDef.getRecord());
}
+ @PUT
+ @Path("{clusterId}/statemodeldefs/{statemodel}")
+ public Response createClusterStateModelDefinition(@PathParam("clusterId")
String clusterId,
+ @PathParam("statemodel") String statemodel, String content) {
+ ZNRecord record;
+ try {
+ record = toZNRecord(content);
+ } catch (IOException e) {
+ _logger.error("Failed to deserialize user's input " + content + ",
Exception: " + e);
+ return badRequest("Input is not a valid ZNRecord!");
+ }
+ HelixZkClient zkClient = getHelixZkClient();
+ String path = PropertyPathBuilder.stateModelDef(clusterId);
+ try {
+ ZKUtil.createChildren(zkClient, path, record);
+ } catch (Exception e) {
+ _logger.error("Failed to create zk node with path " + path + ",
Exception:" + e);
+ return badRequest("Failed to create a Znode for stateModel! " + e);
+ }
+
+ return OK();
+ }
+
+ @POST
+ @Path("{clusterId}/statemodeldefs/{statemodel}")
+ public Response setClusterStateModelDefinition(@PathParam("clusterId")
String clusterId,
+ @PathParam("statemodel") String statemodel, String content) {
+ ZNRecord record;
+ try {
+ record = toZNRecord(content);
+ } catch (IOException e) {
+ _logger.error("Failed to deserialize user's input " + content + ",
Exception: " + e);
+ return badRequest("Input is not a valid ZNRecord!");
+ }
+
+ StateModelDefinition stateModelDefinition = new
StateModelDefinition(record);
+ HelixDataAccessor dataAccessor = getDataAccssor(clusterId);
+
+ PropertyKey key =
dataAccessor.keyBuilder().stateModelDef(stateModelDefinition.getId());
+ boolean retcode = true;
+ try {
+ retcode = dataAccessor.setProperty(key, stateModelDefinition);
+ } catch (Exception e) {
+ _logger.error("Failed to set StateModelDefinition key:" + key + ",
Exception: " + e);
+ return badRequest("Failed to set the content " + content);
+ }
+
+ return OK();
+ }
+
+ @DELETE
+ @Path("{clusterId}/statemodeldefs/{statemodel}")
+ public Response removeClusterStateModelDefinition(@PathParam("clusterId")
String clusterId,
+ @PathParam("statemodel") String statemodel) {
+ //Shall we validate the statemodel string not having special character
such as ../ etc?
+ if (!StringUtils.isAlphanumeric(statemodel)) {
+ return badRequest("Invalid statemodel name!");
+ }
+
+ HelixDataAccessor dataAccessor = getDataAccssor(clusterId);
+ PropertyKey key = dataAccessor.keyBuilder().stateModelDef(statemodel);
+ boolean retcode = true;
+ try {
+ retcode = dataAccessor.removeProperty(key);
+ } catch (Exception e) {
+ _logger.error("Failed to remove StateModelDefinition key:" + key + ",
Exception: " + e);
+ retcode = false;
+ }
+ if (!retcode) {
+ return badRequest("Failed to remove!");
+ }
+ return OK();
+ }
+
+ @GET
+ @Path("{clusterId}/maintenance")
+ public Response getClusterMaintenanceMode(@PathParam("clusterId") String
clusterId) {
+ return JSONRepresentation(
+ ImmutableMap.of(ClusterProperties.maintenance.name(),
getHelixAdmin().isInMaintenanceMode(clusterId)));
+ }
private boolean doesClusterExist(String cluster) {
HelixZkClient zkClient = getHelixZkClient();
return ZKUtil.isClusterSetup(cluster, zkClient);
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 ab43ae0..6a28937 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
@@ -29,6 +29,7 @@ import java.util.Map;
import java.util.Set;
import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -320,7 +321,7 @@ public class TestClusterAccessor extends AbstractTestClass {
Response.Status.NOT_FOUND.getStatusCode(), false);
}
- @Test(dependsOnMethods = "testEnableDisableMaintenanceModeWithCustomFields")
+ @Test(dependsOnMethods = "testEnableDisableMaintenanceMode")
public void testGetControllerLeadershipHistory() throws IOException {
System.out.println("Start test :" + TestHelper.getTestMethodName());
String cluster = _clusters.iterator().next();
@@ -380,7 +381,7 @@ public class TestClusterAccessor extends AbstractTestClass {
Assert.assertTrue(lastMaintenanceEntry.contains(reason));
}
- @Test(dependsOnMethods = "testEnableDisableMaintenanceMode")
+ @Test(dependsOnMethods = "testGetMaintenanceHistory")
public void testEnableDisableMaintenanceModeWithCustomFields() {
System.out.println("Start test :" + TestHelper.getTestMethodName());
String cluster = _clusters.iterator().next();
@@ -405,7 +406,76 @@ public class TestClusterAccessor extends AbstractTestClass
{
accessor.getBaseDataAccessor().exists(accessor.keyBuilder().maintenance().getPath(),
0));
}
- @Test()
+ @Test(dependsOnMethods = "testEnableDisableMaintenanceModeWithCustomFields")
+ public void testGetStateModelDef() throws IOException {
+
+ System.out.println("Start test :" + TestHelper.getTestMethodName());
+ String cluster = "TestCluster_1";
+ String urlBase = "clusters/TestCluster_1/statemodeldefs/";
+ String stateModelDefs =
+ get(urlBase, null, Response.Status.OK.getStatusCode(), true);
+ Map<String, Object> defMap = OBJECT_MAPPER.readValue(stateModelDefs, new
TypeReference<HashMap<String, Object>>() {
+ });
+
+ Assert.assertTrue(defMap.size() == 2);
+ Assert.assertTrue(defMap.get("stateModelDefinitions") instanceof List);
+ List<String> stateModelNames = (List<String>)
defMap.get("stateModelDefinitions");
+ Assert.assertEquals(stateModelNames.size(), 6);
+
+ String oneModel = stateModelNames.get(1);
+ String twoModel = stateModelNames.get(2);
+
+ String oneModelUri = urlBase + oneModel;
+ String oneResult = get(oneModelUri, null,
Response.Status.OK.getStatusCode(), true);
+ ZNRecord oneRecord = OBJECT_MAPPER.readValue(oneResult, ZNRecord.class);
+
+ String twoResult =
+ get("clusters/" + cluster + "/statemodeldefs/" + twoModel, null,
Response.Status.OK.getStatusCode(), true);
+ ZNRecord twoRecord = OBJECT_MAPPER.readValue(twoResult, ZNRecord.class);
+
+ // delete one, expect success
+ String deleteOneUri = urlBase + oneRecord.getId();
+ Response deleteOneRsp = target(deleteOneUri).request().delete();
+ Assert.assertEquals(deleteOneRsp.getStatus(),
Response.Status.OK.getStatusCode());
+
+ Response queryRsp = target(oneModelUri).request().get();
+ Assert.assertTrue(queryRsp.getStatus() ==
Response.Status.BAD_REQUEST.getStatusCode());
+
+ // delete one again, expect success
+ Response deleteOneRsp2 = target(deleteOneUri).request().delete();
+ Assert.assertTrue(deleteOneRsp2.getStatus() ==
Response.Status.OK.getStatusCode());
+
+ // create the delete one, expect success
+ Response createOneRsp = target(oneModelUri).request()
+ .put(Entity.entity(OBJECT_MAPPER.writeValueAsString(oneRecord),
MediaType.APPLICATION_JSON_TYPE));
+ Assert.assertTrue(createOneRsp.getStatus() ==
Response.Status.OK.getStatusCode());
+
+ // create the delete one again, expect failure
+ Response createOneRsp2 = target(oneModelUri).request()
+ .put(Entity.entity(OBJECT_MAPPER.writeValueAsString(oneRecord),
MediaType.APPLICATION_JSON_TYPE));
+ Assert.assertTrue(createOneRsp2.getStatus() ==
Response.Status.BAD_REQUEST.getStatusCode());
+
+ // set the delete one with a modification
+ ZNRecord newRecord = new ZNRecord(twoRecord, oneRecord.getId());
+ Response setOneRsp = target(oneModelUri).request()
+ .post(Entity.entity(OBJECT_MAPPER.writeValueAsString(newRecord),
MediaType.APPLICATION_JSON_TYPE));
+ Assert.assertTrue(setOneRsp.getStatus() ==
Response.Status.OK.getStatusCode());
+
+ String oneResult2 = get(oneModelUri, null,
Response.Status.OK.getStatusCode(), true);
+ ZNRecord oneRecord2 = OBJECT_MAPPER.readValue(oneResult2, ZNRecord.class);
+ Assert.assertEquals(oneRecord2, newRecord);
+
+ // set the delete one with original; namely restore the original condition
+ Response setOneRsp2 = target(oneModelUri).request()
+ .post(Entity.entity(OBJECT_MAPPER.writeValueAsString(oneRecord),
MediaType.APPLICATION_JSON_TYPE));
+ Assert.assertTrue(setOneRsp2.getStatus() ==
Response.Status.OK.getStatusCode());
+
+ String oneResult3 = get(oneModelUri, null,
Response.Status.OK.getStatusCode(), true);
+ ZNRecord oneRecord3 = OBJECT_MAPPER.readValue(oneResult3, ZNRecord.class);
+ Assert.assertEquals(oneRecord3, oneRecord);
+ }
+
+ @Test(dependsOnMethods = "testGetStateModelDef")
public void testActivateSuperCluster() throws Exception {
System.out.println("Start test :" + TestHelper.getTestMethodName());
final String ACTIVATE_SUPER_CLUSTER =
"RestSuperClusterActivationTest_SuperCluster";
diff --git
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
index 9ebad96..ffee3e1 100644
---
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
+++
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestNamespacedAPIAccess.java
@@ -58,7 +58,7 @@ public class TestNamespacedAPIAccess extends
AbstractTestClass {
}
- @Test
+ @Test(dependsOnMethods = "testDefaultNamespaceCompatibility")
public void testNamespacedCRUD() throws IOException {
String testClusterName = "testClusterForNamespacedCRUD";
@@ -91,7 +91,7 @@ public class TestNamespacedAPIAccess extends
AbstractTestClass {
get(String.format("/clusters/%s", testClusterName), null,
Response.Status.OK.getStatusCode(), false);
}
- @Test
+ @Test(dependsOnMethods = "testNamespacedCRUD")
public void testNamespaceServer() throws IOException {
// Default endpoints should not have any namespace information returned
get("/", null, Response.Status.NOT_FOUND.getStatusCode(), false);
diff --git
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
index b3acea8..d9ef426 100644
---
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
+++
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestPerInstanceAccessor.java
@@ -161,7 +161,7 @@ public class TestPerInstanceAccessor extends
AbstractTestClass {
_configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME));
}
- @Test(dependsOnMethods = "updateInstance")
+ @Test(dependsOnMethods = "testGetInstanceById")
public void testAddInstance() throws IOException {
System.out.println("Start test :" + TestHelper.getTestMethodName());
InstanceConfig instanceConfig = new InstanceConfig(INSTANCE_NAME + "TEST");
@@ -183,7 +183,7 @@ public class TestPerInstanceAccessor extends
AbstractTestClass {
_configAccessor.getInstanceConfig(CLUSTER_NAME, INSTANCE_NAME + "TEST");
}
- @Test(dependsOnMethods = "testGetInstanceById")
+ @Test(dependsOnMethods = "testDeleteInstance")
public void updateInstance() throws IOException {
System.out.println("Start test :" + TestHelper.getTestMethodName());
// Disable instance
diff --git
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
index f136d9f..1ad638a 100644
---
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
+++
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestResourceAccessor.java
@@ -133,7 +133,7 @@ public class TestResourceAccessor extends AbstractTestClass
{
_configAccessor.getResourceConfig(CLUSTER_NAME, RESOURCE_NAME));
}
- @Test(dependsOnMethods = "testAddResources")
+ @Test(dependsOnMethods = "testResourceConfig")
public void testIdealState() throws IOException {
System.out.println("Start test :" + TestHelper.getTestMethodName());
@@ -144,7 +144,7 @@ public class TestResourceAccessor extends AbstractTestClass
{
_gSetupTool.getClusterManagementTool().getResourceIdealState(CLUSTER_NAME,
RESOURCE_NAME));
}
- @Test(dependsOnMethods = "testAddResources")
+ @Test(dependsOnMethods = "testIdealState")
public void testExternalView() throws IOException {
System.out.println("Start test :" + TestHelper.getTestMethodName());
@@ -155,7 +155,7 @@ public class TestResourceAccessor extends AbstractTestClass
{
.getResourceExternalView(CLUSTER_NAME, RESOURCE_NAME));
}
- @Test
+ @Test(dependsOnMethods = "testExternalView")
public void testPartitionHealth() throws Exception {
System.out.println("Start test :" + TestHelper.getTestMethodName());
@@ -380,7 +380,7 @@ public class TestResourceAccessor extends AbstractTestClass
{
* Test "update" command of updateResourceIdealState.
* @throws Exception
*/
- @Test(dependsOnMethods = "testResourceHealth")
+ @Test(dependsOnMethods = "deleteFromResourceConfig")
public void updateResourceIdealState() throws Exception {
// Get IdealState ZNode
String zkPath = PropertyPathBuilder.idealState(CLUSTER_NAME,
RESOURCE_NAME);
diff --git
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
index 3f9a0e2..4753e27 100644
---
a/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
+++
b/helix-rest/src/test/java/org/apache/helix/rest/server/TestTaskAccessor.java
@@ -58,7 +58,7 @@ public class TestTaskAccessor extends AbstractTestClass {
Assert.assertEquals(contentStore, map1);
}
- @Test
+ @Test(dependsOnMethods = "testGetAddTaskUserContent")
public void testInvalidGetAddTaskUserContent() {
System.out.println("Start test :" + TestHelper.getTestMethodName());
String validURI = "clusters/" + CLUSTER_NAME +
"/workflows/Workflow_0/jobs/Job_0/tasks/0/userContent";