Skip to content
This repository has been archived by the owner on Oct 30, 2022. It is now read-only.

Commit

Permalink
Add stop command to unmock-server. (#374)
Browse files Browse the repository at this point in the history
* Add stop command by writing server PID to file.

* Fix prettier.

* Add simple test.

* Fix prettier.

* Fix deletion in test.

* Delete server PID file when process exits.

* Clean-up.
  • Loading branch information
Kimmo Sääskilahti committed Jan 28, 2020
1 parent 5e21b98 commit c2a3d21
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/unmock-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"express": "^4.17.1",
"helmet": "^3.21.1",
"http-proxy": "^1.18.0",
"mkdirp": "^0.5.1",
"node-forge": "^0.9.1",
"unmock-core": "file:../unmock-core",
"unmock-node": "file:../unmock-node"
Expand Down
Empty file.
15 changes: 15 additions & 0 deletions packages/unmock-server/src/__tests__/pid.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as path from "path";
import { deletePidFile, readPidIfExists, writePid } from "../pid";

const LOCAL_CONFIG_DIRECTORY = path.resolve(__dirname, ".unmock");

afterEach(() => {
deletePidFile(LOCAL_CONFIG_DIRECTORY);
});

it("should write process PID to given file and read it", () => {
const pid = process.pid;
writePid(LOCAL_CONFIG_DIRECTORY);
const writtenPid = readPidIfExists(LOCAL_CONFIG_DIRECTORY);
expect(writtenPid).toBe(pid);
});
37 changes: 35 additions & 2 deletions packages/unmock-server/src/commands/start.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
import { Command, flags } from "@oclif/command";
import debug from "debug";
import { deletePidFile, writePid } from "../pid";
import { startProxy } from "../proxy";
import { buildApp, startServer } from "../server";

const debugLog = debug("unmock-server:start");

const log = (...args: any[]) => console.log(...args); // tslint:disable-line

const addCleanUp = (callback: () => void) => {
process.on("exit", callback);

process.on("uncaughtException", e => {
log("Uncaught exception: %s", e.stack);
process.exit(99);
});

process.on("SIGINT", () => process.exit(2));
process.on("SIGTERM", () => process.exit(2));
};

export default class Start extends Command {
public static description = "Start unmock server and proxy";

Expand All @@ -17,10 +35,25 @@ export default class Start extends Command {
public static args = [];

public async run() {
debugLog("Starting.");
const run = () => {
const { app } = buildApp();
startServer(app);
startProxy();
const [httpServer, httpsServer] = startServer(app);
const proxyServer = startProxy();

debugLog("Writing PID to file for closing...");
writePid();

const cleanUp = () => {
debugLog("Received SIGTERM. Stopping servers.");
httpServer.close();
httpsServer.close();
proxyServer.close();
debugLog("Servers closed.");
deletePidFile();
};

addCleanUp(cleanUp);
};
run();
}
Expand Down
44 changes: 44 additions & 0 deletions packages/unmock-server/src/commands/stop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Command, flags } from "@oclif/command";
import debug from "debug";
import { readPidIfExists, TERM_SIGNAL } from "../pid";

const debugLog = debug("unmock-server:stop");
const log = (...args: any[]) => console.log(...args); // tslint:disable-line

export default class Stop extends Command {
public static description = "Stop unmock server and proxy.";

public static examples = [
`$ unmock-server stop
`,
];

public static flags = {
help: flags.help({ char: "h" }),
};

public static args = [];

/**
* Server process is stopped by sending a SIGTERM to the process PID written to the file-system.
*/
public async run() {
const pidOrNull = readPidIfExists();

if (pidOrNull === null) {
log("Server not running.");
return;
}

try {
process.kill(pidOrNull, 0);
} catch (ex) {
log("Server not running.");
return;
}

debugLog("Sending %s to PID %d", TERM_SIGNAL, pidOrNull);
process.kill(pidOrNull, TERM_SIGNAL);
log("Server stopped.");
}
}
64 changes: 64 additions & 0 deletions packages/unmock-server/src/pid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Code for saving process PID into local file system.*/
import debug from "debug";
import * as fs from "fs";
import * as mkdirp from "mkdirp";
import * as os from "os";
import * as path from "path";

const debugLog = debug("unmock-server:pid");

export const DEFAULT_CONFIG_DIRECTORY = path.resolve(os.homedir(), ".unmock");

// Signal used for terminating process
export const TERM_SIGNAL = "SIGTERM";

export const PID_FILENAME = "server.pid";

const ensureDirExists = (directory: string) => {
if (!fs.existsSync(directory)) {
debugLog(`Creating directory: ${directory}`);
return mkdirp.sync(directory);
}

if (!fs.lstatSync(directory).isDirectory()) {
throw Error(`Destination exists but is not directory: ${directory}`);
}

debugLog(`Directory exists: ${directory}`);

return;
};

export const writePid = (directory = DEFAULT_CONFIG_DIRECTORY) => {
const pid = process.pid;

ensureDirExists(directory);

const pidFile = path.join(directory, PID_FILENAME);
debugLog("Writing pid (%d) to %s", pid, pidFile);
fs.writeFileSync(pidFile, pid);
};

export const readPidIfExists = (
directory = DEFAULT_CONFIG_DIRECTORY,
): number | null => {
const pidFile = path.join(directory, PID_FILENAME);
if (!fs.existsSync(pidFile)) {
debugLog("File not found: %s", pidFile);
return null;
}

const contents = fs.readFileSync(pidFile).toString();

return parseInt(contents, 10);
};

export const deletePidFile = (directory = DEFAULT_CONFIG_DIRECTORY): void => {
const pidFile = path.join(directory, PID_FILENAME);
if (!fs.existsSync(pidFile)) {
debugLog("File not found: %s", pidFile);
return;
}
debugLog("Deleting file %s.", pidFile);
fs.unlinkSync(pidFile);
};
2 changes: 2 additions & 0 deletions packages/unmock-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,6 @@ export const startServer = (app: express.Express) => {
httpsServer.listen(httpsPort, () => {
log("HTTPS server starting on port: " + httpsPort);
});

return [httpServer, httpsServer];
};

0 comments on commit c2a3d21

Please sign in to comment.