This is an automated email from the ASF dual-hosted git repository. ericpai pushed a commit to branch improve/iotdb-2179 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 99de2f80e7aa121e7dcec6c08a75090b83aafd07 Author: ericpai <[email protected]> AuthorDate: Thu Jun 16 17:45:48 2022 +0800 [IOTDB-2179] New IT framework supports Windows platform --- .github/workflows/cluster-it.yml | 4 +- .../org/apache/iotdb/it/env/ClusterNodeBase.java | 129 ++++++++++++++++++--- .../java/org/apache/iotdb/it/env/ConfigNode.java | 91 ++++++--------- .../java/org/apache/iotdb/it/env/DataNode.java | 85 ++++++-------- 4 files changed, 183 insertions(+), 126 deletions(-) diff --git a/.github/workflows/cluster-it.yml b/.github/workflows/cluster-it.yml index 724d818cfd..644c63dc9b 100644 --- a/.github/workflows/cluster-it.yml +++ b/.github/workflows/cluster-it.yml @@ -27,8 +27,8 @@ jobs: fail-fast: false max-parallel: 20 matrix: - java: [ 8, 11, 17 ] - os: [ ubuntu-latest, macos-latest ] + java: [ 17 ] + os: [ windows-latest ] runs-on: ${{ matrix.os }} steps: diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/ClusterNodeBase.java b/integration-test/src/main/java/org/apache/iotdb/it/env/ClusterNodeBase.java index 42cf9f662c..eeff32fc7c 100644 --- a/integration-test/src/main/java/org/apache/iotdb/it/env/ClusterNodeBase.java +++ b/integration-test/src/main/java/org/apache/iotdb/it/env/ClusterNodeBase.java @@ -21,13 +21,22 @@ package org.apache.iotdb.it.env; import org.apache.iotdb.itbase.env.BaseNode; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.SystemUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.File; +import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import java.util.stream.IntStream; import static org.junit.Assert.fail; @@ -43,7 +52,6 @@ public abstract class ClusterNodeBase implements BaseNode { private int rpcPort; private String nodePath; - private String scriptPath; private String logPath; private Process instance; @@ -68,16 +76,11 @@ public abstract class ClusterNodeBase implements BaseNode { this.nodePath = nodePath; } - protected void setScriptPath(String scriptPath) { - this.scriptPath = scriptPath; - } - protected void setLogPath(String logPath) { this.logPath = logPath; } - protected int[] searchAvailablePorts() { - String cmd = "lsof -iTCP -sTCP:LISTEN -P -n | awk '{print $9}' | grep -E "; + protected final int[] searchAvailablePorts() { boolean flag = true; int portStart = 10001; @@ -95,15 +98,14 @@ public abstract class ClusterNodeBase implements BaseNode { continue; } - StringBuilder port = new StringBuilder(randomPortStart); - for (int i = 0; i < 9; i++) { - port.append("|"); - randomPortStart++; - port.append(randomPortStart); - } + List<Integer> requiredPorts = + IntStream.rangeClosed(randomPortStart, randomPortStart + 9) + .boxed() + .collect(Collectors.toList()); + String cmd = getSearchAvailablePortCmd(requiredPorts); try { - Process proc = Runtime.getRuntime().exec(cmd + "\"" + port + "\""); + Process proc = Runtime.getRuntime().exec(cmd); BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream())); String line; while ((line = br.readLine()) != null) { @@ -121,12 +123,37 @@ public abstract class ClusterNodeBase implements BaseNode { return IntStream.rangeClosed(portStart, portStart + 9).toArray(); } + private String getSearchAvailablePortCmd(List<Integer> ports) { + if (SystemUtils.IS_OS_WINDOWS) { + return getWindowsSearchPortCmd(ports); + } + return getUnixSearchPortCmd(ports); + } + + private String getWindowsSearchPortCmd(List<Integer> ports) { + String cmd = "netstat -aon -p tcp | findStr "; + return cmd + + ports.stream().map(v -> "/C:'127.0.0.1:" + v + "'").collect(Collectors.joining(" ")); + } + + private String getUnixSearchPortCmd(List<Integer> ports) { + String cmd = "lsof -iTCP -sTCP:LISTEN -P -n | awk '{print $9}' | grep -E "; + return cmd + ports.stream().map(String::valueOf).collect(Collectors.joining("|")) + "\""; + } + @Override public void createDir() { // Copy templateNodePath to nodePath try { FileUtils.copyDirectoryToDirectory(new File(this.templateNodePath), new File(this.nodePath)); - new File(this.scriptPath).setExecutable(true); + String startScriptPath = getStartScriptPath(); + String stopScriptPath = getStopScriptPath(); + if (!new File(startScriptPath).setExecutable(true)) { + logger.error("Change {} to executable failed.", startScriptPath); + } + if (!new File(stopScriptPath).setExecutable(true)) { + logger.error("Change {} to executable failed.", stopScriptPath); + } } catch (IOException ex) { fail("Copy node dir failed. " + ex); } @@ -145,10 +172,18 @@ public abstract class ClusterNodeBase implements BaseNode { @Override public void start() { try { - ProcessBuilder processBuilder = - new ProcessBuilder(this.scriptPath) - .redirectOutput(new File("/dev/null")) - .redirectError(new File("/dev/null")); + ProcessBuilder processBuilder; + if (SystemUtils.IS_OS_WINDOWS) { + processBuilder = + new ProcessBuilder(getStartScriptPath()) + .redirectOutput(new File("nul")) + .redirectError(new File("nul")); + } else { + processBuilder = + new ProcessBuilder(getStartScriptPath()) + .redirectOutput(new File("/dev/null")) + .redirectError(new File("/dev/null")); + } processBuilder.environment().put("IT_LOG_PATH", this.logPath); processBuilder.environment().put("IT_LOG_LEVEL", "DEBUG"); this.instance = processBuilder.start(); @@ -160,6 +195,24 @@ public abstract class ClusterNodeBase implements BaseNode { @Override public void stop() { this.instance.destroy(); + // In Windows, the IoTDB process is started as a subprocess of the original batch script with a + // new pid, so we need to kill the new subprocess as well. + if (SystemUtils.IS_OS_WINDOWS) { + ProcessBuilder processBuilder = + new ProcessBuilder(getStopScriptPath()) + .redirectOutput(new File("nul")) + .redirectError(new File("nul")); + Process p = null; + try { + p = processBuilder.start(); + p.waitFor(5, TimeUnit.SECONDS); + } catch (IOException | InterruptedException e) { + logger.error("Stop instance in Windows failed", e); + if (p != null) { + p.destroyForcibly(); + } + } + } } @Override @@ -173,6 +226,26 @@ public abstract class ClusterNodeBase implements BaseNode { } } + @Override + public void changeConfig(Properties properties) { + try { + String configPath = getConfigPath(); + Properties configProperties = new Properties(); + try (InputStream confInput = Files.newInputStream(Paths.get(configPath))) { + configProperties.load(confInput); + } + updateConfig(configProperties); + if (properties != null && !properties.isEmpty()) { + configProperties.putAll(properties); + } + try (FileWriter confOutput = new FileWriter(configPath)) { + configProperties.store(confOutput, null); + } + } catch (IOException ex) { + fail("Change the config of data node failed. " + ex); + } + } + @Override public String getIp() { return ip; @@ -187,4 +260,22 @@ public abstract class ClusterNodeBase implements BaseNode { public String getIpAndPortString() { return this.getIp() + ":" + this.rpcPort; } + + protected String workDirFilePath(String dirName, String fileName) { + return getNodePath() + + File.separator + + "template-node" + + File.separator + + dirName + + File.separator + + fileName; + } + + protected abstract String getConfigPath(); + + protected abstract void updateConfig(Properties properties); + + protected abstract String getStartScriptPath(); + + protected abstract String getStopScriptPath(); } diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/ConfigNode.java b/integration-test/src/main/java/org/apache/iotdb/it/env/ConfigNode.java index a6f2d49e5d..1b6c958049 100644 --- a/integration-test/src/main/java/org/apache/iotdb/it/env/ConfigNode.java +++ b/integration-test/src/main/java/org/apache/iotdb/it/env/ConfigNode.java @@ -18,19 +18,14 @@ */ package org.apache.iotdb.it.env; +import org.apache.commons.lang3.SystemUtils; + import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Properties; -import static org.junit.Assert.fail; - public class ConfigNode extends ClusterNodeBase { private final int consensusPort; - private final String configPath; private final String targetConfigNode; public ConfigNode(boolean isSeed, String targetConfigNode, String testName) { @@ -59,30 +54,6 @@ public class ConfigNode extends ClusterNodeBase { + ".log"; super.setLogPath(logPath); - String scriptPath = - super.getNodePath() - + File.separator - + "template-node" - + File.separator - + "confignode" - + File.separator - + "sbin" - + File.separator - + "start-confignode" - + ".sh"; - super.setScriptPath(scriptPath); - - this.configPath = - super.getNodePath() - + File.separator - + "template-node" - + File.separator - + "confignode" - + File.separator - + "conf" - + File.separator - + "iotdb-confignode.properties"; - if (isSeed) { this.targetConfigNode = getIpAndPortString(); } else { @@ -91,29 +62,41 @@ public class ConfigNode extends ClusterNodeBase { } @Override - public void changeConfig(Properties properties) { - try { - Properties configProperties = new Properties(); - configProperties.load(Files.newInputStream(Paths.get(this.configPath))); - configProperties.setProperty("rpc_address", super.getIp()); - configProperties.setProperty("rpc_port", String.valueOf(super.getPort())); - configProperties.setProperty("consensus_port", String.valueOf(this.consensusPort)); - configProperties.setProperty("target_confignode", this.targetConfigNode); - configProperties.setProperty( - "schema_region_consensus_protocol_class", - "org.apache.iotdb.consensus.ratis.RatisConsensus"); - configProperties.setProperty( - "data_region_consensus_protocol_class", - "org.apache.iotdb.consensus.ratis.RatisConsensus"); - configProperties.setProperty("schema_replication_factor", "2"); - configProperties.setProperty("data_replication_factor", "2"); - configProperties.setProperty("connection_timeout_ms", "30000"); - if (properties != null && !properties.isEmpty()) { - configProperties.putAll(properties); - } - configProperties.store(new FileWriter(this.configPath), null); - } catch (IOException ex) { - fail("Change the config of config node failed. " + ex); + protected void updateConfig(Properties properties) { + properties.setProperty("rpc_address", super.getIp()); + properties.setProperty("rpc_port", String.valueOf(super.getPort())); + properties.setProperty("consensus_port", String.valueOf(this.consensusPort)); + properties.setProperty("target_confignode", this.targetConfigNode); + properties.setProperty( + "schema_region_consensus_protocol_class", + "org.apache.iotdb.consensus.ratis.RatisConsensus"); + properties.setProperty( + "data_region_consensus_protocol_class", "org.apache.iotdb.consensus.ratis.RatisConsensus"); + properties.setProperty("schema_replication_factor", "2"); + properties.setProperty("data_replication_factor", "2"); + properties.setProperty("connection_timeout_ms", "30000"); + } + + @Override + protected String getConfigPath() { + return workDirFilePath("confignode" + File.separator + "conf", "iotdb-confignode.properties"); + } + + @Override + protected String getStartScriptPath() { + String scriptName = "start-confignode.sh"; + if (SystemUtils.IS_OS_WINDOWS) { + scriptName = "start-confignode.bat"; + } + return workDirFilePath("confignode" + File.separator + "sbin", scriptName); + } + + @Override + protected String getStopScriptPath() { + String scriptName = "stop-confignode.sh"; + if (SystemUtils.IS_OS_WINDOWS) { + scriptName = "stop-confignode.bat"; } + return workDirFilePath("confignode" + File.separator + "sbin", scriptName); } } diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/DataNode.java b/integration-test/src/main/java/org/apache/iotdb/it/env/DataNode.java index b49c0ec540..88848bdf8d 100644 --- a/integration-test/src/main/java/org/apache/iotdb/it/env/DataNode.java +++ b/integration-test/src/main/java/org/apache/iotdb/it/env/DataNode.java @@ -18,19 +18,14 @@ */ package org.apache.iotdb.it.env; +import org.apache.commons.lang3.SystemUtils; + import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.Properties; -import static org.junit.Assert.fail; - public class DataNode extends ClusterNodeBase { private final String targetConfigNode; - private final String configPath; private final int dataBlockManagerPort; private final int internalPort; @@ -67,55 +62,43 @@ public class DataNode extends ClusterNodeBase { + super.getId() + ".log"; super.setLogPath(logPath); + } - String scriptPath = - super.getNodePath() - + File.separator - + "template-node" - + File.separator - + "datanode" - + File.separator - + "sbin" - + File.separator - + "start-datanode" - + ".sh"; - super.setScriptPath(scriptPath); + @Override + protected void updateConfig(Properties properties) { + properties.setProperty("rpc_address", super.getIp()); + properties.setProperty("internal_ip", "127.0.0.1"); + properties.setProperty("rpc_port", String.valueOf(super.getPort())); + properties.setProperty("data_block_manager_port", String.valueOf(this.dataBlockManagerPort)); + properties.setProperty("internal_port", String.valueOf(this.internalPort)); + properties.setProperty( + "data_region_consensus_port", String.valueOf(this.dataRegionConsensusPort)); + properties.setProperty( + "schema_region_consensus_port", String.valueOf(this.schemaRegionConsensusPort)); + properties.setProperty("connection_timeout_ms", "30000"); + properties.setProperty("config_nodes", this.targetConfigNode); + } - this.configPath = - super.getNodePath() - + File.separator - + "template-node" - + File.separator - + "datanode" - + File.separator - + "conf" - + File.separator - + "iotdb-engine.properties"; + @Override + protected String getConfigPath() { + return workDirFilePath("datanode" + File.separator + "conf", "iotdb-engine.properties"); + } + + @Override + protected String getStartScriptPath() { + String scriptName = "start-datanode.sh"; + if (SystemUtils.IS_OS_WINDOWS) { + scriptName = "start-datanode.bat"; + } + return workDirFilePath("datanode" + File.separator + "sbin", scriptName); } @Override - public void changeConfig(Properties properties) { - try { - Properties configProperties = new Properties(); - configProperties.load(Files.newInputStream(Paths.get(this.configPath))); - configProperties.setProperty("rpc_address", super.getIp()); - configProperties.setProperty("internal_ip", "127.0.0.1"); - configProperties.setProperty("rpc_port", String.valueOf(super.getPort())); - configProperties.setProperty( - "data_block_manager_port", String.valueOf(this.dataBlockManagerPort)); - configProperties.setProperty("internal_port", String.valueOf(this.internalPort)); - configProperties.setProperty( - "data_region_consensus_port", String.valueOf(this.dataRegionConsensusPort)); - configProperties.setProperty( - "schema_region_consensus_port", String.valueOf(this.schemaRegionConsensusPort)); - configProperties.setProperty("connection_timeout_ms", "30000"); - configProperties.setProperty("config_nodes", this.targetConfigNode); - if (properties != null && !properties.isEmpty()) { - configProperties.putAll(properties); - } - configProperties.store(new FileWriter(this.configPath), null); - } catch (IOException ex) { - fail("Change the config of data node failed. " + ex); + protected String getStopScriptPath() { + String scriptName = "stop-datanode.sh"; + if (SystemUtils.IS_OS_WINDOWS) { + scriptName = "stop-datanode.bat"; } + return workDirFilePath("datanode" + File.separator + "sbin", scriptName); } }
