/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under 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 com.streamsets.datacollector.util;
import com.streamsets.datacollector.MiniSDC;
import com.streamsets.datacollector.MiniSDCTestingUtility;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.MiniYARNCluster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
public class ClusterUtil {
private static final Logger LOG = LoggerFactory.getLogger(ClusterUtil.class);
private static final String SPARK_TEST_HOME = "SPARK_TEST_HOME";
private static final String SPARK_PROPERTY_FILE = "SPARK_PROPERTY_FILE";
private static MiniSDCTestingUtility miniSDCTestingUtility;
private static URI serverURI;
private static MiniSDC miniSDC;
private static MiniYARNCluster miniYarnCluster;
public static void setupCluster(String testName, String pipelineJson, YarnConfiguration yarnConfiguration) throws Exception {
System.setProperty("sdc.testing-mode", "true");
System.setProperty(MiniSDCTestingUtility.PRESERVE_TEST_DIR, "true");
yarnConfiguration.set("yarn.nodemanager.delete.debug-delay-sec", "600");
miniSDCTestingUtility = new MiniSDCTestingUtility();
File dataTestDir = miniSDCTestingUtility.getDataTestDir();
//copy spark files under the test data directory into a dir called "spark"
File sparkHome = ClusterUtil.createSparkHome(dataTestDir);
//start mini yarn cluster
miniYarnCluster = miniSDCTestingUtility.startMiniYarnCluster(testName, 1, 1, 1, yarnConfiguration);
Configuration config = miniYarnCluster.getConfig();
long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10);
while (config.get(YarnConfiguration.RM_ADDRESS).split(":")[1] == "0") {
if (System.currentTimeMillis() > deadline) {
throw new IllegalStateException("Timed out waiting for RM to come up.");
}
LOG.debug("RM address still not set in configuration, waiting...");
TimeUnit.MILLISECONDS.sleep(100);
}
LOG.debug("RM at " + config.get(YarnConfiguration.RM_ADDRESS));
Properties sparkHadoopProps = new Properties();
for (Map.Entry<String, String> entry : config) {
sparkHadoopProps.setProperty("spark.hadoop." + entry.getKey(), entry.getValue());
}
LOG.debug("Creating spark properties file at " + dataTestDir);
File propertiesFile = new File(dataTestDir, "spark.properties");
propertiesFile.createNewFile();
FileOutputStream sdcOutStream = new FileOutputStream(propertiesFile);
sparkHadoopProps.store(sdcOutStream, null);
sdcOutStream.flush();
sdcOutStream.close();
// Need to pass this property file to spark-submit for it pick up yarn confs
System.setProperty(SPARK_PROPERTY_FILE, propertiesFile.getAbsolutePath());
File sparkBin = new File(sparkHome, "bin");
for (File file : sparkBin.listFiles()) {
MiniSDCTestingUtility.setExecutePermission(file.toPath());
}
miniSDC = miniSDCTestingUtility.createMiniSDC(MiniSDC.ExecutionMode.CLUSTER);
miniSDC.startSDC();
serverURI = miniSDC.getServerURI();
miniSDC.createPipeline(pipelineJson);
miniSDC.startPipeline();
int attempt = 0;
//Hard wait for 2 minutes
while(miniSDC.getListOfSlaveSDCURI().size() == 0 && attempt < 24) {
Thread.sleep(5000);
attempt++;
LOG.debug("Attempt no: " + attempt + " to retrieve list of slaves");
}
if(miniSDC.getListOfSlaveSDCURI().size() == 0) {
throw new IllegalStateException("Timed out waiting for slaves to come up.");
}
}
public static void tearDownCluster(String testName) throws Exception {
if (miniSDCTestingUtility != null) {
miniSDCTestingUtility.stopMiniSDC();
ClusterUtil.killYarnApp(testName);
miniSDCTestingUtility.stopMiniYarnCluster();
miniSDCTestingUtility.cleanupTestDir();
ClusterUtil.cleanUpYarnDirs(testName);
}
}
public static void cleanUpYarnDirs(String testName) throws IOException {
if (!Boolean.getBoolean(MiniSDCTestingUtility.PRESERVE_TEST_DIR)) {
MiniSDCTestingUtility.deleteDir(new File(new File(System.getProperty("user.dir"), "target"), testName));
}
}
public static void killYarnApp(String testName) throws Exception {
// TODO - remove this hack
// We dont know app id, but yarn creates its data dir under $HOME/target/TESTNAME, so kill the process by
// grep for the yarn testname
String killCmd = signalCommand(testName, "SIGKILL");
LOG.info("Signal kill command to yarn app " + killCmd);
String[] killCommand = new String[] { "/usr/bin/env", "bash", "-c", killCmd };
Process p = Runtime.getRuntime().exec(killCommand);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
LOG.info("Process output is ");
while ((line = reader.readLine()) != null) {
LOG.debug(line + "\n");
}
}
public static String findPidCommand(String service) {
return String.format("ps aux | grep %s | grep -v grep | tr -s ' ' | cut -d ' ' -f2", service);
}
public static String signalCommand(String service, String signal) {
return String.format("%s | xargs kill -s %s", findPidCommand(service), signal);
}
public static File createSparkHome(File dataTestDir) throws IOException {
File sparkDir = new File(new File(System.getProperty("user.dir"), "target"), "spark");
if (!sparkDir.exists()) {
throw new RuntimeException("'Cannot find spark assembly dir at location " + sparkDir.getAbsolutePath());
}
File sparkHome = new File(dataTestDir, "spark");
System.setProperty(SPARK_TEST_HOME, sparkHome.getAbsolutePath());
FileUtils.copyDirectory(sparkDir, sparkHome);
return sparkHome;
}
public static MiniSDCTestingUtility getMiniSDCTestingUtility() {
return miniSDCTestingUtility;
}
public static URI getServerURI() {
return serverURI;
}
public static MiniSDC getMiniSDC() {
return miniSDC;
}
public static MiniYARNCluster getMiniYarnCluster() {
return miniYarnCluster;
}
}