Skip to content

Instantly share code, notes, and snippets.

@dhruvbaldawa
Created August 22, 2022 13:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhruvbaldawa/5a5f21bc6691d9d8693b59ab28b1d680 to your computer and use it in GitHub Desktop.
Save dhruvbaldawa/5a5f21bc6691d9d8693b59ab28b1d680 to your computer and use it in GitHub Desktop.

Installation

npm install -g .

Run

$ sudo -E xfwd
#!/usr/bin/env node
const path = require("path");
const chalk = require("chalk");
const k8s = require("@kubernetes/client-node");
const { spawn } = require("child_process");
const _ = require("lodash");
const { EventEmitter } = require("stream");
class ManagedProcess extends EventEmitter {
constructor(command, args) {
super();
this.command = command;
this.args = args;
this._process = null;
this.receivedKill = false;
process.on("SIGINT", this.handleKill.bind(this));
process.on("SIGTERM", this.handleKill.bind(this));
}
isRunning() {
return this._process && this._process.exitCode === null;
}
start() {
if (this.isRunning()) {
console.log(chalk.yellow("cant start, the process is already running"));
// process.exit(1);
}
console.log(chalk.green.bold(`> Starting ${this.command}...`));
this._process = spawn(this.command, this.args);
this._process.ref();
this._process.stdout.on("data", (data) => {
process.stdout.write(data.toString());
});
this._process.stderr.on("data", (data) => {
process.stderr.write(data.toString());
});
this._process.on("error", (error) => {
console.log(chalk.red("!! err: ", err));
});
this._process.once("close", (code, signal) => {
if (code === 0) return;
console.log(
chalk.red(
`!! child process exited with code: ${code}, signal: ${signal}`
)
);
});
}
stop() {
console.log(chalk.red.bold(`> Stopping ${this.command}...`));
this._process.kill("SIGTERM");
this._process = null;
}
die() {
if (!this._process) {
this.restart();
return;
}
this._process.once("close", () => {
if (this.receivedKill) {
process.exit(1);
return;
}
this.restart();
});
}
handleKill() {
console.log(chalk.red.bold(`> Killing ${this.command}...`));
this.receivedKill = true;
this.die();
}
restart() {
console.log(chalk.green.bold(`> Restarting ${this.command}...`));
// try starting the process if it is not running yet
if (!this._process) {
setTimeout(this.start.bind(this), 2000);
return;
}
this._process.once("close", () => {
this.start();
});
this.stop();
}
}
function restartK8SWatcher(watchReq, processManager) {
console.log(chalk.green.bold(">> Restarting k8s events watcher in 5s..."));
watchReq.abort();
setTimeout(() => watchK8SEvents(processManager), 5000);
}
function watchK8SEvents(processManager) {
const debouncedRestartProcess = _.debounce(
processManager.restart.bind(processManager),
5000
);
const debouncedStartProcess = _.debounce(
processManager.start.bind(processManager),
2000
);
const watch = new k8s.Watch(kc);
let watchReq;
watch
.watch(
`/api/v1/namespaces/${currentNamespace}/pods`,
{
labelSelector: "app.kubernetes.io/name=devspace-app",
},
(type, apiObj) => {
console.log(
chalk.blue.bold(
`>> Detected change (${type}) in ${apiObj.metadata.name}...`
)
);
if (!processManager.isRunning()) {
debouncedStartProcess();
return;
}
if (type === "ADDED" || type === "DELETED") {
debouncedRestartProcess();
}
},
(err) => {
console.log(
chalk.red("!! There was an error", JSON.stringify({ err }))
);
restartK8SWatcher(watchReq, processManager);
}
)
.then((req) => {
console.log(
chalk.green.bold(
`>> Watching k8s events for ${kc.getCurrentContext()}:${currentNamespace}`
)
);
watchReq = req;
})
.catch((err) => {
console.log(chalk.red("!! Watcher error: ", err));
restartK8SWatcher(watchReq, processManager);
});
process.on("exit", () => {
watchReq.abort();
});
}
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
const currentContextConfig = kc.getContextObject(kc.getCurrentContext());
const currentNamespace = currentContextConfig.namespace;
const kubefwdProcessManager = new ManagedProcess("kubefwd", [
"svc",
"-n",
currentNamespace,
]);
watchK8SEvents(kubefwdProcessManager);
{
"name": "commands",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"xfwd": "./fwd.js",
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "ISC",
"dependencies": {
"@kubernetes/client-node": "^0.16.1",
"chalk": "^4.0.0",
"lodash": "^4.17.21"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment