Repository: incubator-slider
Updated Branches:
  refs/heads/develop 9104caad8 -> 7a8f39f46


SLIDER-905 Container request fails when Slider requests container with node 
label and host constraints


Project: http://git-wip-us.apache.org/repos/asf/incubator-slider/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-slider/commit/7a8f39f4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-slider/tree/7a8f39f4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-slider/diff/7a8f39f4

Branch: refs/heads/develop
Commit: 7a8f39f46a140f560efb5ced8cf344f7b6b87327
Parents: 9104caa
Author: Steve Loughran <ste...@apache.org>
Authored: Tue Jun 16 13:17:51 2015 +0100
Committer: Steve Loughran <ste...@apache.org>
Committed: Tue Jun 16 13:17:51 2015 +0100

----------------------------------------------------------------------
 .../appmaster/state/OutstandingRequest.java     | 57 ++++++++++++++++++--
 ...tRoleHistoryOutstandingRequestTracker.groovy | 51 ++++++++++++++++++
 2 files changed, 104 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/7a8f39f4/slider-core/src/main/java/org/apache/slider/server/appmaster/state/OutstandingRequest.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/OutstandingRequest.java
 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/OutstandingRequest.java
index e674835..2d31ffd 100644
--- 
a/slider-core/src/main/java/org/apache/slider/server/appmaster/state/OutstandingRequest.java
+++ 
b/slider-core/src/main/java/org/apache/slider/server/appmaster/state/OutstandingRequest.java
@@ -23,6 +23,7 @@ import com.google.common.base.Preconditions;
 import org.apache.hadoop.yarn.api.records.Priority;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.client.api.AMRMClient;
