This is an automated email from the ASF dual-hosted git repository.
zihaoxiang pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git
The following commit(s) were added to refs/heads/dev by this push:
new e8f8a8ca9a [Fix-17316][Task-API] Add check process status after
killing task (#17320)
e8f8a8ca9a is described below
commit e8f8a8ca9a5fa282cd2f47baa0c5d0b255049964
Author: njnu-seafish <[email protected]>
AuthorDate: Fri Jul 18 15:19:12 2025 +0800
[Fix-17316][Task-API] Add check process status after killing task (#17320)
---
deploy/kubernetes/dolphinscheduler/README.md | 1 +
deploy/kubernetes/dolphinscheduler/values.yaml | 3 +
.../resources/docker/file-manage/common.properties | 6 +-
.../common/constants/Constants.java | 5 +
.../src/main/resources/common.properties | 3 +
.../src/test/resources/common.properties | 3 +
.../resources/docker/file-manage/common.properties | 5 +-
.../plugin/task/api/utils/ProcessUtils.java | 111 +++++++++++++++++++--
.../plugin/task/api/utils/ProcessUtilsTest.java | 96 ++++++++++++++++--
.../src/test/resources/common.properties | 3 +
10 files changed, 220 insertions(+), 16 deletions(-)
diff --git a/deploy/kubernetes/dolphinscheduler/README.md
b/deploy/kubernetes/dolphinscheduler/README.md
index bcdf1eb625..ad3de24692 100644
--- a/deploy/kubernetes/dolphinscheduler/README.md
+++ b/deploy/kubernetes/dolphinscheduler/README.md
@@ -155,6 +155,7 @@ Please refer to the [Quick Start in
Kubernetes](../../../docs/docs/en/guide/inst
| conf.common."resource.manager.httpaddress.port" | int | `8088` |
resourcemanager port, the default value is 8088 if not specified |
| conf.common."resource.storage.type" | string | `"S3"` | resource storage
type: HDFS, S3, OSS, GCS, ABS, NONE |
| conf.common."resource.storage.upload.base.path" | string |
`"/dolphinscheduler"` | resource store on HDFS/S3 path, resource file will
store to this base path, self configuration, please make sure the directory
exists on hdfs and have read write permissions. "/dolphinscheduler" is
recommended |
+| conf.common."shell.kill.wait.timeout" | int | `10` | If the shell process is
still active after this timeout value (in seconds), then will use kill -9 to
kill it |
| conf.common."sudo.enable" | bool | `true` | use sudo or not, if set true,
executing user is tenant user and deploy user needs sudo permissions; if set
false, executing user is the deploy user and doesn't need sudo permissions |
| conf.common."support.hive.oneSession" | bool | `false` | Whether hive SQL is
executed in the same session |
| conf.common."task.resource.limit.state" | bool | `false` | Task resource
limit state |
diff --git a/deploy/kubernetes/dolphinscheduler/values.yaml
b/deploy/kubernetes/dolphinscheduler/values.yaml
index 2c881c774c..85c112f89f 100644
--- a/deploy/kubernetes/dolphinscheduler/values.yaml
+++ b/deploy/kubernetes/dolphinscheduler/values.yaml
@@ -345,6 +345,9 @@ conf:
# -- development state
development.state: false
+ # -- If the shell process is still active after this timeout value (in
seconds), then will use kill -9 to kill it
+ shell.kill.wait.timeout: 10
+
# -- set path of conda.sh
conda.path: /opt/anaconda3/etc/profile.d/conda.sh
diff --git
a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/docker/file-manage/common.properties
b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/docker/file-manage/common.properties
index 4070c1705f..d429884212 100644
---
a/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/docker/file-manage/common.properties
+++
b/dolphinscheduler-api-test/dolphinscheduler-api-test-case/src/test/resources/docker/file-manage/common.properties
@@ -99,6 +99,9 @@ sudo.enable=true
# development state
development.state=false
+# If the shell process is still active after this timeout value (in seconds),
then will use kill -9 to kill it
+shell.kill.wait.timeout=10
+
# set path of conda.sh
conda.path=/opt/anaconda3/etc/profile.d/conda.sh
@@ -111,4 +114,5 @@
ml.mlflow.preset_repository=https://github.com/apache/dolphinscheduler-mlflow
ml.mlflow.preset_repository_version="main"
# way to collect applicationId: log(original regex match), aop
-appId.collect: log
+appId.collect=log
+
diff --git
a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java
b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java
index 7d365bf86f..03742f875f 100644
---
a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java
+++
b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/constants/Constants.java
@@ -69,6 +69,11 @@ public final class Constants {
*/
public static final String DEVELOPMENT_STATE = "development.state";
+ /**
+ * shell.kill.wait.timeout: this property defines the wait timeout in
seconds before using SIGKILL.
+ */
+ public static final String SHELL_KILL_WAIT_TIMEOUT =
"shell.kill.wait.timeout";
+
/**
* sudo enable
*/
diff --git a/dolphinscheduler-common/src/main/resources/common.properties
b/dolphinscheduler-common/src/main/resources/common.properties
index 00eff646ae..81143ae21d 100644
--- a/dolphinscheduler-common/src/main/resources/common.properties
+++ b/dolphinscheduler-common/src/main/resources/common.properties
@@ -84,6 +84,9 @@ dolphin.scheduler.network.interface.restrict=docker0
# development state
development.state=false
+# If the shell process is still active after this timeout value (in seconds),
then will use kill -9 to kill it
+shell.kill.wait.timeout=10
+
# set path of conda.sh
conda.path=/opt/anaconda3/etc/profile.d/conda.sh
diff --git a/dolphinscheduler-common/src/test/resources/common.properties
b/dolphinscheduler-common/src/test/resources/common.properties
index 9b9eaa2e47..f849b1c3e0 100644
--- a/dolphinscheduler-common/src/test/resources/common.properties
+++ b/dolphinscheduler-common/src/test/resources/common.properties
@@ -148,6 +148,9 @@ dolphin.scheduler.network.interface.restrict=docker0
# development state
development.state=false
+# If the shell process is still active after this timeout value (in seconds),
then will use kill -9 to kill it
+shell.kill.wait.timeout=10
+
# set path of conda.sh
conda.path=/opt/anaconda3/etc/profile.d/conda.sh
diff --git
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/file-manage/common.properties
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/file-manage/common.properties
index 6f5d24d082..d5cee84417 100644
---
a/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/file-manage/common.properties
+++
b/dolphinscheduler-e2e/dolphinscheduler-e2e-case/src/test/resources/docker/file-manage/common.properties
@@ -99,6 +99,9 @@ sudo.enable=true
# development state
development.state=false
+# If the shell process is still active after this timeout value (in seconds),
then will use kill -9 to kill it
+shell.kill.wait.timeout=10
+
# set path of conda.sh
conda.path=/opt/anaconda3/etc/profile.d/conda.sh
@@ -111,4 +114,4 @@
ml.mlflow.preset_repository=https://github.com/apache/dolphinscheduler-mlflow
ml.mlflow.preset_repository_version="main"
# way to collect applicationId: log(original regex match), aop
-appId.collect: log
+appId.collect=log
diff --git
a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtils.java
b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtils.java
index b27697fd41..3b47004330 100644
---
a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtils.java
+++
b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/main/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtils.java
@@ -17,11 +17,14 @@
package org.apache.dolphinscheduler.plugin.task.api.utils;
+import static
org.apache.dolphinscheduler.common.constants.Constants.SLEEP_TIME_MILLIS;
import static
org.apache.dolphinscheduler.plugin.task.api.TaskConstants.APPID_COLLECT;
import static org.apache.dolphinscheduler.plugin.task.api.TaskConstants.COMMA;
import static
org.apache.dolphinscheduler.plugin.task.api.TaskConstants.DEFAULT_COLLECT_WAY;
import static
org.apache.dolphinscheduler.plugin.task.api.TaskConstants.TASK_TYPE_SET_K8S;
+import org.apache.dolphinscheduler.common.constants.Constants;
+import org.apache.dolphinscheduler.common.thread.ThreadUtils;
import org.apache.dolphinscheduler.common.utils.OSUtils;
import org.apache.dolphinscheduler.common.utils.PropertyUtils;
import org.apache.dolphinscheduler.plugin.task.api.K8sTaskExecutionContext;
@@ -46,8 +49,10 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@@ -56,6 +61,10 @@ import io.fabric8.kubernetes.client.dsl.LogWatch;
@Slf4j
public final class ProcessUtils {
+ // If the shell process is still active after this timeout value (in
seconds), then will use kill -9 to kill it
+ private static final Integer SHELL_KILL_WAIT_TIMEOUT =
+ PropertyUtils.getInt(Constants.SHELL_KILL_WAIT_TIMEOUT, 10);
+
private ProcessUtils() {
throw new IllegalStateException("Utility class");
}
@@ -77,13 +86,18 @@ public final class ProcessUtils {
/**
* Expression of PID recognition in Windows scene
*/
- private static final Pattern WINDOWSPATTERN = Pattern.compile("(\\d+)");
+ private static final Pattern WINDOWSPATTERN =
Pattern.compile("\\((\\d+)\\)");
/**
* Expression of PID recognition in Linux scene
*/
private static final Pattern LINUXPATTERN =
Pattern.compile("\\((\\d+)\\)");
+ /**
+ * PID recognition pattern
+ */
+ private static final Pattern PID_PATTERN = Pattern.compile("\\s+");
+
/**
* Terminate the task process, support multi-level signal processing and
fallback strategy
* @param request Task execution context
@@ -100,21 +114,24 @@ public final class ProcessUtils {
// Get all child processes
String pids = getPidsStr(processId);
- String[] pidArray = pids.split("\\s+");
+ String[] pidArray = PID_PATTERN.split(pids);
if (pidArray.length == 0) {
log.warn("No valid PIDs found for process: {}", processId);
return true;
}
+ // Convert PID string to list of integers
+ List<Integer> pidList =
Arrays.stream(pidArray).map(Integer::parseInt).collect(Collectors.toList());
+
// 1. Try to terminate gracefully (SIGINT)
- boolean gracefulKillSuccess = sendKillSignal("SIGINT", pids,
request.getTenantCode());
+ boolean gracefulKillSuccess = sendKillSignal("SIGINT", pidList,
request.getTenantCode());
if (gracefulKillSuccess) {
log.info("Successfully killed process tree using SIGINT,
processId: {}", processId);
return true;
}
// 2. Try to terminate forcefully (SIGTERM)
- boolean termKillSuccess = sendKillSignal("SIGTERM", pids,
request.getTenantCode());
+ boolean termKillSuccess = sendKillSignal("SIGTERM", pidList,
request.getTenantCode());
if (termKillSuccess) {
log.info("Successfully killed process tree using SIGTERM,
processId: {}", processId);
return true;
@@ -122,7 +139,7 @@ public final class ProcessUtils {
// 3. As a last resort, use `kill -9`
log.warn("SIGINT & SIGTERM failed, using SIGKILL as a last resort
for processId: {}", processId);
- boolean forceKillSuccess = sendKillSignal("SIGKILL", pids,
request.getTenantCode());
+ boolean forceKillSuccess = sendKillSignal("SIGKILL", pidList,
request.getTenantCode());
if (forceKillSuccess) {
log.info("Successfully sent SIGKILL signal to process tree,
processId: {}", processId);
} else {
@@ -139,23 +156,100 @@ public final class ProcessUtils {
/**
* Send a kill signal to a process group
* @param signal Signal type (SIGINT, SIGTERM, SIGKILL)
- * @param pids Process ID list
+ * @param pidList Process ID list
* @param tenantCode Tenant code
*/
- private static boolean sendKillSignal(String signal, String pids, String
tenantCode) {
+ private static boolean sendKillSignal(String signal, List<Integer>
pidList, String tenantCode) {
+ if (pidList == null || pidList.isEmpty()) {
+ log.info("No process needs to be killed.");
+ return true;
+ }
+
+ List<Integer> alivePidList = getAlivePidList(pidList, tenantCode);
+ if (alivePidList.isEmpty()) {
+ log.info("All processes already terminated.");
+ return true;
+ }
+
+ String pids = alivePidList.stream()
+ .map(String::valueOf)
+ .collect(Collectors.joining(" "));
+
try {
+ // 1. Send the kill signal
String killCmd = String.format("kill -s %s %s", signal, pids);
killCmd = OSUtils.getSudoCmd(tenantCode, killCmd);
log.info("Sending {} to process group: {}, command: {}", signal,
pids, killCmd);
OSUtils.exeCmd(killCmd);
- return true;
+ // 2. Wait for the processes to terminate with a timeout-based
polling mechanism
+ // Max wait time
+ long timeoutMillis =
TimeUnit.SECONDS.toMillis(SHELL_KILL_WAIT_TIMEOUT);
+
+ long startTime = System.currentTimeMillis();
+ while (!alivePidList.isEmpty() && (System.currentTimeMillis() -
startTime < timeoutMillis)) {
+ // Remove if process is no longer alive
+ alivePidList.removeIf(pid -> !isProcessAlive(pid, tenantCode));
+ if (!alivePidList.isEmpty()) {
+ // Wait for a short interval before checking process
statuses again, to avoid excessive CPU usage
+ // from tight-loop polling.
+ ThreadUtils.sleep(SLEEP_TIME_MILLIS);
+ }
+ }
+
+ // 3. Return final result based on whether all processes were
terminated
+ if (alivePidList.isEmpty()) {
+ // All processes have been successfully terminated
+ log.debug("Kill command: {}, kill succeeded", killCmd);
+ return true;
+ } else {
+ String remainingPids = alivePidList.stream()
+ .map(String::valueOf)
+ .collect(Collectors.joining(" "));
+ log.info("Kill command: {}, timed out, still running PIDs:
{}", killCmd, remainingPids);
+ return false;
+ }
} catch (Exception e) {
log.error("Error sending {} to process: {}", signal, pids, e);
return false;
}
}
+ /**
+ * Returns a list of process IDs that are still running.
+ * This method filters the provided list of PIDs by checking whether each
process is still active
+ *
+ * @param pidList the list of process IDs to check
+ * @param tenantCode the tenant identifier used for permission control or
logging context
+ * @return a new list containing only the PIDs of processes that are still
running;
+ * returns an empty list if none are alive
+ */
+ private static List<Integer> getAlivePidList(List<Integer> pidList, String
tenantCode) {
+ return pidList.stream()
+ .filter(pid -> isProcessAlive(pid, tenantCode))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Check if a process with the specified PID is alive.
+ *
+ * @param pid the process ID to check
+ * @return true if the process exists and is running, false otherwise
+ */
+ private static boolean isProcessAlive(int pid, String tenantCode) {
+ try {
+ // Use kill -0 to check if the process exists; it does not
actually send a signal
+ String checkCmd = String.format("kill -0 %d", pid);
+ checkCmd = OSUtils.getSudoCmd(tenantCode, checkCmd);
+ OSUtils.exeCmd(checkCmd);
+ // If the command executes successfully, the process exists
+ return true;
+ } catch (Exception e) {
+ // If the command fails, the process does not exist
+ return false;
+ }
+ }
+
/**
* get pids str.
*
@@ -249,6 +343,7 @@ public final class ProcessUtils {
}
ApplicationManager applicationManager =
applicationManagerMap.get(ResourceManagerType.YARN);
applicationManager.killApplication(new
YarnApplicationManagerContext(executePath, tenantCode, appIds));
+ log.info("yarn application [{}] is killed or already
finished", appIds);
}
} catch (Exception e) {
log.error("Cancel application failed: {}", e.getMessage());
diff --git
a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtilsTest.java
b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtilsTest.java
index a9a164c7c8..526dc1a7f3 100644
---
a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtilsTest.java
+++
b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/java/org/apache/dolphinscheduler/plugin/task/api/utils/ProcessUtilsTest.java
@@ -111,30 +111,114 @@ public class ProcessUtilsTest {
}
@Test
- void testKillProcessSuccessWithSigInt() throws Exception {
+ void testKillProcessSuccessWithNoAlivePids() {
// Arrange
TaskExecutionContext taskRequest =
Mockito.mock(TaskExecutionContext.class);
Mockito.when(taskRequest.getProcessId()).thenReturn(12345);
Mockito.when(taskRequest.getTenantCode()).thenReturn("testTenant");
// Mock getPidsStr
- mockedOSUtils.when(() ->
OSUtils.exeCmd(Mockito.matches(".*pstree.*12345"))).thenReturn("1234 12345");
+ mockedOSUtils.when(() ->
OSUtils.exeCmd(Mockito.matches(".*pstree.*12345")))
+ .thenReturn("sudo(12345)---86.sh(1234)");
+
+ // Mock kill -0
+ mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -0.*")))
+ .thenReturn("kill -0 12345");
+ mockedOSUtils.when(() -> OSUtils.exeCmd(Mockito.matches(".*kill
-0.*")))
+ .thenThrow(new RuntimeException("Command failed"));
+
+ // Act
+ boolean result = ProcessUtils.kill(taskRequest);
+
+ // Assert
+ Assertions.assertTrue(result);
+
+ // Verify SIGINT, SIGTERM, SIGKILL never called
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGINT 12345"),
Mockito.never());
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGTERM 12345"),
Mockito.never());
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGKILL 12345"),
Mockito.never());
+ }
+
+ @Test
+ void testKillProcessSuccessWithSigInt() {
+ // Arrange
+ TaskExecutionContext taskRequest =
Mockito.mock(TaskExecutionContext.class);
+ Mockito.when(taskRequest.getProcessId()).thenReturn(12345);
+ Mockito.when(taskRequest.getTenantCode()).thenReturn("testTenant");
+
+ // Mock getPidsStr
+ mockedOSUtils.when(() ->
OSUtils.exeCmd(Mockito.matches(".*pstree.*12345")))
+ .thenReturn("sudo(12345)---86.sh(1234)");
// Mock SIGINT command
mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -s SIGINT.*")))
.thenReturn("kill -s SIGINT 12345");
mockedOSUtils.when(() -> OSUtils.exeCmd("kill -s SIGINT
12345")).thenReturn("");
- // Mock process check - process dies after SIGINT
- mockedOSUtils.when(() -> OSUtils.exeCmd("ps -p
12345")).thenReturn(null);
+ // Mock kill -0
+ mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -0.*")))
+ .thenReturn("kill -0 12345");
+ // Mock the static method OSUtils.exeCmd that matches "kill -0" command
+ mockedOSUtils.when(() -> OSUtils.exeCmd(Mockito.matches(".*kill
-0.*")))
+ .thenReturn("") // First invocation succeeds (process is alive)
+ .thenReturn("") // Second invocation succeeds (process is
alive)
+ // Subsequent invocations fail (process is no longer alive)
+ .thenThrow(new RuntimeException("Command failed"));
// Act
boolean result = ProcessUtils.kill(taskRequest);
// Assert
Assertions.assertTrue(result);
- // Verify SIGKILL was never called
- mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -9 12345"),
Mockito.never());
+
+ // Verify SIGINT was called
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGINT 12345"),
Mockito.times(1));
+ // Verify SIGTERM,SIGKILL was never called
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGTERM 12345"),
Mockito.never());
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGKILL 12345"),
Mockito.never());
+ }
+
+ @Test
+ void testKillProcessFail() {
+ // Arrange
+ TaskExecutionContext taskRequest =
Mockito.mock(TaskExecutionContext.class);
+ Mockito.when(taskRequest.getProcessId()).thenReturn(12345);
+ Mockito.when(taskRequest.getTenantCode()).thenReturn("testTenant");
+
+ // Mock getPidsStr
+ mockedOSUtils.when(() ->
OSUtils.exeCmd(Mockito.matches(".*pstree.*12345")))
+ .thenReturn("sudo(12345)---86.sh(1234)");
+
+ // Mock SIGINT command
+ mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -s SIGINT.*")))
+ .thenReturn("kill -s SIGINT 12345");
+ mockedOSUtils.when(() -> OSUtils.exeCmd("kill -s SIGINT
12345")).thenReturn("");
+
+ // Mock SIGTERM command
+ mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -s SIGTERM.*")))
+ .thenReturn("kill -s SIGTERM 12345");
+ mockedOSUtils.when(() -> OSUtils.exeCmd("kill -s SIGTERM
12345")).thenReturn("");
+
+ // Mock SIGKILL command
+ mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -s SIGKILL.*")))
+ .thenReturn("kill -s SIGKILL 12345");
+ mockedOSUtils.when(() -> OSUtils.exeCmd("kill -s SIGKILL
12345")).thenReturn("");
+
+ // Mock kill -0
+ mockedOSUtils.when(() -> OSUtils.getSudoCmd(Mockito.eq("testTenant"),
Mockito.matches("kill -0.*")))
+ .thenReturn("kill -0 12345");
+ mockedOSUtils.when(() -> OSUtils.exeCmd(Mockito.matches(".*kill
-0.*"))).thenReturn("");
+
+ // Act
+ boolean result = ProcessUtils.kill(taskRequest);
+
+ // Assert
+ Assertions.assertFalse(result);
+
+ // Verify SIGINT, SIGTERM, SIGKILL was called
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGINT 12345"),
Mockito.times(1));
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGTERM 12345"),
Mockito.times(1));
+ mockedOSUtils.verify(() -> OSUtils.exeCmd("kill -s SIGKILL 12345"),
Mockito.times(1));
}
@Test
diff --git
a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/resources/common.properties
b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/resources/common.properties
index f0d9698b8b..737c498208 100644
---
a/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/resources/common.properties
+++
b/dolphinscheduler-task-plugin/dolphinscheduler-task-api/src/test/resources/common.properties
@@ -88,6 +88,9 @@ sudo.enable=true
# development state
development.state=false
+# If the shell process is still active after this timeout value (in seconds),
then will use kill -9 to kill it
+shell.kill.wait.timeout=10
+
# set path of conda.sh
conda.path=/opt/anaconda3/etc/profile.d/conda.sh