blob: 6c2287478b793b240a6e965e768d704579b5d7e5 [file] [log] [blame]
/*
* 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.drill.yarn.core;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.yarn.api.ApplicationConstants;
import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.api.records.LocalResource;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.util.Records;
/**
* Abstract description of a remote process launch that describes the many
* details needed to launch a process on a remote node.
* <p>
* Based on <a href="https://github.com/hortonworks/simple-yarn-app">Simple YARN
* App</a>.
*/
public class LaunchSpec {
/**
* List of (key, file) pairs to be localized to the node before running the
* command. The file must exist in a distributed file system (such as HDFS)
* visible to both the client and remote node. Typically, the path is relative
* or absolute within the file system defined by the fs.defaultFS parameter in
* core-site.xml.
* <p>
* TODO: Can the value also be a URL such as
* <p>
* <code>hdfs://somehost:1234//path/to/file
* <p>
* The key is used as (what?).
*/
public Map<String, LocalResource> resources = new HashMap<>();
/**
* Defines environment variables to be set on the remote host before launching
* the remote app. Note: do not set CLASSPATH here; use {@link #classPath}
* instead.
*/
public Map<String, String> env = new HashMap<>();
/**
* Set to the name of the OS command to run when we wish to run a non-Java
* command.
*/
public String command;
/**
* Set to the name of the Java main class (the one with the main method) when
* we wish to run a Java command.
*/
public String mainClass;
/**
* Set to the application-specific class path for the Java application. These
* values are added to the Hadoop-provided values. These items are relative to
* (what?), use (what variables) to refer to the localized application
* directory.
*/
public List<String> classPath = new ArrayList<>();
/**
* Optional VM arguments to pass to the JVM when running a Java class; ignored
* when running an OS command.
*/
public List<String> vmArgs = new ArrayList<>();
/**
* Arguments to the remote command.
*/
public List<String> cmdArgs = new ArrayList<>();
public LaunchSpec() {
}
/**
* Create the command line to run on the remote node. The command can either
* be a simple OS command (if the {@link #command} member is set) or can be a
* Java class (if the {@link #mainClass} member is set. If the command is
* Java, then we pass along optional Java VM arguments.
* <p>
* In all cases we append arguments to the command itself, and redirect stdout
* and stderr to log files.
*
* @return the complete command string
*/
public String getCommand() {
List<String> cmd = new ArrayList<>();
if (command != null) {
cmd.add(command);
} else {
assert mainClass != null;
// JAVA_HOME is provided by YARN.
cmd.add(Environment.JAVA_HOME.$$() + "/bin/java");
cmd.addAll(vmArgs);
if (!classPath.isEmpty()) {
cmd.add("-cp");
cmd.add(DoYUtil.join(":", classPath));
}
cmd.add(mainClass);
}
cmd.addAll(cmdArgs);
cmd.add("1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stdout");
cmd.add("2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stderr");
// Java 8
// return String.join( " ", cmd );
return DoYUtil.join(" ", cmd);
}
/**
* Given this generic description of an application, create the detailed YARN
* application submission context required to launch the application.
*
* @param conf
* the YARN configuration obtained by reading the Hadoop
* configuration files
* @return the completed application launch context for the given application
* @throws IOException
* if localized resources are not found in the distributed file
* system (such as HDFS)
*/
public ContainerLaunchContext createLaunchContext(YarnConfiguration conf)
throws IOException {
// Set up the container launch context
ContainerLaunchContext container = Records
.newRecord(ContainerLaunchContext.class);
// Set up the list of commands to run. Here, we assume that we run only
// one command.
container.setCommands(Collections.singletonList(getCommand()));
// Add localized resources
container.setLocalResources(resources);
// Environment.
container.setEnvironment(env);
return container;
}
public void dump(PrintStream out) {
if (command != null) {
out.print("Command: ");
out.println(command);
}
if (mainClass != null) {
out.print("Main Class: ");
out.println(mainClass);
out.println("VM Args:");
if (vmArgs.isEmpty()) {
out.println(" None");
} else {
for (String item : vmArgs) {
out.print(" ");
out.println(item);
}
}
out.println("Class Path:");
if (classPath.isEmpty()) {
out.println(" None");
} else {
for (String item : classPath) {
out.print(" ");
out.println(item);
}
}
}
out.println("Program Args:");
if (cmdArgs.isEmpty()) {
out.println(" None");
} else {
for (String item : cmdArgs) {
out.print(" ");
out.println(item);
}
}
out.println("Environment:");
if (env.isEmpty()) {
out.println(" None");
} else {
for (String key : env.keySet()) {
out.print(" ");
out.print(key);
out.print("=");
out.println(env.get(key));
}
}
out.println("Resources: ");
if (resources.isEmpty()) {
out.println(" None");
} else {
for (String key : resources.keySet()) {
out.print(" Key: ");
out.println(key);
LocalResource resource = resources.get(key);
out.print(" URL: ");
out.println(resource.getResource().toString());
out.print(" Size: ");
out.println(resource.getSize());
out.print(" Timestamp: ");
out.println(DoYUtil.toIsoTime(resource.getTimestamp()));
out.print(" Type: ");
out.println(resource.getType().toString());
out.print(" Visiblity: ");
out.println(resource.getVisibility().toString());
}
}
}
}