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);
   }
 }

Reply via email to