+import org.apache.hadoop.yarn.client.api.InvalidContainerRequestException;
 import org.apache.slider.server.appmaster.operations.CancelSingleRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,6 +62,12 @@ public final class OutstandingRequest {
   public final String hostname;
 
   /**
+   * Optional label. This is cached as the request option (explicit-location + 
label) is forbidden,
+   * yet the label needs to be retained for escalation.
+   */
+  public String label;
+
+  /**
    * Requested time in millis.
    * <p>
    * Only valid after {@link #buildContainerRequest(Resource, RoleStatus, 
long, String)}
@@ -178,12 +185,15 @@ public final class OutstandingRequest {
     Preconditions.checkArgument(resource != null, "null `resource` arg");
     Preconditions.checkArgument(role != null, "null `role` arg");
 
+    // cache label for escalation
+    label = labelExpression;
     requestedTimeMillis = time;
     escalationTimeoutMillis = time + role.getPlacementTimeoutSeconds() * 1000;
     String[] hosts;
     boolean relaxLocality;
     boolean strictPlacement = role.isStrictPlacement();
     NodeInstance target = this.node;
+    String nodeLabels;
 
     if (target != null) {
       // placed request. Hostname is used in request
@@ -197,6 +207,7 @@ public final class OutstandingRequest {
       // enable escalation for all but strict placements.
       escalated = false;
       mayEscalate = !strictPlacement;
+      nodeLabels = null;
     } else {
       // no hosts
       hosts = null;
@@ -206,6 +217,7 @@ public final class OutstandingRequest {
       escalated = true;
       // and forbid it happening
       mayEscalate = false;
+      nodeLabels = labelExpression;
     }
     Priority pri = ContainerPriority.createPriority(roleId, !relaxLocality);
     priority = pri.getPriority();
@@ -214,8 +226,9 @@ public final class OutstandingRequest {
                                       null,
                                       pri,
                                       relaxLocality,
-                                      labelExpression);
+                                      nodeLabels);
 
+    validate();
     return issuedRequest;
   }
 
@@ -235,7 +248,7 @@ public final class OutstandingRequest {
     Priority pri = ContainerPriority.createPriority(roleId, true);
     String[] nodes;
     List<String> issuedRequestNodes = issuedRequest.getNodes();
-    if (issuedRequestNodes != null) {
+    if (label == null && issuedRequestNodes != null) {
       nodes = issuedRequestNodes.toArray(new 
String[issuedRequestNodes.size()]);
     } else {
       nodes = null;
@@ -247,8 +260,9 @@ public final class OutstandingRequest {
             null,
             pri,
             true,
-            issuedRequest.getNodeLabelExpression());
+            label);
     issuedRequest = newRequest;
+    validate();
     return issuedRequest;
   }
       
@@ -342,7 +356,42 @@ public final class OutstandingRequest {
    * @return an operation that can be used to cancel the request
    */
   public CancelSingleRequest createCancelOperation() {
-    Preconditions.checkState(issuedRequest!=null, "No issued request to 
cancel");
+    Preconditions.checkState(issuedRequest != null, "No issued request to 
cancel");
     return new CancelSingleRequest(issuedRequest);
   }
+
+
+  /**
+   * Valid if a node label expression specified on container request is valid 
or
+   * not. Mimics the logic in AMRMClientImpl, so can be used for preflight 
checking
+   * and in mock tests
+   *
+   */
+  public  void validate() throws InvalidContainerRequestException {
+    Preconditions.checkNotNull(issuedRequest, "request has not yet been built 
up");
+    AMRMClient.ContainerRequest containerRequest = issuedRequest;
+    String exp = containerRequest.getNodeLabelExpression();
+
+    if (null == exp || exp.isEmpty()) {
+      return;
+    }
+
+    // Don't support specifying >= 2 node labels in a node label expression now
+    if (exp.contains("&&") || exp.contains("||")) {
+      throw new InvalidContainerRequestException(
+          "Cannot specify more than two node labels"
+              + " in a single node label expression");
+    }
+
+    // Don't allow specify node label against ANY request
+    if ((containerRequest.getRacks() != null &&
+             (!containerRequest.getRacks().isEmpty()))
+        ||
+        (containerRequest.getNodes() != null &&
+             (!containerRequest.getNodes().isEmpty()))) {
+      throw new InvalidContainerRequestException(
+          "Cannot specify node label with rack and node");
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/7a8f39f4/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/history/TestRoleHistoryOutstandingRequestTracker.groovy
----------------------------------------------------------------------
diff --git 
a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/history/TestRoleHistoryOutstandingRequestTracker.groovy
 
b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/history/TestRoleHistoryOutstandingRequestTracker.groovy
index 99e4190..653af84 100644
--- 
a/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/history/TestRoleHistoryOutstandingRequestTracker.groovy
+++ 
b/slider-core/src/test/groovy/org/apache/slider/server/appmaster/model/history/TestRoleHistoryOutstandingRequestTracker.groovy
@@ -208,6 +208,57 @@ class TestRoleHistoryOutstandingRequestTracker extends 
BaseMockAppStateTest {
   }
 
   /**
+   * If the placement does include a label, the initial request must
+   * <i>not</i> include it.
+   * The escalation request will contain the label, while
+   * leaving out the node list.
+   * retains the node list, but sets relaxLocality==true
+   * @throws Throwable
+   */
+  @Test
+  public void testRequestLabelledPlacement() throws Throwable {
+    NodeInstance ni = new NodeInstance("host1", 0)
+    def req1 = tracker.newRequest(ni, 0)
+    def resource = factory.newResource()
+    resource.virtualCores = 1
+    resource.memory = 48;
+
+    def label = "workers"
+    // initial request
+    def yarnRequest = req1.buildContainerRequest(resource, role0Status, 0, 
label)
+    assert (yarnRequest.nodeLabelExpression == null)
+    assert (!yarnRequest.relaxLocality)
+    def yarnRequest2 = req1.escalate()
+    assert (yarnRequest2.nodeLabelExpression == label)
+    assert (yarnRequest2.relaxLocality)
+  }
+
+  /**
+   * If the placement doesnt include a lablel, then the escalation request
+   * retains the node list, but sets relaxLocality==true
+   * @throws Throwable
+   */
+  @Test
+  public void testRequestUnlabelledPlacement() throws Throwable {
+    NodeInstance ni = new NodeInstance("host1", 0)
+    def req1 = tracker.newRequest(ni, 0)
+    def resource = factory.newResource()
+    resource.virtualCores = 1
+    resource.memory = 48;
+
+    def label = null
+    // initial request
+    def yarnRequest = req1.buildContainerRequest(resource, role0Status, 0, 
label)
+    assert (yarnRequest.nodes != null)
+    assert (yarnRequest.nodeLabelExpression == null)
+    assert (!yarnRequest.relaxLocality)
+    def yarnRequest2 = req1.escalate()
+    assert (yarnRequest2.nodes != null)
+    assert (yarnRequest2.relaxLocality)
+  }
+
+
+  /**
    * Create a new request (always against host1)
    * @param r role status
    * @return (resource, oustanding-request)

Reply via email to