This is an automated email from the ASF dual-hosted git repository.

snemeth pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/trunk by this push:
     new e2a7008  YARN-10585. Create a class which can convert from legacy 
mapping rule format to the new JSON format. Contributed by Gergely Pollak
e2a7008 is described below

commit e2a7008d50d7cf2ec031c98a2b1da8075c48e0ec
Author: Szilard Nemeth <snem...@apache.org>
AuthorDate: Tue Jan 26 18:31:39 2021 +0100

    YARN-10585. Create a class which can convert from legacy mapping rule 
format to the new JSON format. Contributed by Gergely Pollak
---
 .../resourcemanager/placement/MappingRule.java     |   6 +-
 .../placement/MappingRuleActions.java              |   5 +-
 .../converter/LegacyMappingRuleToJson.java         | 405 +++++++++++++++++++++
 .../placement/TestCSMappingPlacementRule.java      |   2 +-
 .../placement/TestMappingRuleActions.java          |   5 +-
 .../converter/TestLegacyMappingRuleToJson.java     | 240 ++++++++++++
 6 files changed, 657 insertions(+), 6 deletions(-)

diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRule.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRule.java
index e61ad95..9d67d78 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRule.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRule.java
@@ -106,7 +106,11 @@ public class MappingRule {
 
     switch (type) {
     case USER_MAPPING:
-      matcher = MappingRuleMatchers.createUserMatcher(source);
+      if (source.equals("%user")) {
+        matcher = MappingRuleMatchers.createAllMatcher();
+      } else {
+        matcher = MappingRuleMatchers.createUserMatcher(source);
+      }
       break;
     case GROUP_MAPPING:
       matcher = MappingRuleMatchers.createUserGroupMatcher(source);
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRuleActions.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRuleActions.java
index 13cdbe8..35d7276 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRuleActions.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/MappingRuleActions.java
@@ -96,8 +96,9 @@ public final class MappingRuleActions {
     @Override
     public String toString() {
       return "PlaceToQueueAction{" +
-          "queueName='" + queuePattern + '\'' +
-          '}';
+          "queueName='" + queuePattern + "'," +
+          "allowCreate=" + allowCreate +
+          "}";
     }
   }
 
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/placement/converter/LegacyMappingRuleToJson.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/placement/converter/LegacyMappingRuleToJson.java
new file mode 100644
index 0000000..113b08b
--- /dev/null
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/placement/converter/LegacyMappingRuleToJson.java
@@ -0,0 +1,405 @@
+/**
+ * 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.scheduler.capacity.placement.converter;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.hadoop.util.StringUtils;
+import 
org.apache.hadoop.yarn.server.resourcemanager.placement.MappingQueuePath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+
+public class LegacyMappingRuleToJson {
+  //Legacy rule parse helper constants
+  public static final String RULE_PART_DELIMITER = ":";
+  public static final String PREFIX_USER_MAPPING = "u";
+  public static final String PREFIX_GROUP_MAPPING = "g";
+
+  //Legacy rule matcher variables
+  public static final String MATCHER_APPLICATION = "%application";
+  public static final String MATCHER_USER = "%user";
+
+  //Legacy rule mapping variables, which can be used in target queues
+  public static final String MAPPING_PRIMARY_GROUP = "%primary_group";
+  public static final String MAPPING_SECONDARY_GROUP = "%secondary_group";
+  public static final String MAPPING_USER = MATCHER_USER;
+
+  //JSON Format match all token (actually only used for users)
+  public static final String JSON_MATCH_ALL = "*";
+
+  //Frequently used JSON node names for rule definitions
+  public static final String JSON_NODE_POLICY = "policy";
+  public static final String JSON_NODE_PARENT_QUEUE = "parentQueue";
+  public static final String JSON_NODE_CUSTOM_PLACEMENT = "customPlacement";
+  public static final String JSON_NODE_MATCHES = "matches";
+
+  /**
+   * Our internal object mapper, used to create JSON nodes.
+   */
+  private ObjectMapper objectMapper = new ObjectMapper();
+
+  /**
+   * Collection to store the legacy group mapping rule strings.
+   */
+  private Collection<String> userGroupMappingRules = new ArrayList<>();
+  /**
+   * Collection to store the legacy application name mapping rule strings.
+   */
+  private Collection<String> applicationNameMappingRules = new ArrayList<>();
+
+  /**
+   * This setter method is used to set the raw string format of the legacy
+   * user group mapping rules. This method expect a string formatted just like
+   * in the configuration file of the Capacity Scheduler.
+   * eg. u:bob:root.groups.%primary_group,u:%user:root.default
+   *
+   * @param rules The string containing ALL the UserGroup mapping rules in
+   *              legacy format
+   * @return This object for daisy chain support
+   */
+  public LegacyMappingRuleToJson setUserGroupMappingRules(String rules) {
+    setUserGroupMappingRules(StringUtils.getTrimmedStringCollection(rules));
+    return this;
+  }
+
+  /**
+   * This setter method is used to set the the user group mapping rules as a
+   * string collection, where each entry is one rule.
+   *
+   * @param rules One rule per entry
+   * @return This object for daisy chain support
+   */
+  public LegacyMappingRuleToJson setUserGroupMappingRules(
+      Collection<String> rules) {
+    if (rules != null) {
+      userGroupMappingRules = rules;
+    } else {
+      userGroupMappingRules = new ArrayList<>();
+    }
+    return this;
+  }
+
+  /**
+   * This setter method is used to set the raw string format of the legacy
+   * application name mapping rules. This method expect a string formatted
+   * just like in the configuration file of the Capacity Scheduler.
+   * eg. mapreduce:root.apps.%application,%application:root.default
+   *
+   * @param rules The string containing ALL the application name mapping rules
+   *              in legacy format
+   * @return This object for daisy chain support
+   */
+  public LegacyMappingRuleToJson setAppNameMappingRules(String rules) {
+    setAppNameMappingRules(StringUtils.getTrimmedStringCollection(rules));
+    return this;
+  }
+
+  /**
+   * This setter method is used to set the the application name mapping rules 
as
+   * a string collection, where each entry is one rule.
+   *
+   * @param rules One rule per entry
+   * @return This object for daisy chain support
+   */
+  public LegacyMappingRuleToJson setAppNameMappingRules(
+      Collection<String> rules) {
+    if (rules != null) {
+      applicationNameMappingRules = rules;
+    } else {
+      applicationNameMappingRules = new ArrayList<>();
+    }
+
+    return this;
+  }
+
+  /**
+   * This method will do the conversion based on the already set mapping rules.
+   * First the rules to be converted must be set via setAppNameMappingRules and
+   * setUserGroupMappingRules methods.
+   * @return JSON Format of the provided mapping rules, null if no rules are 
set
+   */
+  public String convert() {
+    if (userGroupMappingRules == null && applicationNameMappingRules == null) {
+      return null;
+    }
+
+    //creating the basic JSON config structure
+    ObjectNode rootNode = objectMapper.createObjectNode();
+    ArrayNode rulesNode = objectMapper.createArrayNode();
+    rootNode.set("rules", rulesNode);
+
+    //Processing and adding all the user group mapping rules
+    for (String rule : userGroupMappingRules) {
+      rulesNode.add(convertUserGroupMappingRule(rule));
+    }
+
+    //Processing and adding all the application name mapping rules
+    for (String rule : applicationNameMappingRules) {
+      rulesNode.add(convertAppNameMappingRule(rule));
+    }
+
+    //If there are no converted rules we return null
+    if (rulesNode.size() == 0) {
+      return null;
+    }
+
+    try {
+      return objectMapper
+          .writerWithDefaultPrettyPrinter()
+          .writeValueAsString(rootNode);
+    } catch (JsonProcessingException e) {
+      e.printStackTrace();
+    }
+
+    return null;
+  }
+
+  /**
+   * This intermediate helper method is used to process User Group mapping 
rules
+   * and invoke the proper mapping rule creation method.
+   * @param rule The legacy format of the single rule to be converted.
+   * @return The ObjectNode which can be added to the rules part of the config.
+   */
+  ObjectNode convertUserGroupMappingRule(String rule) {
+    String[] mapping = splitRule(rule, 3);
+    String ruleType = mapping[0];
+    String ruleMatch = mapping[1];
+    String ruleTarget = mapping[2];
+
+    if (ruleType.equals(PREFIX_USER_MAPPING)) {
+      return createUserMappingRule(ruleMatch, ruleTarget);
+    }
+
+    if (ruleType.equals(PREFIX_GROUP_MAPPING)) {
+      return createGroupMappingRule(ruleMatch, ruleTarget);
+    }
+
+    throw new IllegalArgumentException(
+        "User group mapping rule must start with prefix '" +
+            PREFIX_USER_MAPPING + "' or '" + PREFIX_GROUP_MAPPING + "'");
+  }
+
+  /**
+   * This intermediate helper method is used to process Application name 
mapping
+   * rules and invoke the proper mapping rule creation method.
+   * @param rule The legacy format of the single rule to be converted.
+   * @return The ObjectNode which can be added to the rules part of the config.
+   */
+  ObjectNode convertAppNameMappingRule(String rule) {
+    String[] mapping = splitRule(rule, 2);
+    String ruleMatch = mapping[0];
+    String ruleTarget = mapping[1];
+
+    return createApplicationNameMappingRule(ruleMatch, ruleTarget);
+  }
+  /**
+   * Helper method which splits the rules into parts, and checks if it has
+   * exactly the required amount of parts, and none of them is empty!
+   * @param rule The mapping rule to be split
+   * @param expectedParts The number of expected parts
+   * @return The split String[] of the parts
+   * @throws IllegalArgumentException if the number of parts don't match or any
+   *  of them is empty.
+   */
+  private String[] splitRule(String rule, int expectedParts) {
+    //Splitting
+    String[] mapping = StringUtils
+        .getTrimmedStringCollection(rule, RULE_PART_DELIMITER)
+        .toArray(new String[] {});
+
+    //Checking for part count
+    if (mapping.length != expectedParts) {
+      throw new IllegalArgumentException("Invalid rule '" + rule +
+          "' expected parts: " + expectedParts +
+          " actual parts: " + mapping.length);
+    }
+
+    //Checking for empty parts
+    for (int i = 0; i < mapping.length; i++) {
+      if (mapping[i].length() == 0) {
+        throw new IllegalArgumentException("Invalid rule '" + rule +
+            "' with empty part, mapping rules must not contain empty parts!");
+      }
+    }
+
+    return mapping;
+  }
+
+  /**
+   * This helper method is to create a default rule node for the converter,
+   * setting fields which are common in all rules.
+   * @param type The type of the rule can be user/group/application
+   * @return The object node with the preset fields
+   */
+  private ObjectNode createDefaultRuleNode(String type) {
+    return objectMapper
+        .createObjectNode()
+        .put("type", type)
+        //All legacy rule fallback to place to default
+        .put("fallbackResult", "placeDefault")
+        //All legacy rules allow creation
+        .put("create", true);
+  }
+
+  /**
+   * This method will create the JSON node for a single User Mapping Rule.
+   * @param match The match part of the rule it can be either an actual user
+   *              name or '%user' to match all users
+   * @param target The queue to place to user into, some queue path variables
+   *               are supported (%user, %primary_group, %secondary_group).
+   * @return The ObjectNode which represents the rule
+   */
+  private ObjectNode createUserMappingRule(String match, String target) {
+    ObjectNode ruleNode = createDefaultRuleNode("user");
+    MappingQueuePath targetPath = new MappingQueuePath(target);
+
+    //We have a special token in the JSON format to match all user, replacing
+    //matcher
+    if (match.equals(MATCHER_USER)) {
+      match = JSON_MATCH_ALL;
+    }
+    ruleNode.put(JSON_NODE_MATCHES, match);
+
+    switch (targetPath.getLeafName()) {
+    case MAPPING_USER:
+      ruleNode.put(JSON_NODE_POLICY, "user");
+      if (targetPath.hasParent()) {
+        //Parsing parent path, to be able to determine the short name of parent
+        MappingQueuePath targetParentPath =
+            new MappingQueuePath(targetPath.getParent());
+        String parentShortName = targetParentPath.getLeafName();
+
+        if (parentShortName.equals(MAPPING_PRIMARY_GROUP)) {
+          //%primary_group.%user mapping
+          ruleNode.put(JSON_NODE_POLICY, "primaryGroupUser");
+
+          //Yep, this is confusing. The policy primaryGroupUser actually
+          // appends the %primary_group.%user to the parent path, so we need to
+          // remove it from the parent path to avoid duplication.
+          targetPath = new MappingQueuePath(targetParentPath.getParent(),
+              targetPath.getLeafName());
+        } else if (parentShortName.equals(MAPPING_SECONDARY_GROUP)) {
+          //%secondary_group.%user mapping
+          ruleNode.put(JSON_NODE_POLICY, "secondaryGroupUser");
+
+          //Yep, this is confusing. The policy secondaryGroupUser actually
+          // appends the %secondary_group.%user to the parent path, so we need
+          // to remove it from the parent path to avoid duplication.
+          targetPath = new MappingQueuePath(targetParentPath.getParent(),
+              targetPath.getLeafName());
+        }
+
+        //[parent].%user mapping
+      }
+      break;
+    case MAPPING_PRIMARY_GROUP:
+      //[parent].%primary_group mapping
+      ruleNode.put(JSON_NODE_POLICY, "primaryGroup");
+      break;
+    case MAPPING_SECONDARY_GROUP:
+      //[parent].%secondary_group mapping
+      ruleNode.put(JSON_NODE_POLICY, "secondaryGroup");
+      break;
+    default:
+      //static path mapping
+      ruleNode.put(JSON_NODE_POLICY, "custom");
+      ruleNode.put(JSON_NODE_CUSTOM_PLACEMENT, targetPath.getFullPath());
+      break;
+    }
+
+    //if the target queue has a parent part, and the rule can have a parent
+    //we add it to the node
+    if (targetPath.hasParent()) {
+      ruleNode.put(JSON_NODE_PARENT_QUEUE, targetPath.getParent());
+    }
+
+    return ruleNode;
+  }
+
+  /**
+   * This method will create the JSON node for a single Group Mapping Rule.
+   * @param match The name of the group to match for
+   * @param target The queue to place to user into, some queue path variables
+   *               are supported (%user).
+   * @return The ObjectNode which represents the rule
+   */
+  private ObjectNode createGroupMappingRule(String match, String target) {
+    ObjectNode ruleNode = createDefaultRuleNode("group");
+    MappingQueuePath targetPath = new MappingQueuePath(target);
+
+    //we simply used the source match part all valid legacy matchers are valid
+    //matchers for the JSON format as well
+    ruleNode.put(JSON_NODE_MATCHES, match);
+
+    if (targetPath.getLeafName().matches(MATCHER_USER)) {
+      //g:group:[parent].%user mapping
+      ruleNode.put(JSON_NODE_POLICY, "user");
+
+      //if the target queue has a parent part we add it to the node
+      if (targetPath.hasParent()) {
+        ruleNode.put(JSON_NODE_PARENT_QUEUE, targetPath.getParent());
+      }
+    } else {
+      //static path mapping
+      ruleNode.put(JSON_NODE_POLICY, "custom");
+      ruleNode.put(JSON_NODE_CUSTOM_PLACEMENT, targetPath.getFullPath());
+    }
+
+    return ruleNode;
+  }
+
+
+  /**
+   * This method will create the JSON node for a single Application Name
+   * Mapping Rule.
+   * @param match The name of the application to match for or %application to
+   *              match all applications
+   * @param target The queue to place to user into, some queue path variables
+   *               are supported (%application).
+   * @return The ObjectNode which represents the rule
+   */
+  private ObjectNode createApplicationNameMappingRule(
+      String match, String target) {
+    ObjectNode ruleNode = createDefaultRuleNode("application");
+    MappingQueuePath targetPath = new MappingQueuePath(target);
+
+    //we simply used the source match part all valid legacy matchers are valid
+    //matchers for the JSON format as well
+    ruleNode.put(JSON_NODE_MATCHES, match);
+
+    if (targetPath.getLeafName().matches(MATCHER_APPLICATION)) {
+      //[parent].%application mapping
+      ruleNode.put(JSON_NODE_POLICY, "applicationName");
+
+      //if the target queue has a parent part we add it to the node
+      if (targetPath.hasParent()) {
+        ruleNode.put(JSON_NODE_PARENT_QUEUE, targetPath.getParent());
+      }
+    } else {
+      //static path mapping
+      ruleNode.put(JSON_NODE_POLICY, "custom");
+      ruleNode.put(JSON_NODE_CUSTOM_PLACEMENT, targetPath.getFullPath());
+    }
+
+    return ruleNode;
+  }
+}
\ No newline at end of file
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestCSMappingPlacementRule.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestCSMappingPlacementRule.java
index 6ee7b5df..703d517 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestCSMappingPlacementRule.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestCSMappingPlacementRule.java
@@ -467,7 +467,7 @@ public class TestCSMappingPlacementRule {
     assertTrue("Rule's match value should be bob",
         ruleStr.contains("value='bob'"));
     assertTrue("Rule's action should be place to queue", ruleStr.contains(
-        "action=PlaceToQueueAction{queueName='%primary_group'}"));
+        "action=PlaceToQueueAction{queueName='%primary_group'"));
   }
 
   @Test
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestMappingRuleActions.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestMappingRuleActions.java
index 769d051..4d4daa1 100644
--- 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestMappingRuleActions.java
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/TestMappingRuleActions.java
@@ -166,9 +166,10 @@ public class TestMappingRuleActions {
         "%var", "value");
     MappingRuleAction reject = new MappingRuleActions.RejectAction();
 
