From d78a501462b8518001a9c4f4eea2c9f9b3702d41 Mon Sep 17 00:00:00 2001 From: Fabian Hoffmann Date: Thu, 17 Feb 2022 16:50:13 +0100 Subject: [PATCH 1/2] Refactor: extract start command --- package-lock.json | 6 +- src/app-definition.type.ts | 4 + src/commands/start.command.ts | 133 ++++++++++++++++++++++++++++++ src/ecosystem.config.ts | 12 +++ src/index.ts | 148 +--------------------------------- 5 files changed, 155 insertions(+), 148 deletions(-) create mode 100644 src/app-definition.type.ts create mode 100644 src/commands/start.command.ts create mode 100644 src/ecosystem.config.ts diff --git a/package-lock.json b/package-lock.json index fe6ec4c..73b3b85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "1.0.0", "dependencies": { "commander": "^9.0.0", - "ts-node": "^10.4.0", - "typescript": "^4.5.5" + "ts-node": "^10.4.0" }, "bin": { "dev-process-manager": "src/index.ts" @@ -18,7 +17,8 @@ "devDependencies": { "@comet/eslint-config": "^0.0.1-canary.8102.6588", "eslint": "^7.0.0", - "ts-loader": "^8.0.0" + "ts-loader": "^8.0.0", + "typescript": "^4.5.5" }, "engines": { "node": "14" diff --git a/src/app-definition.type.ts b/src/app-definition.type.ts new file mode 100644 index 0000000..c8c45d9 --- /dev/null +++ b/src/app-definition.type.ts @@ -0,0 +1,4 @@ +export interface AppDefinition { + name: string; + script: string; +} diff --git a/src/commands/start.command.ts b/src/commands/start.command.ts new file mode 100644 index 0000000..f2277fc --- /dev/null +++ b/src/commands/start.command.ts @@ -0,0 +1,133 @@ +import { spawn, ChildProcess } from "child_process"; +import { Socket, createServer, createConnection } from "net"; +import { AppDefinition } from "../app-definition.type"; + +export const start = async (apps: AppDefinition[]) => { + const processes: { [key: string]: ChildProcess } = {}; + const logSockets: { socket: Socket, name: string | null; }[] = []; + let shuttingDown = false; + + function startProcess(app: AppDefinition) { + + console.log("starting " + app.script); + const p = spawn("bash", ["-c", app.script]); + p.stdout.on('data', data => { + process.stdout.write(data); + logSockets.forEach(s => { + if (!s.name || s.name == app.name) { + s.socket.write(data); + } + }); + }); + p.stderr.on('data', data => { + process.stderr.write(data); + logSockets.forEach(s => { + if (!s.name || s.name == app.name) { + s.socket.write(data); + } + }); + }); + p.on('close', code => { + if (!shuttingDown) { + console.error("process stopped", app.name, ", restarting"); + startProcess(app); + } + }) + p.on('error', (err) => { + // TODO handle + console.error(err); + console.error("Failed starting process", app.name); + }); + processes[app.name] = p; + } + + const server = createServer(); + server.listen(".pm.sock"); + server.on('connection', (s) => { + s.on('data', async (command) => { + //console.log("received command", command.toString()); + const cmd = command.toString(); + if (cmd == "logs" || cmd.startsWith("logs ")) { + const name = cmd != "logs" ? cmd.substring(5) : null; // null means all + if (name && !apps.find((app) => app.name === name)) { + console.error("Unknown name"); + } else { + logSockets.push({ socket: s, name }); + s.on('close', () => { + var index = logSockets.findIndex(i => i.socket == s); + if (index !== -1) { + logSockets.splice(index, 1); + } + }); + } + } else if (cmd.startsWith("restart ")) { + const name = cmd.substring(8); + const p = processes[name]; + if (!p) { + console.error("Unknown name"); + s.end(); + return; + } + + if (!p.killed) { + console.log("killing " + name); + p.kill("SIGINT"); + while (!p.killed) { + console.log("waiting for killed"); + await new Promise(r => setTimeout(r, 100)); + } + } + + const app = apps.find(i => i.name == name); + if (!app) { + console.error("Unknown name"); + s.end(); + return; + } + startProcess(app); + s.end(); + + } else if (cmd == "status") { + const response = Object.keys(processes).map(name => { + const p = processes[name]; + return { + name, + running: !p.killed + } + }); + console.log("sending status reponse", response); + s.write(JSON.stringify(response)); + s.end(); + } else if (cmd == "shutdown") { + console.log("shutting down"); + shuttingDown = true; + Object.values(processes).forEach(p => { + if (!p.killed) p.kill("SIGINT"); + }) + server.close(); + process.exit(); + } else { + console.error("Unknown command", cmd); + } + }); + + }); + + + apps.forEach(app => { + startProcess(app); + }); + + process.on("SIGINT", function () { + console.log("shutting down") + server.close(); + shuttingDown = true; + for (const name in processes) { + const p = processes[name]; + if (!p.killed) { + console.log("killing " + name); + p.kill("SIGINT"); + } + } + }); +} diff --git a/src/ecosystem.config.ts b/src/ecosystem.config.ts new file mode 100644 index 0000000..0fb1fbb --- /dev/null +++ b/src/ecosystem.config.ts @@ -0,0 +1,12 @@ +export default { + apps: [ + { + name: "sleep60", + script: "echo sleep60 && sleep 60", + }, + { + name: "sleep3", + script: "while true; do echo sleep3 && sleep 3; done", + }, + ] +}; diff --git a/src/index.ts b/src/index.ts index e0a621c..efa9543 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,29 +1,13 @@ import { spawn, ChildProcess } from "child_process"; import { Socket, createServer, createConnection } from "net"; import { Command } from 'commander'; - -const eco = { - apps: [ - { - name: "sleep60", - script: "echo sleep60 && sleep 60", - }, - { - name: "sleep3", - script: "while true; do echo sleep3 && sleep 3; done", - }, - ] -}; - -interface AppDefinition { - name: string; - script: string; -} +import { start } from "./commands/start.command"; +import eco from "./ecosystem.config"; const program = new Command(); program.command("start") .action(() => { - start(); + start(eco.apps); }); program.command("logs [name]") @@ -95,129 +79,3 @@ async function status() { console.log(data.toString()); }) } - -async function start() { - const processes: { [key: string]: ChildProcess } = {}; - const logSockets: { socket: Socket, name: string | null; }[] = []; - let shuttingDown = false; - - function startProcess(app: AppDefinition) { - - console.log("starting " + app.script); - const p = spawn("bash", ["-c", app.script]); - p.stdout.on('data', data => { - process.stdout.write(data); - logSockets.forEach(s => { - if (!s.name || s.name == app.name) { - s.socket.write(data); - } - }); - }); - p.stderr.on('data', data => { - process.stderr.write(data); - logSockets.forEach(s => { - if (!s.name || s.name == app.name) { - s.socket.write(data); - } - }); - }); - p.on('close', code => { - if (!shuttingDown) { - console.error("process stopped", app.name, ", restarting"); - startProcess(app); - } - }) - p.on('error', (err) => { - // TODO handle - console.error(err); - console.error("Failed starting process", app.name); - }); - processes[app.name] = p; - } - - const server = createServer(); - server.listen(".pm.sock"); - server.on('connection', (s) => { - s.on('data', async (command) => { - //console.log("received command", command.toString()); - const cmd = command.toString(); - if (cmd == "logs" || cmd.startsWith("logs ")) { - const name = cmd != "logs" ? cmd.substring(5) : null; // null means all - logSockets.push({ socket: s, name }); - s.on('close', () => { - var index = logSockets.findIndex(i => i.socket == s); - if (index !== -1) { - logSockets.splice(index, 1); - } - }); - } else if (cmd.startsWith("restart ")) { - const name = cmd.substring(8); - const p = processes[name]; - if (!p) { - console.error("Unknown name"); - s.end(); - return; - } - - if (!p.killed) { - console.log("killing " + name); - p.kill("SIGINT"); - while (!p.killed) { - console.log("waiting for killed"); - await new Promise(r => setTimeout(r, 100)); - } - } - - const app = eco.apps.find(i => i.name == name); - if (!app) { - console.error("Unknown name"); - s.end(); - return; - } - startProcess(app); - s.end(); - - } else if (cmd == "status") { - const response = Object.keys(processes).map(name => { - const p = processes[name]; - return { - name, - running: !p.killed - } - }); - console.log("sending status reponse", response); - s.write(JSON.stringify(response)); - s.end(); - } else if (cmd == "shutdown") { - console.log("shutting down"); - shuttingDown = true; - Object.values(processes).forEach(p => { - if (!p.killed) p.kill("SIGINT"); - }) - server.close(); - process.exit(); - } else { - console.error("Unknown command", cmd); - } - }); - - }); - - - eco.apps.forEach(app => { - startProcess(app); - }); - - process.on("SIGINT", function () { - console.log("shutting down") - server.close(); - shuttingDown = true; - for (const name in processes) { - const p = processes[name]; - if (!p.killed) { - console.log("killing " + name); - p.kill("SIGINT"); - } - } - }); -} From d3f1213944a6d52fba938179d6b076a7939df354 Mon Sep 17 00:00:00 2001 From: Fabian Hoffmann Date: Thu, 17 Feb 2022 17:05:56 +0100 Subject: [PATCH 2/2] Refactor: Extract other commands --- src/commands/index.ts | 5 +++ src/commands/logs.command.ts | 18 +++++++++++ src/commands/restart.command.ts | 13 ++++++++ src/commands/shutdown.command.ts | 8 +++++ src/commands/start.command.ts | 4 --- src/commands/status.command.ts | 11 +++++++ src/index.ts | 52 +------------------------------- 7 files changed, 56 insertions(+), 55 deletions(-) create mode 100644 src/commands/index.ts create mode 100644 src/commands/logs.command.ts create mode 100644 src/commands/restart.command.ts create mode 100644 src/commands/shutdown.command.ts create mode 100644 src/commands/status.command.ts diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 0000000..c1f2f83 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,5 @@ +export { start } from "./start.command"; +export { shutdown } from "./shutdown.command"; +export { status } from "./status.command"; +export { restart } from "./restart.command"; +export { logs } from "./logs.command"; diff --git a/src/commands/logs.command.ts b/src/commands/logs.command.ts new file mode 100644 index 0000000..a639ba2 --- /dev/null +++ b/src/commands/logs.command.ts @@ -0,0 +1,18 @@ +import { createConnection } from "net"; + +export const logs = async (name?: string) => { + const client = createConnection(".pm.sock") + client.on('connect', () => { + if (name) { + client.write("logs " + name); + } else { + //all logs + client.write("logs"); + } + + }); + client.on("data", (data) => { + //TODO handle stderr/stdin and also write on stderr + process.stdout.write(data); + }) +} diff --git a/src/commands/restart.command.ts b/src/commands/restart.command.ts new file mode 100644 index 0000000..6e2998b --- /dev/null +++ b/src/commands/restart.command.ts @@ -0,0 +1,13 @@ +import { createConnection } from "net"; + +export const restart = async (name: string) => { + const client = createConnection(".pm.sock") + client.on('connect', () => { + client.write("restart " + name); + }); + client.on("data", (data) => { + //TODO handle stderr/stdin and also write on stderr + process.stdout.write(data); + }) +} + diff --git a/src/commands/shutdown.command.ts b/src/commands/shutdown.command.ts new file mode 100644 index 0000000..99f1c84 --- /dev/null +++ b/src/commands/shutdown.command.ts @@ -0,0 +1,8 @@ +import { createConnection } from "net"; + +export const shutdown = async () => { + const client = createConnection(".pm.sock") + client.on('connect', () => { + client.write("shutdown"); + }); +} diff --git a/src/commands/start.command.ts b/src/commands/start.command.ts index f2277fc..8e69685 100644 --- a/src/commands/start.command.ts +++ b/src/commands/start.command.ts @@ -8,7 +8,6 @@ export const start = async (apps: AppDefinition[]) => { let shuttingDown = false; function startProcess(app: AppDefinition) { - console.log("starting " + app.script); const p = spawn("bash", ["-c", app.script]); p.stdout.on('data', data => { @@ -45,7 +44,6 @@ export const start = async (apps: AppDefinition[]) => { server.listen(".pm.sock"); server.on('connection', (s) => { s.on('data', async (command) => { - //console.log("received command", command.toString()); const cmd = command.toString(); if (cmd == "logs" || cmd.startsWith("logs ")) { const name = cmd != "logs" ? cmd.substring(5) : null; // null means all @@ -110,10 +108,8 @@ export const start = async (apps: AppDefinition[]) => { console.error("Unknown command", cmd); } }); - }); - apps.forEach(app => { startProcess(app); }); diff --git a/src/commands/status.command.ts b/src/commands/status.command.ts new file mode 100644 index 0000000..fe4a5dd --- /dev/null +++ b/src/commands/status.command.ts @@ -0,0 +1,11 @@ +import { createConnection } from "net"; + +export const status = async () => { + const client = createConnection(".pm.sock") + client.on('connect', () => { + client.write("status"); + }); + client.on("data", (data) => { + console.log(data.toString()); + }) +} diff --git a/src/index.ts b/src/index.ts index efa9543..f1db71c 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,5 @@ -import { spawn, ChildProcess } from "child_process"; -import { Socket, createServer, createConnection } from "net"; import { Command } from 'commander'; -import { start } from "./commands/start.command"; +import { start, shutdown, status, logs, restart } from "./commands"; import eco from "./ecosystem.config"; const program = new Command(); @@ -31,51 +29,3 @@ program.command("shutdown") }) program.parse(process.argv); - -async function shutdown() { - const client = createConnection(".pm.sock") - client.on('connect', () => { - client.write("shutdown"); - }); -} - -async function logs(name?: string) { - const client = createConnection(".pm.sock") - client.on('connect', () => { - if (name) { - client.write("logs " + name); - } else { - //all logs - client.write("logs"); - } - - }); - client.on("data", (data) => { - //TODO handle stderr/stdin and also write on stderr - process.stdout.write(data); - }) -} - - - -async function restart(name: string) { - const client = createConnection(".pm.sock") - client.on('connect', () => { - client.write("restart " + name); - }); - client.on("data", (data) => { - //TODO handle stderr/stdin and also write on stderr - process.stdout.write(data); - }) -} - - -async function status() { - const client = createConnection(".pm.sock") - client.on('connect', () => { - client.write("status"); - }); - client.on("data", (data) => { - console.log(data.toString()); - }) -}