From 184b5dc66c2acbf881e0b894ae24f4493cb45f92 Mon Sep 17 00:00:00 2001 From: Wildan M Date: Sat, 25 May 2024 22:09:19 +0700 Subject: [PATCH] Add restart feature ability --- package-lock.json | 4 +- package.json | 2 +- src/controllers/logman.js | 9 ++++ src/executor/logman.js | 100 ++++++++++++++++++++++--------------- src/executor/runnercode.js | 9 ++++ 5 files changed, 82 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index a23433d..041196c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "domcloud-bridge", - "version": "0.48.1", + "version": "0.49.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "domcloud-bridge", - "version": "0.48.1", + "version": "0.49.0", "license": "MIT", "dependencies": { "axios": "^1.6.8", diff --git a/package.json b/package.json index db2e0da..2de16f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "domcloud-bridge", - "version": "0.48.1", + "version": "0.49.0", "description": "Deployment runner for DOM Cloud", "main": "app.js", "engines": { diff --git a/src/controllers/logman.js b/src/controllers/logman.js index 51a1602..3d2d651 100644 --- a/src/controllers/logman.js +++ b/src/controllers/logman.js @@ -22,5 +22,14 @@ export default function () { next(err); } }); + router.post('/restart', checkGet(['domain']), async function (req, res, next) { + try { + let domain = await virtualminExec.getDomainInfo(req.query.domain.toString()); + let output = await logmanExec.restartPassenger(domain); + return res.json(output); + } catch (err) { + next(err); + } + }); return router; } \ No newline at end of file diff --git a/src/executor/logman.js b/src/executor/logman.js index c8b0811..cfdcd32 100644 --- a/src/executor/logman.js +++ b/src/executor/logman.js @@ -41,45 +41,7 @@ class LogmanExecutor { "tail", "-n", n, domain['Error log']]); case 'passenger': const user = domain['Username']; - let peo; - try { - const pe = process.env.NODE_ENV === 'development' ? - { stdout: await readFile('./test/passenger-status', { encoding: 'utf-8' }) } : - await spawnSudoUtil("SHELL_SUDO", [user, - "passenger-status", "--show=xml"]); - peo = pe.stdout.trim(); - } catch (error) { - } - if (!peo) { - return { - code: 255, - stderr: 'No passenger app is found or it\'s not initialized yet', - } - } - const parser = new XMLParser(); - let peom = parser.parse(peo); - let peoma = peom?.info?.supergroups?.supergroup; - if (!peoma) { - return { - code: 255, - stderr: 'incomplete response from passenger-status', - stdout: '' - } - } - let peomaa = Array.isArray(peoma) ? peoma : [peoma]; - let peomaps = peomaa.map(x => x.group).filter(x => x.processes); - if (!peomaps.length) { - return { - code: 255, - stderr: 'No processes reported from passenger-status is running', - stdout: '' - } - } - let procs = peomaps.reduce((a, b) => { - let x = (Array.isArray(b.processes.process) ? b.processes.process : [b.processes.process]); - a[b.name] = x.map(y => y.pid).filter(y => typeof y === "number"); - return a; - }, {}); + const procs = await this.getPassengerPids(user); let pids = Object.values(procs).flatMap(x => x).join('\\|'); let pes = await spawnSudoUtil("SHELL_SUDO", ["root", "bash", "-c", `grep -w "\\(^App\\|process\\) \\(${pids}\\)" "${this.PASSENGERLOG}" | tail -n ${n}` @@ -96,6 +58,66 @@ class LogmanExecutor { } } } + /** + * @param {any} domain + */ + async restartPassenger(domain) { + const user = domain['Username']; + const procs = await this.getPassengerPids(user); + let pids = Object.values(procs).flatMap(x => x).join(' '); + if (pids) { + await spawnSudoUtil("SHELL_SUDO", ["root", + "bash", "-c", `kill -9 ${pids}` + ]); + return "Sent SIGKILL to processes " + pids; + } + return "No processes currently running."; + } + /** + * @param {string} user + */ + async getPassengerPids(user) { + let peo; + try { + const pe = process.env.NODE_ENV === 'development' ? + { stdout: await readFile('./test/passenger-status', { encoding: 'utf-8' }) } : + await spawnSudoUtil("SHELL_SUDO", [user, + "passenger-status", "--show=xml"]); + peo = pe.stdout.trim(); + } catch (error) { + } + if (!peo) { + return { + code: 255, + stderr: 'No passenger app is found or it\'s not initialized yet', + } + } + const parser = new XMLParser(); + let peom = parser.parse(peo); + let peoma = peom?.info?.supergroups?.supergroup; + if (!peoma) { + return { + code: 255, + stderr: 'incomplete response from passenger-status', + stdout: '' + } + } + let peomaa = Array.isArray(peoma) ? peoma : [peoma]; + let peomaps = peomaa.map(x => x.group).filter(x => x.processes); + if (!peomaps.length) { + return { + code: 255, + stderr: 'No processes reported from passenger-status is running', + stdout: '' + } + } + let procs = peomaps.reduce((a, b) => { + let x = (Array.isArray(b.processes.process) ? b.processes.process : [b.processes.process]); + a[b.name] = x.map(y => y.pid).filter(y => typeof y === "number"); + return a; + }, {}); + return procs; + } } export const logmanExec = new LogmanExecutor(); diff --git a/src/executor/runnercode.js b/src/executor/runnercode.js index 90f0eb9..2b73266 100644 --- a/src/executor/runnercode.js +++ b/src/executor/runnercode.js @@ -1,5 +1,6 @@ import { getJavaVersion, getPythonVersion, getRubyVersion } from "../util.js"; import { dockerExec } from "./docker.js"; +import { logmanExec } from "./logman.js"; /** * @param {string} key @@ -11,6 +12,10 @@ import { dockerExec } from "./docker.js"; export async function runConfigCodeFeatures(key, value, writeLog, domaindata, sshExec) { let arg; switch (key) { + case 'restart': + await writeLog("$> Restarting passenger processes"); + await writeLog(await logmanExec.restartPassenger(domaindata)); + break; case 'docker': await sshExec(`export XDG_RUNTIME_DIR=/run/user/$(id -u)`, false); await sshExec(`export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus`, false); @@ -58,6 +63,7 @@ export async function runConfigCodeFeatures(key, value, writeLog, domaindata, ss await writeLog("$> Removing Node engine"); await sshExec("rm -rf ~/.local/opt/node-* ~/.local/opt/node ~/Downloads/webi/node"); await sshExec("rm -rf ~/.cache/yarn ~/.cache/node ~/.config/yarn ~/.npm"); + await sshExec("pathman remove .local/opt/node/bin"); } else { if (value == "latest" || value == "current") { arg = ""; @@ -78,6 +84,7 @@ export async function runConfigCodeFeatures(key, value, writeLog, domaindata, ss if (arg == 'off') { await writeLog("$> Removing Deno engine"); await sshExec("rm -rf ~/.local/opt/deno-* ~/.deno ~/.local/bin/deno ~/Downloads/webi/deno"); + await sshExec("pathman remove ~/.deno/bin/"); } else { if (value == "latest" || value == "current") { arg = ""; @@ -118,6 +125,7 @@ export async function runConfigCodeFeatures(key, value, writeLog, domaindata, ss if (arg == 'off') { await writeLog("$> Removing Rust engine"); await sshExec("rustup self uninstall -y"); + await sshExec(`pathman remove $HOME/.cargo/bin`); } else { await writeLog(arg ? "$> Changing Rust engine to " + arg : "$> installing Rust engine"); await sshExec(`command -v rustup &> /dev/null || (curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none)`); @@ -181,6 +189,7 @@ export async function runConfigCodeFeatures(key, value, writeLog, domaindata, ss if (arg == 'off') { await writeLog("$> Removing Dotnet engine"); await sshExec("rm -rf ~/.dotnet"); + await sshExec("pathman remove ~/.dotnet"); } else { if (value == "latest" || value == "current") { arg = "--version latest";