package io.lumify.yarn; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import org.apache.hadoop.fs.*; import org.apache.hadoop.yarn.api.ApplicationConstants; import org.apache.hadoop.yarn.api.records.*; import org.apache.hadoop.yarn.client.api.AMRMClient; import org.apache.hadoop.yarn.client.api.NMClient; import org.apache.hadoop.yarn.client.api.async.AMRMClientAsync; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.util.ConverterUtils; import org.apache.hadoop.yarn.util.Records; import java.io.IOException; import java.util.*; public abstract class ApplicationMasterBase implements AMRMClientAsync.CallbackHandler { @Parameter(names = {"-memory", "-mem"}, description = "Memory for each process in MB.") private int memory = 512; @Parameter(names = {"-cores"}, description = "Number of virtual cores each process uses.") private int virtualCores = 1; @Parameter(names = {"-instances", "-i"}, description = "Number of instances to start.") private int instances = 1; @Parameter(names = {"-appname"}, description = "App name.") private String appName = null; @Parameter(names = {"-remotepath"}, description = "Path to the remote files.") private String remotePath = null; private NMClient nmClient; private FileSystem fs; private List<Path> resources; private String classPathEnv; private int numContainersToWaitFor; private Priority priority; private Resource capability; private AMRMClientAsync<AMRMClient.ContainerRequest> rmClient; protected void run(String[] args) throws Exception { System.out.println("BEGIN " + this.getClass().getName()); new JCommander(this, args); System.out.println("memory: " + memory); System.out.println("virtualCores: " + virtualCores); System.out.println("instances: " + instances); System.out.println("appName: " + appName); System.out.println("remotePath: " + remotePath); if (remotePath == null) { throw new Exception("remotePath is required"); } ClientBase.printEnv(); final String myClasspath = System.getProperty("java.class.path"); final YarnConfiguration conf = new YarnConfiguration(); fs = FileSystem.get(conf); resources = getResourceList(fs, new Path(remotePath)); final StringBuilder classPathEnvBuilder = new StringBuilder(myClasspath); for (Path p : resources) { classPathEnvBuilder.append(':'); classPathEnvBuilder.append(p.getName()); } System.out.println("Classpath: " + classPathEnvBuilder); classPathEnv = classPathEnvBuilder.toString(); nmClient = createNodeManagerClient(conf); rmClient = createResourceManagerClient(conf); rmClient.registerApplicationMaster("", 0, ""); makeContainerRequests(); System.out.println("[AM] waiting for containers to finish"); while (!doneWithContainers()) { Thread.sleep(100); } rmClient.unregisterApplicationMaster(FinalApplicationStatus.SUCCEEDED, "", ""); } private boolean doneWithContainers() { return numContainersToWaitFor == 0; } private List<Path> getResourceList(FileSystem fs, Path remotePath) throws IOException { List<Path> resources = new ArrayList<>(); RemoteIterator<LocatedFileStatus> files = fs.listFiles(remotePath, false); while (files.hasNext()) { LocatedFileStatus file = files.next(); System.out.println("Adding local resource: " + file.getPath().toString()); resources.add(file.getPath()); } return resources; } private Map<String, LocalResource> createLocalResources(FileSystem fs, List<Path> resources) throws IOException { Map<String, LocalResource> localResources = new HashMap<>(); for (Path p : resources) { FileStatus fileStatus = fs.getFileStatus(p); LocalResource rsc = LocalResource.newInstance(ConverterUtils.getYarnUrlFromURI(p.toUri()), LocalResourceType.FILE, LocalResourceVisibility.APPLICATION, fileStatus.getLen(), fileStatus.getModificationTime()); localResources.put(p.getName(), rsc); } return localResources; } private void makeContainerRequests() { numContainersToWaitFor = instances; for (int i = 0; i < instances; ++i) { System.out.println("Making res-req " + i); makeContainerRequest(); } } private void makeContainerRequest() { ensureCreatePriorityRecord(); ensureCreateResourceRecord(); AMRMClient.ContainerRequest containerAsk = new AMRMClient.ContainerRequest(capability, null, null, priority); rmClient.addContainerRequest(containerAsk); } private void ensureCreateResourceRecord() { if (capability == null) { capability = Records.newRecord(Resource.class); capability.setMemory(memory); capability.setVirtualCores(virtualCores); } } private void ensureCreatePriorityRecord() { if (priority == null) { priority = Records.newRecord(Priority.class); priority.setPriority(0); } } private NMClient createNodeManagerClient(YarnConfiguration conf) { NMClient nmClient = NMClient.createNMClient(); nmClient.init(conf); nmClient.start(); return nmClient; } private AMRMClientAsync<AMRMClient.ContainerRequest> createResourceManagerClient(YarnConfiguration conf) throws IOException, YarnException { AMRMClientAsync<AMRMClient.ContainerRequest> rmClient = AMRMClientAsync.createAMRMClientAsync(100, this); rmClient.init(conf); rmClient.start(); return rmClient; } @Override public void onContainersCompleted(List<ContainerStatus> statuses) { for (ContainerStatus status : statuses) { int exitStatus = status.getExitStatus(); System.out.println("[AM] Completed container " + status.getContainerId() + " (return code: " + exitStatus + ")"); if (exitStatus != ContainerExitStatus.SUCCESS) { System.out.println("[AM] Restarting failed process (return code: " + exitStatus + ")"); makeContainerRequest(); } else { synchronized (this) { numContainersToWaitFor--; } } } } @Override public void onContainersAllocated(List<Container> containers) { for (Container container : containers) { try { launchContainer(container); } catch (Exception ex) { System.err.println("[AM] Error launching container " + container.getId() + " " + ex); } } } private void launchContainer(Container container) throws YarnException, IOException { ContainerLaunchContext ctx = Records.newRecord(ContainerLaunchContext.class); Map<String, LocalResource> localResources = createLocalResources(fs, resources); ctx.setLocalResources(localResources); String command = "${JAVA_HOME}/bin/java" + " -Xmx" + memory + "M" + " -Djava.net.preferIPv4Stack=true" + " -cp " + classPathEnv + " " + getTaskClass().getName() + " 1>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stdout" + " 2>" + ApplicationConstants.LOG_DIR_EXPANSION_VAR + "/stderr"; System.out.println("Running: " + command); ctx.setCommands(Collections.singletonList(command)); ctx.getEnvironment().putAll(System.getenv()); System.out.println("Launching container " + container.getId()); nmClient.startContainer(container, ctx); } protected abstract Class getTaskClass(); @Override public void onShutdownRequest() { } @Override public void onNodesUpdated(List<NodeReport> updatedNodes) { } @Override public float getProgress() { return 0; } @Override public void onError(Throwable e) { System.out.println("[AM] error " + e); } }