@biorate/haproxy
Version:
Haproxy runner
150 lines (149 loc) • 5.89 kB
JavaScript
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 };