UNPKG

@deploystack/docker-to-iac

Version:

Translate docker run and docker compose file to Infrastructure as Code

190 lines (189 loc) 9.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_parser_1 = require("./base-parser"); const parsePort_1 = require("../utils/parsePort"); const parseCommand_1 = require("../utils/parseCommand"); const digitalOceanParserServiceName_1 = require("../utils/digitalOceanParserServiceName"); const normalizeDigitalOceanImageInfo_1 = require("../utils/normalizeDigitalOceanImageInfo"); const getDigitalOceanDatabaseType_1 = require("../utils/getDigitalOceanDatabaseType"); const isDigitalOceanManagedDatabase_1 = require("../utils/isDigitalOceanManagedDatabase"); const defaultParserConfig = { files: [ { path: '.do/deploy.template.yaml', templateFormat: base_parser_1.TemplateFormat.yaml, isMain: true, description: 'DigitalOcean App Platform deployment template' } ], region: 'nyc', subscriptionName: 'basic-xxs' }; function getRegistryType(dockerImageInfo) { switch (dockerImageInfo.registry_type) { case 'GHCR': return 'GHCR'; case 'DOCKER_HUB': default: return 'DOCKER_HUB'; } } class DigitalOceanParser extends base_parser_1.BaseParser { parse(config, templateFormat = base_parser_1.TemplateFormat.yaml) { return super.parse(config, templateFormat); } parseFiles(config) { const services = []; const databases = []; const databaseServiceMap = new Map(); let isFirstService = true; // First pass: identify database services that should be managed databases for (const [serviceName, serviceConfig] of Object.entries(config.services)) { if ((0, isDigitalOceanManagedDatabase_1.isDigitalOceanManagedDatabase)(serviceConfig.image)) { // Create a database entry const dbName = (0, digitalOceanParserServiceName_1.digitalOceanParserServiceName)(`${serviceName}-db`); // Track the mapping between service name and database name databaseServiceMap.set(serviceName, dbName); // Get database config const dbConfig = (0, getDigitalOceanDatabaseType_1.getDigitalOceanDatabaseType)(serviceConfig.image); // Create database config object with proper typing const dbEntry = { name: dbName, engine: dbConfig?.engine || 'PG' // Default to PostgreSQL if unknown }; databases.push(dbEntry); // Skip creating a service for this database continue; } const dockerImageInfo = serviceConfig.image; const databaseConfig = (0, getDigitalOceanDatabaseType_1.getDigitalOceanDatabaseType)(dockerImageInfo); const normalizedImage = (0, normalizeDigitalOceanImageInfo_1.normalizeDigitalOceanImageInfo)(dockerImageInfo); // Prepare base service configuration const baseService = { name: (0, digitalOceanParserServiceName_1.digitalOceanParserServiceName)(serviceName), image: { registry_type: getRegistryType(dockerImageInfo), registry: normalizedImage.registry, repository: normalizedImage.repository, tag: dockerImageInfo.tag || 'latest' }, instance_count: 1, instance_size_slug: defaultParserConfig.subscriptionName, run_command: (0, parseCommand_1.parseCommand)(serviceConfig.command), envs: Object.entries(serviceConfig.environment) .map(([key, value]) => ({ key, value: value.toString(), scope: 'RUN_TIME' })) }; // Process service connections - this is provider specific logic if (config.serviceConnections) { for (const connection of config.serviceConnections) { if (connection.fromService === serviceName) { // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const [varName, varInfo] of Object.entries(connection.variables)) { // Find the environment variable index const envIndex = baseService.envs.findIndex(env => env.key === varName); // Check if target service is a managed database if (databaseServiceMap.has(connection.toService)) { const dbServiceName = databaseServiceMap.get(connection.toService); const transformedValue = `\${${dbServiceName}.DATABASE_URL}`; if (envIndex !== -1) { // Update existing variable baseService.envs[envIndex].value = transformedValue; } else { // Add new variable baseService.envs.push({ key: varName, value: transformedValue, scope: 'RUN_TIME' }); } } else { // Regular service connection - not typically used in DigitalOcean but included for completeness const targetServiceName = (0, digitalOceanParserServiceName_1.digitalOceanParserServiceName)(connection.toService); const transformedValue = `\${${targetServiceName}.PRIVATE_URL}`; if (envIndex !== -1) { baseService.envs[envIndex].value = transformedValue; } else { baseService.envs.push({ key: varName, value: transformedValue, scope: 'RUN_TIME' }); } } } } } } if (databaseConfig && !(0, isDigitalOceanManagedDatabase_1.isDigitalOceanManagedDatabase)(dockerImageInfo)) { // Non-managed database/TCP service configuration services.push({ ...baseService, health_check: { port: databaseConfig.portNumber }, internal_ports: [databaseConfig.portNumber] }); } else { const ports = new Set(); if (serviceConfig.ports) { serviceConfig.ports.forEach(port => { if (typeof port === 'object' && port !== null) { ports.add(port.container); } else { const parsedPort = (0, parsePort_1.parsePort)(port); if (parsedPort) { ports.add(parsedPort); } } }); } const routePath = isFirstService ? '/' : `/${serviceName}`; isFirstService = false; services.push({ ...baseService, http_port: ports.size > 0 ? Array.from(ports)[0] : undefined, routes: [{ path: routePath }] }); } } const digitalOceanConfig = { spec: { name: 'deploystack', region: defaultParserConfig.region, services } }; // Add databases section if we have any if (databases.length > 0) { digitalOceanConfig.spec.databases = databases; } return { '.do/deploy.template.yaml': { content: this.formatFileContent(digitalOceanConfig, base_parser_1.TemplateFormat.yaml), format: base_parser_1.TemplateFormat.yaml, isMain: true } }; } getInfo() { return { providerWebsite: 'https://www.digitalocean.com/', providerName: 'DigitalOcean', providerNameAbbreviation: 'DO', languageOfficialDocs: 'https://docs.digitalocean.com/products/app-platform/', languageAbbreviation: 'DOP', languageName: 'DigitalOcean App Spec', defaultParserConfig: defaultParserConfig }; } } exports.default = new DigitalOceanParser();