1
0
Quellcode durchsuchen

YARN-6655. Validate yarn native services application submission side to ensure that the hostname should be less than 63 characters. Contributed by Billie Rinaldi

Jian He vor 8 Jahren
Ursprung
Commit
59a9b4f438
10 geänderte Dateien mit 155 neuen und 37 gelöschten Zeilen
  1. 2 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml
  2. 6 3
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/client/SliderClient.java
  3. 1 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java
  4. 7 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/util/RestApiErrorMessages.java
  5. 22 2
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/util/ServiceApiUtil.java
  6. 7 3
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/core/conf/TestConfigurationResolve.java
  7. 3 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/core/conf/TestExampleAppJson.java
  8. 1 1
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/mock/BaseMockAppStateTest.java
  9. 101 23
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/utils/TestServiceApiUtil.java
  10. 5 0
      hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/main/java/org/apache/hadoop/registry/client/api/RegistryConstants.java

+ 2 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml

@@ -153,7 +153,7 @@ definitions:
     properties:
     properties:
       name:
       name:
         type: string
         type: string
-        description: A unique application name.
+        description: A unique application name. If Registry DNS is enabled, the max length is 63 characters.
       id:
       id:
         type: string
         type: string
         description: A unique application id.
         description: A unique application id.
@@ -255,7 +255,7 @@ definitions:
     properties:
     properties:
       name:
       name:
         type: string
         type: string
-        description: Name of the application component (mandatory).
+        description: Name of the application component (mandatory). If Registry DNS is enabled, the max length is 63 characters. If unique component support is enabled, the max length is lowered to 44 characters.
       dependencies:
       dependencies:
         type: array
         type: array
         items:
         items:

+ 6 - 3
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/client/SliderClient.java

