http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/client/ClientUtils.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/client/ClientUtils.java b/slider-core/src/main/java/org/apache/slider/client/ClientUtils.java new file mode 100644 index 0000000..c3ccb1d --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/client/ClientUtils.java @@ -0,0 +1,111 @@ +/* + * 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.slider.client; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.PathNotFoundException; +import org.apache.hadoop.registry.client.api.RegistryOperations; +import org.apache.hadoop.registry.client.binding.RegistryPathUtils; +import org.apache.hadoop.registry.client.exceptions.NoRecordException; +import org.apache.hadoop.registry.client.types.ServiceRecord; +import org.apache.slider.common.SliderKeys; +import org.apache.slider.core.exceptions.BadCommandArgumentsException; +import org.apache.slider.core.exceptions.NotFoundException; +import org.apache.slider.core.exceptions.SliderException; +import org.apache.slider.core.registry.docstore.ConfigFormat; +import org.apache.slider.core.registry.docstore.PublishedConfigSet; +import org.apache.slider.core.registry.docstore.PublishedConfiguration; +import org.apache.slider.core.registry.docstore.PublishedConfigurationOutputter; +import org.apache.slider.core.registry.retrieve.RegistryRetriever; + +import java.io.File; +import java.io.IOException; + +import static org.apache.hadoop.registry.client.binding.RegistryUtils.currentUser; +import static org.apache.hadoop.registry.client.binding.RegistryUtils.servicePath; + +public class ClientUtils { + public static ServiceRecord lookupServiceRecord(RegistryOperations rops, + String user, String name) throws IOException, SliderException { + return lookupServiceRecord(rops, user, null, name); + } + + public static ServiceRecord lookupServiceRecord(RegistryOperations rops, + String user, String type, String name) throws IOException, + SliderException { + if (StringUtils.isEmpty(user)) { + user = currentUser(); + } else { + user = RegistryPathUtils.encodeForRegistry(user); + } + if (StringUtils.isEmpty(type)) { + type = SliderKeys.APP_TYPE; + } + + String path = servicePath(user, type, name); + return resolve(rops, path); + } + + public static ServiceRecord resolve(RegistryOperations rops, String path) + throws IOException, SliderException { + try { + return rops.resolve(path); + } catch (PathNotFoundException | NoRecordException e) { + throw new NotFoundException(e.getPath().toString(), e); + } + } + + public static PublishedConfiguration getConfigFromRegistry( + RegistryOperations rops, Configuration configuration, + String configName, String appName, String user, boolean external) + throws IOException, SliderException { + ServiceRecord instance = lookupServiceRecord(rops, user, appName); + + RegistryRetriever retriever = new RegistryRetriever(configuration, instance); + PublishedConfigSet configurations = retriever.getConfigurations(external); + + PublishedConfiguration published = retriever.retrieveConfiguration( + configurations, configName, external); + return published; + } + + public static String saveOrReturnConfig(PublishedConfiguration published, + String format, File destPath, String fileName) + throws BadCommandArgumentsException, IOException { + ConfigFormat configFormat = ConfigFormat.resolve(format); + if (configFormat == null) { + throw new BadCommandArgumentsException( + "Unknown/Unsupported format %s ", format); + } + PublishedConfigurationOutputter outputter = + PublishedConfigurationOutputter.createOutputter(configFormat, + published); + boolean print = destPath == null; + if (!print) { + if (destPath.isDirectory()) { + // creating it under a directory + destPath = new File(destPath, fileName); + } + outputter.save(destPath); + return null; + } else { + return outputter.asString(); + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/client/SliderClient.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java index f3dcea3..8210f4d 100644 --- a/slider-core/src/main/java/org/apache/slider/client/SliderClient.java +++ b/slider-core/src/main/java/org/apache/slider/client/SliderClient.java @@ -101,6 +101,7 @@ import org.apache.slider.common.params.ActionNodesArgs; import org.apache.slider.common.params.ActionPackageArgs; import org.apache.slider.common.params.ActionRegistryArgs; import org.apache.slider.common.params.ActionResolveArgs; +import org.apache.slider.common.params.ActionResourceArgs; import org.apache.slider.common.params.ActionStatusArgs; import org.apache.slider.common.params.ActionThawArgs; import org.apache.slider.common.params.ActionTokensArgs; @@ -177,7 +178,6 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.File; -import java.io.FilenameFilter; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -415,7 +415,7 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe case ACTION_INSTALL_PACKAGE: exitCode = actionInstallPkg(serviceArgs.getActionInstallPackageArgs()); break; - + case ACTION_KEYTAB: exitCode = actionKeytab(serviceArgs.getActionKeytabArgs()); break; @@ -443,7 +443,11 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe case ACTION_RESOLVE: exitCode = actionResolve(serviceArgs.getActionResolveArgs()); break; - + + case ACTION_RESOURCE: + exitCode = actionResource(serviceArgs.getActionResourceArgs()); + break; + case ACTION_STATUS: exitCode = actionStatus(clusterName, serviceArgs.getActionStatusArgs()); break; @@ -1029,7 +1033,8 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe Path fileInFs = new Path(pkgPath, keytabInfo.keytab ); log.info("Deleting keytab {}", fileInFs); FileSystem sfs = sliderFileSystem.getFileSystem(); - require(sfs.exists(fileInFs), "No keytab to delete found at %s", fileInFs.toUri()); + require(sfs.exists(fileInFs), "No keytab to delete found at %s", + fileInFs.toUri()); sfs.delete(fileInFs, false); return EXIT_SUCCESS; @@ -1105,6 +1110,106 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe } @Override + public int actionResource(ActionResourceArgs resourceInfo) + throws YarnException, IOException { + if (resourceInfo.help) { + actionHelp(ACTION_RESOURCE); + return EXIT_SUCCESS; + } else if (resourceInfo.install) { + return actionInstallResource(resourceInfo); + } else if (resourceInfo.delete) { + return actionDeleteResource(resourceInfo); + } else if (resourceInfo.list) { + return actionListResource(resourceInfo); + } else { + throw new BadCommandArgumentsException( + "Resource option specified not found.\n" + + CommonArgs.usage(serviceArgs, ACTION_RESOURCE)); + } + } + + private int actionListResource(ActionResourceArgs resourceInfo) throws IOException { + String folder = resourceInfo.folder != null ? resourceInfo.folder : StringUtils.EMPTY; + Path path = sliderFileSystem.buildResourcePath(folder); + RemoteIterator<LocatedFileStatus> files = + sliderFileSystem.getFileSystem().listFiles(path, true); + log.info("Resources:"); + while (files.hasNext()) { + log.info("\t" + files.next().getPath().toString()); + } + + return EXIT_SUCCESS; + } + + private int actionDeleteResource(ActionResourceArgs resourceInfo) + throws BadCommandArgumentsException, IOException { + if (StringUtils.isEmpty(resourceInfo.resource)) { + throw new BadCommandArgumentsException("A file name is required."); + } + + Path fileInFs; + if (resourceInfo.folder == null) { + fileInFs = sliderFileSystem.buildResourcePath(resourceInfo.resource); + } else { + fileInFs = sliderFileSystem.buildResourcePath(resourceInfo.folder, + resourceInfo.resource); + } + + log.info("Deleting resource {}", fileInFs); + FileSystem sfs = sliderFileSystem.getFileSystem(); + require(sfs.exists(fileInFs), "No resource to delete found at %s", fileInFs.toUri()); + sfs.delete(fileInFs, true); + + return EXIT_SUCCESS; + } + + private int actionInstallResource(ActionResourceArgs resourceInfo) + throws BadCommandArgumentsException, IOException { + Path srcFile = null; + String folder = resourceInfo.folder != null ? resourceInfo.folder : StringUtils.EMPTY; + + requireArgumentSet(Arguments.ARG_RESOURCE, resourceInfo.resource); + File file = new File(resourceInfo.resource); + require(file.isFile() || file.isDirectory(), + "Unable to access supplied file at %s", file.getAbsolutePath()); + + File[] files; + if (file.isDirectory()) { + files = file.listFiles(); + } else { + files = new File[] { file }; + } + + Path pkgPath = sliderFileSystem.buildResourcePath(folder); + FileSystem sfs = sliderFileSystem.getFileSystem(); + + if (!sfs.exists(pkgPath)) { + sfs.mkdirs(pkgPath); + sfs.setPermission(pkgPath, new FsPermission( + FsAction.ALL, FsAction.NONE, FsAction.NONE)); + } else { + require(sfs.isDirectory(pkgPath), "Specified folder %s exists and is " + + "not a directory", folder); + } + + for (File f : files) { + srcFile = new Path(f.toURI()); + + Path fileInFs = new Path(pkgPath, srcFile.getName()); + log.info("Installing file {} at {} and overwrite is {}.", + srcFile, fileInFs, resourceInfo.overwrite); + require(!(sfs.exists(fileInFs) && !resourceInfo.overwrite), + "File exists at %s. Use --overwrite to overwrite.", fileInFs.toUri()); + + sfs.copyFromLocalFile(false, resourceInfo.overwrite, srcFile, fileInFs); + sfs.setPermission(fileInFs, + new FsPermission(FsAction.READ_WRITE, FsAction.NONE, FsAction.NONE)); + } + + return EXIT_SUCCESS; + } + + @Override public int actionClient(ActionClientArgs clientInfo) throws YarnException, IOException { @@ -1210,8 +1315,21 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe E_INVALID_INSTALL_PATH + ": " + clientInfo.installLocation.getAbsolutePath()); File pkgFile; - require(isSet(clientInfo.packageURI), E_INVALID_APPLICATION_PACKAGE_LOCATION); - pkgFile = new File(clientInfo.packageURI); + File tmpDir = null; + + require(isSet(clientInfo.packageURI) || isSet(clientInfo.name), + E_INVALID_APPLICATION_PACKAGE_LOCATION); + if (isSet(clientInfo.packageURI)) { + pkgFile = new File(clientInfo.packageURI); + } else { + Path appDirPath = sliderFileSystem.buildAppDefDirPath(clientInfo.name); + Path appDefPath = new Path(appDirPath, SliderKeys.DEFAULT_APP_PKG); + require(sliderFileSystem.isFile(appDefPath), + E_INVALID_APPLICATION_PACKAGE_LOCATION); + tmpDir = Files.createTempDir(); + pkgFile = new File(tmpDir, SliderKeys.DEFAULT_APP_PKG); + sliderFileSystem.copyHdfsFileToLocal(appDefPath, pkgFile); + } require(pkgFile.isFile(), E_UNABLE_TO_READ_SUPPLIED_PACKAGE_FILE + " at %s", pkgFile.getAbsolutePath()); @@ -1233,6 +1351,8 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe AbstractClientProvider provider = createClientProvider(SliderProviderFactory.DEFAULT_CLUSTER_TYPE); provider.processClientOperation(sliderFileSystem, + getRegistryOperations(), + getConfig(), "INSTALL", clientInfo.installLocation, pkgFile, @@ -4077,17 +4197,9 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe @VisibleForTesting public PublishedConfiguration actionRegistryGetConfig(ActionRegistryArgs registryArgs) throws YarnException, IOException { - ServiceRecord instance = lookupServiceRecord(registryArgs); - - RegistryRetriever retriever = new RegistryRetriever(getConfig(), instance); - boolean external = !registryArgs.internal; - PublishedConfigSet configurations = - retriever.getConfigurations(external); - - PublishedConfiguration published = retriever.retrieveConfiguration(configurations, - registryArgs.getConf, - external); - return published; + return ClientUtils.getConfigFromRegistry(getRegistryOperations(), + getConfig(), registryArgs.getConf, registryArgs.name, registryArgs.user, + !registryArgs.internal); } /** @@ -4130,27 +4242,11 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe // decide whether or not to print String entry = registryArgs.getConf; String format = registryArgs.format; - ConfigFormat configFormat = ConfigFormat.resolve(format); - if (configFormat == null) { - throw new BadCommandArgumentsException( - "Unknown/Unsupported format %s ", format); - } - PublishedConfigurationOutputter outputter = - PublishedConfigurationOutputter.createOutputter(configFormat, - published); - boolean print = registryArgs.out == null; - if (!print) { - File outputPath = registryArgs.out; - if (outputPath.isDirectory()) { - // creating it under a directory - outputPath = new File(outputPath, entry + "." + format); - } - log.debug("Destination path: {}", outputPath); - outputter.save(outputPath); - } else { - print(outputter.asString()); + String output = ClientUtils.saveOrReturnConfig(published, + registryArgs.format, registryArgs.out, entry + "." + format); + if (output != null) { + print(output); } - } /** @@ -4200,32 +4296,8 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe private ServiceRecord lookupServiceRecord(ActionRegistryArgs registryArgs) throws SliderException, IOException { - String user; - if (StringUtils.isNotEmpty(registryArgs.user)) { - user = RegistryPathUtils.encodeForRegistry(registryArgs.user); - } else { - user = currentUser(); - } - - String path = servicePath(user, registryArgs.serviceType, - registryArgs.name); - return resolve(path); - } - - /** - * Look up a service record of the current user - * @param serviceType service type - * @param id instance ID - * @return instance data - * @throws UnknownApplicationInstanceException no path or service record - * at the end of the path - * @throws SliderException other failures - * @throws IOException IO problems or wrapped exceptions - */ - public ServiceRecord lookupServiceRecord(String serviceType, String id) - throws IOException, SliderException { - String path = servicePath(currentUser(), serviceType, id); - return resolve(path); + return ClientUtils.lookupServiceRecord(getRegistryOperations(), + registryArgs.user, registryArgs.serviceType, registryArgs.name); } /** @@ -4240,11 +4312,7 @@ public class SliderClient extends AbstractSliderLaunchedService implements RunSe */ public ServiceRecord resolve(String path) throws IOException, SliderException { - try { - return getRegistryOperations().resolve(path); - } catch (PathNotFoundException | NoRecordException e) { - throw new NotFoundException(e.getPath().toString(), e); - } + return ClientUtils.resolve(getRegistryOperations(), path); } /** http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java b/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java index 5c5d96b..30f6ba9 100644 --- a/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java +++ b/slider-core/src/main/java/org/apache/slider/client/SliderClientAPI.java @@ -43,6 +43,7 @@ import org.apache.slider.common.params.ActionKillContainerArgs; import org.apache.slider.common.params.ActionListArgs; import org.apache.slider.common.params.ActionRegistryArgs; import org.apache.slider.common.params.ActionResolveArgs; +import org.apache.slider.common.params.ActionResourceArgs; import org.apache.slider.common.params.ActionStatusArgs; import org.apache.slider.common.params.ActionThawArgs; import org.apache.slider.common.params.ActionUpgradeArgs; @@ -117,7 +118,6 @@ public interface SliderClientAPI extends Service { * @throws YarnException Yarn problems * @throws IOException other problems * @throws BadCommandArgumentsException bad arguments. - * @deprecated use #actionKeytab */ int actionKeytab(ActionKeytabArgs keytabInfo) throws YarnException, IOException; @@ -134,6 +134,17 @@ public interface SliderClientAPI extends Service { throws YarnException, IOException; /** + * Manage file resources leveraged by slider + * + * @param resourceInfo the arguments needed to manage the resource + * @throws YarnException Yarn problems + * @throws IOException other problems + * @throws BadCommandArgumentsException bad arguments. + */ + int actionResource(ActionResourceArgs resourceInfo) + throws YarnException, IOException; + + /** * Perform client operations such as install or configure * * @param clientInfo the arguments needed for client operations http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java b/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java index 05c7048..ba3effc 100644 --- a/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java +++ b/slider-core/src/main/java/org/apache/slider/common/SliderKeys.java @@ -116,7 +116,8 @@ public interface SliderKeys extends SliderXmlConfKeys { String HISTORY_FILENAME_SUFFIX = "json"; String HISTORY_FILENAME_PREFIX = "rolehistory-"; String KEYTAB_DIR = "keytabs"; - + String RESOURCE_DIR = "resources"; + /** * Filename pattern is required to save in strict temporal order. * Important: older files must sort less-than newer files when using http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/common/params/ActionResourceArgs.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/ActionResourceArgs.java b/slider-core/src/main/java/org/apache/slider/common/params/ActionResourceArgs.java new file mode 100644 index 0000000..60fcc87 --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/common/params/ActionResourceArgs.java @@ -0,0 +1,68 @@ +/* + * 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.slider.common.params; + +import com.beust.jcommander.Parameter; +import com.beust.jcommander.Parameters; + +@Parameters(commandNames = {SliderActions.ACTION_RESOURCE}, + commandDescription = SliderActions.DESCRIBE_ACTION_RESOURCE) + +public class ActionResourceArgs extends AbstractActionArgs { + + @Override + public String getActionName() { + return SliderActions.ACTION_RESOURCE; + } + + @Parameter(names = {ARG_INSTALL}, + description = "Install the resource(s)") + public boolean install; + + @Parameter(names = {ARG_DELETE}, + description = "Delete the file") + public boolean delete; + + @Parameter(names = {ARG_LIST}, + description = "List of installed files") + public boolean list; + + @Parameter(names = {ARG_RESOURCE}, + description = "Name of the file or directory") + public String resource; + + @Parameter(names = {ARG_DESTDIR}, + description = "The name of the folder in which to store the resources") + public String folder; + + @Parameter(names = {ARG_OVERWRITE}, description = "Overwrite existing resource(s)") + public boolean overwrite = false; + + /** + * Get the min #of params expected + * @return the min number of params in the {@link #parameters} field + */ + public int getMinParams() { + return 0; + } + + @Override + public int getMaxParams() { + return 3; + } +} http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java b/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java index 0a8388d..aec4e26 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/Arguments.java @@ -104,6 +104,7 @@ public interface Arguments { String ARG_PROVIDER = "--provider"; String ARG_QUEUE = "--queue"; String ARG_REPLACE_PKG = "--replacepkg"; + String ARG_RESOURCE = "--resource"; String ARG_RESOURCES = "--resources"; String ARG_RES_COMP_OPT = "--rescompopt"; String ARG_RES_COMP_OPT_SHORT = "--rco"; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java b/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java index 0a658ea..4016cc9 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/ClientArgs.java @@ -75,6 +75,7 @@ public class ClientArgs extends CommonArgs { private final ActionPackageArgs actionPackageArgs = new ActionPackageArgs(); private final ActionRegistryArgs actionRegistryArgs = new ActionRegistryArgs(); private final ActionResolveArgs actionResolveArgs = new ActionResolveArgs(); + private final ActionResourceArgs actionResourceArgs = new ActionResourceArgs(); private final ActionStatusArgs actionStatusArgs = new ActionStatusArgs(); private final ActionThawArgs actionThawArgs = new ActionThawArgs(); private final ActionTokensArgs actionTokenArgs = new ActionTokensArgs(); @@ -116,6 +117,7 @@ public class ClientArgs extends CommonArgs { actionPackageArgs, actionRegistryArgs, actionResolveArgs, + actionResourceArgs, actionStatusArgs, actionThawArgs, actionTokenArgs, @@ -227,6 +229,10 @@ public class ClientArgs extends CommonArgs { return actionResolveArgs; } + public ActionResourceArgs getActionResourceArgs() { + return actionResourceArgs; + } + public ActionStatusArgs getActionStatusArgs() { return actionStatusArgs; } @@ -346,6 +352,10 @@ public class ClientArgs extends CommonArgs { bindCoreAction(actionResolveArgs); break; + case ACTION_RESOURCE: + bindCoreAction(actionResourceArgs); + break; + case ACTION_STATUS: bindCoreAction(actionStatusArgs); break; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java b/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java index aab7c98..204ad9a 100644 --- a/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java +++ b/slider-core/src/main/java/org/apache/slider/common/params/SliderActions.java @@ -51,6 +51,7 @@ public interface SliderActions { String ACTION_RECONFIGURE = "reconfigure"; String ACTION_REGISTRY = "registry"; String ACTION_RESOLVE = "resolve"; + String ACTION_RESOURCE = "resource"; String ACTION_STATUS = "status"; String ACTION_THAW = "start"; String ACTION_TOKENS = "tokens"; @@ -106,6 +107,7 @@ public interface SliderActions { " Deprecated, use '" + ACTION_KEYTAB + " " + ClientArgs.ARG_INSTALL + "'."; String DESCRIBE_ACTION_KEYTAB = "Manage a Kerberos keytab file (install, delete, list) in the sub-folder 'keytabs' of the user's Slider base directory"; String DESCRIBE_ACTION_DIAGNOSTIC = "Diagnose the configuration of the running slider application and slider client"; + String DESCRIBE_ACTION_RESOURCE = "Manage a file (install, delete, list) in the 'resources' sub-folder of the user's Slider base directory"; } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java b/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java index 6a02367..aa5edf1 100644 --- a/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java +++ b/slider-core/src/main/java/org/apache/slider/common/tools/CoreFileSystem.java @@ -23,6 +23,7 @@ import com.google.common.base.Preconditions; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeys; +import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; @@ -64,6 +65,8 @@ public class CoreFileSystem { private static final Logger log = LoggerFactory.getLogger(CoreFileSystem.class); + private static final String UTF_8 = "UTF-8"; + protected final FileSystem fileSystem; protected final Configuration configuration; @@ -209,6 +212,55 @@ public class CoreFileSystem { } /** + * Build up the path string for resource install location -no attempt to + * create the directory is made + * + * @return the path for resource + */ + public Path buildResourcePath(String resourceFolder) { + Preconditions.checkNotNull(resourceFolder); + Path path = getBaseApplicationPath(); + return new Path(path, SliderKeys.RESOURCE_DIR + "/" + resourceFolder); + } + + /** + * Build up the path string for resource install location -no attempt to + * create the directory is made + * + * @return the path for resource + */ + public Path buildResourcePath(String dirName, String fileName) { + Preconditions.checkNotNull(dirName); + Preconditions.checkNotNull(fileName); + Path path = getBaseApplicationPath(); + return new Path(path, SliderKeys.RESOURCE_DIR + "/" + dirName + "/" + fileName); + } + + /** + * Build up the path string for cluster resource install location -no + * attempt to create the directory is made + * + * @return the path for resource + */ + public Path buildClusterResourcePath(String clusterName, String component) { + Preconditions.checkNotNull(clusterName); + Path path = buildClusterDirPath(clusterName); + return new Path(path, SliderKeys.RESOURCE_DIR + "/" + component); + } + + /** + * Build up the path string for cluster resource install location -no + * attempt to create the directory is made + * + * @return the path for resource + */ + public Path buildClusterResourcePath(String clusterName) { + Preconditions.checkNotNull(clusterName); + Path path = buildClusterDirPath(clusterName); + return new Path(path, SliderKeys.RESOURCE_DIR); + } + + /** * Create the Slider cluster path for a named cluster and all its subdirs * This is a directory; a mkdirs() operation is executed * to ensure that it is there. @@ -713,6 +765,17 @@ public class CoreFileSystem { fileSystem.setPermission(destPath, fp); } + public void copyHdfsFileToLocal(Path hdfsPath, File destFile) + throws IOException { + if (hdfsPath == null || destFile == null) { + throw new IOException("Either hdfsPath or destPath is null"); + } + log.info("Copying file {} to {}", hdfsPath.toUri(), destFile.toURI()); + + Path destPath = new Path(destFile.getPath()); + fileSystem.copyToLocalFile(hdfsPath, destPath); + } + /** * list entries in a filesystem directory * @@ -767,15 +830,36 @@ public class CoreFileSystem { } public void touch(Path path, boolean overwrite) throws IOException { - FSDataOutputStream out = fileSystem.create(path, overwrite); - out.close(); + FSDataOutputStream out = null; + try { + out = fileSystem.create(path, overwrite); + } finally { + IOUtils.closeStream(out); + } } public void cat(Path path, boolean overwrite, String data) throws IOException { - FSDataOutputStream out = fileSystem.create(path, overwrite); - byte[] bytes = data.getBytes(Charset.forName("UTF-8")); - out.write(bytes); - out.close(); + FSDataOutputStream out = null; + try { + out = fileSystem.create(path, overwrite); + byte[] bytes = data.getBytes(Charset.forName("UTF-8")); + out.write(bytes); + } finally { + IOUtils.closeStream(out); + } + } + + public String cat(Path path) throws IOException { + FileStatus status = fileSystem.getFileStatus(path); + byte[] b = new byte[(int) status.getLen()]; + FSDataInputStream in = null; + try { + in = fileSystem.open(path); + int count = in.read(b); + return new String(b, 0, count, UTF_8); + } finally { + IOUtils.closeStream(in); + } } /** http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/core/conf/ConfTreeOperations.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/conf/ConfTreeOperations.java b/slider-core/src/main/java/org/apache/slider/core/conf/ConfTreeOperations.java index 9013edb..d24a158 100644 --- a/slider-core/src/main/java/org/apache/slider/core/conf/ConfTreeOperations.java +++ b/slider-core/src/main/java/org/apache/slider/core/conf/ConfTreeOperations.java @@ -345,7 +345,7 @@ public class ConfTreeOperations { confTreeSerDeser.fromFile(resource) ); return ops; } - + /** * Build from an existing instance -which is cloned via JSON ser/deser * @param instance the source instance @@ -431,6 +431,20 @@ public class ConfTreeOperations { } /** + * Get a component opt as a boolean using {@link Boolean#valueOf(String)}. + * + * @param name component name + * @param option option name + * @param defVal default value + * @return parsed value + * @throws NumberFormatException if the role could not be parsed. + */ + public boolean getComponentOptBool(String name, String option, boolean defVal) { + String val = getComponentOpt(name, option, Boolean.toString(defVal)); + return Boolean.valueOf(val); + } + + /** * Set a component option, creating the component if necessary * @param component component name * @param option option name http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/core/launch/AbstractLauncher.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/launch/AbstractLauncher.java b/slider-core/src/main/java/org/apache/slider/core/launch/AbstractLauncher.java index 0348828..5a3eb3d 100644 --- a/slider-core/src/main/java/org/apache/slider/core/launch/AbstractLauncher.java +++ b/slider-core/src/main/java/org/apache/slider/core/launch/AbstractLauncher.java @@ -50,6 +50,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; /** * Launcher of applications: base class @@ -71,6 +72,7 @@ public abstract class AbstractLauncher extends Configured { Records.newRecord(ContainerLaunchContext.class); protected final List<String> commands = new ArrayList<>(20); protected final Map<String, LocalResource> localResources = new HashMap<>(); + protected final Map<String, String> mountPaths = new HashMap<>(); private final Map<String, ByteBuffer> serviceData = new HashMap<>(); // security protected final Credentials credentials; @@ -131,8 +133,13 @@ public abstract class AbstractLauncher extends Configured { return localResources; } - public void addLocalResource(String subpath, LocalResource resource) { - localResources.put(subpath, resource); + public void addLocalResource(String subPath, LocalResource resource) { + localResources.put(subPath, resource); + } + + public void addLocalResource(String subPath, LocalResource resource, String mountPath) { + localResources.put(subPath, resource); + mountPaths.put(subPath, mountPath); } /** @@ -227,6 +234,16 @@ public abstract class AbstractLauncher extends Configured { env.put("YARN_CONTAINER_RUNTIME_TYPE", "docker"); env.put("YARN_CONTAINER_RUNTIME_DOCKER_IMAGE", dockerImage);//if yarnDockerMode, then dockerImage is set env.put("YARN_CONTAINER_RUNTIME_DOCKER_RUN_PRIVILEGED_CONTAINER", runPrivilegedContainer); + StringBuilder sb = new StringBuilder(); + for (Entry<String,String> mount : mountPaths.entrySet()) { + if (sb.length() > 0) { + sb.append(","); + } + sb.append(mount.getKey()); + sb.append(":"); + sb.append(mount.getValue()); + } + env.put("YARN_CONTAINER_RUNTIME_DOCKER_LOCAL_RESOURCE_MOUNTS", sb.toString()); log.info("yarn docker env var has been set {}", containerLaunchContext.getEnvironment().toString()); } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/core/persist/AppDefinitionPersister.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/persist/AppDefinitionPersister.java b/slider-core/src/main/java/org/apache/slider/core/persist/AppDefinitionPersister.java index 8efaa5b..7fb3158 100644 --- a/slider-core/src/main/java/org/apache/slider/core/persist/AppDefinitionPersister.java +++ b/slider-core/src/main/java/org/apache/slider/core/persist/AppDefinitionPersister.java @@ -150,7 +150,11 @@ public class AppDefinitionPersister { pkgSrcDir.mkdirs(); File destMetaInfo = new File(pkgSrcDir, "metainfo.json"); if (isFileUsed) { - Files.copy(buildInfo.appMetaInfo, destMetaInfo); + if (buildInfo.appMetaInfo.getName().endsWith(".xml")) { + Files.copy(buildInfo.appMetaInfo, new File(pkgSrcDir, "metainfo.xml")); + } else { + Files.copy(buildInfo.appMetaInfo, destMetaInfo); + } } else { Files.write( buildInfo.appMetaInfoJson.getBytes(Charset.forName("UTF-8")), http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormat.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormat.java b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormat.java index 12581d7..ddab606 100644 --- a/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormat.java +++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigFormat.java @@ -23,8 +23,10 @@ public enum ConfigFormat { JSON("json"), PROPERTIES("properties"), XML("xml"), + HADOOP_XML("hadoop-xml"), ENV("env"), -// YAML("yaml"); + TEMPLATE("template"), + YAML("yaml"), ; ConfigFormat(String suffix) { this.suffix = suffix; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/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 new file mode 100644 index 0000000..2e1615b --- /dev/null +++ b/slider-core/src/main/java/org/apache/slider/core/registry/docstore/ConfigUtils.java @@ -0,0 +1,96 @@ +/* + * 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.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; +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()) { + tokens.put("${" + entry.getKey() + "}", entry.getValue()); + tokens.put("{{" + entry.getKey() + "}}", entry.getValue()); + } + String value = content; + for (Map.Entry<String,String> token : tokens.entrySet()) { + value = value.replaceAll(Pattern.quote(token.getKey()), + Matcher.quoteReplacement(token.getValue())); + } + return value; + } + + public static Map<String, String> replacePropsInConfig( + Map<String, String> config, Map<String, String> env) { + Map<String, String> tokens = new HashMap<>(); + for (Entry<String, String> entry : env.entrySet()) { + tokens.put("${" + entry.getKey() + "}", entry.getValue()); + } + Map<String, String> newConfig = new HashMap<>(); + for (Entry<String, String> entry : config.entrySet()) { + String value = entry.getValue(); + for (Map.Entry<String,String> token : tokens.entrySet()) { + value = value.replaceAll(Pattern.quote(token.getKey()), + Matcher.quoteReplacement(token.getValue())); + } + newConfig.put(entry.getKey(), entry.getValue()); + } + 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/424b2a40/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 15ac207..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 @@ -24,9 +24,11 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.hadoop.conf.Configuration; import org.apache.slider.common.tools.ConfigHelper; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.DumperOptions.FlowStyle; +import org.yaml.snakeyaml.Yaml; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.StringWriter; @@ -57,14 +59,7 @@ public abstract class PublishedConfigurationOutputter { } */ public void save(File dest) throws IOException { - FileOutputStream out = null; - try { - out = new FileOutputStream(dest); - save(out); - out.close(); - } finally { - org.apache.hadoop.io.IOUtils.closeStream(out); - } + FileUtils.writeStringToFile(dest, asString(), Charsets.UTF_8); } /** @@ -89,12 +84,13 @@ public abstract class PublishedConfigurationOutputter { * @param owner owning config * @return the outputter */ - + public static PublishedConfigurationOutputter createOutputter(ConfigFormat format, PublishedConfiguration owner) { Preconditions.checkNotNull(owner); switch (format) { case XML: + case HADOOP_XML: return new XmlOutputter(owner); case PROPERTIES: return new PropertiesOutputter(owner); @@ -102,11 +98,15 @@ public abstract class PublishedConfigurationOutputter { return new JsonOutputter(owner); case ENV: return new EnvOutputter(owner); + case TEMPLATE: + return new TemplateOutputter(owner); + case YAML: + return new YamlOutputter(owner); default: throw new RuntimeException("Unsupported format :" + format); } } - + public static class XmlOutputter extends PublishedConfigurationOutputter { @@ -131,7 +131,7 @@ public abstract class PublishedConfigurationOutputter { return configuration; } } - + public static class PropertiesOutputter extends PublishedConfigurationOutputter { private final Properties properties; @@ -146,15 +146,15 @@ public abstract class PublishedConfigurationOutputter { properties.store(out, ""); } - + public String asString() throws IOException { StringWriter sw = new StringWriter(); properties.store(sw, ""); return sw.toString(); } } - - + + public static class JsonOutputter extends PublishedConfigurationOutputter { public JsonOutputter(PublishedConfiguration owner) { @@ -162,11 +162,6 @@ public abstract class PublishedConfigurationOutputter { } @Override - public void save(File dest) throws IOException { - FileUtils.writeStringToFile(dest, asString(), Charsets.UTF_8); - } - - @Override public String asString() throws IOException { return owner.asJson(); } @@ -180,19 +175,36 @@ public abstract class PublishedConfigurationOutputter { } @Override - public void save(File dest) throws IOException { - FileUtils.writeStringToFile(dest, asString(), Charsets.UTF_8); - } - - @Override public String asString() throws IOException { if (!owner.entries.containsKey("content")) { throw new IOException("Configuration has no content field and cannot " + "be retrieved as type 'env'"); } - return owner.entries.get("content"); + String content = owner.entries.get("content"); + return ConfigUtils.replaceProps(owner.entries, content); } } + public static class TemplateOutputter extends EnvOutputter { + public TemplateOutputter(PublishedConfiguration owner) { + super(owner); + } + } + + public static class YamlOutputter extends PublishedConfigurationOutputter { + + private final Yaml yaml; + + public YamlOutputter(PublishedConfiguration owner) { + super(owner); + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(FlowStyle.BLOCK); + yaml = new Yaml(options); + } + + public String asString() throws IOException { + return yaml.dump(owner.entries); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/providers/AbstractClientProvider.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/AbstractClientProvider.java b/slider-core/src/main/java/org/apache/slider/providers/AbstractClientProvider.java index fcab65e..510de5d 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/AbstractClientProvider.java +++ b/slider-core/src/main/java/org/apache/slider/providers/AbstractClientProvider.java @@ -21,6 +21,7 @@ package org.apache.slider.providers; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configured; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.registry.client.api.RegistryOperations; import org.apache.slider.common.tools.SliderFileSystem; import org.apache.slider.core.conf.AggregateConf; import org.apache.slider.core.conf.ConfTreeOperations; @@ -223,18 +224,22 @@ public abstract class AbstractClientProvider extends Configured { /** * Process client operations for applications such as install, configure * @param fileSystem + * @param registryOperations + * @param configuration * @param operation * @param clientInstallPath * @param clientPackage - * @param config + * @param clientConfig * @param name * @throws SliderException */ public void processClientOperation(SliderFileSystem fileSystem, + RegistryOperations registryOperations, + Configuration configuration, String operation, File clientInstallPath, File clientPackage, - JSONObject config, + JSONObject clientConfig, String name) throws SliderException { throw new SliderException("Provider does not support client operations."); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/providers/agent/AgentClientProvider.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentClientProvider.java b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentClientProvider.java index f3dcd1d..4c6a50b 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentClientProvider.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentClientProvider.java @@ -22,13 +22,17 @@ import com.google.common.io.Files; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.registry.client.api.RegistryOperations; import org.apache.hadoop.registry.client.binding.RegistryUtils; import org.apache.slider.api.InternalKeys; import org.apache.slider.api.ResourceKeys; +import org.apache.slider.client.ClientUtils; import org.apache.slider.common.SliderKeys; import org.apache.slider.common.tools.SliderFileSystem; import org.apache.slider.common.tools.SliderUtils; @@ -38,13 +42,18 @@ import org.apache.slider.core.conf.MapOperations; import org.apache.slider.core.exceptions.BadConfigException; import org.apache.slider.core.exceptions.SliderException; import org.apache.slider.core.launch.AbstractLauncher; +import org.apache.slider.core.registry.docstore.PublishedConfiguration; import org.apache.slider.providers.AbstractClientProvider; import org.apache.slider.providers.ProviderRole; import org.apache.slider.providers.ProviderUtils; import org.apache.slider.providers.agent.application.metadata.Application; import org.apache.slider.providers.agent.application.metadata.Component; +import org.apache.slider.providers.agent.application.metadata.ConfigFile; import org.apache.slider.providers.agent.application.metadata.Metainfo; import org.apache.slider.providers.agent.application.metadata.MetainfoParser; +import org.apache.slider.providers.agent.application.metadata.OSPackage; +import org.apache.slider.providers.agent.application.metadata.OSSpecific; +import org.apache.slider.providers.agent.application.metadata.Package; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.slf4j.Logger; @@ -53,7 +62,6 @@ import org.slf4j.LoggerFactory; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -291,6 +299,8 @@ public class AgentClientProvider extends AbstractClientProvider @Override public void processClientOperation(SliderFileSystem fileSystem, + RegistryOperations rops, + Configuration configuration, String operation, File clientInstallPath, File appPackage, @@ -319,51 +329,37 @@ public class AgentClientProvider extends AbstractClientProvider { ZipEntry zipEntry = zipInputStream.getNextEntry(); while (zipEntry != null) { - if ("metainfo.xml".equals(zipEntry.getName())) { - int size = (int) zipEntry.getSize(); - if (size != -1) { - log.info("Reading {} of size {}", zipEntry.getName(), - zipEntry.getSize()); - byte[] content = new byte[size]; - int offset = 0; - while (offset < size) { - offset += zipInputStream.read(content, offset, size - offset); - } - metaInfo = new MetainfoParser().fromXmlStream(new ByteArrayInputStream(content)); - } - } else if ("metainfo.json".equals(zipEntry.getName())) { - int size = (int) zipEntry.getSize(); - if (size != -1) { - log.info("Reading {} of size {}", zipEntry.getName(), - zipEntry.getSize()); - byte[] content = new byte[size]; - int offset = 0; - while (offset < size) { - offset += zipInputStream.read(content, offset, size - offset); + log.info("Processing {}", zipEntry.getName()); + String filePath = appPkgDir + File.separator + zipEntry.getName(); + if (!zipEntry.isDirectory()) { + log.info("Extracting file {}", filePath); + extractFile(zipInputStream, filePath); + + if ("metainfo.xml".equals(zipEntry.getName())) { + FileInputStream input = null; + try { + input = new FileInputStream(filePath); + metaInfo = new MetainfoParser().fromXmlStream(input); + } finally { + IOUtils.closeStream(input); } - metaInfo = new MetainfoParser().fromJsonStream(new ByteArrayInputStream(content)); - } - } else if ("clientInstallConfig-default.json".equals(zipEntry.getName())) { - int size = (int) zipEntry.getSize(); - if (size != -1) { - log.info("Reading {} of size {}", zipEntry.getName(), - zipEntry.getSize()); - byte[] content = new byte[size]; - int offset = 0; - while (offset < size) { - offset += zipInputStream.read(content, offset, size - offset); + } else if ("metainfo.json".equals(zipEntry.getName())) { + FileInputStream input = null; + try { + input = new FileInputStream(filePath); + metaInfo = new MetainfoParser().fromJsonStream(input); + } finally { + IOUtils.closeStream(input); } + } else if ("clientInstallConfig-default.json".equals(zipEntry.getName())) { try { - defaultConfig = new JSONObject(new String(content, Charset.defaultCharset())); + defaultConfig = new JSONObject(FileUtils.readFileToString(new File(filePath), Charset.defaultCharset())); } catch (JSONException jex) { throw new SliderException("Unable to read default client config.", jex); } } - } - String filePath = appPkgDir + File.separator + zipEntry.getName(); - if (!zipEntry.isDirectory()) { - extractFile(zipInputStream, filePath); } else { + log.info("Creating dir {}", filePath); File dir = new File(filePath); dir.mkdir(); } @@ -379,34 +375,109 @@ public class AgentClientProvider extends AbstractClientProvider throw new BadConfigException(E_COULD_NOT_READ_METAINFO); } - expandAgentTar(agentPkgDir); - - JSONObject commandJson = getCommandJson(defaultConfig, config, metaInfo, clientInstallPath, name); - FileWriter file = new FileWriter(new File(cmdDir, "command.json")); - try { - file.write(commandJson.toString()); - - } catch (IOException e) { - e.printStackTrace(); - } finally { - file.flush(); - file.close(); - } - - String client_script = null; + String clientScript = null; + String clientComponent = null; for (Component component : metaInfo.getApplication().getComponents()) { if (component.getCategory().equals("CLIENT")) { - client_script = component.getCommandScript().getScript(); - log.info("Installing CLIENT {} using script {}", component.getName(), client_script); + clientComponent = component.getName(); + if (component.getCommandScript() != null) { + clientScript = component.getCommandScript().getScript(); + } break; } } - if (SliderUtils.isUnset(client_script)) { - throw new SliderException("No valid CLIENT component found. Aborting install."); - } + if (SliderUtils.isUnset(clientScript)) { + log.info("Installing CLIENT without script"); + List<Package> packages = metaInfo.getApplication().getPackages(); + if (packages.size() > 0) { + // retrieve package resources from HDFS and extract + for (Package pkg : packages) { + Path pkgPath = fileSystem.buildResourcePath(pkg.getName()); + if (!fileSystem.isFile(pkgPath) && name != null) { + pkgPath = fileSystem.buildResourcePath(name, pkg.getName()); + } + if (!fileSystem.isFile(pkgPath)) { + throw new IOException("Package doesn't exist as a resource: " + + pkg.getName()); + } + if ("archive".equals(pkg.getType())) { + File pkgFile = new File(tmpDir, pkg.getName()); + fileSystem.copyHdfsFileToLocal(pkgPath, pkgFile); + expandTar(pkgFile, clientInstallPath); + } else { + File pkgFile = new File(clientInstallPath, pkg.getName()); + fileSystem.copyHdfsFileToLocal(pkgPath, pkgFile); + } + } + } else { + // extract tarball from app def + for (OSSpecific osSpecific : metaInfo.getApplication() + .getOSSpecifics()) { + for (OSPackage pkg : osSpecific.getPackages()) { + if ("tarball".equals(pkg.getType())) { + File pkgFile = new File(appPkgDir, pkg.getName()); + expandTar(pkgFile, clientInstallPath); + } + } + } + } + if (name == null) { + log.warn("Conf files not being generated because no app name was " + + "provided"); + return; + } + File confInstallDir; + String clientRoot = null; + if (config != null) { + try { + clientRoot = config.getJSONObject("global") + .getString(AgentKeys.APP_CLIENT_ROOT); + } catch (JSONException e) { + log.info("Couldn't read {} from provided client config, falling " + + "back on default", AgentKeys.APP_CLIENT_ROOT); + } + } + if (clientRoot == null && defaultConfig != null) { + try { + clientRoot = defaultConfig.getJSONObject("global") + .getString(AgentKeys.APP_CLIENT_ROOT); + } catch (JSONException e) { + log.info("Couldn't read {} from default client config, using {}", + AgentKeys.APP_CLIENT_ROOT, clientInstallPath); + } + } + if (clientRoot == null) { + confInstallDir = clientInstallPath; + } else { + confInstallDir = new File(new File(clientInstallPath, clientRoot), "conf"); + if (!confInstallDir.exists()) { + confInstallDir.mkdirs(); + } + } + String user = RegistryUtils.currentUser(); + for (ConfigFile configFile : metaInfo.getComponentConfigFiles(clientComponent)) { + retrieveConfigFile(rops, configuration, configFile, name, user, + confInstallDir); + } + } else { + log.info("Installing CLIENT using script {}", clientScript); + expandAgentTar(agentPkgDir); + + JSONObject commandJson = getCommandJson(defaultConfig, config, metaInfo, clientInstallPath, name); + FileWriter file = new FileWriter(new File(cmdDir, "command.json")); + try { + file.write(commandJson.toString()); + + } catch (IOException e) { + log.error("Couldn't write command.json to file"); + } finally { + file.flush(); + file.close(); + } - runCommand(appPkgDir, agentPkgDir, cmdDir, client_script); + runCommand(appPkgDir, agentPkgDir, cmdDir, clientScript); + } } catch (IOException ioex) { log.warn("Error while executing INSTALL command {}", ioex.getMessage()); @@ -481,6 +552,11 @@ public class AgentClientProvider extends AbstractClientProvider String libDirProp = System.getProperty(SliderKeys.PROPERTY_LIB_DIR); File tarFile = new File(libDirProp, SliderKeys.AGENT_TAR); + expandTar(tarFile, agentPkgDir); + } + + private void expandTar(File tarFile, File destDir) throws IOException { + log.info("Expanding tar {} to {}", tarFile, destDir); TarArchiveInputStream tarIn = new TarArchiveInputStream( new GzipCompressorInputStream( new BufferedInputStream( @@ -491,11 +567,14 @@ public class AgentClientProvider extends AbstractClientProvider try { TarArchiveEntry tarEntry = tarIn.getNextTarEntry(); while (tarEntry != null) { - File destPath = new File(agentPkgDir, tarEntry.getName()); + File destPath = new File(destDir, tarEntry.getName()); + File parent = destPath.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } if (tarEntry.isDirectory()) { destPath.mkdirs(); } else { - destPath.createNewFile(); byte[] byteToRead = new byte[1024]; BufferedOutputStream buffOut = new BufferedOutputStream(new FileOutputStream(destPath)); @@ -508,6 +587,9 @@ public class AgentClientProvider extends AbstractClientProvider buffOut.close(); } } + if ((tarEntry.getMode() & 0100) != 0) { + destPath.setExecutable(true); + } tarEntry = tarIn.getNextTarEntry(); } } finally { @@ -515,6 +597,17 @@ public class AgentClientProvider extends AbstractClientProvider } } + private void retrieveConfigFile(RegistryOperations rops, + Configuration configuration, ConfigFile configFile, String name, + String user, File destDir) throws IOException, SliderException { + log.info("Retrieving config {} to {}", configFile.getDictionaryName(), + destDir); + PublishedConfiguration published = ClientUtils.getConfigFromRegistry(rops, + configuration, configFile.getDictionaryName(), name, user, true); + ClientUtils.saveOrReturnConfig(published, configFile.getType(), + destDir, configFile.getFileName()); + } + protected JSONObject getCommandJson(JSONObject defaultConfig, JSONObject inputConfig, Metainfo metainfo, http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java index b027939..01a3f1a 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/AgentKeys.java @@ -48,6 +48,7 @@ public interface AgentKeys { */ String APP_HOME = "app.home"; String APP_ROOT = "site.global.app_root"; + String APP_CLIENT_ROOT = "client_root"; /** * Runas user of the application */ @@ -77,11 +78,16 @@ public interface AgentKeys { String APP_RESOURCES = "application.resources"; String APP_RESOURCES_DIR = "app/resources"; + String APP_CONF_DIR = "app/conf"; + String AGENT_INSTALL_DIR = "infra/agent"; String APP_DEFINITION_DIR = "app/definition"; String ADDON_DEFINITION_DIR = "addon/definition"; String AGENT_CONFIG_FILE = "infra/conf/agent.ini"; String AGENT_VERSION_FILE = "infra/version"; + String APP_PACKAGES_DIR = "app/packages"; + String PER_COMPONENT = "per.component"; + String PER_GROUP = "per.group"; String JAVA_HOME = "java_home"; String PACKAGE_LIST = "package_list"; @@ -97,6 +103,7 @@ public interface AgentKeys { String CERT_FILE_LOCALIZATION_PATH = INFRA_RUN_SECURITY_DIR + "ca.crt"; String KEY_CONTAINER_LAUNCH_DELAY = "container.launch.delay.sec"; String TEST_RELAX_VERIFICATION = "test.relax.validation"; + String AM_CONFIG_GENERATION = "am.config.generation"; } http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/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 f20757a..4ffae7c 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 @@ -28,6 +28,7 @@ import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; +import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.registry.client.binding.RegistryPathUtils; import org.apache.hadoop.registry.client.types.Endpoint; import org.apache.hadoop.registry.client.types.ProtocolTypes; @@ -60,8 +61,11 @@ import org.apache.slider.core.exceptions.NoSuchNodeException; import org.apache.slider.core.exceptions.SliderException; import org.apache.slider.core.launch.CommandLineBuilder; import org.apache.slider.core.launch.ContainerLauncher; +import org.apache.slider.core.registry.docstore.ConfigFormat; +import org.apache.slider.core.registry.docstore.ConfigUtils; import org.apache.slider.core.registry.docstore.ExportEntry; import org.apache.slider.core.registry.docstore.PublishedConfiguration; +import org.apache.slider.core.registry.docstore.PublishedConfigurationOutputter; import org.apache.slider.core.registry.docstore.PublishedExports; import org.apache.slider.core.registry.info.CustomRegistryConstants; import org.apache.slider.providers.AbstractProviderService; @@ -124,7 +128,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Map.Entry; import java.util.Scanner; import java.util.Set; import java.util.TreeMap; @@ -132,7 +135,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Pattern; import static org.apache.slider.server.appmaster.web.rest.RestPaths.SLIDER_PATH_AGENTS; @@ -169,6 +171,7 @@ public class AgentProviderService extends AbstractProviderService implements private AgentClientProvider clientProvider; private AtomicInteger taskId = new AtomicInteger(0); private volatile Metainfo metaInfo = null; + private SliderFileSystem fileSystem = null; private Map<String, DefaultConfig> defaultConfigs = null; private ComponentCommandOrder commandOrder = null; private HeartbeatMonitor monitor; @@ -281,6 +284,7 @@ public class AgentProviderService extends AbstractProviderService implements if (metaInfo == null) { synchronized (syncLock) { if (metaInfo == null) { + this.fileSystem = fileSystem; readAndSetHeartbeatMonitoringInterval(instanceDefinition); initializeAgentDebugCommands(instanceDefinition); @@ -435,6 +439,26 @@ public class AgentProviderService extends AbstractProviderService implements LocalResourceType.ARCHIVE); launcher.addLocalResource(AgentKeys.APP_DEFINITION_DIR, appDefRes); + for (Package pkg : getMetaInfo().getApplication().getPackages()) { + Path pkgPath = fileSystem.buildResourcePath(pkg.getName()); + if (!fileSystem.isFile(pkgPath)) { + pkgPath = fileSystem.buildResourcePath(getClusterName(), + pkg.getName()); + } + if (!fileSystem.isFile(pkgPath)) { + throw new IOException("Package doesn't exist as a resource: " + + pkg.getName()); + } + log.info("Adding resource {}", pkg.getName()); + LocalResourceType type = LocalResourceType.FILE; + if ("archive".equals(pkg.getType())) { + type = LocalResourceType.ARCHIVE; + } + LocalResource packageResource = fileSystem.createAmResource( + pkgPath, type); + launcher.addLocalResource(AgentKeys.APP_PACKAGES_DIR, packageResource); + } + String agentConf = instanceDefinition.getAppConfOperations(). getGlobalOptions().getOption(AgentKeys.AGENT_CONF, ""); if (SliderUtils.isSet(agentConf)) { @@ -476,6 +500,15 @@ public class AgentProviderService extends AbstractProviderService implements generatedConfPath, SliderKeys.PROPAGATED_CONF_DIR_NAME)); + if (appComponent.getOptionBool(AgentKeys.AM_CONFIG_GENERATION, false)) { + // build and localize configuration files + Map<String, Map<String, String>> configurations = + buildCommandConfigurations(instanceDefinition.getAppConfOperations(), + container.getId().toString(), roleName, roleGroup); + localizeConfigFiles(launcher, roleName, roleGroup, getMetaInfo(), + configurations, launcher.getEnv(), fileSystem); + } + String label = getContainerLabel(container, roleName, roleGroup); CommandLineBuilder operation = new CommandLineBuilder(); @@ -646,19 +679,41 @@ public class AgentProviderService extends AbstractProviderService implements private Path uploadSecurityResource(File resource, SliderFileSystem fileSystem) throws IOException { Path certsDir = fileSystem.buildClusterSecurityDirPath(getClusterName()); - if (!fileSystem.getFileSystem().exists(certsDir)) { - fileSystem.getFileSystem().mkdirs(certsDir, + return uploadResource(resource, fileSystem, certsDir); + } + + private Path uploadResource(File resource, SliderFileSystem fileSystem, + String roleName) throws IOException { + Path dir; + if (roleName == null) { + dir = fileSystem.buildClusterResourcePath(getClusterName()); + } else { + dir = fileSystem.buildClusterResourcePath(getClusterName(), roleName); + } + return uploadResource(resource, fileSystem, dir); + } + + 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)); } - Path destPath = new Path(certsDir, resource.getName()); + Path destPath = new Path(parentDir, resource.getName()); if (!fileSystem.getFileSystem().exists(destPath)) { - FSDataOutputStream os = fileSystem.getFileSystem().create(destPath); - byte[] contents = FileUtils.readFileToByteArray(resource); - os.write(contents, 0, contents.length); - - os.flush(); - os.close(); + FSDataOutputStream os = null; + try { + os = fileSystem.getFileSystem().create(destPath); + byte[] contents = FileUtils.readFileToByteArray(resource); + os.write(contents, 0, contents.length); + os.flush(); + } finally { + IOUtils.closeStream(os); + } log.info("Uploaded {} to localization path {}", resource, destPath); + } else { + log.info("Resource {} already existed at localization path {}", resource, + destPath); } while (!fileSystem.getFileSystem().exists(destPath)) { @@ -718,6 +773,69 @@ public class AgentProviderService extends AbstractProviderService implements } } + private void createConfigFile(SliderFileSystem fileSystem, File file, + 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, + publishedConfiguration); + configurationOutputter.save(file); + } + + @VisibleForTesting + protected void localizeConfigFiles(ContainerLauncher launcher, + String roleName, String roleGroup, + Metainfo metainfo, + Map<String, Map<String, String>> configs, + MapOperations env, + SliderFileSystem fileSystem) + throws IOException { + for (ConfigFile configFile : metainfo.getComponentConfigFiles(roleGroup)) { + Map<String, String> config = ConfigUtils.replacePropsInConfig( + configs.get(configFile.getDictionaryName()), env.options); + String fileName = ConfigUtils.replaceProps(config, + configFile.getFileName()); + File localFile = new File(SliderKeys.RESOURCE_DIR); + if (!localFile.exists()) { + localFile.mkdir(); + } + localFile = new File(localFile, new File(fileName).getName()); + + String folder = null; + if ("true".equals(config.get(PER_COMPONENT))) { + folder = roleName; + } else if ("true".equals(config.get(PER_GROUP))) { + folder = roleGroup; + } + + 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); + + File destFile = new File(fileName); + if (destFile.isAbsolute()) { + launcher.addLocalResource( + SliderKeys.RESOURCE_DIR + "/" + destFile.getName(), + configResource, fileName); + } else { + launcher.addLocalResource(AgentKeys.APP_CONF_DIR + "/" + fileName, + configResource); + } + } + } + /** * build the zookeeper registry path. * @@ -1242,6 +1360,69 @@ 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.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 = getAmState().getAppConfSnapshot(); + MapOperations clientOperations = appConf.getOrAddComponent(client.getName()); + appConf.resolve(); + 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); + log.info("Publishing AM configuration {}", configFile.getDictionaryName()); + } } @Override @@ -1585,7 +1766,9 @@ public class AgentProviderService extends AbstractProviderService implements if (status.getConfigs() != null) { Application application = getMetaInfo().getApplication(); - if (canAnyMasterPublishConfig() == false || canPublishConfig(componentGroup)) { + if ((!canAnyMasterPublishConfig() || canPublishConfig(componentGroup)) && + !getAmState().getAppConfSnapshot().getComponentOptBool( + componentGroup, AgentKeys.AM_CONFIG_GENERATION, false)) { // If no Master can explicitly publish then publish if its a master // Otherwise, wait till the master that can publish is ready @@ -1709,7 +1892,11 @@ public class AgentProviderService extends AbstractProviderService implements simpleEntries.put(entry.getKey(), entry.getValue().get(0).getValue()); } } - publishApplicationInstanceData(groupName, groupName, simpleEntries.entrySet()); + if (!getAmState().getAppConfSnapshot().getComponentOptBool( + groupName, AgentKeys.AM_CONFIG_GENERATION, false)) { + publishApplicationInstanceData(groupName, groupName, + simpleEntries.entrySet()); + } PublishedExports exports = new PublishedExports(groupName); exports.setUpdated(new Date().getTime()); @@ -2036,7 +2223,7 @@ public class AgentProviderService extends AbstractProviderService implements cmd.setConfigurations(configurations); Map<String, Map<String, String>> componentConfigurations = buildComponentConfigurations(appConf); cmd.setComponentConfigurations(componentConfigurations); - + if (SliderUtils.isSet(scriptPath)) { cmd.setCommandParams(commandParametersSet(scriptPath, timeout, false)); } else { @@ -2154,10 +2341,10 @@ public class AgentProviderService extends AbstractProviderService implements List<String> packages = new ArrayList<>(); if (application != null) { if (application.getPackages().size() > 0) { - List<Package> appPackages = application.getPackages(); - for (Package appPackage : appPackages) { - packages.add(String.format(pkgFormatString, appPackage.getType(), appPackage.getName())); - } + // no-op if there are packages that are not OS-specific, as these + // will be localized by AM rather than the Agent + // this should be backwards compatible, as there was previously an + // XML parsing bug that ensured non-OS-specific packages did not exist } else { List<OSSpecific> osSpecifics = application.getOSSpecifics(); if (osSpecifics != null && osSpecifics.size() > 0) { @@ -2821,14 +3008,41 @@ public class AgentProviderService extends AbstractProviderService implements } } + boolean finished = false; + while (!finished) { + finished = true; + for (Map.Entry<String, String> entry : allConfigs.entrySet()) { + String configValue = entry.getValue(); + for (Map.Entry<String, String> lookUpEntry : allConfigs.entrySet()) { + String lookUpValue = lookUpEntry.getValue(); + if (lookUpValue.contains("${@//site/")) { + continue; + } + String lookUpKey = lookUpEntry.getKey(); + if (configValue != null && configValue.contains(lookUpKey)) { + configValue = configValue.replace(lookUpKey, lookUpValue); + } + } + if (!configValue.equals(entry.getValue())) { + finished = false; + allConfigs.put(entry.getKey(), configValue); + } + } + } + for (String configType : configurations.keySet()) { Map<String, String> configBucket = configurations.get(configType); for (Map.Entry<String, String> entry: configBucket.entrySet()) { String configName = entry.getKey(); String configValue = entry.getValue(); - for (String lookUpKey : allConfigs.keySet()) { + for (Map.Entry<String, String> lookUpEntry : allConfigs.entrySet()) { + String lookUpValue = lookUpEntry.getValue(); + if (lookUpValue.contains("${@//site/")) { + continue; + } + String lookUpKey = lookUpEntry.getKey(); if (configValue != null && configValue.contains(lookUpKey)) { - configValue = configValue.replace(lookUpKey, allConfigs.get(lookUpKey)); + configValue = configValue.replace(lookUpKey, lookUpValue); } } configBucket.put(configName, configValue); @@ -2974,6 +3188,7 @@ public class AgentProviderService extends AbstractProviderService implements config.put("app_log_dir", "${AGENT_LOG_ROOT}"); config.put("app_pid_dir", "${AGENT_WORK_ROOT}/app/run"); config.put("app_install_dir", "${AGENT_WORK_ROOT}/app/install"); + config.put("app_conf_dir", "${AGENT_WORK_ROOT}/" + AgentKeys.APP_CONF_DIR); config.put("app_input_conf_dir", "${AGENT_WORK_ROOT}/" + SliderKeys.PROPAGATED_CONF_DIR_NAME); config.put("app_container_id", containerId); config.put("app_container_tag", tags.getTag(roleName, containerId)); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java index 1b63b58..b6ae4de 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/AbstractComponent.java @@ -65,6 +65,10 @@ public abstract class AbstractComponent implements Validate { this.commands = commands; } + public void addCommand(ComponentCommand command) { + commands.add(command); + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("{"); http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java index 63546a4..5556c7f 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Application.java @@ -116,6 +116,10 @@ public class Application extends AbstractMetainfoSchema { return commandOrders; } + public void addPackage(Package pkg) { + packages.add(pkg); + } + @JsonProperty("packages") public List<Package> getPackages() { return packages; http://git-wip-us.apache.org/repos/asf/incubator-slider/blob/424b2a40/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java ---------------------------------------------------------------------- diff --git a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java index 3f23455..78bb8c1 100644 --- a/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java +++ b/slider-core/src/main/java/org/apache/slider/providers/agent/application/metadata/Component.java @@ -38,7 +38,8 @@ public class Component extends AbstractComponent { String type = TYPE_STANDARD; List<ComponentExport> componentExports = new ArrayList<>(); List<DockerContainer> dockerContainers = new ArrayList<>(); - + List<ConfigFile> configFiles = new ArrayList<>(); + public Component() { } @@ -155,6 +156,15 @@ public class Component extends AbstractComponent { return Boolean.parseBoolean(this.autoStartOnFailure); } + public void addConfigFile(ConfigFile configFile) { + this.configFiles.add(configFile); + } + + @JsonProperty("configFiles") + public List<ConfigFile> getConfigFiles() { + return configFiles; + } + @Override public String toString() { final StringBuilder sb =