UNPKG

phantom-fleet

Version:

Phantom Fleet: Optimize Docker images with dynamic proxy management.

232 lines 10.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const http_proxy_middleware_1 = require("http-proxy-middleware"); const cors_1 = __importDefault(require("cors")); const express_1 = __importDefault(require("express")); const dockerode_1 = __importDefault(require("dockerode")); const portscanner_1 = __importDefault(require("portscanner")); class PhantomFleet { constructor(CONTAINER_NAME, LOG_START_TEXT, TIMEOUT_INACTIVE, IMAGE, PORT, HOST_PORT, TARGET, APP_PORT, ENV) { this.container = null; this.timer = null; this.CONTAINER_NAME = ""; this.LOG_START_TEXT = ""; this.IMAGE = ""; this.PORT = ""; this.HOST_PORT = ""; this.start = () => { try { this.app.use((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (this.container === null) { this.container = yield this.docker.getContainer(this.CONTAINER_NAME); yield this.startContainer(); this.restartTimer(); } else { this.container = yield this.docker.getContainer(this.CONTAINER_NAME); const containerInfo = yield this.container.inspect(); console.log("++containerInfo.State.Paused", containerInfo.State.Paused); } next(); })); this.app.use('/', (0, http_proxy_middleware_1.createProxyMiddleware)({ target: this.TARGET, changeOrigin: true, onError: (err, req, res) => __awaiter(this, void 0, void 0, function* () { this.container = null; if (err.code === 'ECONNREFUSED') { console.error('Connection refused, restarting container...'); yield this.startContainer(); // @ts-ignore (0, http_proxy_middleware_1.createProxyMiddleware)({ target: this.TARGET, changeOrigin: true, })(req, res); this.restartTimer(); } else { res.status(500).send('Proxy Error'); } }) })); portscanner_1.default.checkPortStatus(this.APP_PORT, '127.0.0.1', (error, status) => { if (status === "open") { console.log(`The port ${this.APP_PORT} is busy!`); } else { this.app.listen(this.APP_PORT, () => { console.log(`Proxy server listening on port ${this.APP_PORT}...`); }); } }); } catch (e) { console.log(e); } }; this.remove = () => { this.container && this.container.stop((err, data) => { if (err) { console.error('Error stopping the container:', err); } else { this.container && this.container.remove({ force: true }, function (err, data) { if (err) { if (err.statusCode === 409) { console.error('Container is already in progress of removal'); } else { console.error('Error removing the container:', err); } } else { console.log('Container removed successfully'); } }); } }); }; this.stop = () => { this.stopContainer(); }; this.stopContainer = () => __awaiter(this, void 0, void 0, function* () { if (this.container) { yield this.container.pause(); this.container = null; console.log(`Container ${this.CONTAINER_NAME} stopped.`); } }); this.pullImage = (imageName) => __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { this.docker.pull(imageName, (err, stream) => { if (err) reject(err); this.docker.modem.followProgress(stream, (err, output) => { if (err) reject(err); else resolve(output); }); }); }); }); this.waitForLogMessage = (container, message) => __awaiter(this, void 0, void 0, function* () { const logsStream = yield container.attach({ stream: true, stdout: true, stderr: true, timestamps: true, }); return new Promise((resolve, reject) => { logsStream.on('data', (data) => { const logMessage = data.toString(); if (logMessage.includes(message)) { logsStream.destroy(); // Destroy the stream when the message is found resolve(); } }); logsStream.on('end', () => { reject(new Error(`Message "${message}" not found in the logs.`)); }); logsStream.on('error', (err) => { reject(err); }); }); }); this.createNewContainer = () => __awaiter(this, void 0, void 0, function* () { let exposedPortsObj = {}; exposedPortsObj["PORT"] = {}; let hostConfigObj = {}; hostConfigObj[this.PORT] = [{ HostPort: this.HOST_PORT }]; const newContainer = yield this.docker.createContainer({ Image: this.IMAGE, name: this.CONTAINER_NAME, ExposedPorts: exposedPortsObj, HostConfig: { PortBindings: hostConfigObj }, }); this.container = this.docker.getContainer(newContainer.id); yield this.container.start(); console.log(`Waiting for "${this.LOG_START_TEXT}" message in logs...`); try { if (this.LOG_START_TEXT) yield this.waitForLogMessage(this.container, this.LOG_START_TEXT); console.log(`"${this.LOG_START_TEXT}" message found in logs.`); } catch (error) { console.error(`Error waiting for "${this.LOG_START_TEXT}" message:`, error.message); } console.log(`Container ${this.CONTAINER_NAME} started.`); }); this.restartTimer = () => { if (this.timer) clearTimeout(this.timer); this.timer = setTimeout(this.stopContainer, this.TIMEOUT_INACTIVE); }; this.startContainer = () => __awaiter(this, void 0, void 0, function* () { try { const containers = yield this.docker.listContainers({ all: true }); const existingContainer = containers.find((containerInfo) => containerInfo.Names.includes(`/${this.CONTAINER_NAME}`)); if (existingContainer) { this.container = yield this.docker.getContainer(this.CONTAINER_NAME); const containerInfo = yield this.container.inspect(); console.log("++containerInfo.State.Paused", containerInfo.State.Paused); if (!containerInfo.State.Running) { yield this.container.start(); yield this.container.logs({ stdout: true, stderr: true, timestamps: true, }); if (this.LOG_START_TEXT) yield this.waitForLogMessage(this.container, this.LOG_START_TEXT); console.log(`Container ${this.CONTAINER_NAME} run.`); } else { yield this.container.unpause(); console.log(`Container ${this.CONTAINER_NAME} started.`); } } else { const images = yield this.docker.listImages(); const existingImage = images.find((imageInfo) => imageInfo.RepoTags && imageInfo.RepoTags.includes(this.IMAGE)); if (!existingImage) { // Если образа нет локально, скачиваем его с Docker Hub console.log(`Image ${this.IMAGE} not found locally. Pulling from Docker Hub...`); yield this.pullImage(this.IMAGE); } yield this.createNewContainer(); } } catch (err) { console.error(`Error starting container ${this.CONTAINER_NAME}:`, err.message); } }); this.CONTAINER_NAME = CONTAINER_NAME; this.LOG_START_TEXT = LOG_START_TEXT; this.TIMEOUT_INACTIVE = TIMEOUT_INACTIVE; this.IMAGE = IMAGE; this.PORT = PORT; this.TARGET = TARGET; this.APP_PORT = APP_PORT; this.HOST_PORT = HOST_PORT; this.docker = new dockerode_1.default(); this.app = (0, express_1.default)(); this.app.use((0, cors_1.default)()); } } exports.default = PhantomFleet; //# sourceMappingURL=lib.js.map