Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 114 additions & 50 deletions docs/docker-to-iac/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,40 @@ console.log(parsers);
{
providerWebsite: 'https://aws.amazon.com/cloudformation/',
providerName: 'Amazon Web Services',
provieerNameAbbreviation: 'AWS',
providerNameAbbreviation: 'AWS',
languageOfficialDocs: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html',
languageAbbreviation: 'CFN',
languageName: 'AWS CloudFormation',
defaultParserConfig: { fileName: 'aws-cloudformation.yaml', cpu: 512, memory: '1GB', templateFormat: 'yaml' }
defaultParserConfig: { files: [Array], cpu: 512, memory: '1GB' }
},
{
providerWebsite: 'https://render.com/docs',
providerName: 'Render',
provieerNameAbbreviation: 'RND',
providerNameAbbreviation: 'RND',
languageOfficialDocs: 'https://docs.render.com/infrastructure-as-code',
languageAbbreviation: 'RND',
languageName: 'Render Blue Print',
defaultParserConfig: {
subscriptionName: 'free',
files: [Array],
subscriptionName: 'starter',
region: 'oregon',
fileName: 'render.yaml',
templateFormat: 'yaml'
diskSizeGB: 10
}
},
{
providerWebsite: 'https://www.digitalocean.com/',
providerName: 'DigitalOcean',
providerNameAbbreviation: 'DO',
languageOfficialDocs: 'https://docs.digitalocean.com/products/app-platform/',
languageAbbreviation: 'DOP',
languageName: 'DigitalOcean App Spec',
defaultParserConfig: { files: [Array], region: 'nyc', subscriptionName: 'basic-xxs' }
}
]
```

**Note the files array**: that's because we have a [multi file strategy](/docs/docker-to-iac/multi-file-configuration.md).

### Type

```typescript
Expand All @@ -77,13 +88,24 @@ console.log(awsInfo);

```json
{
providerWebsite: 'https://aws.amazon.com/cloudformation/',
providerName: 'Amazon Web Services',
provieerNameAbbreviation: 'AWS',
languageOfficialDocs: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html',
languageAbbreviation: 'CFN',
languageName: 'AWS CloudFormation',
defaultParserConfig: { fileName: 'aws-cloudformation.yaml', cpu: 512, memory: '1GB', templateFormat: 'yaml' }
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: {
files: [
{
path: 'aws-cloudformation.cf.yml',
templateFormat: 'yaml',
isMain: true,
description: 'AWS CloudFormation template'
}
],
cpu: 512,
memory: '1GB'
}
}
```

Expand All @@ -107,75 +129,117 @@ translate(input: string, options: {
environmentVariableGeneration?: EnvironmentVariableGenerationConfig;
environmentVariables?: Record<string, string>;
persistenceKey?: string;
}): any
}): TranslationResult
```

Where `TranslationResult` has the structure:

```typescript
interface TranslationResult {
files: {
[path: string]: FileOutput
};
}

interface FileOutput {
content: string;
format: TemplateFormat;
isMain?: boolean;
}
```

### Examples

#### Translating Docker Compose

```javascript
import { readFileSync, writeFileSync } from 'fs';
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { translate } from '@deploystack/docker-to-iac';

const dockerComposeContent = readFileSync('path/to/docker-compose.yml', 'utf8');

