package mil.nga.giat.geowave.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.apache.commons.io.IOUtils;
import org.codehaus.plexus.archiver.tar.TarGZipUnArchiver;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.slf4j.LoggerFactory;
public class BigtableEmulator
{
private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BigtableEmulator.class);
// these need to move to config
private final static String GCLOUD_URL = "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/";
private final static String GCLOUD_TAR = "google-cloud-sdk-136.0.0-linux-x86_64.tar.gz";
private final static String GCLOUD_EXE = "google-cloud-sdk/bin/gcloud";
private static final String HOST_PORT = "localhost:8128";
private static final long EMULATOR_SPINUP_DELAY_MS = 30000L;
private final File sdkDir;
private ExecuteWatchdog watchdog;
public BigtableEmulator(
String sdkDir ) {
if (TestUtils.isSet(sdkDir)) {
this.sdkDir = new File(
sdkDir);
}
else {
this.sdkDir = new File(
TestUtils.TEMP_DIR,
"gcloud");
}
if (!this.sdkDir.exists() && !this.sdkDir.mkdirs()) {
LOGGER.warn("unable to create directory " + this.sdkDir.getAbsolutePath());
}
}
public boolean start() {
if (!isInstalled()) {
try {
if (!install()) {
return false;
}
}
catch (IOException e) {
LOGGER.error(e.getMessage());
return false;
}
}
try {
startEmulator();
}
catch (IOException | InterruptedException e) {
LOGGER.error(e.getMessage());
return false;
}
return true;
}
public boolean isRunning() {
return (watchdog != null && watchdog.isWatching());
}
public void stop() {
// first, ask the watchdog nicely:
watchdog.destroyProcess();
// then kill all the extra emulator processes like this:
final String KILL_CMD_1 = "for i in $(ps -ef | grep -i \"[b]eta emulators bigtable\" | awk '{print $2}'); do kill -9 $i; done";
final String KILL_CMD_2 = "for i in $(ps -ef | grep -i \"[c]btemulator\" | awk '{print $2}'); do kill -9 $i; done";
File bashFile = new File(
TestUtils.TEMP_DIR,
"kill-bigtable.sh");
PrintWriter scriptWriter;
try {
Writer w = new OutputStreamWriter(
new FileOutputStream(
bashFile),
"UTF-8");
scriptWriter = new PrintWriter(
w);
scriptWriter.println("#!/bin/bash");
scriptWriter.println("set -ev");
scriptWriter.println(KILL_CMD_1);
scriptWriter.println(KILL_CMD_2);
scriptWriter.close();
bashFile.setExecutable(true);
}
catch (FileNotFoundException e1) {
LOGGER.error(
"Unable to create bigtable emulator kill script",
e1);
return;
}
catch (UnsupportedEncodingException e) {
LOGGER.error(
"Unable to create bigtable emulator kill script",
e);
}
CommandLine cmdLine = new CommandLine(
bashFile.getAbsolutePath());
DefaultExecutor executor = new DefaultExecutor();
int exitValue = 0;
try {
exitValue = executor.execute(cmdLine);
}
catch (IOException ex) {
LOGGER.error(
"Unable to execute bigtable emulator kill script",
ex);
}
LOGGER.warn("Bigtable emulator " + (exitValue == 0 ? "stopped" : "failed to stop"));
}
private boolean isInstalled() {
final File gcloudExe = new File(
sdkDir,
GCLOUD_EXE);
return (gcloudExe.canExecute());
}
protected boolean install()
throws IOException {
URL url = new URL(
GCLOUD_URL + GCLOUD_TAR);
final File downloadFile = new File(
sdkDir,
GCLOUD_TAR);
if (!downloadFile.exists()) {
try (FileOutputStream fos = new FileOutputStream(
downloadFile)) {
IOUtils.copyLarge(
url.openStream(),
fos);
fos.flush();
}
}
final TarGZipUnArchiver unarchiver = new TarGZipUnArchiver();
unarchiver.enableLogging(new ConsoleLogger(
Logger.LEVEL_WARN,
"Gcloud SDK Unarchive"));
unarchiver.setSourceFile(downloadFile);
unarchiver.setDestDirectory(sdkDir);
unarchiver.extract();
if (!downloadFile.delete()) {
LOGGER.warn("cannot delete " + downloadFile.getAbsolutePath());
}
// Check the install
if (!isInstalled()) {
LOGGER.error("Gcloud install failed");
return false;
}
// Install the beta components
CommandLine cmdLine = new CommandLine(
sdkDir + "/" + GCLOUD_EXE);
cmdLine.addArgument("components");
cmdLine.addArgument("install");
cmdLine.addArgument("beta");
cmdLine.addArgument("--quiet");
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
LOGGER.warn("KAM >>> gcloud install beta; exit code = " + exitValue);
return (exitValue == 0);
}
/**
* Using apache commons exec for cmd line execution
*
* @param command
* @return exitCode
* @throws ExecuteException
* @throws IOException
* @throws InterruptedException
*/
private void startEmulator()
throws ExecuteException,
IOException,
InterruptedException {
CommandLine cmdLine = new CommandLine(
sdkDir + "/" + GCLOUD_EXE);
cmdLine.addArgument("beta");
cmdLine.addArgument("emulators");
cmdLine.addArgument("bigtable");
cmdLine.addArgument("start");
cmdLine.addArgument("--quiet");
cmdLine.addArgument("--host-port");
cmdLine.addArgument(HOST_PORT);
// Using a result handler makes the emulator run async
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
// watchdog shuts down the emulator, later
watchdog = new ExecuteWatchdog(
ExecuteWatchdog.INFINITE_TIMEOUT);
Executor executor = new DefaultExecutor();
executor.setWatchdog(watchdog);
executor.execute(
cmdLine,
resultHandler);
// we need to wait here for a bit, in case the emulator needs to update
// itself
Thread.sleep(EMULATOR_SPINUP_DELAY_MS);
}
}