-    assertEquals("PlaceToQueueAction{queueName='queue'}", place.toString());
+    assertEquals("PlaceToQueueAction{queueName='queue',allowCreate=true}",
+        place.toString());
     assertEquals("VariableUpdateAction{variableName='%var'" +
-            ", variableValue='value'}", varUpdate.toString());
+        ", variableValue='value'}", varUpdate.toString());
     assertEquals("RejectAction", reject.toString());
   }
 }
\ No newline at end of file
diff --git 
a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/placement/converter/TestLegacyMappingRuleToJson.java
 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/placement/converter/TestLegacyMappingRuleToJson.java
new file mode 100644
index 0000000..9a2b97f
--- /dev/null
+++ 
b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/placement/converter/TestLegacyMappingRuleToJson.java
@@ -0,0 +1,240 @@
+/**
+ * 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.scheduler.capacity.placement.converter;
+
+import static org.junit.Assert.*;
+
+import org.apache.hadoop.yarn.server.resourcemanager.placement.MappingRule;
+import 
org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+public class TestLegacyMappingRuleToJson {
+
+  void validateConversion(String legacyUserGroup, String legacyAppName)
+      throws IOException {
+    //Creating a capacity scheduler config, because this way we can run
+    //both the legacy and the JSON rules through the parser engine, and
+    //we can check if we get the same mapping rules
+    CapacitySchedulerConfiguration conf = new CapacitySchedulerConfiguration();
+
+    //First we configure the capacity scheduler to parse the legacy config
+    conf.set(
+        CapacitySchedulerConfiguration.MAPPING_RULE_FORMAT,
+        CapacitySchedulerConfiguration.MAPPING_RULE_FORMAT_LEGACY);
+    conf.set(CapacitySchedulerConfiguration.QUEUE_MAPPING, legacyUserGroup);
+    conf.set(CapacitySchedulerConfiguration.QUEUE_MAPPING_NAME, legacyAppName);
+
+    //These are the legacyRules generated by CS, this can be used as a 
reference
+    //we can test the JSON format against these
+    List<MappingRule> legacyRules = conf.getMappingRules();
+
+    //Converting the legacy format to JSON
+    LegacyMappingRuleToJson converter = new LegacyMappingRuleToJson();
+    String json = converter
+        .setUserGroupMappingRules(legacyUserGroup)
+        .setAppNameMappingRules(legacyAppName)
+        .convert();
+
+    //First we configure the capacity scheduler to parse the CONVERTED JSON
+    conf.set(
+        CapacitySchedulerConfiguration.MAPPING_RULE_FORMAT,
+        CapacitySchedulerConfiguration.MAPPING_RULE_FORMAT_JSON);
+    conf.set(CapacitySchedulerConfiguration.MAPPING_RULE_JSON, json);
+
+    //These are the rules which are generated from the JSON format
+    List<MappingRule> jsonRules = conf.getMappingRules();
+
+    //Sanity check
+    assertEquals("Number of rules should mach",
+        legacyRules.size(), jsonRules.size());
+
+    //We expect ALL rules to match no matter if it was parsed from legacy 
format
+    //or from JSON
+    for (int i = 0; i < legacyRules.size(); i++) {
+      assertEquals(
+          "Rule #" + i + " should match",
+          legacyRules.get(i).toString(),
+          jsonRules.get(i).toString());
+
+      assertEquals(
+          "Rule #" + i + " fallback should match",
+          legacyRules.get(i).getFallback().toString(),
+          jsonRules.get(i).getFallback().toString());
+    }
+
+  }
+
+  @Test
+  public void testApplicationNameMappingConversion() throws IOException {
+    String appMapping = String.join(",",
+        "namedMatch:simple",
+        "namedMatch:root.deep",
+        "namedMatch:%application",
+        "namedMatch:root.deep.%application",
+        "%application:simple",
+        "%application:root.deep",
+        "%application:%application",
+        "%application:root.deep.%application");
+
+    validateConversion("", appMapping);
+  }
+
+  @Test
+  public void testGroupMappingConversion() throws IOException {
+    String groupMapping = String.join(",",
+        "g:testers:simple",
+        "g:developers:root.very.deep",
+        "g:users:%user",
+        "g:testers:root.very.deep.%user");
+
+    validateConversion(groupMapping, "");
+  }
+
+  @Test
+  public void testUserMappingConversion() throws IOException {
+    String groupMapping = String.join(",",
+        "u:alice:alice",
+        "u:beatrix:root.beatrix",
+        "u:claire:%primary_group",
+        "u:donna:root.deep.%primary_group",
+        "u:emily:%secondary_group",
+        "u:felicity:root.deep.%secondary_group",
+        "u:%user:simple",
+        "u:%user:root.deep",
+        "u:%user:%primary_group",
+        "u:%user:%secondary_group",
+        "u:%user:root.deep.%primary_group",
+        "u:%user:root.deep.%secondary_group",
+        "u:%user:%primary_group.%user",
+        "u:%user:root.%primary_group.%user",
+        "u:%user:root.deep.%primary_group.%user",
+        "u:%user:%secondary_group.%user",
+        "u:%user:root.%secondary_group.%user",
+        "u:%user:root.deep.%secondary_group.%user",
+        "u:%user:%user",
+        "u:%user:root.deep.%user");
+
+    validateConversion(groupMapping, "");
+  }
+
+  @Test
+  public void testTotalConversion() throws IOException {
+    String appMapping = String.join(",",
+        "namedMatch:simple",
+        "namedMatch:root.deep",
+        "namedMatch:%application",
+        "namedMatch:root.deep.%application",
+        "%application:simple",
+        "%application:root.deep",
+        "%application:%application",
+        "%application:root.deep.%application");
+
+    String userGroupMapping = String.join(",",
+        "u:alice:alice",
+        "u:beatrix:root.beatrix",
+        "u:claire:%primary_group",
+        "u:donna:root.deep.%primary_group",
+        "u:emily:%secondary_group",
+        "u:felicity:root.deep.%secondary_group",
+        "u:%user:simple",
+        "u:%user:root.deep",
+        "g:testers:simple",
+        "g:developers:root.very.deep",
+        "g:users:%user",
+        "g:testers:root.very.deep.%user",
+        "u:%user:%primary_group",
+        "u:%user:%secondary_group",
+        "u:%user:root.deep.%primary_group",
+        "u:%user:root.deep.%secondary_group",
+        "u:%user:%primary_group.%user",
+        "u:%user:root.%primary_group.%user",
+        "u:%user:root.deep.%primary_group.%user",
+        "u:%user:%secondary_group.%user",
+        "u:%user:root.%secondary_group.%user",
+        "u:%user:root.deep.%secondary_group.%user",
+        "u:%user:%user",
+        "u:%user:root.%user.something",
+        "u:%user:root.deep.%user");
+
+    validateConversion(userGroupMapping, appMapping);
+  }
+
+  @Test
+  public void testErrorHandling() {
+    LegacyMappingRuleToJson converter = new LegacyMappingRuleToJson();
+    //Empty converter should return null
+    assertNull(converter.convert());
+
+    converter
+        .setAppNameMappingRules("")
+        .setUserGroupMappingRules("");
+    //Empty converter should still return null
+    assertNull(converter.convert());
+
+    converter
+        .setAppNameMappingRules((Collection<String>)null)
+        .setUserGroupMappingRules((Collection<String>)null);
+    //Setting nulls should also result in null return.
+    assertNull(converter.convert());
+
+    try {
+      converter
+          .setAppNameMappingRules("%application:")
+          .setUserGroupMappingRules("")
+          .convert();
+      fail("Empty app name mapping part should throw exception");
+    } catch (IllegalArgumentException e) {}
+
+    try {
+      converter
+          .setAppNameMappingRules("%application:sdfsdf:sdfsfd")
+          .setUserGroupMappingRules("")
+          .convert();
+      fail("Incorrect number of app name mapping parts should throw 
exception");
+    } catch (IllegalArgumentException e) {}
+
+    try {
+      converter
+          .setAppNameMappingRules("")
+          .setUserGroupMappingRules("u::root.default")
+          .convert();
+      fail("Empty user group mapping part should throw exception");
+    } catch (IllegalArgumentException e) {}
+
+    try {
+      converter
+          .setAppNameMappingRules("")
+          .setUserGroupMappingRules("u:bob")
+          .convert();
+      fail("Incorrect number of user group mapping parts should " +
+          "throw exception");
+    } catch (IllegalArgumentException e) {}
+
+    try {
+      converter
+          .setAppNameMappingRules("")
+          .setUserGroupMappingRules("X:bob:root.bob")
+          .convert();
+      fail("Invalid user group mapping prefix should throw exception");
+    } catch (IllegalArgumentException e) {}
+  }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-commits-h...@hadoop.apache.org

Reply via email to