SLIDER-1107 fix AM-generated config publishing and race condition in config 
generation


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

Branch: refs/heads/feature/SLIDER-1107_AM_config_generation
Commit: 1699a714dfcea6473d9256a7e0a4536976485acc
Parents: c7a7c0d
Author: Billie Rinaldi <billie.rina...@gmail.com>
Authored: Thu May 12 18:22:08 2016 -0700
Committer: Billie Rinaldi <billie.rina...@gmail.com>
Committed: Thu May 12 18:51:38 2016 -0700

----------------------------------------------------------------------
 .../core/registry/docstore/ConfigUtils.java     |  37 ++++++
 .../docstore/PublishedConfiguration.java        |  22 ----
 .../PublishedConfigurationOutputter.java        |  53 +--------
 .../providers/agent/AgentProviderService.java   | 113 ++++++++++++++-----
 4 files changed, 120 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1699a714/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigUtils.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigUtils.java
 
b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigUtils.java
index 7e6ee5a..2e1615b 100644
--- 
a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigUtils.java
+++ 
b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigUtils.java
@@ -17,6 +17,10 @@
  */
 package org.apache.slider.core.registry.docstore;
 
+import org.apache.hadoop.fs.Path;
+import org.apache.slider.common.tools.SliderFileSystem;
+
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -24,6 +28,8 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class ConfigUtils {
+  public static final String TEMPLATE_FILE = "template.file";
+
   public static String replaceProps(Map<String, String> config, String 
content) {
     Map<String, String> tokens = new HashMap<>();
     for (Entry<String, String> entry : config.entrySet()) {
@@ -56,4 +62,35 @@ public class ConfigUtils {
     return newConfig;
   }
 
+  public static void prepConfigForTemplateOutputter(ConfigFormat configFormat,
+      Map<String, String> config, SliderFileSystem fileSystem,
+      String clusterName, String fileName) throws IOException {
+    if (!configFormat.equals(ConfigFormat.TEMPLATE)) {
+      return;
+    }
+    Path templateFile = null;
+    if (config.containsKey(TEMPLATE_FILE)) {
+      templateFile = fileSystem.buildResourcePath(config.get(TEMPLATE_FILE));
+      if (!fileSystem.isFile(templateFile)) {
+        templateFile = fileSystem.buildResourcePath(clusterName,
+            config.get(TEMPLATE_FILE));
+      }
+      if (!fileSystem.isFile(templateFile)) {
+        throw new IOException("config specified template file " + config
+            .get(TEMPLATE_FILE) + " but " + templateFile + " doesn't exist");
+      }
+    }
+    if (templateFile == null && fileName != null) {
+      templateFile = fileSystem.buildResourcePath(fileName);
+      if (!fileSystem.isFile(templateFile)) {
+        templateFile = fileSystem.buildResourcePath(clusterName,
+            fileName);
+      }
+    }
+    if (fileSystem.isFile(templateFile)) {
+      config.put("content", fileSystem.cat(templateFile));
+    } else {
+      config.put("content", "");
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1699a714/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
 
b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
index cabdce6..477f7d3 100644
--- 
a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
+++ 
b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfiguration.java
@@ -20,7 +20,6 @@ package org.apache.slider.core.registry.docstore;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.slider.common.tools.ConfigHelper;
-import org.apache.slider.common.tools.SliderFileSystem;
 import org.apache.slider.core.exceptions.BadConfigException;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.map.ObjectMapper;
@@ -50,9 +49,6 @@ public class PublishedConfiguration {
 
   public Map<String, String> entries = new HashMap<>();
 
-  public SliderFileSystem fileSystem;
-  public String clusterName;
-
   public PublishedConfiguration() {
   }
 
@@ -90,24 +86,6 @@ public class PublishedConfiguration {
   }
 
   /**
-   * Build a configuration from the entries
-   * @param description configuration description
-   * @param entries entries to put
-   * @param fileSystem Slider file system (source of configuration templates)
-   * @param clusterName cluster name
-   */
-  public PublishedConfiguration(String description,
-      Iterable<Map.Entry<String, String>> entries,
-      SliderFileSystem fileSystem,
-      String clusterName) {
-    this.description = description;
-    putValues(entries);
-    this.fileSystem = fileSystem;
-    this.clusterName = clusterName;
-  }
-
-
-  /**
    * Is the configuration empty. This means either that it has not
    * been given any values, or it is stripped down copy set down over the
    * wire.

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1699a714/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
 
b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
index 23cfc8f..9bdcfcb 100644
--- 
a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
+++ 
b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/PublishedConfigurationOutputter.java
@@ -23,9 +23,7 @@ import com.google.common.base.Preconditions;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.Path;
 import org.apache.slider.common.tools.ConfigHelper;
-import org.apache.slider.common.tools.SliderFileSystem;
 import org.yaml.snakeyaml.DumperOptions;
 import org.yaml.snakeyaml.DumperOptions.FlowStyle;
 import org.yaml.snakeyaml.Yaml;
@@ -34,7 +32,6 @@ import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.StringWriter;
-import java.util.Map;
 import java.util.Properties;
 
 /**
@@ -188,58 +185,10 @@ public abstract class PublishedConfigurationOutputter {
     }
   }
 
-  public static class TemplateOutputter extends 
PublishedConfigurationOutputter {
-
-    public static final String TEMPLATE_FILE = "template.file";
-
+  public static class TemplateOutputter extends EnvOutputter {
     public TemplateOutputter(PublishedConfiguration owner) {
       super(owner);
     }
-
-    @Override
-    public void save(File dest) throws IOException {
-      FileUtils.writeStringToFile(dest, asString(dest.getName()),
-          Charsets.UTF_8);
-    }
-
-    public String asString(String fileName) throws IOException {
-      if (owner.fileSystem == null) {
-        throw new IOException("File system not specified for template " +
-            "configuration");
-      }
-      Map<String,String> config = owner.entries;
-      SliderFileSystem fileSystem = owner.fileSystem;
-      Path templateFile = null;
-      if (config.containsKey(TEMPLATE_FILE)) {
-        templateFile = fileSystem.buildResourcePath(config.get(TEMPLATE_FILE));
-        if (!fileSystem.isFile(templateFile)) {
-          templateFile = fileSystem.buildResourcePath(owner.clusterName,
-              config.get(TEMPLATE_FILE));
-        }
-        if (!fileSystem.isFile(templateFile)) {
-          throw new IOException("config specified template file " + config
-              .get(TEMPLATE_FILE) + " for config " + owner.description +
-              " but " + templateFile + " doesn't exist");
-        }
-      }
-      if (templateFile == null && fileName != null) {
-        templateFile = fileSystem.buildResourcePath(fileName);
-        if (!fileSystem.isFile(templateFile)) {
-          templateFile = fileSystem.buildResourcePath(owner.clusterName,
-              fileName);
-        }
-      }
-      if (fileSystem.isFile(templateFile)) {
-        return ConfigUtils.replaceProps(config, fileSystem.cat(templateFile));
-      } else {
-        return "";
-      }
-    }
-
-    @Override
-    public String asString() throws IOException {
-      return asString(null);
-    }
   }
 
   public static class YamlOutputter extends PublishedConfigurationOutputter {

http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/1699a714/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
----------------------------------------------------------------------
diff --git 
a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
 
b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
index ab4da47..155ff09 100644
--- 
a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
+++ 
b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentProviderService.java
@@ -170,6 +170,8 @@ public class AgentProviderService extends 
AbstractProviderService implements
   private AgentClientProvider clientProvider;
   private AtomicInteger taskId = new AtomicInteger(0);
   private volatile Metainfo metaInfo = null;
+  private AggregateConf instanceDefinition = null;
+  private SliderFileSystem fileSystem = null;
   private Map<String, DefaultConfig> defaultConfigs = null;
   private ComponentCommandOrder commandOrder = null;
   private HeartbeatMonitor monitor;
@@ -282,6 +284,8 @@ public class AgentProviderService extends 
AbstractProviderService implements
     if (metaInfo == null) {
       synchronized (syncLock) {
         if (metaInfo == null) {
+          this.instanceDefinition = instanceDefinition;
+          this.fileSystem = fileSystem;
           readAndSetHeartbeatMonitoringInterval(instanceDefinition);
           initializeAgentDebugCommands(instanceDefinition);
 
@@ -679,21 +683,6 @@ public class AgentProviderService extends 
AbstractProviderService implements
     return uploadResource(resource, fileSystem, certsDir);
   }
 
-  private Path checkResourceExists(File resource, SliderFileSystem
-      fileSystem, String roleName) throws IOException {
-    Path dir;
-    if (roleName == null) {
-      dir = fileSystem.buildClusterResourcePath(getClusterName());
-    } else {
-      dir = fileSystem.buildClusterResourcePath(getClusterName(), roleName);
-    }
-    Path destPath = new Path(dir, resource.getName());
-    if (fileSystem.getFileSystem().exists(destPath)) {
-      return destPath;
-    }
-    return null;
-  }
-
   private Path uploadResource(File resource, SliderFileSystem fileSystem,
       String roleName) throws IOException {
     Path dir;
@@ -705,8 +694,8 @@ public class AgentProviderService extends 
AbstractProviderService implements
     return uploadResource(resource, fileSystem, dir);
   }
 
-  private static Path uploadResource(File resource, SliderFileSystem 
fileSystem,
-                              Path parentDir) throws IOException {
+  private static synchronized Path uploadResource(File resource,
+      SliderFileSystem fileSystem, Path parentDir) throws IOException {
     if (!fileSystem.getFileSystem().exists(parentDir)) {
       fileSystem.getFileSystem().mkdirs(parentDir,
         new FsPermission(FsAction.ALL, FsAction.NONE, FsAction.NONE));
@@ -783,14 +772,19 @@ public class AgentProviderService extends 
AbstractProviderService implements
   }
 
   private void createConfigFile(SliderFileSystem fileSystem, File file,
-      ConfigFile configFile, Map<String, String> config) throws IOException {
+      ConfigFile configFile, Map<String, String> config)
+      throws IOException {
     ConfigFormat configFormat = ConfigFormat.resolve(configFile.getType());
     log.info("Writing {} file {}", configFormat, file);
 
+    ConfigUtils.prepConfigForTemplateOutputter(configFormat, config,
+        fileSystem, getClusterName(), file.getName());
+    PublishedConfiguration publishedConfiguration =
+        new PublishedConfiguration(configFile.getDictionaryName(),
+            config.entrySet());
     PublishedConfigurationOutputter configurationOutputter =
       PublishedConfigurationOutputter.createOutputter(configFormat,
-          new PublishedConfiguration(configFile.getDictionaryName(),
-              config.entrySet(), fileSystem, getClusterName()));
+          publishedConfiguration);
     configurationOutputter.save(file);
   }
 
@@ -820,16 +814,11 @@ public class AgentProviderService extends 
AbstractProviderService implements
         folder = roleGroup;
       }
 
-      Path destPath = checkResourceExists(localFile, fileSystem, folder);
-      if (destPath == null) {
-        log.info("Localizing {} configs to config file {} (destination {}) " +
-                "based on {} configs",
-            config.size(), localFile, fileName, 
configFile.getDictionaryName());
-        createConfigFile(fileSystem, localFile, configFile, config);
-        destPath = uploadResource(localFile, fileSystem, folder);
-      } else {
-        log.info("Config already exists at {}, not recreating it", destPath);
-      }
+      log.info("Localizing {} configs to config file {} (destination {}) " +
+          "based on {} configs", config.size(), localFile, fileName,
+          configFile.getDictionaryName());
+      createConfigFile(fileSystem, localFile, configFile, config);
+      Path destPath = uploadResource(localFile, fileSystem, folder);
       LocalResource configResource = fileSystem.createAmResource(destPath,
           LocalResourceType.FILE);
 
@@ -1033,7 +1022,7 @@ public class AgentProviderService extends 
AbstractProviderService implements
             role = amState.getOwnedContainer(containerId);
             role.ip = status.getIp();
           }
-          if(status.getHostname() != null & !status.getHostname().isEmpty()){
+          if(status.getHostname() != null && !status.getHostname().isEmpty()){
             role = amState.getOwnedContainer(containerId);
             role.hostname = status.getHostname();
           }
@@ -1369,6 +1358,68 @@ public class AgentProviderService extends 
AbstractProviderService implements
     } catch (URISyntaxException e) {
       throw new IOException(e);
     }
+
+    // identify client component
+    Component client = null;
+    for (Component component : getMetaInfo().getApplication().getComponents()) 
{
+      if (component != null && component.getCategory().equals("CLIENT")) {
+        client = component;
+        break;
+      }
+    }
+    if (client == null) {
+      log.info("No client component specified, not publishing client configs");
+      return;
+    }
+
+    // register AM-generated client configs
+    ConfTreeOperations appConf = instanceDefinition.getAppConfOperations();
+    MapOperations clientOperations = instanceDefinition.getAppConfOperations()
+        .getOrAddComponent(client.getName());
+    if (!clientOperations.getOptionBool(AgentKeys.AM_CONFIG_GENERATION,
+        false)) {
+      log.info("AM config generation is false, not publishing client configs");
+      return;
+    }
+
+    // build and localize configuration files
+    Map<String, Map<String, String>> configurations = new TreeMap<String, 
Map<String, String>>();
+    Map<String, String> tokens = null;
+    try {
+      tokens = getStandardTokenMap(appConf, client.getName(), 
client.getName());
+    } catch (SliderException e) {
+      throw new IOException(e);
+    }
+
+    for (ConfigFile configFile : getMetaInfo()
+        .getComponentConfigFiles(client.getName())) {
+      addNamedConfiguration(configFile.getDictionaryName(),
+          appConf.getGlobalOptions().options, configurations, tokens, null,
+          client.getName());
+      if (appConf.getComponent(client.getName()) != null) {
+        addNamedConfiguration(configFile.getDictionaryName(),
+            appConf.getComponent(client.getName()).options, configurations,
+            tokens, null, client.getName());
+      }
+    }
+
+    //do a final replacement of re-used configs
+    dereferenceAllConfigs(configurations);
+
+    for (ConfigFile configFile : getMetaInfo()
+        .getComponentConfigFiles(client.getName())) {
+      ConfigFormat configFormat = ConfigFormat.resolve(configFile.getType());
+
+      Map<String, String> config = 
configurations.get(configFile.getDictionaryName());
+      ConfigUtils.prepConfigForTemplateOutputter(configFormat, config,
+          fileSystem, getClusterName(),
+          new File(configFile.getFileName()).getName());
+      PublishedConfiguration publishedConfiguration =
+          new PublishedConfiguration(configFile.getDictionaryName(),
+              config.entrySet());
+      getAmState().getPublishedSliderConfigurations().put(
+          configFile.getDictionaryName(), publishedConfiguration);
+    }
   }
 
   @Override

Reply via email to