This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 944334c8de73 CAMEL-26931: camel-jbang - camel infra stop --kill no
longer kills a process that got the pid of a service that is gone (#29940)
944334c8de73 is described below
commit 944334c8de73a0ded8c03a8de6d118c320f93f7e
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 19:30:05 2026 +0100
CAMEL-26931: camel-jbang - camel infra stop --kill no longer kills a
process that got the pid of a service that is gone (#29940)
A pid file of a service that was killed hard stays behind, and its pid may
be taken by another
process, which infra stop --kill then killed. A pid file older than the
process with its pid is
left over, as ProcessHelper.isStale finds for the files of an integration
(CAMEL-26911).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../core/commands/infra/InfraBaseCommand.java | 13 +++++++-
.../jbang/core/commands/infra/InfraStopTest.java | 39 ++++++++++++++++++++++
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
index a15ac0b7b524..a5d4277baa46 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraBaseCommand.java
@@ -47,6 +47,7 @@ import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.common.CamelTableColumns;
import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
import org.apache.camel.dsl.jbang.core.common.TerminalWidthHelper;
import org.apache.camel.dsl.jbang.core.model.InfraBaseDTO;
import org.apache.camel.dsl.jbang.run.common.PathUtils;
@@ -158,7 +159,9 @@ public abstract class InfraBaseCommand extends CamelCommand
{
// not a file we recognise, so neither report nor prune it
continue;
}
- if (!aliveCheck.test(pidNumber)) {
+ // a pid file older than the process with its pid is left over
from a service that is gone, and the
+ // pid is now of another process, which infra stop --kill
would kill (CAMEL-26931)
+ if (!aliveCheck.test(pidNumber) || isReusedPid(pidNumber,
pidFile)) {
pruneStaleFiles(camelDir, sn, pid);
continue;
}
@@ -175,6 +178,14 @@ public abstract class InfraBaseCommand extends
CamelCommand {
return answer;
}
+ /**
+ * Whether the pid file was last written before the process with the pid
started, as {@link ProcessHelper#isStale}
+ * finds for the files of an integration (CAMEL-26911).
+ */
+ private static boolean isReusedPid(long pid, Path pidFile) {
+ return ProcessHandle.of(pid).map(ph -> ProcessHelper.isStale(ph,
pidFile)).orElse(false);
+ }
+
/**
* Removes the pid and log files left behind by an instance whose process
is gone, so it stops being reported as
* running.
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
index 96942372ac4c..a928cf78e41b 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/infra/InfraStopTest.java
@@ -16,6 +16,11 @@
*/
package org.apache.camel.dsl.jbang.core.commands.infra;
+import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.common.CliException;
import org.apache.camel.dsl.jbang.core.common.CliExitCode;
@@ -89,6 +94,40 @@ class InfraStopTest extends InfraCommandTestSupport {
assertThat(pidFile("kafka", 1234)).exists();
}
+ @Test
+ void aPidFileOlderThanTheProcessOfItsPidIsNotTheService() throws Exception
{
+ // the service was killed hard and its pid is now of another process,
here this JVM: infra stop --kill killed
+ // that process (CAMEL-26931)
+ long pid = ProcessHandle.current().pid();
+ writePidFile("kafka", pid, "{}");
+ writeLogFile("kafka", pid, "started");
+ Instant started =
ProcessHandle.current().info().startInstant().orElseThrow();
+ Files.setLastModifiedTime(pidFile("kafka", pid),
FileTime.from(started.minus(1, ChronoUnit.HOURS)));
+
+ InfraStop command = command("kafka");
+ command.aliveCheck = p -> true;
+
+ assertThatThrownBy(command::doCall)
+ .isInstanceOf(CliException.class)
+ .extracting(e -> ((CliException)
e).getExitCode()).isEqualTo(CliExitCode.NOT_FOUND);
+ assertThat(printer.getStdout()).doesNotContain("Shutting down");
+ // the files it left behind are removed
+ assertThat(pidFile("kafka", pid)).doesNotExist();
+ assertThat(logFile("kafka", pid)).doesNotExist();
+ }
+
+ @Test
+ void aPidFileWrittenByTheProcessIsTheService() throws Exception {
+ long pid = ProcessHandle.current().pid();
+ writePidFile("kafka", pid, "{}");
+
+ InfraStop command = command("kafka");
+ command.aliveCheck = p -> true;
+
+ assertThat(command.doCall()).isZero();
+ assertThat(printer.getStdout()).isEqualTo("Shutting down external
services (PID: " + pid + ")");
+ }
+
@Test
void serviceWhoseProcessIsGoneIsNotFound() throws Exception {
writePidFile("kafka", 1234, "{}");