TestLegacyMappingRuleToJson.java
/**
* 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.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail;
import org.apache.hadoop.yarn.server.resourcemanager.placement.csmappingrule.MappingRule;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration;
import org.junit.jupiter.api.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(legacyRules.size(), jsonRules.size(),
"Number of rules should mach");
//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(
legacyRules.get(i).toString(),
jsonRules.get(i).toString(),
"Rule #" + i + " should match");
assertEquals(
legacyRules.get(i).getFallback().toString(),
jsonRules.get(i).getFallback().toString(),
"Rule #" + i + " fallback should match");
}
}
@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) {}
}
}