// Copyright 2016 Twitter. All rights reserved.
//
// Licensed 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 com.twitter.heron.scheduler.mesos;
import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;
import com.twitter.heron.common.basics.FileUtils;
import com.twitter.heron.common.basics.SysUtils;
import com.twitter.heron.scheduler.utils.Runtime;
import com.twitter.heron.scheduler.utils.SchedulerUtils;
import com.twitter.heron.spi.common.Config;
import com.twitter.heron.spi.common.Context;
import com.twitter.heron.spi.common.Key;
import com.twitter.heron.spi.packing.PackingPlan;
import com.twitter.heron.spi.scheduler.ILauncher;
import com.twitter.heron.spi.utils.ShellUtils;
/**
* Launch a topology to mesos cluster
*/
public class MesosLauncher implements ILauncher {
private static final Logger LOG = Logger.getLogger(MesosLauncher.class.getName());
private Config config;
private Config runtime;
private String schedulerWorkingDirectory;
@Override
public void initialize(Config mConfig, Config mRuntime) {
this.config = mConfig;
this.runtime = mRuntime;
// get the scheduler working directory
this.schedulerWorkingDirectory = MesosContext.getSchedulerWorkingDirectory(config);
}
@Override
public void close() {
}
@Override
public boolean launch(PackingPlan packing) {
// setup the scheduler working directory
// mainly it downloads and extracts the topology package
if (!setupWorkingDirectory()) {
LOG.severe("Failed to setup working directory");
return false;
}
String[] schedulerCmd = getSchedulerCommand();
Process p = startScheduler(schedulerCmd);
if (p == null) {
LOG.severe("Failed to start SchedulerMain using: " + Arrays.toString(schedulerCmd));
return false;
}
LOG.info(String.format(
"For checking the status and logs of the topology, use the working directory %s",
MesosContext.getSchedulerWorkingDirectory(config)));
return true;
}
protected String[] getSchedulerCommand() {
List<Integer> freePorts = new ArrayList<>(SchedulerUtils.PORTS_REQUIRED_FOR_SCHEDULER);
for (int i = 0; i < SchedulerUtils.PORTS_REQUIRED_FOR_SCHEDULER; i++) {
freePorts.add(SysUtils.getFreePort());
}
List<String> commands = new ArrayList<>();
// The java executable should be "{JAVA_HOME}/bin/java"
String javaExecutable = String.format("%s/%s", Context.javaHome(config), "bin/java");
commands.add(javaExecutable);
// Add the path to load mesos library
commands.add(String.format("-Djava.library.path=%s",
MesosContext.getHeronMesosNativeLibraryPath(config)));
commands.add("-cp");
// Construct the complete classpath to start scheduler
String completeSchedulerProcessClassPath = System.getProperty("java.class.path");
commands.add(completeSchedulerProcessClassPath);
commands.add("com.twitter.heron.scheduler.SchedulerMain");
// Add default scheduler properties override
// Add the topology package uri as a String and pass the SchedulerMain as a property
commands.add(String.format("-%s%s=%s",
SchedulerUtils.SCHEDULER_COMMAND_LINE_PROPERTIES_OVERRIDE_OPTION,
Key.TOPOLOGY_PACKAGE_URI.value(),
Runtime.topologyPackageUri(runtime).toString()));
String[] commandArgs = schedulerCommandArgs(freePorts);
commands.addAll(Arrays.asList(commandArgs));
return commands.toArray(new String[0]);
}
///////////////////////////////////////////////////////////////////////////////
// Utils methods leveraging unit tests
///////////////////////////////////////////////////////////////////////////////
protected boolean setupWorkingDirectory() {
if (!FileUtils.isDirectoryExists(schedulerWorkingDirectory)) {
FileUtils.createDirectory(schedulerWorkingDirectory);
}
String topologyPackageURL = String.format("file://%s", Context.topologyPackageFile(config));
String topologyPackageDestination = Paths.get(
schedulerWorkingDirectory, "topology.tar.gz").toString();
return SchedulerUtils.curlAndExtractPackage(
schedulerWorkingDirectory, topologyPackageURL,
topologyPackageDestination, true, Context.verbose(config));
}
protected String[] schedulerCommandArgs(List<Integer> freePorts) {
return SchedulerUtils.schedulerCommandArgs(config, runtime, freePorts);
}
protected Process startScheduler(String[] schedulerCmd) {
return ShellUtils.runASyncProcess(Context.verbose(config), schedulerCmd,
new File(schedulerWorkingDirectory));
}
}