UNPKG

@biorate/haproxy

Version:
150 lines (149 loc) 5.89 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; 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 _HaproxyConnector_configs; import HAProxy from 'haproxy'; import { timer } from '@biorate/tools'; import { injectable, kill } from '@biorate/inversion'; import { Connector } from '@biorate/connector'; import { path } from '@biorate/tools'; import { tmpdir, EOL } from 'os'; import { unlinkSync, writeFileSync } from 'fs'; import { promisify } from 'util'; import { HaproxyCantConnectError, HaproxyConnectionTimeoutError } from './errors.js'; export * from './errors.js'; export * from './interfaces.js'; let HaproxyConnector = class HaproxyConnector extends Connector { constructor() { super(...arguments); _HaproxyConnector_configs.set(this, new WeakMap()); this.namespace = 'Haproxy'; } async connect(config) { let connection; try { this.cleanup(config); const cfgFile = this.createConfig(config); connection = new HAProxy(this.path(config, 'sock'), { pidFile: this.path(config, 'pid'), config: cfgFile, }); for (const method of [ 'start', 'stop', 'softstop', 'reload', 'verify', 'running', 'clear', 'disable', 'enable', 'pause', 'resume', 'errors', 'weight', 'maxconn', 'ratelimit', 'compression', 'info', 'session', 'stat', ]) connection[method] = promisify(connection[method].bind(connection)); await connection.start(); await this.readiness(connection, config); __classPrivateFieldGet(this, _HaproxyConnector_configs, "f").set(connection, config); } catch (e) { throw new HaproxyCantConnectError(e); } return connection; } async readiness(connection, config) { if (config?.readiness?.nodes?.length) { let i = 0; w: while (true) { const stats = await connection.stat(); for (const stat of stats) { if (!config.readiness.nodes.includes(stat.svname)) continue; if (stat.status === 'UP') break w; } console.debug(`Attempt to connect to Haproxy: [%s]`, config.name); await timer.wait(config?.readiness?.delay ?? 1000); ++i; if (i > (config?.readiness?.retries ?? 1)) throw new HaproxyConnectionTimeoutError(config.name); } } } path(config, ext) { return path.create(tmpdir(), `${config.name}.haproxy.${ext}`); } cleanup(config) { try { unlinkSync(this.path(config, 'sock')); } catch { } try { unlinkSync(this.path(config, 'pid')); } catch { } try { unlinkSync(this.path(config, 'config')); } catch { } } createConfig(config) { let data = ''; const file = this.path(config, 'config'); for (const header in config.config) { data += header + EOL; if (Array.isArray(config.config[header])) for (const field of config.config[header]) data += ' ' + field + EOL; else for (const field in config.config[header]) data += ' ' + field + ' ' + config.config[header][field] + EOL; } data = data.replace('{{stat_socket_path}}', this.path(config, 'sock')); writeFileSync(file, data, 'utf-8'); if (config.debug) console.debug(`Haproxy [${config.name}] config:${EOL}`, data); return file; } async destructor() { for (const [, connection] of this.connections) { try { await connection.stop(); this.cleanup(__classPrivateFieldGet(this, _HaproxyConnector_configs, "f").get(connection)); } catch (e) { console.error(e); } } } }; _HaproxyConnector_configs = new WeakMap(); __decorate([ kill(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], HaproxyConnector.prototype, "destructor", null); HaproxyConnector = __decorate([ injectable() ], HaproxyConnector); export { HaproxyConnector };