Chris Hillery has submitted this change and it was merged. Change subject: ASTERIXDB-1482: Added NCServiceExecutionIT, HyracksVirtualCluster. ......................................................................
ASTERIXDB-1482: Added NCServiceExecutionIT, HyracksVirtualCluster. NCServiceExecutionIT runs all execution tests against a local cluster managed by the NCService deployment framework. HyracksVirtualCluster offers programmatic NCService deployment control along with improved HyracksNCProcess/HyracksCCProcess. Further fixes and improvements: 1. Fix handling of iodevices/storagedir (ASTERIXDB-1482) 2. Proper handling of [nc] default section in all cases 3. Ensure asterixnc, etc. scripts are executable 4. Consolidate Ini handling 5. Pruned some dead code, including VirtualClusterDriver 6. A bit of refactoring and extended commenting Change-Id: If3eb450782a595cf85d04a2c2e9cc732564e65e6 Reviewed-on: https://asterix-gerrit.ics.uci.edu/958 Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Reviewed-by: Ian Maxon <ima...@apache.org> --- M asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java M asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java M asterixdb/asterix-server/pom.xml A asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java A asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf A asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf A asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java M hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java M hyracks-fullstack/hyracks/hyracks-server/pom.xml D hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java M hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java M hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java M hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java A hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.java M hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java M hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties 22 files changed, 536 insertions(+), 369 deletions(-) Approvals: Ian Maxon: Looks good to me, approved Jenkins: Verified diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java index 9a8fba4..677fc78 100644 --- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java +++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixMetadataProperties.java @@ -39,7 +39,7 @@ } public ClusterPartition getMetadataPartition() { - return accessor.getMetadataPartiton(); + return accessor.getMetadataPartition(); } public Map<String, String[]> getStores() { diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java index 507a393..7309f0c 100644 --- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java +++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/AsterixPropertiesAccessor.java @@ -18,6 +18,7 @@ */ package org.apache.asterix.common.config; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; @@ -53,10 +54,12 @@ private final List<String> nodeNames = new ArrayList<>();; private final Map<String, String[]> stores = new HashMap<>();; private final Map<String, String> coredumpConfig = new HashMap<>(); + + // This can be removed when asterix-configuration.xml is no longer required. private final Map<String, Property> asterixConfigurationParams; private final IApplicationConfig cfg; private final Map<String, String> transactionLogDirs = new HashMap<>(); - private final Map<String, String> asterixBuildProperties; + private final Map<String, String> asterixBuildProperties = new HashMap<>(); private final Map<String, ClusterPartition[]> nodePartitionsMap; private final SortedMap<Integer, ClusterPartition> clusterPartitions = new TreeMap<>(); @@ -94,6 +97,13 @@ List<Store> configuredStores = asterixConfiguration.getStore(); nodePartitionsMap = new HashMap<>(); int uniquePartitionId = 0; + // Here we iterate through all <store> elements in asterix-configuration.xml. + // For each one, we create an array of ClusterPartitions and store this array + // in nodePartitionsMap, keyed by the node name. The array is the same length + // as the comma-separated <storeDirs> child element, because Managix will have + // arranged for that element to be populated with the full paths to each + // partition directory (as formed by appending the <store> subdirectory to + // each <iodevices> path from the user's original cluster.xml). for (Store store : configuredStores) { String trimmedStoreDirs = store.getStoreDirs().trim(); String[] nodeStores = trimmedStoreDirs.split(","); @@ -117,35 +127,29 @@ for (TransactionLogDir txnLogDir : asterixConfiguration.getTransactionLogDir()) { transactionLogDirs.put(txnLogDir.getNcId(), txnLogDir.getTxnLogDirPath()); } - Properties gitProperties = new Properties(); - try { - gitProperties.load(getClass().getClassLoader().getResourceAsStream("git.properties")); - asterixBuildProperties = new HashMap<String, String>(); - for (final String name : gitProperties.stringPropertyNames()) { - asterixBuildProperties.put(name, gitProperties.getProperty(name)); - } - } catch (IOException e) { - throw new AsterixException(e); - } + loadAsterixBuildProperties(); } /** * Constructor which wraps an IApplicationConfig. */ - public AsterixPropertiesAccessor(IApplicationConfig cfg) { + public AsterixPropertiesAccessor(IApplicationConfig cfg) throws AsterixException { this.cfg = cfg; instanceName = cfg.getString("asterix", "instance", "DEFAULT_INSTANCE"); String mdNode = null; nodePartitionsMap = new HashMap<>(); int uniquePartitionId = 0; + + // Iterate through each configured NC. for (String section : cfg.getSections()) { if (!section.startsWith("nc/")) { continue; } String ncId = section.substring(3); + // Here we figure out which is the metadata node. If any NCs + // declare "metadata.port", use that one; otherwise just use the first. if (mdNode == null) { - // Default is first node == metadata node mdNode = ncId; } if (cfg.getString(section, "metadata.port") != null) { @@ -153,27 +157,46 @@ mdNode = ncId; } + // Now we assign the coredump and txnlog directories for this node. // QQQ Default values? Should they be specified here? Or should there - // be a default.ini? They can't be inserted by TriggerNCWork except - // possibly for hyracks-specified values. Certainly wherever they are, - // they should be platform-dependent. + // be a default.ini? Certainly wherever they are, they should be platform-dependent. coredumpConfig.put(ncId, cfg.getString(section, "coredumpdir", "/var/lib/asterixdb/coredump")); transactionLogDirs.put(ncId, cfg.getString(section, "txnlogdir", "/var/lib/asterixdb/txn-log")); - String[] storeDirs = cfg.getString(section, "storagedir", "storage").trim().split(","); - ClusterPartition[] nodePartitions = new ClusterPartition[storeDirs.length]; + + // Now we create an array of ClusterPartitions for all the partitions + // on this NC. + String[] iodevices = cfg.getString(section, "iodevices", "/var/lib/asterixdb/iodevice").split(","); + String storageSubdir = cfg.getString(section, "storagedir", "storage"); + String[] nodeStores = new String[iodevices.length]; + ClusterPartition[] nodePartitions = new ClusterPartition[iodevices.length]; for (int i = 0; i < nodePartitions.length; i++) { + // Construct final storage path from iodevice dir + storage subdir. + nodeStores[i] = iodevices[i] + File.separator + storageSubdir; + // Create ClusterPartition instances for this NC. ClusterPartition partition = new ClusterPartition(uniquePartitionId++, ncId, i); clusterPartitions.put(partition.getPartitionId(), partition); nodePartitions[i] = partition; } - stores.put(ncId, storeDirs); + stores.put(ncId, nodeStores); nodePartitionsMap.put(ncId, nodePartitions); nodeNames.add(ncId); } metadataNodeName = mdNode; asterixConfigurationParams = null; - asterixBuildProperties = null; + loadAsterixBuildProperties(); + } + + private void loadAsterixBuildProperties() throws AsterixException { + Properties gitProperties = new Properties(); + try { + gitProperties.load(getClass().getClassLoader().getResourceAsStream("git.properties")); + for (final String name : gitProperties.stringPropertyNames()) { + asterixBuildProperties.put(name, gitProperties.getProperty(name)); + } + } catch (IOException e) { + throw new AsterixException(e); + } } public String getMetadataNodeName() { @@ -204,20 +227,6 @@ return asterixBuildProperties; } - public void putCoredumpPaths(String nodeId, String coredumpPath) { - if (coredumpConfig.containsKey(nodeId)) { - throw new IllegalStateException("Cannot override value for coredump path"); - } - coredumpConfig.put(nodeId, coredumpPath); - } - - public void putTransactionLogDir(String nodeId, String txnLogDir) { - if (transactionLogDirs.containsKey(nodeId)) { - throw new IllegalStateException("Cannot override value for txnLogDir"); - } - transactionLogDirs.put(nodeId, txnLogDir); - } - public <T> T getProperty(String property, T defaultValue, IPropertyInterpreter<T> interpreter) { String value; Property p = null; @@ -246,18 +255,11 @@ } } - private static <T> void logConfigurationError(Property p, T defaultValue) { - if (LOGGER.isLoggable(Level.SEVERE)) { - LOGGER.severe("Invalid property value '" + p.getValue() + "' for property '" + p.getName() - + "'.\n See the description: \n" + p.getDescription() + "\nDefault = " + defaultValue); - } - } - public String getInstanceName() { return instanceName; } - public ClusterPartition getMetadataPartiton() { + public ClusterPartition getMetadataPartition() { // metadata partition is always the first partition on the metadata node return nodePartitionsMap.get(metadataNodeName)[0]; } diff --git a/asterixdb/asterix-server/pom.xml b/asterixdb/asterix-server/pom.xml index 812fd59..92fde73 100644 --- a/asterixdb/asterix-server/pom.xml +++ b/asterixdb/asterix-server/pom.xml @@ -125,6 +125,24 @@ </executions> </plugin> <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <version>1.6</version> + <executions> + <execution> + <id>process-test-classes</id> + <phase>package</phase> + <configuration> + <target> + <chmod file="target/appassembler/bin/*" perm="755" /> + </target> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2-beta-5</version> <executions> @@ -181,11 +199,37 @@ <scope>compile</scope> </dependency> <dependency> + <groupId>org.apache.hyracks</groupId> + <artifactId>hyracks-server</artifactId> + <type>jar</type> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.asterix</groupId> <artifactId>asterix-app</artifactId> <version>0.8.9-SNAPSHOT</version> </dependency> <dependency> + <groupId>org.apache.asterix</groupId> + <artifactId>asterix-app</artifactId> + <version>0.8.9-SNAPSHOT</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.asterix</groupId> + <artifactId>asterix-common</artifactId> + <version>0.8.9-SNAPSHOT</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.asterix</groupId> + <artifactId>asterix-test-framework</artifactId> + <version>0.8.9-SNAPSHOT</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.codehaus.mojo.appassembler</groupId> <artifactId>appassembler-booter</artifactId> <version>1.3.1</version> diff --git a/asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java b/asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java new file mode 100644 index 0000000..c179103 --- /dev/null +++ b/asterixdb/asterix-server/src/test/java/org/apache/asterix/server/test/NCServiceExecutionIT.java @@ -0,0 +1,186 @@ +/* + * Licensed to 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 org.apache.asterix.server.test; + +import org.apache.asterix.test.aql.TestExecutor; +import org.apache.asterix.test.runtime.HDFSCluster; +import org.apache.asterix.testframework.context.TestCaseContext; +import org.apache.asterix.testframework.xml.TestGroup; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.hyracks.server.process.HyracksVirtualCluster; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Logger; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) +public class NCServiceExecutionIT { + + // Important paths and files for this test. + + // The "target" subdirectory of asterix-server. All outputs go here. + private static final String TARGET_DIR = StringUtils + .join(new String[] { System.getProperty("basedir"), "target" }, File.separator); + + // Directory where the NCs create and store all data, as configured by + // src/test/resources/NCServiceExecutionIT/cc.conf. + private static final String INSTANCE_DIR = StringUtils + .join(new String[] { TARGET_DIR, "tmp" }, File.separator); + + // The log directory, where all CC, NCService, and NC logs are written. CC and + // NCService logs are configured on the HyracksVirtualCluster below. NC logs + // are configured in src/test/resources/NCServiceExecutionIT/ncservice*.conf. + private static final String LOG_DIR = StringUtils + .join(new String[] { TARGET_DIR, "failsafe-reports" }, File.separator); + + // Directory where *.conf files are located. + private static final String CONF_DIR = StringUtils + .join(new String[] { TARGET_DIR, "test-classes", "NCServiceExecutionIT" }, + File.separator); + + // The app.home specified for HyracksVirtualCluster. The NCService expects + // to find the NC startup script in ${app.home}/bin. + private static final String APP_HOME = StringUtils + .join(new String[] { TARGET_DIR, "appassembler" }, File.separator); + + // Path to the asterix-app directory. This is used as the current working + // directory for the CC and NCService processes, which allows relative file + // paths in "load" statements in test queries to find the right data. It is + // also used for HDFSCluster. + private static final String ASTERIX_APP_DIR = StringUtils + .join(new String[] { System.getProperty("basedir"), "..", "asterix-app" }, + File.separator); + + // Path to the actual AQL test files, which we borrow from asterix-app. This is + // passed to TestExecutor. + protected static final String TESTS_DIR = StringUtils + .join(new String[] { ASTERIX_APP_DIR, "src", "test", "resources", "runtimets" }, + File.separator); + + // Path that actual results are written to. We create and clean this directory + // here, and also pass it to TestExecutor which writes the test output there. + private static final String ACTUAL_RESULTS_DIR = StringUtils + .join(new String[] { TARGET_DIR, "ittest" }, File.separator); + + private static final Logger LOGGER = Logger.getLogger(NCServiceExecutionIT.class.getName()); + + private final TestCaseContext tcCtx; + private static final TestExecutor testExecutor = new TestExecutor(); + private static HyracksVirtualCluster cluster; + + @BeforeClass + public static void setUp() throws Exception { + // Create actual-results output directory. + File outDir = new File(ACTUAL_RESULTS_DIR); + outDir.mkdirs(); + + // Remove any instance data from previous runs. + File instanceDir = new File(INSTANCE_DIR); + if (instanceDir.isDirectory()) { + FileUtils.deleteDirectory(instanceDir); + } + + // HDFSCluster requires the input directory to end with a file separator. + HDFSCluster.getInstance().setup(ASTERIX_APP_DIR + File.separator); + + cluster = new HyracksVirtualCluster(new File(APP_HOME), new File(ASTERIX_APP_DIR)); + cluster.addNC( + new File(CONF_DIR, "ncservice1.conf"), + new File(LOG_DIR, "ncservice1.log") + ); + cluster.addNC( + new File(CONF_DIR, "ncservice2.conf"), + new File(LOG_DIR, "ncservice2.log") + ); + + try { + Thread.sleep(2000); + } + catch (InterruptedException ignored) { + } + + // Start CC + cluster.start( + new File(CONF_DIR, "cc.conf"), + new File(LOG_DIR, "cc.log") + ); + + LOGGER.info("Sleeping while cluster comes online..."); + try { + Thread.sleep(6000); + } + catch (InterruptedException ignored) { + } + } + + @AfterClass + public static void tearDown() throws Exception { + File outdir = new File(ACTUAL_RESULTS_DIR); + File[] files = outdir.listFiles(); + if (files == null || files.length == 0) { + outdir.delete(); + } + cluster.stop(); + HDFSCluster.getInstance().cleanup(); + } + + @Parameters + public static Collection<Object[]> tests() throws Exception { + Collection<Object[]> testArgs = new ArrayList<Object[]>(); + TestCaseContext.Builder b = new TestCaseContext.Builder(); + for (TestCaseContext ctx : b.build(new File(TESTS_DIR))) { + if (!skip(ctx)) { + testArgs.add(new Object[]{ctx}); + } + } + return testArgs; + } + + private static boolean skip(TestCaseContext tcCtx) { + // For now we skip feeds tests and external-library tests. + for (TestGroup group : tcCtx.getTestGroups()) { + if (group.getName().startsWith("external-") || group.getName().equals("feeds")) { + LOGGER.info("Skipping test: " + tcCtx.toString()); + return true; + } + } + return false; + } + + + public NCServiceExecutionIT(TestCaseContext ctx) { + this.tcCtx = ctx; + } + + @Test + public void test() throws Exception { + testExecutor.executeTest(ACTUAL_RESULTS_DIR, tcCtx, null, false); + } +} diff --git a/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf new file mode 100644 index 0000000..c4c76e6 --- /dev/null +++ b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/cc.conf @@ -0,0 +1,25 @@ +[nc/asterix_nc1] +txnlogdir=../asterix-server/target/tmp/asterix_nc1/txnlog +coredumpdir=../asterix-server/target/tmp/asterix_nc1/coredump +iodevices=../asterix-server/target/tmp/asterix_nc1/iodevice1,../asterix-server/target/tmp/asterix_nc1/iodevice2 + +[nc/asterix_nc2] +port=9091 +txnlogdir=../asterix-server/target/tmp/asterix_nc2/txnlog +coredumpdir=../asterix-server/target/tmp/asterix_nc2/coredump +iodevices=../asterix-server/target/tmp/asterix_nc2/iodevice1,../asterix-server/target/tmp/asterix_nc2/iodevice2 + +[nc] +address=127.0.0.1 +command=asterixnc +app.class=org.apache.asterix.hyracks.bootstrap.NCApplicationEntryPoint +jvm.args=-Xmx4096m -Dnode.Resolver="org.apache.asterix.external.util.IdentitiyResolverFactory" +storagedir=test_storage + +[cc] +cluster.address = 127.0.0.1 +app.class=org.apache.asterix.hyracks.bootstrap.CCApplicationEntryPoint + +[asterix] +storage.memorycomponent.globalbudget = 1073741824 + diff --git a/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf new file mode 100644 index 0000000..fa44fa2 --- /dev/null +++ b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice1.conf @@ -0,0 +1,3 @@ +[ncservice] +logdir=../asterix-server/target/failsafe-reports + diff --git a/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf new file mode 100644 index 0000000..53d8d9b --- /dev/null +++ b/asterixdb/asterix-server/src/test/resources/NCServiceExecutionIT/ncservice2.conf @@ -0,0 +1,4 @@ +[ncservice] +logdir=../asterix-server/target/failsafe-reports +port=9091 + diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java index 2aa3f37..4cddef1 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/ClusterControllerService.java @@ -276,7 +276,7 @@ continue; } String ncid = section.substring(3); - String address = ini.get(section, "address"); + String address = IniUtils.getString(ini, section, "address", null); int port = IniUtils.getInt(ini, section, "port", 9090); if (address == null) { address = InetAddress.getLoopbackAddress().getHostAddress(); diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java index ee79d38..7d2ff25 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/TriggerNCWork.java @@ -87,37 +87,15 @@ } /** - * Utility routine to copy all keys from a named section in Ini a - * to a named section in Ini b. We need to do this the hard way - * because Ini4j reacts inscrutably when attempting to copy - * Ini.Sections directly from one Ini to another. - */ - private void copyIniSection(Ini a, String asect, Ini b, String bsect) { - Ini.Section source = a.get(asect); - for (String key : source.keySet()) { - b.put(bsect, key, source.get(key)); - } - } - /** * Given an Ini object, serialize it to String with some enhancements. * @param ccini */ String serializeIni(Ini ccini) throws IOException { - Ini ini = new Ini(); - - // First copy the global [nc] section to a new section named for - // *this* NC, so that those values serve as defaults. - String ncsection = "nc/" + ncId; - copyIniSection(ccini, "nc", ini, ncsection); - // Now copy all sections to their same name in the derived config. - for (String section : ccini.keySet()) { - copyIniSection(ccini, section, ini, section); - } + StringWriter iniString = new StringWriter(); + ccini.store(iniString); // Finally insert *this* NC's name into localnc section - this is a fixed // entry point so that NCs can determine where all their config is. - ini.put("localnc", "id", ncId); - StringWriter iniString = new StringWriter(); - ini.store(iniString); + iniString.append("\n[localnc]\nid=" + ncId + "\n"); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Returning Ini file:\n" + iniString.toString()); } diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java index 3a8a2de..22fe318 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/application/IniApplicationConfig.java @@ -19,6 +19,7 @@ package org.apache.hyracks.control.common.application; import org.apache.hyracks.api.application.IApplicationConfig; +import org.apache.hyracks.control.common.controllers.IniUtils; import org.ini4j.Ini; import java.util.Set; @@ -37,39 +38,34 @@ } } - private <T> T getIniValue(String section, String key, T default_value, Class<T> clazz) { - T value = ini.get(section, key, clazz); - return (value != null) ? value : default_value; - } - @Override public String getString(String section, String key) { - return getIniValue(section, key, null, String.class); + return IniUtils.getString(ini, section, key, null); } @Override public String getString(String section, String key, String defaultValue) { - return getIniValue(section, key, defaultValue, String.class); + return IniUtils.getString(ini, section, key, defaultValue); } @Override public int getInt(String section, String key) { - return getIniValue(section, key, 0, Integer.class); + return IniUtils.getInt(ini, section, key, 0); } @Override public int getInt(String section, String key, int defaultValue) { - return getIniValue(section, key, defaultValue, Integer.class); + return IniUtils.getInt(ini, section, key, defaultValue); } @Override public long getLong(String section, String key) { - return getIniValue(section, key, (long) 0, Long.class); + return IniUtils.getLong(ini, section, key, (long) 0); } @Override public long getLong(String section, String key, long defaultValue) { - return getIniValue(section, key, defaultValue, Long.class); + return IniUtils.getLong(ini, section, key, defaultValue); } @Override diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java index a04d750..64bd7d1 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/CCConfig.java @@ -152,47 +152,4 @@ public IApplicationConfig getAppConfig() { return new IniApplicationConfig(ini); } - - public void toCommandLine(List<String> cList) { - cList.add("-client-net-ip-address"); - cList.add(clientNetIpAddress); - cList.add("-client-net-port"); - cList.add(String.valueOf(clientNetPort)); - cList.add("-cluster-net-ip-address"); - cList.add(clusterNetIpAddress); - cList.add("-cluster-net-port"); - cList.add(String.valueOf(clusterNetPort)); - cList.add("-http-port"); - cList.add(String.valueOf(httpPort)); - cList.add("-heartbeat-period"); - cList.add(String.valueOf(heartbeatPeriod)); - cList.add("-max-heartbeat-lapse-periods"); - cList.add(String.valueOf(maxHeartbeatLapsePeriods)); - cList.add("-profile-dump-period"); - cList.add(String.valueOf(profileDumpPeriod)); - cList.add("-default-max-job-attempts"); - cList.add(String.valueOf(defaultMaxJobAttempts)); - cList.add("-job-history-size"); - cList.add(String.valueOf(jobHistorySize)); - cList.add("-result-time-to-live"); - cList.add(String.valueOf(resultTTL)); - cList.add("-result-sweep-threshold"); - cList.add(String.valueOf(resultSweepThreshold)); - cList.add("-cc-root"); - cList.add(ccRoot); - if (clusterTopologyDefinition != null) { - cList.add("-cluster-topology"); - cList.add(clusterTopologyDefinition.getAbsolutePath()); - } - if (appCCMainClass != null) { - cList.add("-app-cc-main-class"); - cList.add(appCCMainClass); - } - if (appArgs != null && !appArgs.isEmpty()) { - cList.add("--"); - for (String appArg : appArgs) { - cList.add(appArg); - } - } - } } diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java index 9a5c9a0..538bb0b 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/IniUtils.java @@ -26,21 +26,39 @@ /** * Some utility functions for reading Ini4j objects with default values. + * For all getXxx() methods: if the 'section' contains a slash, and the 'key' + * is not found in that section, we will search for the key in the section named + * by stripping the leaf of the section name (final slash and anything following). + * eg. getInt(ini, "nc/red", "dir", null) will first look for the key "dir" in + * the section "nc/red", but if it is not found, will look in the section "nc". */ public class IniUtils { + private static <T> T getIniValue(Ini ini, String section, String key, T default_value, Class<T> clazz) { + T value; + while (true) { + value = ini.get(section, key, clazz); + if (value == null) { + int idx = section.lastIndexOf('/'); + if (idx > -1) { + section = section.substring(0, idx); + continue; + } + } + break; + } + return (value != null) ? value : default_value; + } + public static String getString(Ini ini, String section, String key, String defaultValue) { - String value = ini.get(section, key, String.class); - return (value != null) ? value : defaultValue; + return getIniValue(ini, section, key, defaultValue, String.class); } public static int getInt(Ini ini, String section, String key, int defaultValue) { - Integer value = ini.get(section, key, Integer.class); - return (value != null) ? value : defaultValue; + return getIniValue(ini, section, key, defaultValue, Integer.class); } public static long getLong(Ini ini, String section, String key, long defaultValue) { - Long value = ini.get(section, key, Long.class); - return (value != null) ? value : defaultValue; + return getIniValue(ini, section, key, defaultValue, Long.class); } public static Ini loadINIFile(String configFile) throws IOException { diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java index b408083..d08df60 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-common/src/main/java/org/apache/hyracks/control/common/controllers/NCConfig.java @@ -191,66 +191,6 @@ return new IniApplicationConfig(ini); } - public void toCommandLine(List<String> cList) { - cList.add("-cc-host"); - cList.add(ccHost); - cList.add("-cc-port"); - cList.add(String.valueOf(ccPort)); - cList.add("-cluster-net-ip-address"); - cList.add(clusterNetIPAddress); - cList.add("-cluster-net-port"); - cList.add(String.valueOf(clusterNetPort)); - cList.add("-cluster-net-public-ip-address"); - cList.add(clusterNetPublicIPAddress); - cList.add("-cluster-net-public-port"); - cList.add(String.valueOf(clusterNetPublicPort)); - cList.add("-node-id"); - cList.add(nodeId); - cList.add("-data-ip-address"); - cList.add(dataIPAddress); - cList.add("-data-port"); - cList.add(String.valueOf(dataPort)); - cList.add("-data-public-ip-address"); - cList.add(dataPublicIPAddress); - cList.add("-data-public-port"); - cList.add(String.valueOf(dataPublicPort)); - cList.add("-result-ip-address"); - cList.add(resultIPAddress); - cList.add("-result-port"); - cList.add(String.valueOf(resultPort)); - cList.add("-result-public-ip-address"); - cList.add(resultPublicIPAddress); - cList.add("-result-public-port"); - cList.add(String.valueOf(resultPublicPort)); - cList.add("-retries"); - cList.add(String.valueOf(retries)); - cList.add("-iodevices"); - cList.add(ioDevices); - cList.add("-net-thread-count"); - cList.add(String.valueOf(nNetThreads)); - cList.add("-net-buffer-count"); - cList.add(String.valueOf(nNetBuffers)); - cList.add("-max-memory"); - cList.add(String.valueOf(maxMemory)); - cList.add("-result-time-to-live"); - cList.add(String.valueOf(resultTTL)); - cList.add("-result-sweep-threshold"); - cList.add(String.valueOf(resultSweepThreshold)); - cList.add("-result-manager-memory"); - cList.add(String.valueOf(resultManagerMemory)); - - if (appNCMainClass != null) { - cList.add("-app-nc-main-class"); - cList.add(appNCMainClass); - } - if (appArgs != null && !appArgs.isEmpty()) { - cList.add("--"); - for (String appArg : appArgs) { - cList.add(appArg); - } - } - } - public void toMap(Map<String, String> configuration) { configuration.put("cc-host", ccHost); configuration.put("cc-port", (String.valueOf(ccPort))); diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java index e3fe959..4102b4c 100644 --- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java +++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-nc-service/src/main/java/org/apache/hyracks/control/nc/service/NCService.java @@ -19,6 +19,7 @@ package org.apache.hyracks.control.nc.service; import org.apache.commons.lang3.SystemUtils; +import org.apache.hyracks.control.common.controllers.IniUtils; import org.ini4j.Ini; import org.kohsuke.args4j.CmdLineParser; @@ -71,23 +72,13 @@ private static final String MAGIC_COOKIE = "hyncmagic"; - private static String getStringINIOpt(Ini ini, String section, String key, String default_value) { - String value = ini.get(section, key, String.class); - return (value != null) ? value : default_value; - } - - private static int getIntINIOpt(Ini ini, String section, String key, int default_value) { - Integer value = ini.get(section, key, Integer.class); - return (value != null) ? value : default_value; - } - private static List<String> buildCommand() throws IOException { List<String> cList = new ArrayList<String>(); // Find the command to run. For now, we allow overriding the name, but // still assume it's located in the bin/ directory of the deployment. // Even this is likely more configurability than we need. - String command = getStringINIOpt(ini, nodeSection, "command", "hyracksnc"); + String command = IniUtils.getString(ini, nodeSection, "command", "hyracksnc"); // app.home is specified by the Maven appassembler plugin. If it isn't set, // fall back to user's home dir. Again this is likely more flexibility // than we need. @@ -110,10 +101,16 @@ private static void configEnvironment(Map<String,String> env) { if (env.containsKey("JAVA_OPTS")) { + if (LOGGER.isLoggable(Level.INFO)) { + LOGGER.info("Keeping JAVA_OPTS from environment"); + } return; } - String jvmargs = getStringINIOpt(ini, nodeSection, "jvm.args", "-Xmx1536m"); + String jvmargs = IniUtils.getString(ini, nodeSection, "jvm.args", "-Xmx1536m"); env.put("JAVA_OPTS", jvmargs); + if (LOGGER.isLoggable(Level.INFO)) { + LOGGER.info("Setting JAVA_OPTS to " + jvmargs); + } } /** @@ -146,6 +143,8 @@ // If the directory IS there, all is well } File logfile = new File(config.logdir, "nc-" + ncId + ".log"); + // Don't care if this succeeds or fails: + logfile.delete(); pb.redirectOutput(ProcessBuilder.Redirect.appendTo(logfile)); if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Logging to " + logfile.getCanonicalPath()); @@ -192,7 +191,7 @@ } String iniString = ois.readUTF(); ini = new Ini(new StringReader(iniString)); - ncId = getStringINIOpt(ini, "localnc", "id", ""); + ncId = IniUtils.getString(ini, "localnc", "id", ""); nodeSection = "nc/" + ncId; return launchNCProcess(); } catch (Exception e) { diff --git a/hyracks-fullstack/hyracks/hyracks-server/pom.xml b/hyracks-fullstack/hyracks/hyracks-server/pom.xml index 3bda1d3..52958f8 100644 --- a/hyracks-fullstack/hyracks/hyracks-server/pom.xml +++ b/hyracks-fullstack/hyracks/hyracks-server/pom.xml @@ -86,10 +86,6 @@ <mainClass>org.apache.hyracks.control.nc.service.NCService</mainClass> <name>hyracksncservice</name> </program> - <program> - <mainClass>org.apache.hyracks.server.drivers.VirtualClusterDriver</mainClass> - <name>hyracks-virtual-cluster</name> - </program> </programs> <repositoryLayout>flat</repositoryLayout> <repositoryName>lib</repositoryName> diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java deleted file mode 100644 index 41c14a7..0000000 --- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/drivers/VirtualClusterDriver.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to 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 org.apache.hyracks.server.drivers; - -import org.kohsuke.args4j.CmdLineParser; -import org.kohsuke.args4j.Option; - -import org.apache.hyracks.control.common.controllers.CCConfig; -import org.apache.hyracks.control.common.controllers.NCConfig; -import org.apache.hyracks.server.process.HyracksCCProcess; -import org.apache.hyracks.server.process.HyracksNCProcess; - -public class VirtualClusterDriver { - private static class Options { - @Option(name = "-n", required = false, usage = "Number of node controllers (default: 2)") - public int n = 2; - - @Option(name = "-cc-client-net-port", required = false, usage = "CC Port (default: 1098)") - public int ccClientNetPort = 1098; - - @Option(name = "-cc-cluster-net-port", required = false, usage = "CC Port (default: 1099)") - public int ccClusterNetPort = 1099; - - @Option(name = "-cc-http-port", required = false, usage = "CC Port (default: 16001)") - public int ccHttpPort = 16001; - } - - public static void main(String[] args) throws Exception { - Options options = new Options(); - CmdLineParser cp = new CmdLineParser(options); - try { - cp.parseArgument(args); - } catch (Exception e) { - System.err.println(e.getMessage()); - cp.printUsage(System.err); - return; - } - - CCConfig ccConfig = new CCConfig(); - ccConfig.clusterNetIpAddress = "127.0.0.1"; - ccConfig.clusterNetPort = options.ccClusterNetPort; - ccConfig.clientNetIpAddress = "127.0.0.1"; - ccConfig.clientNetPort = options.ccClientNetPort; - ccConfig.httpPort = options.ccHttpPort; - HyracksCCProcess ccp = new HyracksCCProcess(ccConfig); - ccp.start(); - - Thread.sleep(5000); - - HyracksNCProcess ncps[] = new HyracksNCProcess[options.n]; - for (int i = 0; i < options.n; ++i) { - NCConfig ncConfig = new NCConfig(); - ncConfig.ccHost = "127.0.0.1"; - ncConfig.ccPort = options.ccClusterNetPort; - ncConfig.clusterNetIPAddress = "127.0.0.1"; - ncConfig.nodeId = "nc" + i; - ncConfig.dataIPAddress = "127.0.0.1"; - ncps[i] = new HyracksNCProcess(ncConfig); - ncps[i].start(); - } - - while (true) { - Thread.sleep(10000); - } - } -} diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java index d0d8d63..4a70120 100644 --- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java +++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksCCProcess.java @@ -18,25 +18,28 @@ */ package org.apache.hyracks.server.process; +import org.apache.hyracks.control.cc.CCDriver; + +import java.io.File; import java.util.List; -import org.apache.hyracks.control.cc.CCDriver; -import org.apache.hyracks.control.common.controllers.CCConfig; - public class HyracksCCProcess extends HyracksServerProcess { - private CCConfig config; - public HyracksCCProcess(CCConfig config) { - this.config = config; - } - - @Override - protected void addCmdLineArgs(List<String> cList) { - config.toCommandLine(cList); + public HyracksCCProcess(File configFile, File logFile, File appHome, File workingDir) { + this.configFile = configFile; + this.logFile = logFile; + this.appHome = appHome; + this.workingDir = workingDir; } @Override protected String getMainClassName() { return CCDriver.class.getName(); } + + @Override + protected void addJvmArgs(List<String> cList) { + // CC needs more than default memory + cList.add("-Xmx1024m"); + } } diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java index c4517e6..8bc1694 100644 --- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java +++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksNCProcess.java @@ -18,25 +18,28 @@ */ package org.apache.hyracks.server.process; +import org.apache.hyracks.control.nc.service.NCService; + +import java.io.File; import java.util.List; -import org.apache.hyracks.control.common.controllers.NCConfig; -import org.apache.hyracks.control.nc.NCDriver; - public class HyracksNCProcess extends HyracksServerProcess { - private NCConfig config; - public HyracksNCProcess(NCConfig config) { - this.config = config; - } - - @Override - protected void addCmdLineArgs(List<String> cList) { - config.toCommandLine(cList); + public HyracksNCProcess(File configFile, File logFile, File appHome, File workingDir) { + this.configFile = configFile; + this.logFile = logFile; + this.appHome = appHome; + this.workingDir = workingDir; } @Override protected String getMainClassName() { - return NCDriver.class.getName(); + return NCService.class.getName(); + } + + @Override + protected void addJvmArgs(List<String> cList) { + // NCService needs little memory + cList.add("-Xmx128m"); } } diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java index 9dec0ec..13cb445 100644 --- a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java +++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksServerProcess.java @@ -29,53 +29,69 @@ import java.util.logging.Level; import java.util.logging.Logger; -public abstract class HyracksServerProcess { +abstract class HyracksServerProcess { private static final Logger LOGGER = Logger.getLogger(HyracksServerProcess.class.getName()); protected Process process; + protected File configFile = null; + protected File logFile = null; + protected File appHome = null; + protected File workingDir = null; public void start() throws IOException { String[] cmd = buildCommand(); if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Starting command: " + Arrays.toString(cmd)); } - process = Runtime.getRuntime().exec(cmd, null, null); - dump(process.getInputStream()); - dump(process.getErrorStream()); + + ProcessBuilder pb = new ProcessBuilder(cmd); + pb.redirectErrorStream(true); + if (logFile != null) { + if (LOGGER.isLoggable(Level.INFO)) { + LOGGER.info("Logging to: " + logFile.getCanonicalPath()); + } + logFile.getParentFile().mkdirs(); + logFile.delete(); + pb.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile)); + } else { + if (LOGGER.isLoggable(Level.INFO)) { + LOGGER.info("Logfile not set, subprocess will output to stdout"); + } + } + pb.directory(workingDir); + process = pb.start(); } - private void dump(InputStream input) { - final int streamBufferSize = 1000; - final Reader in = new InputStreamReader(input); - new Thread(new Runnable() { - public void run() { - try { - char[] chars = new char[streamBufferSize]; - int c; - while ((c = in.read(chars)) != -1) { - if (c > 0) { - System.out.print(String.valueOf(chars, 0, c)); - } - } - } catch (IOException e) { - } - } - }).start(); + public void stop() { + process.destroy(); + try { + process.waitFor(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } private String[] buildCommand() { List<String> cList = new ArrayList<String>(); cList.add(getJavaCommand()); - cList.add("-Dbasedir=" + System.getProperty("basedir")); - cList.add("-Djava.rmi.server.hostname=127.0.0.1"); + addJvmArgs(cList); + cList.add("-Dapp.home=" + appHome.getAbsolutePath()); cList.add("-classpath"); cList.add(getClasspath()); cList.add(getMainClassName()); + if (configFile != null) { + cList.add("-config-file"); + cList.add(configFile.getAbsolutePath()); + } addCmdLineArgs(cList); return cList.toArray(new String[cList.size()]); } - protected abstract void addCmdLineArgs(List<String> cList); + protected void addJvmArgs(List<String> cList) { + } + + protected void addCmdLineArgs(List<String> cList) { + } protected abstract String getMainClassName(); @@ -83,7 +99,7 @@ return System.getProperty("java.class.path"); } - protected final String getJavaCommand() { + private final String getJavaCommand() { return System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; } } diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.java b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.java new file mode 100644 index 0000000..f08bb43 --- /dev/null +++ b/hyracks-fullstack/hyracks/hyracks-server/src/main/java/org/apache/hyracks/server/process/HyracksVirtualCluster.java @@ -0,0 +1,84 @@ +/* + * Licensed to 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 org.apache.hyracks.server.process; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Starts a local hyracks-based cluster (NC and CC child processes). + */ +public class HyracksVirtualCluster { + private final File appHome; + private final File workingDir; + private List<HyracksNCProcess> ncProcs = new ArrayList<>(3); + private HyracksCCProcess ccProc = null; + + /** + * Construct a Hyracks-based cluster. + * @param appHome - path to the installation root of the Hyracks application. + * At least bin/hyracksnc (or the equivalent NC script for + * the application) must exist in this directory. + * @param workingDir - directory to use as CWD for all child processes. May + * be null, in which case the CWD of the invoking process is used. + */ + public HyracksVirtualCluster(File appHome, File workingDir) { + this.appHome = appHome; + this.workingDir = workingDir; + } + + /** + * Creates and starts an NCService. + * @param configFile - full path to an ncservice.conf. May be null to accept all defaults. + * @throws IOException - if there are errors starting the process. + */ + public void addNC(File configFile, File logFile) throws IOException { + HyracksNCProcess proc = new HyracksNCProcess(configFile, logFile, appHome, workingDir); + proc.start(); + ncProcs.add(proc); + } + + /** + * Starts the CC, initializing the cluster. Expects that any NCs referenced + * in the cluster configuration have already been started with addNC(). + * @param ccConfigFile - full path to a cluster conf file. May be null to accept all + * defaults, although this is seldom useful since there are no NCs. + * @throws IOException - if there are errors starting the process. + */ + public void start(File ccConfigFile, File logFile) throws IOException { + ccProc = new HyracksCCProcess(ccConfigFile, logFile, appHome, workingDir); + ccProc.start(); + } + + /** + * Stops all processes in the cluster. + * QQQ Someday this should probably do a graceful stop of NCs rather than + * killing the NCService. + */ + public void stop() { + ccProc.stop(); + for (HyracksNCProcess proc : ncProcs) { + proc.stop(); + } + } +} + diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java index bd99c8c..9a231a0 100644 --- a/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java +++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/java/org/apache/hyracks/server/test/NCServiceIT.java @@ -23,6 +23,7 @@ import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.lang3.StringUtils; +import org.apache.hyracks.server.process.HyracksVirtualCluster; import org.json.JSONArray; import org.json.JSONObject; import org.junit.AfterClass; @@ -30,6 +31,7 @@ import org.junit.Test; import java.io.File; +import java.io.IOException; import java.net.InetAddress; import java.util.ArrayList; import java.util.List; @@ -38,23 +40,29 @@ public class NCServiceIT { private static final String TARGET_DIR = StringUtils - .join(new String[]{System.getProperty("basedir"), "target"}, File.separator); + .join(new String[] { System.getProperty("basedir"), "target" }, File.separator); private static final String LOG_DIR = StringUtils - .join(new String[]{TARGET_DIR, "surefire-reports"}, File.separator); + .join(new String[] { TARGET_DIR, "failsafe-reports" }, File.separator); private static final String RESOURCE_DIR = StringUtils - .join(new String[]{TARGET_DIR, "test-classes", "NCServiceIT"}, File.separator); - private static final String APP_DIR = StringUtils - .join(new String[]{TARGET_DIR, "appassembler", "bin"}, File.separator); + .join(new String[] { TARGET_DIR, "test-classes", "NCServiceIT" }, File.separator); + private static final String APP_HOME = StringUtils + .join(new String[] { TARGET_DIR, "appassembler" }, File.separator); private static final Logger LOGGER = Logger.getLogger(NCServiceIT.class.getName()); - private static List<Process> procs = new ArrayList<>(); + + private static HyracksVirtualCluster cluster = null; @BeforeClass public static void setUp() throws Exception { - // Start two NC Services - don't read their output as they don't terminate - procs.add(invoke("nc-red.log", APP_DIR + File.separator + "hyracksncservice", - "-config-file", RESOURCE_DIR + File.separator + "nc-red.conf")); - procs.add(invoke("nc-blue.log", APP_DIR + File.separator + "hyracksncservice", - "-config-file", RESOURCE_DIR + File.separator + "nc-blue.conf")); + cluster = new HyracksVirtualCluster(new File(APP_HOME), null); + cluster.addNC( + new File(RESOURCE_DIR, "nc-red.conf"), + new File(LOG_DIR, "nc-red.log") + ); + cluster.addNC( + new File(RESOURCE_DIR, "nc-blue.conf"), + new File(LOG_DIR, "nc-blue.log") + ); + try { Thread.sleep(2000); } @@ -62,8 +70,11 @@ } // Start CC - procs.add(invoke("cc.log", APP_DIR + File.separator + "hyrackscc", - "-config-file", RESOURCE_DIR + File.separator + "cc.conf")); + cluster.start( + new File(RESOURCE_DIR, "cc.conf"), + new File(LOG_DIR, "cc.log") + ); + try { Thread.sleep(10000); } @@ -72,11 +83,8 @@ } @AfterClass - public static void tearDown() throws Exception { - for (Process p : procs) { - p.destroy(); - p.waitFor(); - } + public static void tearDown() throws IOException { + cluster.stop(); } private static String getHttp(String url) throws Exception { @@ -95,18 +103,6 @@ } else { throw new Exception("HTTP error " + statusCode + ":\n" + response); } - } - - private static Process invoke(String logfile, String... args) throws Exception { - ProcessBuilder pb = new ProcessBuilder(args); - pb.redirectErrorStream(true); - File logDir = new File(LOG_DIR); - logDir.mkdirs(); - File log = new File(logDir, logfile); - log.delete(); - pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log)); - Process p = pb.start(); - return p; } @Test @@ -138,5 +134,4 @@ tearDown(); } } - } diff --git a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties index c888bb1..e9f8479 100644 --- a/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties +++ b/hyracks-fullstack/hyracks/hyracks-server/src/test/resources/logging.properties @@ -46,8 +46,8 @@ # Note that the ConsoleHandler also has a separate level # setting to limit messages printed to the console. -.level= WARNING -# .level= INFO +# .level= WARNING +.level= INFO # .level= FINE # .level = FINEST -- To view, visit https://asterix-gerrit.ics.uci.edu/958 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-MessageType: merged Gerrit-Change-Id: If3eb450782a595cf85d04a2c2e9cc732564e65e6 Gerrit-PatchSet: 4 Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-Owner: Chris Hillery <c...@lambda.nu> Gerrit-Reviewer: Chris Hillery <c...@lambda.nu> Gerrit-Reviewer: Ian Maxon <ima...@apache.org> Gerrit-Reviewer: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Gerrit-Reviewer: Till Westmann <ti...@apache.org>