YARN-2505. Supported get/add/remove/change labels in RM REST API. Contributed by Craig Welch.
This commit is contained in:
parent
df36edf751
commit
9a4e0d343e
|
@ -198,6 +198,9 @@ Release 2.6.0 - UNRELEASED
|
||||||
YARN-2632. Document NM Restart feature. (Junping Du and Vinod Kumar
|
YARN-2632. Document NM Restart feature. (Junping Du and Vinod Kumar
|
||||||
Vavilapalli via jlowe)
|
Vavilapalli via jlowe)
|
||||||
|
|
||||||
|
YARN-2505. Supported get/add/remove/change labels in RM REST API. (Craig Welch
|
||||||
|
via zjshen)
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
|
||||||
YARN-2197. Add a link to YARN CHANGES.txt in the left side of doc
|
YARN-2197. Add a link to YARN CHANGES.txt in the left side of doc
|
||||||
|
|
|
@ -151,6 +151,13 @@ public class ConverterUtils {
|
||||||
public static String toString(ContainerId cId) {
|
public static String toString(ContainerId cId) {
|
||||||
return cId == null ? null : cId.toString();
|
return cId == null ? null : cId.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static NodeId toNodeIdWithDefaultPort(String nodeIdStr) {
|
||||||
|
if (nodeIdStr.indexOf(":") < 0) {
|
||||||
|
return toNodeId(nodeIdStr + ":0");
|
||||||
|
}
|
||||||
|
return toNodeId(nodeIdStr);
|
||||||
|
}
|
||||||
|
|
||||||
public static NodeId toNodeId(String nodeIdStr) {
|
public static NodeId toNodeId(String nodeIdStr) {
|
||||||
String[] parts = nodeIdStr.split(":");
|
String[] parts = nodeIdStr.split(":");
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.yarn.api.TestContainerId;
|
import org.apache.hadoop.yarn.api.TestContainerId;
|
||||||
import org.apache.hadoop.yarn.api.records.ContainerId;
|
import org.apache.hadoop.yarn.api.records.ContainerId;
|
||||||
import org.apache.hadoop.yarn.api.records.URL;
|
import org.apache.hadoop.yarn.api.records.URL;
|
||||||
|
import org.apache.hadoop.yarn.api.records.NodeId;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class TestConverterUtils {
|
public class TestConverterUtils {
|
||||||
|
@ -85,4 +86,17 @@ public class TestConverterUtils {
|
||||||
public void testContainerIdNull() throws URISyntaxException {
|
public void testContainerIdNull() throws URISyntaxException {
|
||||||
assertNull(ConverterUtils.toString((ContainerId)null));
|
assertNull(ConverterUtils.toString((ContainerId)null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNodeIdWithDefaultPort() throws URISyntaxException {
|
||||||
|
NodeId nid;
|
||||||
|
|
||||||
|
nid = ConverterUtils.toNodeIdWithDefaultPort("node:10");
|
||||||
|
assertEquals(nid.getPort(), 10);
|
||||||
|
assertEquals(nid.getHost(), "node");
|
||||||
|
|
||||||
|
nid = ConverterUtils.toNodeIdWithDefaultPort("node");
|
||||||
|
assertEquals(nid.getPort(), 0);
|
||||||
|
assertEquals(nid.getHost(), "node");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.security.AccessControlException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.security.PrivilegedExceptionAction;
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
@ -133,6 +134,8 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ResourceInfo;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerInfo;
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerInfo;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo;
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.StatisticsItemInfo;
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.StatisticsItemInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
|
||||||
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
|
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
|
||||||
import org.apache.hadoop.yarn.util.ConverterUtils;
|
import org.apache.hadoop.yarn.util.ConverterUtils;
|
||||||
import org.apache.hadoop.yarn.webapp.BadRequestException;
|
import org.apache.hadoop.yarn.webapp.BadRequestException;
|
||||||
|
@ -715,6 +718,179 @@ public class RMWebServices {
|
||||||
|
|
||||||
return Response.status(Status.OK).entity(ret).build();
|
return Response.status(Status.OK).entity(ret).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/get-node-to-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public NodeToLabelsInfo getNodeToLabels(@Context HttpServletRequest hsr)
|
||||||
|
throws IOException {
|
||||||
|
init();
|
||||||
|
|
||||||
|
NodeToLabelsInfo ntl = new NodeToLabelsInfo();
|
||||||
|
HashMap<String, NodeLabelsInfo> ntlMap = ntl.getNodeToLabels();
|
||||||
|
Map<NodeId, Set<String>> nodeIdToLabels =
|
||||||
|
rm.getRMContext().getNodeLabelManager().getNodeLabels();
|
||||||
|
|
||||||
|
for (Map.Entry<NodeId, Set<String>> nitle : nodeIdToLabels.entrySet()) {
|
||||||
|
ntlMap.put(nitle.getKey().toString(),
|
||||||
|
new NodeLabelsInfo(nitle.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ntl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/replace-node-to-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public Response replaceLabelsOnNodes(
|
||||||
|
final NodeToLabelsInfo newNodeToLabels,
|
||||||
|
@Context HttpServletRequest hsr)
|
||||||
|
throws IOException {
|
||||||
|
init();
|
||||||
|
|
||||||
|
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
|
||||||
|
if (callerUGI == null) {
|
||||||
|
String msg = "Unable to obtain user name, user not authenticated for"
|
||||||
|
+ " post to .../replace-node-to-labels";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
if (!rm.getRMContext().getNodeLabelManager().checkAccess(callerUGI)) {
|
||||||
|
String msg = "User " + callerUGI.getShortUserName() + " not authorized"
|
||||||
|
+ " for post to .../replace-node-to-labels ";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<NodeId, Set<String>> nodeIdToLabels =
|
||||||
|
new HashMap<NodeId, Set<String>>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, NodeLabelsInfo> nitle :
|
||||||
|
newNodeToLabels.getNodeToLabels().entrySet()) {
|
||||||
|
nodeIdToLabels.put(ConverterUtils.toNodeIdWithDefaultPort(nitle.getKey()),
|
||||||
|
new HashSet<String>(nitle.getValue().getNodeLabels()));
|
||||||
|
}
|
||||||
|
|
||||||
|
rm.getRMContext().getNodeLabelManager().replaceLabelsOnNode(nodeIdToLabels);
|
||||||
|
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/get-node-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public NodeLabelsInfo getClusterNodeLabels(@Context HttpServletRequest hsr)
|
||||||
|
throws IOException {
|
||||||
|
init();
|
||||||
|
|
||||||
|
NodeLabelsInfo ret =
|
||||||
|
new NodeLabelsInfo(rm.getRMContext().getNodeLabelManager()
|
||||||
|
.getClusterNodeLabels());
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/add-node-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public Response addToClusterNodeLabels(final NodeLabelsInfo newNodeLabels,
|
||||||
|
@Context HttpServletRequest hsr)
|
||||||
|
throws Exception {
|
||||||
|
init();
|
||||||
|
|
||||||
|
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
|
||||||
|
if (callerUGI == null) {
|
||||||
|
String msg = "Unable to obtain user name, user not authenticated for"
|
||||||
|
+ " post to .../add-node-labels";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
if (!rm.getRMContext().getNodeLabelManager().checkAccess(callerUGI)) {
|
||||||
|
String msg = "User " + callerUGI.getShortUserName() + " not authorized"
|
||||||
|
+ " for post to .../add-node-labels ";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
rm.getRMContext().getNodeLabelManager()
|
||||||
|
.addToCluserNodeLabels(new HashSet<String>(
|
||||||
|
newNodeLabels.getNodeLabels()));
|
||||||
|
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/remove-node-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public Response removeFromCluserNodeLabels(final NodeLabelsInfo oldNodeLabels,
|
||||||
|
@Context HttpServletRequest hsr)
|
||||||
|
throws Exception {
|
||||||
|
init();
|
||||||
|
|
||||||
|
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
|
||||||
|
if (callerUGI == null) {
|
||||||
|
String msg = "Unable to obtain user name, user not authenticated for"
|
||||||
|
+ " post to .../remove-node-labels";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
if (!rm.getRMContext().getNodeLabelManager().checkAccess(callerUGI)) {
|
||||||
|
String msg = "User " + callerUGI.getShortUserName() + " not authorized"
|
||||||
|
+ " for post to .../remove-node-labels ";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
rm.getRMContext().getNodeLabelManager()
|
||||||
|
.removeFromClusterNodeLabels(new HashSet<String>(
|
||||||
|
oldNodeLabels.getNodeLabels()));
|
||||||
|
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/nodes/{nodeId}/get-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public NodeLabelsInfo getLabelsOnNode(@Context HttpServletRequest hsr,
|
||||||
|
@PathParam("nodeId") String nodeId)
|
||||||
|
throws IOException {
|
||||||
|
init();
|
||||||
|
|
||||||
|
NodeId nid = ConverterUtils.toNodeIdWithDefaultPort(nodeId);
|
||||||
|
return new NodeLabelsInfo(
|
||||||
|
rm.getRMContext().getNodeLabelManager().getLabelsOnNode(nid));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/nodes/{nodeId}/replace-labels")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public Response replaceLabelsOnNode(NodeLabelsInfo newNodeLabelsInfo,
|
||||||
|
@Context HttpServletRequest hsr, @PathParam("nodeId") String nodeId)
|
||||||
|
throws Exception {
|
||||||
|
init();
|
||||||
|
|
||||||
|
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
|
||||||
|
if (callerUGI == null) {
|
||||||
|
String msg = "Unable to obtain user name, user not authenticated for"
|
||||||
|
+ " post to .../nodes/nodeid/replace-labels";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rm.getRMContext().getNodeLabelManager().checkAccess(callerUGI)) {
|
||||||
|
String msg = "User " + callerUGI.getShortUserName() + " not authorized"
|
||||||
|
+ " for post to .../nodes/nodeid/replace-labels";
|
||||||
|
throw new AuthorizationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeId nid = ConverterUtils.toNodeIdWithDefaultPort(nodeId);
|
||||||
|
|
||||||
|
Map<NodeId, Set<String>> newLabelsForNode = new HashMap<NodeId,
|
||||||
|
Set<String>>();
|
||||||
|
|
||||||
|
newLabelsForNode.put(nid, new HashSet<String>(newNodeLabelsInfo.getNodeLabels()));
|
||||||
|
|
||||||
|
rm.getRMContext().getNodeLabelManager().replaceLabelsOnNode(newLabelsForNode);
|
||||||
|
|
||||||
|
return Response.status(Status.OK).build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected Response killApp(RMApp app, UserGroupInformation callerUGI,
|
protected Response killApp(RMApp app, UserGroupInformation callerUGI,
|
||||||
HttpServletRequest hsr) throws IOException, InterruptedException {
|
HttpServletRequest hsr) throws IOException, InterruptedException {
|
||||||
|
@ -965,7 +1141,9 @@ public class RMWebServices {
|
||||||
newApp.getCancelTokensWhenComplete(), newApp.getMaxAppAttempts(),
|
newApp.getCancelTokensWhenComplete(), newApp.getMaxAppAttempts(),
|
||||||
createAppSubmissionContextResource(newApp),
|
createAppSubmissionContextResource(newApp),
|
||||||
newApp.getApplicationType(),
|
newApp.getApplicationType(),
|
||||||
newApp.getKeepContainersAcrossApplicationAttempts());
|
newApp.getKeepContainersAcrossApplicationAttempts(),
|
||||||
|
newApp.getAppNodeLabelExpression(),
|
||||||
|
newApp.getAMContainerNodeLabelExpression());
|
||||||
appContext.setApplicationTags(newApp.getApplicationTags());
|
appContext.setApplicationTags(newApp.getApplicationTags());
|
||||||
|
|
||||||
return appContext;
|
return appContext;
|
||||||
|
|
|
@ -71,6 +71,12 @@ public class ApplicationSubmissionContextInfo {
|
||||||
@XmlElementWrapper(name = "application-tags")
|
@XmlElementWrapper(name = "application-tags")
|
||||||
@XmlElement(name = "tag")
|
@XmlElement(name = "tag")
|
||||||
Set<String> tags;
|
Set<String> tags;
|
||||||
|
|
||||||
|
@XmlElement(name = "app-node-label-expression")
|
||||||
|
String appNodeLabelExpression;
|
||||||
|
|
||||||
|
@XmlElement(name = "am-container-node-label-expression")
|
||||||
|
String amContainerNodeLabelExpression;
|
||||||
|
|
||||||
public ApplicationSubmissionContextInfo() {
|
public ApplicationSubmissionContextInfo() {
|
||||||
applicationId = "";
|
applicationId = "";
|
||||||
|
@ -83,6 +89,8 @@ public class ApplicationSubmissionContextInfo {
|
||||||
keepContainers = false;
|
keepContainers = false;
|
||||||
applicationType = "";
|
applicationType = "";
|
||||||
tags = new HashSet<String>();
|
tags = new HashSet<String>();
|
||||||
|
appNodeLabelExpression = "";
|
||||||
|
amContainerNodeLabelExpression = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getApplicationId() {
|
public String getApplicationId() {
|
||||||
|
@ -132,6 +140,14 @@ public class ApplicationSubmissionContextInfo {
|
||||||
public Set<String> getApplicationTags() {
|
public Set<String> getApplicationTags() {
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAppNodeLabelExpression() {
|
||||||
|
return appNodeLabelExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAMContainerNodeLabelExpression() {
|
||||||
|
return amContainerNodeLabelExpression;
|
||||||
|
}
|
||||||
|
|
||||||
public void setApplicationId(String applicationId) {
|
public void setApplicationId(String applicationId) {
|
||||||
this.applicationId = applicationId;
|
this.applicationId = applicationId;
|
||||||
|
@ -182,5 +198,12 @@ public class ApplicationSubmissionContextInfo {
|
||||||
public void setApplicationTags(Set<String> tags) {
|
public void setApplicationTags(Set<String> tags) {
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAppNodeLabelExpression(String appNodeLabelExpression) {
|
||||||
|
this.appNodeLabelExpression = appNodeLabelExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAMContainerNodeLabelExpression(String nodeLabelExpression) {
|
||||||
|
this.amContainerNodeLabelExpression = nodeLabelExpression;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.resourcemanager.webapp.dao;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
@XmlRootElement(name = "nodeLabelsInfo")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public class NodeLabelsInfo {
|
||||||
|
|
||||||
|
protected ArrayList<String> nodeLabels = new ArrayList<String>();
|
||||||
|
|
||||||
|
public NodeLabelsInfo() {
|
||||||
|
} // JAXB needs this
|
||||||
|
|
||||||
|
public NodeLabelsInfo(ArrayList<String> nodeLabels) {
|
||||||
|
this.nodeLabels = nodeLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeLabelsInfo(Set<String> nodeLabelsSet) {
|
||||||
|
this.nodeLabels = new ArrayList<String>(nodeLabelsSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<String> getNodeLabels() {
|
||||||
|
return nodeLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodeLabels(ArrayList<String> nodeLabels) {
|
||||||
|
this.nodeLabels = nodeLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.resourcemanager.webapp.dao;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
@XmlRootElement(name = "nodeToLabelsInfo")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public class NodeToLabelsInfo {
|
||||||
|
|
||||||
|
protected HashMap<String, NodeLabelsInfo> nodeToLabels =
|
||||||
|
new HashMap<String, NodeLabelsInfo>();
|
||||||
|
|
||||||
|
public NodeToLabelsInfo() {
|
||||||
|
} // JAXB needs this
|
||||||
|
|
||||||
|
public HashMap<String, NodeLabelsInfo> getNodeToLabels() {
|
||||||
|
return nodeToLabels;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hadoop.yarn.server.resourcemanager.webapp;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
|
||||||
|
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
||||||
|
import org.codehaus.jettison.json.JSONArray;
|
||||||
|
import org.codehaus.jettison.json.JSONException;
|
||||||
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import com.google.inject.servlet.GuiceServletContextListener;
|
||||||
|
import com.google.inject.servlet.ServletModule;
|
||||||
|
import com.sun.jersey.api.client.ClientResponse;
|
||||||
|
import com.sun.jersey.api.client.WebResource;
|
||||||
|
import com.sun.jersey.api.json.JSONJAXBContext;
|
||||||
|
import com.sun.jersey.api.json.JSONMarshaller;
|
||||||
|
import com.sun.jersey.api.json.JSONUnmarshaller;
|
||||||
|
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
|
||||||
|
import com.sun.jersey.test.framework.JerseyTest;
|
||||||
|
import com.sun.jersey.test.framework.WebAppDescriptor;
|
||||||
|
|
||||||
|
public class TestRMWebServicesNodeLabels extends JerseyTest {
|
||||||
|
|
||||||
|
private static final Log LOG = LogFactory
|
||||||
|
.getLog(TestRMWebServicesNodeLabels.class);
|
||||||
|
|
||||||
|
private static MockRM rm;
|
||||||
|
private YarnConfiguration conf;
|
||||||
|
|
||||||
|
private String userName;
|
||||||
|
private String notUserName;
|
||||||
|
|
||||||
|
private Injector injector = Guice.createInjector(new ServletModule() {
|
||||||
|
@Override
|
||||||
|
protected void configureServlets() {
|
||||||
|
bind(JAXBContextResolver.class);
|
||||||
|
bind(RMWebServices.class);
|
||||||
|
bind(GenericExceptionHandler.class);
|
||||||
|
try {
|
||||||
|
userName = UserGroupInformation.getCurrentUser().getShortUserName();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new RuntimeException("Unable to get current user name "
|
||||||
|
+ ioe.getMessage(), ioe);
|
||||||
|
}
|
||||||
|
notUserName = userName + "abc123";
|
||||||
|
conf = new YarnConfiguration();
|
||||||
|
conf.set(YarnConfiguration.YARN_ADMIN_ACL, userName);
|
||||||
|
rm = new MockRM(conf);
|
||||||
|
bind(ResourceManager.class).toInstance(rm);
|
||||||
|
bind(RMContext.class).toInstance(rm.getRMContext());
|
||||||
|
filter("/*").through(
|
||||||
|
TestRMWebServicesAppsModification.TestRMCustomAuthFilter.class);
|
||||||
|
serve("/*").with(GuiceContainer.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
public class GuiceServletConfig extends GuiceServletContextListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Injector getInjector() {
|
||||||
|
return injector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestRMWebServicesNodeLabels() {
|
||||||
|
super(new WebAppDescriptor.Builder(
|
||||||
|
"org.apache.hadoop.yarn.server.resourcemanager.webapp")
|
||||||
|
.contextListenerClass(GuiceServletConfig.class)
|
||||||
|
.filterClass(com.google.inject.servlet.GuiceFilter.class)
|
||||||
|
.contextPath("jersey-guice-filter").servletPath("/").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNodeLabels() throws JSONException, Exception {
|
||||||
|
WebResource r = resource();
|
||||||
|
|
||||||
|
ClientResponse response;
|
||||||
|
JSONObject json;
|
||||||
|
JSONArray jarr;
|
||||||
|
String responseString;
|
||||||
|
|
||||||
|
// Add a label
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("add-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\":\"a\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("get-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
assertEquals("a", json.getString("nodeLabels"));
|
||||||
|
|
||||||
|
// Add another
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("add-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\":\"b\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("get-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
jarr = json.getJSONArray("nodeLabels");
|
||||||
|
assertEquals(2, jarr.length());
|
||||||
|
|
||||||
|
// Add labels to a node
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("replace-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\": [\"a\", \"b\"]}",
|
||||||
|
MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
LOG.info("posted node nodelabel");
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("get-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
jarr = json.getJSONArray("nodeLabels");
|
||||||
|
assertEquals(2, jarr.length());
|
||||||
|
|
||||||
|
// Replace
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("replace-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\":\"a\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
LOG.info("posted node nodelabel");
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("get-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
assertEquals("a", json.getString("nodeLabels"));
|
||||||
|
|
||||||
|
// Replace labels using node-to-labels
|
||||||
|
NodeToLabelsInfo ntli = new NodeToLabelsInfo();
|
||||||
|
NodeLabelsInfo nli = new NodeLabelsInfo();
|
||||||
|
nli.getNodeLabels().add("a");
|
||||||
|
nli.getNodeLabels().add("b");
|
||||||
|
ntli.getNodeToLabels().put("nid:0", nli);
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("replace-node-to-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity(toJson(ntli, NodeToLabelsInfo.class),
|
||||||
|
MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
|
||||||
|
// Verify, using node-to-labels
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("get-node-to-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
ntli = response.getEntity(NodeToLabelsInfo.class);
|
||||||
|
nli = ntli.getNodeToLabels().get("nid:0");
|
||||||
|
assertEquals(2, nli.getNodeLabels().size());
|
||||||
|
assertTrue(nli.getNodeLabels().contains("a"));
|
||||||
|
assertTrue(nli.getNodeLabels().contains("b"));
|
||||||
|
|
||||||
|
// Remove all
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("replace-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
LOG.info("posted node nodelabel");
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("get-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
assertEquals("", json.getString("nodeLabels"));
|
||||||
|
|
||||||
|
// Add a label back for auth tests
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("replace-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\": \"a\"}",
|
||||||
|
MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
LOG.info("posted node nodelabel");
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("get-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
assertEquals("a", json.getString("nodeLabels"));
|
||||||
|
|
||||||
|
// Auth fail replace labels on node
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("replace-labels")
|
||||||
|
.queryParam("user.name", notUserName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\": [\"a\", \"b\"]}",
|
||||||
|
MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("nodes").path("nid:0")
|
||||||
|
.path("get-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
assertEquals("a", json.getString("nodeLabels"));
|
||||||
|
|
||||||
|
// Fail to add a label with post
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("add-node-labels").queryParam("user.name", notUserName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\":\"c\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("get-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
jarr = json.getJSONArray("nodeLabels");
|
||||||
|
assertEquals(2, jarr.length());
|
||||||
|
|
||||||
|
// Remove cluster label (succeed, we no longer need it)
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("remove-node-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\":\"b\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("get-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
assertEquals("a", json.getString("nodeLabels"));
|
||||||
|
|
||||||
|
|
||||||
|
// Remove cluster label with post
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("remove-node-labels")
|
||||||
|
.queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)
|
||||||
|
.entity("{\"nodeLabels\":\"a\"}", MediaType.APPLICATION_JSON)
|
||||||
|
.post(ClientResponse.class);
|
||||||
|
// Verify
|
||||||
|
response =
|
||||||
|
r.path("ws").path("v1").path("cluster")
|
||||||
|
.path("get-node-labels").queryParam("user.name", userName)
|
||||||
|
.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
|
||||||
|
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||||
|
String res = response.getEntity(String.class);
|
||||||
|
assertTrue(res.equals("null"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private String toJson(Object nsli, Class klass) throws Exception {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
JSONJAXBContext ctx = new JSONJAXBContext(klass);
|
||||||
|
JSONMarshaller jm = ctx.createJSONMarshaller();
|
||||||
|
jm.marshallToJSON(nsli, sw);
|
||||||
|
return sw.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
private Object fromJson(String json, Class klass) throws Exception {
|
||||||
|
StringReader sr = new StringReader(json);
|
||||||
|
JSONJAXBContext ctx = new JSONJAXBContext(klass);
|
||||||
|
JSONUnmarshaller jm = ctx.createJSONUnmarshaller();
|
||||||
|
return jm.unmarshalFromJSON(sr, klass);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue