Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kill any previously running processes before starting yaci-cli. #96

Merged
merged 4 commits into from
Jan 3, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
#94 Create pid files for each process and perform cleanup during CLI …
…startup
satran004 committed Jan 3, 2025
commit 4aa9c47d47479e6978b983343a733cc4a3cdcbdc
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;
@@ -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() {
}
Original file line number Diff line number Diff line change
@@ -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;
@@ -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 -> {
@@ -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) {
@@ -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");
@@ -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)));
Original file line number Diff line number Diff line change
@@ -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);

@@ -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 -> {
@@ -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);
Original file line number Diff line number Diff line change
@@ -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;
@@ -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<>();

@@ -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) {
@@ -197,6 +201,8 @@ private Process startStoreApp(ClusterInfo clusterInfo, Era era) throws IOExcepti
writeLn("###########################################################################################################################");
}

processUtil.createProcessId(STORE_PROCESS_NAME, process);

return process;
}

@@ -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 -> {
@@ -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);
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;
@@ -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 {
@@ -75,9 +83,119 @@ 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));
}
}
}