/** * 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; import com.google.common.io.Resources; import com.streamsets.datacollector.MiniSDC; import com.streamsets.datacollector.MiniSDC.ExecutionMode; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; 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.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.UUID; public class MiniSDCTestingUtility { private MiniSDC miniSDC; private MiniYARNCluster miniYarnCluster; private static final Logger LOG = LoggerFactory.getLogger(MiniSDCTestingUtility.class); /** * Property to determine the dist root where libs can be picked up */ private static final String SDC_DIST_DIR = "sdc.dist.dir"; /** * System property key to get base test directory value */ private static final String BASE_TEST_DIRECTORY_KEY = "test.build.data.basedirectory"; /** * System property to find the test data dir, will be used by mini SDC */ private static final String TEST_DATA_DIR = "test.data.dir"; public static final String PRESERVE_TEST_DIR = "sdc.testing.preserve.testdir"; /** * Default base directory for test output. */ private static final String DEFAULT_BASE_TEST_DIRECTORY = "target/test-data"; /** * Directory where we put the data for this instance of MiniSDCTestingUtility */ private File dataTestDir = null; public MiniSDCTestingUtility() { } /** * Gets the current working directory used by test * @return * @throws IOException */ public File getDataTestDir() throws IOException { if (this.dataTestDir == null) { setupDataTestDir(); } return new File(this.dataTestDir.getAbsolutePath()); } /** * Sets up a directory for a test to use. * @return New directory path, if created. * @throws IOException */ protected void setupDataTestDir() throws IOException { if (this.dataTestDir != null) { LOG.warn("Data test dir already setup in " + dataTestDir.getAbsolutePath()); return; } String randomStr = UUID.randomUUID().toString(); File testPath = new File(getBaseTestDir(), randomStr); this.dataTestDir = new File(testPath.toString()).getAbsoluteFile(); // Will be used by MiniSDC System.setProperty(TEST_DATA_DIR, this.dataTestDir.toString()); if (deleteOnExit()) { this.dataTestDir.deleteOnExit(); } dataTestDir.mkdirs(); LOG.debug("Test data dir setup at " + dataTestDir); } /** * @return True if we should delete testing dirs on exit. */ private static boolean deleteOnExit() { String v = System.getProperty(PRESERVE_TEST_DIR); // Let default be true, to delete on exit. return v == null ? true : !Boolean.parseBoolean(v); } /** * @return Where to write test data; usually {@link #DEFAULT_BASE_TEST_DIRECTORY} * @see #setupDataTestDir() */ private File getBaseTestDir() { String pathName = System.getProperty(BASE_TEST_DIRECTORY_KEY, DEFAULT_BASE_TEST_DIRECTORY); return new File(pathName); } /** * @return True if we removed the test dirs * @throws IOException */ public boolean cleanupTestDir() throws IOException { if (deleteDir(this.dataTestDir)) { this.dataTestDir = null; return true; } return false; } /** * @param dir Directory to delete * @return True if we deleted it. * @throws IOException */ public static boolean deleteDir(final File dir) throws IOException { if (dir == null || !dir.exists()) { return true; } int ntries = 0; do { ntries += 1; try { if (deleteOnExit()) { FileUtils.deleteDirectory(dir); } return true; } catch (IOException ex) { LOG.warn("Failed to delete " + dir.getAbsolutePath()); } catch (IllegalArgumentException ex) { LOG.warn("Failed to delete " + dir.getAbsolutePath(), ex); } } while (ntries < 5); return ntries < 5; } /** * Start mini SDC * @param executionMode the Execution mode - could be standalone or cluster * @return * @throws Exception */ public MiniSDC createMiniSDC(ExecutionMode executionMode) throws Exception { Properties miniITProps = new Properties(); InputStream sdcInStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("miniIT.properties"); miniITProps.load(sdcInStream); String sdcDistRoot = (String) miniITProps.get(SDC_DIST_DIR); File sdcDistFile = new File(sdcDistRoot); if (!sdcDistFile.exists()) { throw new RuntimeException("SDC dist root dir " + sdcDistFile.getAbsolutePath() + "doesn't exist"); } LOG.info("SDC dist root at " + sdcDistFile.getAbsolutePath()); sdcInStream.close(); File target = getDataTestDir(); String targetRoot = target.getAbsolutePath(); File etcTarget = new File(target, "etc"); File resourcesTarget = new File(target, "resources"); FileUtils.copyDirectory(new File(sdcDistRoot + "/etc"), etcTarget); FileUtils.copyDirectory(new File(sdcDistRoot + "/resources"), resourcesTarget); FileUtils.copyDirectory(new File(sdcDistRoot + "/libexec"), new File(target, "libexec")); // Set execute permissions back on script Set<PosixFilePermission> set = new HashSet<PosixFilePermission>(); set.add(PosixFilePermission.OWNER_EXECUTE); set.add(PosixFilePermission.OWNER_READ); set.add(PosixFilePermission.OWNER_WRITE); set.add(PosixFilePermission.OTHERS_READ); Files.setPosixFilePermissions(new File(target, "libexec" + "/_cluster-manager").toPath(), set); File staticWebDir = new File(target, "static-web"); staticWebDir.mkdir(); setExecutePermission(new File(target, "libexec" + "/_cluster-manager").toPath()); File log4jProperties = new File(etcTarget, "sdc-log4j.properties"); if (log4jProperties.exists()) { log4jProperties.delete(); } Files.copy(Paths.get(Resources.getResource("log4j.properties").toURI()), log4jProperties.toPath()); File sdcProperties = new File(etcTarget, "sdc.properties"); System.setProperty("sdc.conf.dir", etcTarget.getAbsolutePath()); System.setProperty("sdc.resources.dir", resourcesTarget.getAbsolutePath()); System.setProperty("sdc.libexec.dir", targetRoot + "/libexec"); System.setProperty("sdc.static-web.dir", targetRoot + "/static-web"); rewriteProperties(sdcProperties, executionMode); this.miniSDC = new MiniSDC(sdcDistRoot); return this.miniSDC; } public static void setExecutePermission(Path path) throws IOException { Set<PosixFilePermission> set = new HashSet<PosixFilePermission>(); set.add(PosixFilePermission.OWNER_EXECUTE); set.add(PosixFilePermission.OWNER_READ); set.add(PosixFilePermission.OWNER_WRITE); set.add(PosixFilePermission.OTHERS_READ); Files.setPosixFilePermissions(path, set); } public void stopMiniSDC() { if (miniSDC != null) { miniSDC.stop(); miniSDC = null; } } public MiniYARNCluster startMiniYarnCluster(String testName, int numNodeManager, int numLocalDir, int numLogDir, YarnConfiguration yarnConfiguration) { miniYarnCluster = new MiniYARNCluster(testName, numNodeManager, numLocalDir, numLogDir); miniYarnCluster.init(yarnConfiguration); miniYarnCluster.start(); return miniYarnCluster; } public void stopMiniYarnCluster() { if (miniYarnCluster != null) { miniYarnCluster.stop(); miniYarnCluster = null; } } private Map<String, String> getCommonProperties() { Map<String, String> commonProps = new HashMap<String, String>(); // Start on random port commonProps.put("http.port", "0"); // TODO - MiniSDC creates problems with other form of auth commonProps.put("http.authentication", "none"); // Reduce interval of callback commonProps.put("callback.server.ping.interval.ms", "3000"); return commonProps; } private void rewriteProperties(File sdcPropertiesFile, ExecutionMode executionMode) throws IOException { InputStream sdcInStream = null; OutputStream sdcOutStream = null; Properties sdcProperties = new Properties(); try { sdcInStream = new FileInputStream(sdcPropertiesFile); sdcProperties.load(sdcInStream); for (Map.Entry<String, String> mapEntry : getCommonProperties().entrySet()) { sdcProperties.setProperty(mapEntry.getKey(), mapEntry.getValue()); } sdcOutStream = new FileOutputStream(sdcPropertiesFile); sdcProperties.store(sdcOutStream, null); sdcOutStream.flush(); sdcOutStream.close(); } finally { if (sdcInStream != null) { IOUtils.closeQuietly(sdcInStream); } if (sdcOutStream != null) { IOUtils.closeQuietly(sdcOutStream); } } } }