UNPKG

@twg-group/container-manager

Version:

Container management for Docker, Swarm, Kubernetes

216 lines 8.76 kB
"use strict"; 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DockerStrategy = void 0; const dockerode_1 = __importDefault(require("dockerode")); const common_1 = require("@nestjs/common"); const base_strategy_1 = require("./base.strategy"); const nestjs_logger_1 = require("@twg-group/nestjs-logger"); let DockerStrategy = class DockerStrategy extends base_strategy_1.BaseStrategy { logger; docker; constructor(logger) { super(logger); this.logger = logger; this.docker = new dockerode_1.default({ socketPath: process.env.DOCKER_SOCKET || '/var/run/docker.sock', }); } async start(id) { try { const container = this.docker.getContainer(id); await container.start(); } catch (error) { this.handleError(error, `Failed to start container ${id}`); } } async stop(id, timeout = 10) { try { const container = this.docker.getContainer(id); await container.stop({ t: timeout }); } catch (error) { this.handleError(error, `Failed to stop container ${id}`); } } async deploy(config) { this.validateConfig(config); const containerName = config.name || this.generateName(); const portBindings = this.createPortBindings(config.ports); const volumeBinds = this.createVolumeBinds(config.volumes); try { const container = await this.docker.createContainer({ Image: config.image, name: containerName, Env: this.formatEnvironment(config.env), HostConfig: { PortBindings: portBindings, Binds: volumeBinds, RestartPolicy: { Name: config.restartPolicy ? 'always' : 'no', }, }, Labels: config.labels, }); await container.start(); return container.id; } catch (error) { this.handleError(error, 'Docker deployment failed'); } } async list() { try { const containers = await this.docker.listContainers({ all: true }); return await Promise.all(containers.map((container) => this.formatContainerInfo(container))); } catch (error) { this.handleError(error, 'Failed to list containers'); } } async remove(containerId) { try { const container = this.docker.getContainer(containerId); await container.stop().catch(() => { }); await container.remove(); } catch (error) { this.handleError(error, `Failed to remove container ${containerId}`); } } async logs(containerId, since, tail) { try { const container = this.docker.getContainer(containerId); const logs = await container.logs({ since, tail, stdout: true, stderr: true, timestamps: true, }); return this.parseLogs(logs.toString()); } catch (error) { this.handleError(error, `Failed to get logs for ${containerId}`); } } createPortBindings(ports) { if (!ports?.length) { return {}; } return ports.reduce((acc, port) => { if (!port.containerPort) { return acc; } const protocol = port.protocol || 'tcp'; const key = `${port.containerPort}/${protocol}`; acc[key] = port.hostPort ? [{ HostPort: port.hostPort }] : []; return acc; }, {}); } createVolumeBinds(volumes) { return volumes?.map((v) => `${v.hostPath}:${v.containerPath}:${v.mode || 'rw'}`); } formatEnvironment(env) { return env ? Object.entries(env).map(([k, v]) => `${k}=${v}`) : []; } async formatContainerInfo(container) { const containerInstance = this.docker.getContainer(container.Id); const details = await containerInstance.inspect(); const ports = (container.Ports || []) .map((p) => p.PublicPort ? `${p.PublicPort}:${p.PrivatePort}` : `${p.PrivatePort}`) .filter(Boolean); const env = (details.Config?.Env || []).reduce((acc, envLine) => { const [key, ...value] = envLine.split('='); if (key) acc[key] = value.join('='); return acc; }, {}); return { id: container.Id, name: container.Names?.[0]?.replace(/^\//, '') || 'unnamed', image: container.Image, status: container.State, ports: [...new Set(ports)].sort((a, b) => a.length - b.length || a.localeCompare(b)), createdAt: new Date(container.Created * 1000).toISOString(), labels: details.Config?.Labels || {}, env, }; } parseLogs(logs) { const result = []; const lines = logs.split('\n').filter((line) => line.trim().length > 0); for (const line of lines) { try { const dockerMatch = line.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s(stdout|stderr)\s(.+)/); if (dockerMatch) { result.push({ timestamp: dockerMatch[1].replace('T', ' ').replace('Z', ''), stream: dockerMatch[2], message: dockerMatch[3].trim(), }); continue; } if (line.charCodeAt(0) <= 31) { const timestampMatch = line.match(/(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}[.,]\d+Z?)/); if (timestampMatch) { const timestamp = timestampMatch[1] .replace('T', ' ') .replace(/[,Z]/g, ''); const messageStart = timestampMatch.index + timestampMatch[0].length; const message = line .slice(messageStart) .replace(/[\x00-\x1F]/g, '') .trim(); if (message) { result.push({ timestamp, stream: line.includes('stderr') ? 'stderr' : 'stdout', message, }); } } continue; } const cleanLine = line .replace(/[\x00-\x1F]/g, ' ') .replace(/\s+/g, ' ') .trim(); const fallbackTimestamp = new Date() .toISOString() .replace('T', ' ') .replace('Z', ''); result.push({ timestamp: fallbackTimestamp, stream: cleanLine.toLowerCase().includes('error') ? 'stderr' : 'stdout', message: cleanLine, }); } catch (e) { this.logger.error('Failed to parse log line:', line, e); } } return result.filter((log) => log.message.length > 0 && log.message !== 'Z'); } }; exports.DockerStrategy = DockerStrategy; exports.DockerStrategy = DockerStrategy = __decorate([ (0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }), __metadata("design:paramtypes", [nestjs_logger_1.Logger]) ], DockerStrategy); //# sourceMappingURL=docker.strategy.js.map