UNPKG

kubernetes-health

Version:

Helper library for implementing Kubernetes heath checks and graceful HTTP shutdown in Node applications

184 lines 8.71 kB
"use strict"; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _Health_options, _Health_events, _Health_isReady, _Health_isTerminating, _Health_beforeLiveTasks, _Health_beforeReadyTasks, _Health_beforeTerminationTasks, _Health_pendingTasks; Object.defineProperty(exports, "__esModule", { value: true }); exports.Health = exports.defaultOptions = void 0; const events_1 = require("events"); const _utils_1 = require("./_utils"); /** Default values for Health options, if not specified */ exports.defaultOptions = Object.freeze({ forciblyTerminate: () => process.exit(1), shutdownDelaySeconds: 5, shutdownSignals: ['SIGTERM', 'SIGHUP', 'SIGINT'], terminationGracePeriodSeconds: 30, }); /** * The `Health` class represents the health status if an application. It manages transitions between application * state, and automatically registers shutdown handlers to that run when the process receives a shutdown signal. */ class Health { constructor(options) { _Health_options.set(this, void 0); _Health_events.set(this, new events_1.EventEmitter()); _Health_isReady.set(this, false); _Health_isTerminating.set(this, false); _Health_beforeLiveTasks.set(this, []); _Health_beforeReadyTasks.set(this, []); _Health_beforeTerminationTasks.set(this, []); _Health_pendingTasks.set(this, [] /** * `true` if the application is live, `false` otherwise */ ); __classPrivateFieldSet(this, _Health_options, { ...exports.defaultOptions, ...options }, "f"); for (const signal of __classPrivateFieldGet(this, _Health_options, "f").shutdownSignals) { process.on(signal, () => { this.shutdown(); }); } } /** * `true` if the application is live, `false` otherwise */ get isLive() { return __classPrivateFieldGet(this, _Health_beforeLiveTasks, "f").length === 0; } /** * `true` if the application is ready, `false` otherwise */ get isReady() { return !__classPrivateFieldGet(this, _Health_isTerminating, "f") && __classPrivateFieldGet(this, _Health_isReady, "f") && __classPrivateFieldGet(this, _Health_beforeReadyTasks, "f").length === 0; } /** * The overall application status, transitions between the following status states: * * - `Pending` * - `Live` * - `Ready` * - `NotReady` * - `Terminating` */ get status() { if (__classPrivateFieldGet(this, _Health_isTerminating, "f")) return 'Terminating'; if (__classPrivateFieldGet(this, _Health_beforeLiveTasks, "f").length !== 0) return 'Pending'; if (__classPrivateFieldGet(this, _Health_beforeReadyTasks, "f").length !== 0) return 'Live'; if (__classPrivateFieldGet(this, _Health_isReady, "f")) return 'Ready'; return 'NotReady'; } /** * Add (register) a task that must complete before the application is considered live * * @param task the task to execute */ beforeLive(task) { __classPrivateFieldGet(this, _Health_beforeLiveTasks, "f").push(task); task().then(() => { __classPrivateFieldGet(this, _Health_beforeLiveTasks, "f").splice(__classPrivateFieldGet(this, _Health_beforeLiveTasks, "f").indexOf(task), 1); }); } /** * Add (register) a task that must complete before the application is considered ready * * @param task the task to execute */ beforeReady(task) { __classPrivateFieldGet(this, _Health_beforeReadyTasks, "f").push(task); task().then(() => { __classPrivateFieldGet(this, _Health_beforeReadyTasks, "f").splice(__classPrivateFieldGet(this, _Health_beforeReadyTasks, "f").indexOf(task), 1); }); } /** * Add (register) a task that must complete before the application terminates * * @param task the task to execute */ beforeTermination(handler) { __classPrivateFieldGet(this, _Health_beforeTerminationTasks, "f").push(handler); } /** * Create a new pending task that must be completed before the process terminates. * * @returns a `PendingTask` that can be marked as completed */ createPendingTask() { let isComplete = false; const task = Object.freeze({ markComplete: () => { if (isComplete) return; isComplete = true; __classPrivateFieldGet(this, _Health_pendingTasks, "f").splice(__classPrivateFieldGet(this, _Health_pendingTasks, "f").indexOf(task), 1); __classPrivateFieldGet(this, _Health_events, "f").emit('taskComplete'); }, }); __classPrivateFieldGet(this, _Health_pendingTasks, "f").push(task); return task; } /** * Mark the health instance as ready */ markReady() { if (__classPrivateFieldGet(this, _Health_isTerminating, "f")) return; __classPrivateFieldSet(this, _Health_isReady, true, "f"); } /** * Mark the health instance as not ready */ markNotReady() { if (__classPrivateFieldGet(this, _Health_isTerminating, "f")) return; __classPrivateFieldSet(this, _Health_isReady, false, "f"); } /** * Signals that the process should shut down. Waits for any pending tasks and triggers all shutdown handlers. * If a `terminationGracePeriodSeconds` is defined, the process will be forcibly terminated after that amount * of time if it does not gracefully exit before then. */ async shutdown() { if (__classPrivateFieldGet(this, _Health_isTerminating, "f")) return; __classPrivateFieldSet(this, _Health_isTerminating, true, "f"); if (__classPrivateFieldGet(this, _Health_options, "f").terminationGracePeriodSeconds > 0) { setTimeout(() => { __classPrivateFieldGet(this, _Health_options, "f").forciblyTerminate(); }, __classPrivateFieldGet(this, _Health_options, "f").terminationGracePeriodSeconds * 1000).unref(); } if (__classPrivateFieldGet(this, _Health_options, "f").shutdownDelaySeconds > 0) { await (0, _utils_1.sleep)(__classPrivateFieldGet(this, _Health_options, "f").shutdownDelaySeconds * 1000); } if (__classPrivateFieldGet(this, _Health_pendingTasks, "f").length > 0) { await new Promise((resolve) => { const checkPendingTasks = () => { if (__classPrivateFieldGet(this, _Health_pendingTasks, "f").length === 0) { __classPrivateFieldGet(this, _Health_events, "f").off('taskComplete', checkPendingTasks); resolve(); } }; __classPrivateFieldGet(this, _Health_events, "f").on('taskComplete', checkPendingTasks); checkPendingTasks(); }); } await Promise.allSettled(__classPrivateFieldGet(this, _Health_beforeTerminationTasks, "f")); setTimeout(() => { __classPrivateFieldGet(this, _Health_options, "f").forciblyTerminate(); }, 1000).unref(); } } exports.Health = Health; _Health_options = new WeakMap(), _Health_events = new WeakMap(), _Health_isReady = new WeakMap(), _Health_isTerminating = new WeakMap(), _Health_beforeLiveTasks = new WeakMap(), _Health_beforeReadyTasks = new WeakMap(), _Health_beforeTerminationTasks = new WeakMap(), _Health_pendingTasks = new WeakMap(); //# sourceMappingURL=Health.js.map