Skip to content

Commit

Permalink
Kill any previously running processes before starting yaci-cli. (#96)
Browse files Browse the repository at this point in the history
* #94 Create pid files for each process and perform cleanup during CLI startup

* #94 If yaci-cli.pid exists, kill the process and then invoke yaci-cli

* Bump version
  • Loading branch information
satran004 authored Jan 3, 2025
1 parent 16b1256 commit 35999e3
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.bloxbean.cardano.yacicli;

import com.bloxbean.cardano.yacicli.localcluster.config.GenesisConfig;
import com.bloxbean.cardano.yacicli.util.ProcessUtil;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -11,13 +14,20 @@
@EnableConfigurationProperties(GenesisConfig.class)
@Slf4j
public class YaciCliApplication {
@Autowired
private ProcessUtil processUtil;

public static void main(String[] args) {
new SpringApplicationBuilder(YaciCliApplication.class)
.logStartupInfo(false)
.run(args);
}

@PostConstruct
public void stopRunningProcesses() {
processUtil.killRunningProcesses();
}

@PreDestroy
public void onShutDown() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
@RequiredArgsConstructor
@Slf4j
public class ClusterStartService {
public static final String SUBMIT_API_PROCESS_NAME = "submit-api";
public static final String NODE_PROCESS_NAME = "node";
private final ClusterConfig clusterConfig;
private final ClusterPortInfoHelper clusterPortInfoHelper;
private final ProcessUtil processUtil;
Expand Down Expand Up @@ -123,6 +125,7 @@ public void stopCluster(Consumer<String> writer) {
if (processes != null && processes.size() > 0)
writer.accept(info("Trying to stop the running cluster ..."));

boolean error = false;
for (Process process : processes) {
if (process != null && process.isAlive()) {
process.descendants().forEach(processHandle -> {
Expand All @@ -139,6 +142,12 @@ public void stopCluster(Consumer<String> writer) {
}
}

if (!error) {
//clean pid files
processUtil.deletePidFile(NODE_PROCESS_NAME);
processUtil.deletePidFile(SUBMIT_API_PROCESS_NAME);
}

logs.clear();
submitApiLogs.clear();
} catch (Exception e) {
Expand Down Expand Up @@ -200,7 +209,7 @@ private Process startNode(Path clusterFolder, ClusterInfo clusterInfo, Consumer<
builder.directory(nodeStartDir);

writer.accept(success("Starting node from directory : " + nodeStartDir.getAbsolutePath()));
Process process = processUtil.startLongRunningProcess("Node", builder, logs, writer);
Process process = processUtil.startLongRunningProcess(NODE_PROCESS_NAME, builder, logs, writer);
if (process == null) return null;

Path nodeSocketPath = clusterFolder.resolve(ClusterConfig.NODE_FOLDER_PREFIX).resolve("node.sock");
Expand Down Expand Up @@ -234,7 +243,7 @@ private Process startSubmitApi(ClusterInfo clusterInfo, Path clusterFolder, Cons
File submitApiStartDir = new File(clusterFolderPath);
builder.directory(submitApiStartDir);

Process process = processUtil.startLongRunningProcess("SubmitApi", builder, submitApiLogs, writer);
Process process = processUtil.startLongRunningProcess(SUBMIT_API_PROCESS_NAME, builder, submitApiLogs, writer);
if (process == null) return null;

writer.accept(success("Started submit api : http://localhost:" + clusterPortInfoHelper.getSubmitApiPort(clusterInfo)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,17 @@
@RequiredArgsConstructor
@Slf4j
public class OgmiosService {
private final static String OGMIOS_PROCESS_NAME = "ogmios";
private final static String KUPO_PROCESS_NAME = "kupo";
private final ApplicationConfig appConfig;
private final ClusterService clusterService;
private final ClusterConfig clusterConfig;
private final ClusterPortInfoHelper clusterPortInfoHelper;
private final TemplateEngine templateEngine;
private final ProcessUtil processUtil;

private List<Process> processes = new ArrayList<>();

@Autowired
private TemplateEngine templateEngine;

@Autowired
private ProcessUtil processUtil;

private Queue<String> ogmiosLogs = EvictingQueue.create(300);
private Queue<String> kupoLogs = EvictingQueue.create(300);

Expand Down Expand Up @@ -181,6 +179,7 @@ public void handleClusterStopped(ClusterStopped clusterStopped) {
if (processes != null && processes.size() > 0)
writeLn(info("Trying to stop ogmios/kupo ..."));

boolean error = false;
for (Process process : processes) {
if (process != null && process.isAlive()) {
process.descendants().forEach(processHandle -> {
Expand All @@ -195,10 +194,17 @@ public void handleClusterStopped(ClusterStopped clusterStopped) {
writeLn(success("Killed : " + process));
} else {
writeLn(error("Process could not be killed : " + process));
error = true;
}
}
}

if (!error) {
//clean pid files
processUtil.deletePidFile(OGMIOS_PROCESS_NAME);
processUtil.deletePidFile(KUPO_PROCESS_NAME);
}

ogmiosLogs.clear();
} catch (Exception e) {
log.error("Error stopping process", e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.bloxbean.cardano.yacicli.localcluster.events.ClusterStopped;
import com.bloxbean.cardano.yacicli.util.PortUtil;
import com.bloxbean.cardano.yacicli.util.ProcessStream;
import com.bloxbean.cardano.yacicli.util.ProcessUtil;
import com.google.common.collect.EvictingQueue;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -35,12 +36,14 @@
@RequiredArgsConstructor
@Slf4j
public class YaciStoreService {
private static final String STORE_PROCESS_NAME = "yaci-store";
private final ApplicationConfig appConfig;
private final ClusterService clusterService;
private final ClusterConfig clusterConfig;
private final JreResolver jreResolver;
private final YaciStoreConfigBuilder yaciStoreConfigBuilder;
private final YaciStoreCustomDbHelper customDBHelper;
private final ProcessUtil processUtil;

private List<Process> processes = new ArrayList<>();

Expand Down Expand Up @@ -73,6 +76,7 @@ public void handleClusterStarted(ClusterStarted clusterStarted) {
Process process = startStoreApp(clusterInfo, era);
if (process != null)
processes.add(process);

// Process viewerProcess = startViewerApp(clusterStarted.getClusterName());
// processes.add(viewerProcess);
} catch (Exception e) {
Expand Down Expand Up @@ -197,6 +201,8 @@ private Process startStoreApp(ClusterInfo clusterInfo, Era era) throws IOExcepti
writeLn("###########################################################################################################################");
}

processUtil.createProcessId(STORE_PROCESS_NAME, process);

return process;
}

Expand Down Expand Up @@ -249,6 +255,7 @@ public void handleClusterStopped(ClusterStopped clusterStopped) {
if (processes != null && processes.size() > 0)
writeLn(info("Trying to stop yaci-store ..."));

boolean error = false;
for (Process process : processes) {
if (process != null && process.isAlive()) {
process.descendants().forEach(processHandle -> {
Expand All @@ -267,6 +274,11 @@ public void handleClusterStopped(ClusterStopped clusterStopped) {
}
}

if (!error) {
//clean pid files
processUtil.deletePidFile(STORE_PROCESS_NAME);
}

logs.clear();
} catch (Exception e) {
log.error("Error stopping process", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package com.bloxbean.cardano.yacicli.util;

import com.bloxbean.cardano.yacicli.commands.common.ExecutorHelper;
import com.bloxbean.cardano.yacicli.localcluster.ClusterConfig;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
Expand All @@ -16,6 +23,7 @@
@RequiredArgsConstructor
public class ProcessUtil {
private final ExecutorHelper executorHelper;
private final ClusterConfig clusterConfig;

public boolean executeAndFinish(ProcessBuilder processBuilder, String scriptPurpose, Consumer<String> writer) {
try {
Expand Down Expand Up @@ -75,11 +83,121 @@ public Process startLongRunningProcess(String processName, ProcessBuilder builde
return null;
}

createProcessId(processName, process);

//stop consuming error stream
errorStream.stop();
return process;
}

public String createProcessId(String processName, Process process) {
try {
var yaciCliHome = clusterConfig.getYaciCliHome();
// Validate the yaciCliHome directory
Path homePath = Paths.get(yaciCliHome);
if (!Files.exists(homePath)) {
writeLn(error("yaci-cli home directory does not exist: " + yaciCliHome));
return null;
}

if (!Files.isDirectory(homePath)) {
writeLn(error("yaci-cli home path is not a directory: " + yaciCliHome));
return null;
}

Path pids = homePath.resolve("pids");
if (!Files.exists(pids))
pids.toFile().mkdirs();

Path pidFilePath = pids.resolve(processName + ".pid");
long pid = process.pid();
Files.writeString(pidFilePath, String.valueOf(pid));

// Return the full path to the created PID file as a string
return pidFilePath.toString();
} catch (IOException e) {
writeLn(error("Failed to create the PID file: " + e.getMessage()));
return null;
}
}

public void deletePidFile(String processName) {
var yaciCliHome = clusterConfig.getYaciCliHome();
// Validate the yaciCliHome directory
Path homePath = Paths.get(yaciCliHome);
Path pids = homePath.resolve("pids");

var pidPath = pids.resolve(processName + ".pid");
if (Files.exists(pidPath)) {
pidPath.toFile().delete();
writeLn(info("Deleted pid file : " + processName + ".pid"));
}
}

public void killRunningProcesses() {
var yaciCliHome = clusterConfig.getYaciCliHome();

// Validate the yaciCliHome directory
Path homePath = Paths.get(yaciCliHome);
if (!Files.exists(homePath)) {
writeLn(error("The yaciCliHome directory does not exist: " + yaciCliHome));
return;
}
if (!Files.isDirectory(homePath)) {
writeLn(error("The yaciCliHome path is not a directory: " + yaciCliHome));
return;
}

// Resolve the pids directory
Path pidsDir = homePath.resolve("pids");
if (!Files.exists(pidsDir) || !Files.isDirectory(pidsDir)) {
writeLn(error("The pids directory does not exist or is not a directory: " + pidsDir));
return;
}

List<Long> pidList = new ArrayList<>();
try (DirectoryStream<Path> pidFiles = Files.newDirectoryStream(pidsDir, "*.pid")) {
for (Path pidFile : pidFiles) {
try {
// Read the PID from the file
String pidString = Files.readString(pidFile).trim();
if (!pidString.isEmpty()) {
pidList.add(Long.parseLong(pidString));
}
} catch (IOException | NumberFormatException e) {
writeLn(error("Failed to read or parse PID file: " + pidFile + " - " + e.getMessage()));
}
}
} catch (IOException e) {
writeLn(error("Failed to list PID files in directory: " + pidsDir + " : " + e.getMessage()));
return;
}

var deletedPids = new ArrayList<>();
for (Long pid : pidList) {
try {
ProcessHandle.of(pid)
.ifPresent(processHandle -> {
processHandle.descendants().forEach(ph -> {
deletedPids.add(ph.pid());
ph.destroyForcibly();
});
var result = processHandle.destroyForcibly();
if (!result) {
writeLn(error("Failed to kill process with PID : " + pid));
} else {
deletedPids.add(processHandle.pid());
}
});
} catch (Exception e) {
writeLn(error("Failed to kill process with PID: " + pid + " - " + e.getMessage()));
}
}

if (!deletedPids.isEmpty()) {
writeLn(info("Found existing processes. Killed processes with pids: " + deletedPids));
}
}
public String executeAndReturnOutput(ProcessBuilder processBuilder) {
try {
StringBuilder sb = new StringBuilder();
Expand Down
2 changes: 1 addition & 1 deletion config/version
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
tag=0.10.0-preview4
tag=0.10.0-preview5
revision=
12 changes: 6 additions & 6 deletions npm/yaci-devkit/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 35999e3

Please sign in to comment.