const translatedConfig = translate(dockerComposeContent, {
const result = translate(dockerComposeContent, {
source: 'compose',
target: 'CFN',
templateFormat: 'yaml'
});
console.log(translatedConfig);

// Access individual file contents
console.log(`Generated ${Object.keys(result.files).length} files:`);
Object.keys(result.files).forEach(path => {
console.log(`- ${path}`);
});

// Write files to disk preserving directory structure
Object.entries(result.files).forEach(([path, fileData]) => {
const fullPath = join('output', path);
const dir = dirname(fullPath);

if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}

writeFileSync(fullPath, fileData.content);
});
```

#### Translating Docker Run Command

```javascript
import { translate } from '@deploystack/docker-to-iac';
import { writeFileSync, mkdirSync, existsSync } from 'fs';
import { join, dirname } from 'path';

const dockerRunCommand = 'docker run -d -p 8080:80 nginx:latest';

const translatedConfig = translate(dockerRunCommand, {
const result = translate(dockerRunCommand, {
source: 'run',
target: 'CFN',
target: 'RND',
templateFormat: 'yaml'
});
console.log(translatedConfig);

console.log(result)

// Access and save all generated files
Object.entries(result.files).forEach(([path, fileData]) => {
const fullPath = join('output', path);
const dir = dirname(fullPath);

if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}

writeFileSync(fullPath, fileData.content);
console.log(`Created: ${path}`);
});
```

### Example Output (AWS CloudFormation)

```yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Generated from container configuration by docker-to-iac
Parameters:
VPC:
Type: AWS::EC2::VPC::Id
SubnetA:
Type: AWS::EC2::Subnet::Id
SubnetB:
Type: AWS::EC2::Subnet::Id
ServiceName:
Type: String
Default: DeployStackService
Resources:
Cluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Join ['', [!Ref ServiceName, Cluster]]
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
...
{
files: {
'render.yaml': {
content: 'services:\n' +
' - name: default\n' +
' type: web\n' +
' env: docker\n' +
' runtime: image\n' +
' image:\n' +
' url: docker.io/library/nginx:latest\n' +
' startCommand: ""\n' +
' plan: starter\n' +
' region: oregon\n' +
' envVars:\n' +
' - key: PORT\n' +
' value: "80"\n',
format: 'yaml',
isMain: true
}
}
}
Created: render.yaml
```

#### Translation with Environment Variable Generation
Expand Down
93 changes: 66 additions & 27 deletions docs/docker-to-iac/example-of-a-new-parser.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
description: Example code for adding a new parser to docker-to-iac, supporting both Docker run commands and Docker Compose files
description: Example code for adding a new parser to docker-to-iac, supporting both Docker run commands and Docker Compose files, with multi-file output capabilities
menuTitle: Adding a New Parser
---

# Adding a New Parser

> [!TIP]
> __Thank you__ for your interest in collaborating! The docker-to-iac module will remain open source forever, helping simplify deployments across cloud providers without vendor lock-in.
> Thank you for your interest in collaborating! The docker-to-iac module will remain open source forever, helping simplify deployments across cloud providers without vendor lock-in.

## Parser Implementation

Expand All @@ -16,32 +16,59 @@ Create a new file inside `src/parsers/new-provider.ts`:
import {
BaseParser,
ParserInfo,
ContainerConfig,
TemplateFormat,
formatResponse,
DefaultParserConfig
ParserConfig,
FileOutput
} from './base-parser';

const defaultParserConfig: DefaultParserConfig = {
import { ApplicationConfig } from '../types/container-config';

const defaultParserConfig: ParserConfig = {
files: [
{
path: 'awesome-iac.yaml',
templateFormat: TemplateFormat.yaml,
isMain: true,
description: 'Main IaC configuration file'
}
],
cpu: 512,
memory: '1GB',
fileName: 'awesome-iac.yaml',
templateFormat: TemplateFormat.yaml
memory: '1GB'
};

class NewProviderParser extends BaseParser {
parse(containerConfig: ContainerConfig, templateFormat: TemplateFormat = defaultParserConfig.templateFormat): any {
let response: any = {};

// Get container configurations
const services = containerConfig.services;
// Legacy method implementation (calls parseFiles under the hood)
parse(config: ApplicationConfig, templateFormat: TemplateFormat = TemplateFormat.yaml): any {
return super.parse(config, templateFormat);
}

// New multi-file implementation
parseFiles(config: ApplicationConfig): { [path: string]: FileOutput } {
// Process the application configuration
const services = config.services;

// Your parser implementation here:
// 1. Process each service
// 2. Map container configurations to your IaC format
// 3. Handle provider-specific requirements

return formatResponse(JSON.stringify(response, null, 2), templateFormat);
// Create your primary IaC configuration
const primaryConfig = {
// Your main configuration structure
};

// Return object with file mappings - at minimum return your main file
return {
'awesome-iac.yaml': {
content: this.formatFileContent(primaryConfig, TemplateFormat.yaml),
format: TemplateFormat.yaml,
isMain: true
}
// Add additional files as needed:
// 'templates/service.yaml': {
// content: this.formatFileContent(serviceConfig, TemplateFormat.yaml),
// format: TemplateFormat.yaml
// }
};
}

getInfo(): ParserInfo {
Expand All @@ -62,22 +89,34 @@ export default new NewProviderParser();

## Parser Configuration

### Default Parser Config
### Multi-File Configuration

Define the files your parser will generate, please read more here: [Multi-File Configuration in docker-to-iac](/docs/docker-to-iac/multi-file-configuration.md).

### File Output

Set appropriate defaults for your cloud provider:
Your parser must implement the `parseFiles` method which returns a mapping of file paths to content:

```typescript
const defaultParserConfig: DefaultParserConfig = {
cpu: 512, // Minimum viable CPU allocation
memory: '1GB', // Minimum viable memory
fileName: 'awesome-iac.yaml', // Default output filename
templateFormat: TemplateFormat.yaml // Default format
};
parseFiles(config: ApplicationConfig): { [path: string]: FileOutput } {
return {
'awesome-iac.yaml': {
content: this.formatFileContent(mainConfig, TemplateFormat.yaml),
format: TemplateFormat.yaml,
isMain: true // Mark one file as the main file
},
'templates/deployment.yaml': {
content: this.formatFileContent(deploymentConfig, TemplateFormat.yaml),
format: TemplateFormat.yaml
}
// Add more files as needed
};
}
```

Choose conservative resource defaults to prevent unexpected costs for users.
The `isMain: true` property is required for at least one file - this maintains backward compatibility with existing code.

### Supported Formats
### Supported Ouput Formats

Select appropriate output formats for your provider:

Expand Down Expand Up @@ -143,7 +182,7 @@ npm run build
- Include examples for both Docker run and Docker Compose
- Follow [documentation guidelines](https://github.com/deploystackio/documentation/blob/main/README.md)

## Best Practices
## Checlist

1. Support both input types:
- Docker run commands
Expand Down
Loading