@@ -637,7 +637,8 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
   public int actionBuild(Application application) throws YarnException,
   public int actionBuild(Application application) throws YarnException,
       IOException {
       IOException {
     Path appDir = checkAppNotExistOnHdfs(application);
     Path appDir = checkAppNotExistOnHdfs(application);
-    ServiceApiUtil.validateAndResolveApplication(application, sliderFileSystem);
+    ServiceApiUtil.validateAndResolveApplication(application,
+        sliderFileSystem, getConfig());
     persistApp(appDir, application);
     persistApp(appDir, application);
     deployedClusterName = application.getName();
     deployedClusterName = application.getName();
     return EXIT_SUCCESS;
     return EXIT_SUCCESS;
@@ -647,7 +648,8 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
       throws IOException, YarnException {
       throws IOException, YarnException {
     String appName = application.getName();
     String appName = application.getName();
     validateClusterName(appName);
     validateClusterName(appName);
-    ServiceApiUtil.validateAndResolveApplication(application, sliderFileSystem);
+    ServiceApiUtil.validateAndResolveApplication(application,
+        sliderFileSystem, getConfig());
     verifyNoLiveApp(appName, "Create");
     verifyNoLiveApp(appName, "Create");
     Path appDir = checkAppNotExistOnHdfs(application);
     Path appDir = checkAppNotExistOnHdfs(application);
 
 
@@ -1778,7 +1780,8 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe
     Path appDir = checkAppExistOnHdfs(appName);
     Path appDir = checkAppExistOnHdfs(appName);
     Application application = ServiceApiUtil.loadApplication(sliderFileSystem,
     Application application = ServiceApiUtil.loadApplication(sliderFileSystem,
         appName);
         appName);
-    ServiceApiUtil.validateAndResolveApplication(application, sliderFileSystem);
+    ServiceApiUtil.validateAndResolveApplication(application,
+        sliderFileSystem, getConfig());
     // see if it is actually running and bail out;
     // see if it is actually running and bail out;
     verifyNoLiveApp(appName, "Thaw");
     verifyNoLiveApp(appName, "Thaw");
     ApplicationId appId = submitApp(application);
     ApplicationId appId = submitApp(application);

+ 1 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/common/tools/SliderUtils.java

@@ -33,6 +33,7 @@ import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.io.nativeio.NativeIO;
 import org.apache.hadoop.io.nativeio.NativeIO;
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.registry.client.api.RegistryConstants;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.ExitUtil;
 import org.apache.hadoop.util.ExitUtil;

+ 7 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/util/RestApiErrorMessages.java

@@ -21,8 +21,13 @@ public interface RestApiErrorMessages {
   String ERROR_APPLICATION_NAME_INVALID =
   String ERROR_APPLICATION_NAME_INVALID =
       "Application name is either empty or not provided";
       "Application name is either empty or not provided";
   String ERROR_APPLICATION_NAME_INVALID_FORMAT =
   String ERROR_APPLICATION_NAME_INVALID_FORMAT =
-      "Application name %s is not valid - only lower case letters, digits,"
-          + " underscore and hyphen are allowed";
+      "Application name %s is not valid - only lower case letters, digits, " +
+          "underscore and hyphen are allowed, and the name must be no more " +
+          "than 63 characters";
+  String ERROR_COMPONENT_NAME_INVALID =
+      "Component name must be no more than %s characters: %s";
+  String ERROR_USER_NAME_INVALID =
+      "User name must be no more than 63 characters";
 
 
   String ERROR_APPLICATION_NOT_RUNNING = "Application not running";
   String ERROR_APPLICATION_NOT_RUNNING = "Application not running";
   String ERROR_APPLICATION_DOES_NOT_EXIST = "Application not found";
   String ERROR_APPLICATION_DOES_NOT_EXIST = "Application not found";

+ 22 - 2
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/main/java/org/apache/slider/util/ServiceApiUtil.java

@@ -22,6 +22,8 @@ import com.google.common.annotations.VisibleForTesting;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.registry.client.api.RegistryConstants;
+import org.apache.hadoop.registry.client.binding.RegistryUtils;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.Artifact;
 import org.apache.slider.api.resource.Artifact;
 import org.apache.slider.api.resource.Component;
 import org.apache.slider.api.resource.Component;
@@ -60,12 +62,22 @@ public class ServiceApiUtil {
 
 
   @VisibleForTesting
   @VisibleForTesting
   public static void validateAndResolveApplication(Application application,
   public static void validateAndResolveApplication(Application application,
-      SliderFileSystem fs) throws IOException {
+      SliderFileSystem fs, org.apache.hadoop.conf.Configuration conf) throws
+      IOException {
+    boolean dnsEnabled = conf.getBoolean(RegistryConstants.KEY_DNS_ENABLED,
+        RegistryConstants.DEFAULT_DNS_ENABLED);
+    if (dnsEnabled && RegistryUtils.currentUser().length() > RegistryConstants
+        .MAX_FQDN_LABEL_LENGTH) {
+      throw new IllegalArgumentException(RestApiErrorMessages
+          .ERROR_USER_NAME_INVALID);
+    }
     if (StringUtils.isEmpty(application.getName())) {
     if (StringUtils.isEmpty(application.getName())) {
       throw new IllegalArgumentException(
       throw new IllegalArgumentException(
           RestApiErrorMessages.ERROR_APPLICATION_NAME_INVALID);
           RestApiErrorMessages.ERROR_APPLICATION_NAME_INVALID);
     }
     }
-    if (!SliderUtils.isClusternameValid(application.getName())) {
+    if (!SliderUtils.isClusternameValid(application.getName()) || (dnsEnabled
+        && application.getName().length() > RegistryConstants
+        .MAX_FQDN_LABEL_LENGTH)) {
       throw new IllegalArgumentException(String.format(
       throw new IllegalArgumentException(String.format(
           RestApiErrorMessages.ERROR_APPLICATION_NAME_INVALID_FORMAT,
           RestApiErrorMessages.ERROR_APPLICATION_NAME_INVALID_FORMAT,
           application.getName()));
           application.getName()));
@@ -108,6 +120,14 @@ public class ServiceApiUtil {
     List<Component> componentsToRemove = new ArrayList<>();
     List<Component> componentsToRemove = new ArrayList<>();
     List<Component> componentsToAdd = new ArrayList<>();
     List<Component> componentsToAdd = new ArrayList<>();
     for (Component comp : application.getComponents()) {
     for (Component comp : application.getComponents()) {
+      int maxCompLength = RegistryConstants.MAX_FQDN_LABEL_LENGTH;
+      if (comp.getUniqueComponentSupport()) {
+        maxCompLength = maxCompLength - Long.toString(Long.MAX_VALUE).length();
+      }
+      if (dnsEnabled && comp.getName().length() > maxCompLength) {
+        throw new IllegalArgumentException(String.format(RestApiErrorMessages
+            .ERROR_COMPONENT_NAME_INVALID, maxCompLength, comp.getName()));
+      }
       if (componentNames.contains(comp.getName())) {
       if (componentNames.contains(comp.getName())) {
         throw new IllegalArgumentException("Component name collision: " +
         throw new IllegalArgumentException("Component name collision: " +
             comp.getName());
             comp.getName());

+ 7 - 3
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/core/conf/TestConfigurationResolve.java

@@ -20,6 +20,7 @@ package org.apache.slider.core.conf;
 
 
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.ConfigFile;
 import org.apache.slider.api.resource.ConfigFile;
 import org.apache.slider.api.resource.ConfigFile.TypeEnum;
 import org.apache.slider.api.resource.ConfigFile.TypeEnum;
@@ -95,7 +96,8 @@ public class TestConfigurationResolve extends Assert {
     expect(sfs.buildClusterDirPath(anyObject())).andReturn(
     expect(sfs.buildClusterDirPath(anyObject())).andReturn(
         new Path("cluster_dir_path")).anyTimes();
         new Path("cluster_dir_path")).anyTimes();
     replay(sfs, mockFs);
     replay(sfs, mockFs);
-    ServiceApiUtil.validateAndResolveApplication(orig, sfs);
+    ServiceApiUtil.validateAndResolveApplication(orig, sfs, new
+        YarnConfiguration());
 
 
     global = orig.getConfiguration();
     global = orig.getConfiguration();
     LOG.info("global = {}", global);
     LOG.info("global = {}", global);
@@ -179,7 +181,8 @@ public class TestConfigurationResolve extends Assert {
         new Path("cluster_dir_path")).anyTimes();
         new Path("cluster_dir_path")).anyTimes();
     replay(sfs, mockFs);
     replay(sfs, mockFs);
     Application ext = ExampleAppJson.loadResource(APP_JSON);
     Application ext = ExampleAppJson.loadResource(APP_JSON);
-    ServiceApiUtil.validateAndResolveApplication(ext, sfs);
+    ServiceApiUtil.validateAndResolveApplication(ext, sfs, new
+        YarnConfiguration());
     reset(sfs, mockFs);
     reset(sfs, mockFs);
 
 
     // perform the resolution on original application
     // perform the resolution on original application
@@ -192,7 +195,8 @@ public class TestConfigurationResolve extends Assert {
         .anyTimes();
         .anyTimes();
     replay(sfs, mockFs, jsonSerDeser);
     replay(sfs, mockFs, jsonSerDeser);
     ServiceApiUtil.setJsonSerDeser(jsonSerDeser);
     ServiceApiUtil.setJsonSerDeser(jsonSerDeser);
-    ServiceApiUtil.validateAndResolveApplication(orig, sfs);
+    ServiceApiUtil.validateAndResolveApplication(orig, sfs, new
+        YarnConfiguration());
 
 
     global = orig.getConfiguration();
     global = orig.getConfiguration();
     assertEquals(0, global.getProperties().size());
     assertEquals(0, global.getProperties().size());

+ 3 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/core/conf/TestExampleAppJson.java

@@ -20,6 +20,7 @@ package org.apache.slider.core.conf;
 
 
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.common.tools.SliderFileSystem;
 import org.apache.slider.common.tools.SliderFileSystem;
 import org.apache.slider.util.ServiceApiUtil;
 import org.apache.slider.util.ServiceApiUtil;
@@ -71,7 +72,8 @@ public class TestExampleAppJson extends Assert {
           new Path("cluster_dir_path")).anyTimes();
           new Path("cluster_dir_path")).anyTimes();
       replay(sfs, mockFs);
       replay(sfs, mockFs);
 
 
-      ServiceApiUtil.validateAndResolveApplication(application, sfs);
+      ServiceApiUtil.validateAndResolveApplication(application, sfs,
+          new YarnConfiguration());
     } catch (Exception e) {
     } catch (Exception e) {
       throw new Exception("exception loading " + resource + ":" + e.toString());
       throw new Exception("exception loading " + resource + ":" + e.toString());
     }
     }

+ 1 - 1
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/server/appmaster/model/mock/BaseMockAppStateTest.java

@@ -133,7 +133,7 @@ public abstract class BaseMockAppStateTest extends SliderTestBase implements
     AppStateBindingInfo binding = new AppStateBindingInfo();
     AppStateBindingInfo binding = new AppStateBindingInfo();
     binding.application = buildApplication();
     binding.application = buildApplication();
     ServiceApiUtil.validateAndResolveApplication(binding.application,
     ServiceApiUtil.validateAndResolveApplication(binding.application,
-        sliderFileSystem);
+        sliderFileSystem, SliderUtils.createConfiguration());
     //binding.roles = new ArrayList<>(factory.ROLES);
     //binding.roles = new ArrayList<>(factory.ROLES);
     binding.fs = fs;
     binding.fs = fs;
     binding.historyPath = historyPath;
     binding.historyPath = historyPath;

+ 101 - 23
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-slider/hadoop-yarn-slider-core/src/test/java/org/apache/slider/utils/TestServiceApiUtil.java

@@ -19,6 +19,8 @@ package org.apache.slider.utils;
 
 
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.registry.client.api.RegistryConstants;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.Application;
 import org.apache.slider.api.resource.Artifact;
 import org.apache.slider.api.resource.Artifact;
 import org.apache.slider.api.resource.Component;
 import org.apache.slider.api.resource.Component;
@@ -29,6 +31,7 @@ import org.apache.slider.util.RestApiConstants;
 import org.apache.slider.util.RestApiErrorMessages;
 import org.apache.slider.util.RestApiErrorMessages;
 import org.apache.slider.util.ServiceApiUtil;
 import org.apache.slider.util.ServiceApiUtil;
 import org.junit.Assert;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
@@ -61,26 +64,42 @@ public class TestServiceApiUtil {
   private static final String NO_EXCEPTION_PREFIX = "Should not have thrown " +
   private static final String NO_EXCEPTION_PREFIX = "Should not have thrown " +
       "exception: ";
       "exception: ";
 
 
+  private static final String LEN_64_STR =
+      "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01";
+
+  private static final YarnConfiguration CONF_DEFAULT_DNS = new
+      YarnConfiguration();
+  private static final YarnConfiguration CONF_DNS_ENABLED = new
+      YarnConfiguration();
+
+  @BeforeClass
+  public static void init() {
+    CONF_DNS_ENABLED.setBoolean(RegistryConstants.KEY_DNS_ENABLED, true);
+  }
+
   @Test(timeout = 90000)
   @Test(timeout = 90000)
   public void testResourceValidation() throws Exception {
   public void testResourceValidation() throws Exception {
+    assertEquals(RegistryConstants.MAX_FQDN_LABEL_LENGTH + 1, LEN_64_STR
+        .length());
+
     SliderFileSystem sfs = initMock(null);
     SliderFileSystem sfs = initMock(null);
 
 
     Application app = new Application();
     Application app = new Application();
 
 
     // no name
     // no name
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no name");
       Assert.fail(EXCEPTION_PREFIX + "application with no name");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(ERROR_APPLICATION_NAME_INVALID, e.getMessage());
       assertEquals(ERROR_APPLICATION_NAME_INVALID, e.getMessage());
     }
     }
 
 
     // bad format name
     // bad format name
-    String[] badNames = {"4finance", "Finance", "finance@home"};
+    String[] badNames = {"4finance", "Finance", "finance@home", LEN_64_STR};
     for (String badName : badNames) {
     for (String badName : badNames) {
       app.setName(badName);
       app.setName(badName);
       try {
       try {
-        ServiceApiUtil.validateAndResolveApplication(app, sfs);
+        ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
         Assert.fail(EXCEPTION_PREFIX + "application with bad name " + badName);
         Assert.fail(EXCEPTION_PREFIX + "application with bad name " + badName);
       } catch (IllegalArgumentException e) {
       } catch (IllegalArgumentException e) {
         assertEquals(String.format(
         assertEquals(String.format(
@@ -89,9 +108,20 @@ public class TestServiceApiUtil {
     }
     }
 
 
     // launch command not specified
     // launch command not specified
-    app.setName("finance_home");
+    app.setName(LEN_64_STR);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DEFAULT_DNS);
+      Assert.fail(EXCEPTION_PREFIX + "application with no launch command");
+    } catch (IllegalArgumentException e) {
+      assertEquals(RestApiErrorMessages.ERROR_ABSENT_LAUNCH_COMMAND,
+          e.getMessage());
+    }
+
+    // launch command not specified
+    app.setName(LEN_64_STR.substring(0, RegistryConstants
+        .MAX_FQDN_LABEL_LENGTH));
+    try {
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no launch command");
       Assert.fail(EXCEPTION_PREFIX + "application with no launch command");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(RestApiErrorMessages.ERROR_ABSENT_LAUNCH_COMMAND,
       assertEquals(RestApiErrorMessages.ERROR_ABSENT_LAUNCH_COMMAND,
@@ -101,7 +131,7 @@ public class TestServiceApiUtil {
     // resource not specified
     // resource not specified
     app.setLaunchCommand("sleep 3600");
     app.setLaunchCommand("sleep 3600");
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no resource");
       Assert.fail(EXCEPTION_PREFIX + "application with no resource");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(String.format(
       assertEquals(String.format(
@@ -113,7 +143,7 @@ public class TestServiceApiUtil {
     Resource res = new Resource();
     Resource res = new Resource();
     app.setResource(res);
     app.setResource(res);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no memory");
       Assert.fail(EXCEPTION_PREFIX + "application with no memory");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(String.format(
       assertEquals(String.format(
@@ -125,7 +155,7 @@ public class TestServiceApiUtil {
     res.setMemory("100mb");
     res.setMemory("100mb");
     res.setCpus(-2);
     res.setCpus(-2);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(
       Assert.fail(
           EXCEPTION_PREFIX + "application with invalid no of cpus");
           EXCEPTION_PREFIX + "application with invalid no of cpus");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
@@ -137,7 +167,7 @@ public class TestServiceApiUtil {
     // number of containers not specified
     // number of containers not specified
     res.setCpus(2);
     res.setCpus(2);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no container count");
       Assert.fail(EXCEPTION_PREFIX + "application with no container count");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.assertTrue(e.getMessage()
       Assert.assertTrue(e.getMessage()
@@ -147,7 +177,7 @@ public class TestServiceApiUtil {
     // specifying profile along with cpus/memory raises exception
     // specifying profile along with cpus/memory raises exception
     res.setProfile("hbase_finance_large");
     res.setProfile("hbase_finance_large");
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX
       Assert.fail(EXCEPTION_PREFIX
           + "application with resource profile along with cpus/memory");
           + "application with resource profile along with cpus/memory");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
@@ -162,7 +192,7 @@ public class TestServiceApiUtil {
     res.setCpus(null);
     res.setCpus(null);
     res.setMemory(null);
     res.setMemory(null);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with resource profile only");
       Assert.fail(EXCEPTION_PREFIX + "application with resource profile only");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(ERROR_RESOURCE_PROFILE_NOT_SUPPORTED_YET,
       assertEquals(ERROR_RESOURCE_PROFILE_NOT_SUPPORTED_YET,
@@ -176,7 +206,7 @@ public class TestServiceApiUtil {
 
 
     // null number of containers
     // null number of containers
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "null number of containers");
       Assert.fail(EXCEPTION_PREFIX + "null number of containers");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.assertTrue(e.getMessage()
       Assert.assertTrue(e.getMessage()
@@ -186,7 +216,7 @@ public class TestServiceApiUtil {
     // negative number of containers
     // negative number of containers
     app.setNumberOfContainers(-1L);
     app.setNumberOfContainers(-1L);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "negative number of containers");
       Assert.fail(EXCEPTION_PREFIX + "negative number of containers");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.assertTrue(e.getMessage()
       Assert.assertTrue(e.getMessage()
@@ -196,7 +226,7 @@ public class TestServiceApiUtil {
     // everything valid here
     // everything valid here
     app.setNumberOfContainers(5L);
     app.setNumberOfContainers(5L);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       LOG.error("application attributes specified should be valid here", e);
       LOG.error("application attributes specified should be valid here", e);
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
@@ -218,7 +248,7 @@ public class TestServiceApiUtil {
     Artifact artifact = new Artifact();
     Artifact artifact = new Artifact();
     app.setArtifact(artifact);
     app.setArtifact(artifact);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no artifact id");
       Assert.fail(EXCEPTION_PREFIX + "application with no artifact id");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(ERROR_ARTIFACT_ID_INVALID, e.getMessage());
       assertEquals(ERROR_ARTIFACT_ID_INVALID, e.getMessage());
@@ -227,7 +257,7 @@ public class TestServiceApiUtil {
     // no artifact id fails with APPLICATION type
     // no artifact id fails with APPLICATION type
     artifact.setType(Artifact.TypeEnum.APPLICATION);
     artifact.setType(Artifact.TypeEnum.APPLICATION);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no artifact id");
       Assert.fail(EXCEPTION_PREFIX + "application with no artifact id");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(ERROR_ARTIFACT_ID_INVALID, e.getMessage());
       assertEquals(ERROR_ARTIFACT_ID_INVALID, e.getMessage());
@@ -236,7 +266,7 @@ public class TestServiceApiUtil {
     // no artifact id fails with TARBALL type
     // no artifact id fails with TARBALL type
     artifact.setType(Artifact.TypeEnum.TARBALL);
     artifact.setType(Artifact.TypeEnum.TARBALL);
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with no artifact id");
       Assert.fail(EXCEPTION_PREFIX + "application with no artifact id");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals(ERROR_ARTIFACT_ID_INVALID, e.getMessage());
       assertEquals(ERROR_ARTIFACT_ID_INVALID, e.getMessage());
@@ -246,7 +276,7 @@ public class TestServiceApiUtil {
     artifact.setType(Artifact.TypeEnum.DOCKER);
     artifact.setType(Artifact.TypeEnum.DOCKER);
     artifact.setId("docker.io/centos:centos7");
     artifact.setId("docker.io/centos:centos7");
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       LOG.error("application attributes specified should be valid here", e);
       LOG.error("application attributes specified should be valid here", e);
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
@@ -314,7 +344,7 @@ public class TestServiceApiUtil {
     app.setArtifact(artifact);
     app.setArtifact(artifact);
 
 
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
     }
     }
@@ -333,7 +363,7 @@ public class TestServiceApiUtil {
 
 
     // duplicate component name fails
     // duplicate component name fails
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
       Assert.fail(EXCEPTION_PREFIX + "application with component collision");
       Assert.fail(EXCEPTION_PREFIX + "application with component collision");
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       assertEquals("Component name collision: " + compName, e.getMessage());
       assertEquals("Component name collision: " + compName, e.getMessage());
@@ -353,7 +383,7 @@ public class TestServiceApiUtil {
 
 
     // duplicate component name okay in the case of APPLICATION component
     // duplicate component name okay in the case of APPLICATION component
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
     }
     }
@@ -371,7 +401,7 @@ public class TestServiceApiUtil {
     app.setArtifact(artifact);
     app.setArtifact(artifact);
 
 
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
     }
     }
@@ -384,7 +414,7 @@ public class TestServiceApiUtil {
     app.getComponent("comp2").setArtifact(artifact);
     app.getComponent("comp2").setArtifact(artifact);
 
 
     try {
     try {
-      ServiceApiUtil.validateAndResolveApplication(app, sfs);
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
     } catch (IllegalArgumentException e) {
     } catch (IllegalArgumentException e) {
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
       Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
     }
     }
@@ -441,4 +471,52 @@ public class TestServiceApiUtil {
           .getMessage());
           .getMessage());
     }
     }
   }
   }
+
+  @Test
+  public void testInvalidComponent() throws IOException {
+    SliderFileSystem sfs = initMock(null);
+    testComponent(sfs, false);
+    testComponent(sfs, true);
+  }
+
+  private static void testComponent(SliderFileSystem sfs, boolean unique)
+      throws IOException {
+    int maxLen = RegistryConstants.MAX_FQDN_LABEL_LENGTH;
+    if (unique) {
+      assertEquals(19, Long.toString(Long.MAX_VALUE).length());
+      maxLen = maxLen - Long.toString(Long.MAX_VALUE).length();
+    }
+    String compName = LEN_64_STR.substring(0, maxLen + 1);
+    Application app = createValidApplication(null);
+    app.addComponent(createValidComponent(compName).uniqueComponentSupport(
+        unique));
+
+    // invalid component name fails if dns is enabled
+    try {
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
+      Assert.fail(EXCEPTION_PREFIX + "application with invalid component name");
+    } catch (IllegalArgumentException e) {
+      assertEquals(String.format(RestApiErrorMessages
+          .ERROR_COMPONENT_NAME_INVALID, maxLen, compName), e.getMessage());
+    }
+
+    // does not fail if dns is disabled
+    try {
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DEFAULT_DNS);
+    } catch (IllegalArgumentException e) {
+      Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
+    }
+
+    compName = LEN_64_STR.substring(0, maxLen);
+    app = createValidApplication(null);
+    app.addComponent(createValidComponent(compName).uniqueComponentSupport(
+        unique));
+
+    // does not fail
+    try {
+      ServiceApiUtil.validateAndResolveApplication(app, sfs, CONF_DNS_ENABLED);
+    } catch (IllegalArgumentException e) {
+      Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage());
+    }
+  }
 }
 }

+ 5 - 0
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-registry/src/main/java/org/apache/hadoop/registry/client/api/RegistryConstants.java

@@ -77,6 +77,11 @@ public interface RegistryConstants {
    */
    */
   String KEY_DNS_DOMAIN = DNS_PREFIX + "domain-name";
   String KEY_DNS_DOMAIN = DNS_PREFIX + "domain-name";
 
 
+  /**
+   * Max length of a label (node delimited by a dot in the FQDN).
+   */
+  int MAX_FQDN_LABEL_LENGTH = 63;
+
   /**
   /**
    * DNS bind address.
    * DNS bind address.
    */
    */