UNPKG

@deploystack/docker-to-iac

Version:

Translate docker run and docker compose file to Infrastructure as Code

193 lines (192 loc) 8.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_parser_1 = require("./base-parser"); const getImageUrl_1 = require("../utils/getImageUrl"); const constructImageString_1 = require("../utils/constructImageString"); const parsePort_1 = require("../utils/parsePort"); const parseCommand_1 = require("../utils/parseCommand"); const defaultParserConfig = { files: [ { path: 'aws-cloudformation.cf.yml', templateFormat: base_parser_1.TemplateFormat.yaml, isMain: true, description: 'AWS CloudFormation template' } ], cpu: 512, memory: '1GB' }; class CloudFormationParser extends base_parser_1.BaseParser { replaceSpecialDirectives(input) { const refRegex = /"!Ref ([^"]+)"/g; const joinRegex = /"!Join (\[.*?\])"/g; let output = input.replace(refRegex, '!Ref $1'); output = output.replace(joinRegex, '!Join $1'); return output; } parse(config, templateFormat = base_parser_1.TemplateFormat.yaml) { return super.parse(config, templateFormat); } parseFiles(config) { let response = {}; const parameters = {}; const resources = {}; // Mandatory Template Values parameters['VPC'] = { Type: 'AWS::EC2::VPC::Id' }; parameters['SubnetA'] = { Type: 'AWS::EC2::Subnet::Id' }; parameters['SubnetB'] = { Type: 'AWS::EC2::Subnet::Id' }; parameters['ServiceName'] = { Type: 'String', Default: 'DeployStackService' }; // Base AWS Resources resources['Cluster'] = { Type: 'AWS::ECS::Cluster', Properties: { ClusterName: '!Join [\'\', [!Ref ServiceName, Cluster]]' } }; resources['ExecutionRole'] = { Type: 'AWS::IAM::Role', Properties: { RoleName: '!Join [\'\', [!Ref ServiceName, ExecutionRole]]', AssumeRolePolicyDocument: { Statement: [ { Effect: 'Allow', Principal: { Service: 'ecs-tasks.amazonaws.com' }, Action: 'sts:AssumeRole' } ] }, ManagedPolicyArns: [ 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' ] } }; // Process each service in the docker compose for (const [serviceName, serviceConfig] of Object.entries(config.services)) { resources[`LogGroup${serviceName}`] = { Type: 'AWS::Logs::LogGroup', Properties: { LogGroupName: `!Join ['', [/ecs/, !Ref ServiceName, TaskDefinition${serviceName}]]` } }; 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 environmentVariables = serviceConfig.environment; const commandString = (0, parseCommand_1.parseCommand)(serviceConfig.command); const commandArray = commandString && commandString.trim() ? commandString.trim().split(' ') : undefined; const containerDefinition = { Name: serviceName, Image: (0, getImageUrl_1.getImageUrl)((0, constructImageString_1.constructImageString)(serviceConfig.image)), ReadonlyRootFilesystem: false, PortMappings: Array.from(ports).map(port => ({ ContainerPort: port })), Environment: Object.entries(environmentVariables).map(([key, value]) => ({ name: key, value: value.toString() })), LogConfiguration: { LogDriver: 'awslogs', Options: { 'awslogs-region': '!Ref AWS::Region', 'awslogs-group': `!Ref LogGroup${serviceName}`, 'awslogs-stream-prefix': 'ecs' } } }; if (commandArray && commandArray.length > 0 && commandArray[0] !== '') { containerDefinition.Command = commandArray; } resources[`TaskDefinition${serviceName}`] = { Type: 'AWS::ECS::TaskDefinition', DependsOn: [ `LogGroup${serviceName}` ], Properties: { Family: `!Join ['', [!Ref ServiceName, TaskDefinition${serviceName}]]`, NetworkMode: 'awsvpc', RequiresCompatibilities: ['FARGATE'], Cpu: defaultParserConfig.cpu, Memory: defaultParserConfig.memory, ExecutionRoleArn: '!Ref ExecutionRole', ContainerDefinitions: [containerDefinition] } }; resources[`ContainerSecurityGroup${serviceName}`] = { Type: 'AWS::EC2::SecurityGroup', Properties: { GroupDescription: `!Join ['', [${serviceName}, ContainerSecurityGroup]]`, VpcId: '!Ref VPC', SecurityGroupIngress: { IpProtocol: 'tcp', FromPort: 0, ToPort: 65535, CidrIp: '0.0.0.0/0' } } }; resources[`Service${serviceName}`] = { Type: 'AWS::ECS::Service', Properties: { ServiceName: serviceName, Cluster: '!Ref Cluster', TaskDefinition: `!Ref TaskDefinition${serviceName}`, DesiredCount: 1, LaunchType: 'FARGATE', NetworkConfiguration: { AwsvpcConfiguration: { AssignPublicIp: 'ENABLED', Subnets: [ '!Ref SubnetA', '!Ref SubnetB' ], SecurityGroups: [`!Ref ContainerSecurityGroup${serviceName}`] } } } }; } response = { AWSTemplateFormatVersion: '2010-09-09', Description: 'Generated from container configuration by docker-to-iac', Parameters: parameters, Resources: resources }; // Process the output based on format const yamlContent = this.formatFileContent(response, base_parser_1.TemplateFormat.yaml); const formattedContent = this.replaceSpecialDirectives(yamlContent); return { 'aws-cloudformation.cf.yml': { content: formattedContent, format: base_parser_1.TemplateFormat.yaml, isMain: true } }; } getInfo() { return { providerWebsite: 'https://aws.amazon.com/cloudformation/', providerName: 'Amazon Web Services', providerNameAbbreviation: 'AWS', languageOfficialDocs: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html', languageAbbreviation: 'CFN', languageName: 'AWS CloudFormation', defaultParserConfig: defaultParserConfig }; } } exports.default = new CloudFormationParser();