Marshall cloudformation template as yaml

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-11-05 09:27:14 +01:00
parent 4b88896547
commit cb02622318
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
11 changed files with 251 additions and 357 deletions

View File

@ -129,6 +129,6 @@ func (cs *aciComposeService) Logs(ctx context.Context, project string, w io.Writ
return errdefs.ErrNotImplemented
}
func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project) ([]byte, error) {
func (cs *aciComposeService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
return nil, errdefs.ErrNotImplemented
}

View File

@ -55,6 +55,6 @@ func (c *composeService) List(context.Context, string) ([]compose.Stack, error)
}
// Convert translate compose model into backend's native format
func (c *composeService) Convert(context.Context, *types.Project) ([]byte, error) {
func (c *composeService) Convert(context.Context, *types.Project, string) ([]byte, error) {
return nil, errdefs.ErrNotImplemented
}

View File

@ -36,7 +36,7 @@ type Service interface {
// List executes the equivalent to a `docker stack ls`
List(ctx context.Context, projectName string) ([]Stack, error)
// Convert translate compose model into backend's native format
Convert(ctx context.Context, project *types.Project) ([]byte, error)
Convert(ctx context.Context, project *types.Project, format string) ([]byte, error)
}
// PortPublisher hold status about published port

View File

@ -39,6 +39,7 @@ func convertCommand() *cobra.Command {
convertCmd.Flags().StringVar(&opts.WorkingDir, "workdir", "", "Work dir")
convertCmd.Flags().StringArrayVarP(&opts.ConfigPaths, "file", "f", []string{}, "Compose configuration files")
convertCmd.Flags().StringArrayVarP(&opts.Environment, "environment", "e", []string{}, "Environment variables")
convertCmd.Flags().StringVar(&opts.Format, "format", "yaml", "Format the output. Values: [yaml | json]")
return convertCmd
}
@ -60,7 +61,7 @@ func runConvert(ctx context.Context, opts composeOptions) error {
return err
}
json, err = c.ComposeService().Convert(ctx, project)
json, err = c.ComposeService().Convert(ctx, project, opts.Format)
if err != nil {
return err
}

View File

@ -37,13 +37,13 @@ import (
"github.com/compose-spec/compose-go/types"
)
func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project) ([]byte, error) {
func (b *ecsAPIService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
template, err := b.convert(ctx, project)
if err != nil {
return nil, err
}
return marshall(template)
return marshall(template, format)
}
func (b *ecsAPIService) convert(ctx context.Context, project *types.Project) (*cloudformation.Template, error) {

View File

@ -44,7 +44,7 @@ func TestSimpleConvert(t *testing.T) {
bytes, err := ioutil.ReadFile("testdata/input/simple-single-service.yaml")
assert.NilError(t, err)
template := convertYaml(t, string(bytes), useDefaultVPC)
resultAsJSON, err := marshall(template)
resultAsJSON, err := marshall(template, "yaml")
assert.NilError(t, err)
result := fmt.Sprintf("%s\n", string(resultAsJSON))
expected := "simple/simple-cloudformation-conversion.golden"

View File

@ -57,7 +57,7 @@ func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, deta
return fmt.Errorf("ECS simulation mode require Docker-compose 1.27, found %s", version)
}
converted, err := e.Convert(ctx, project)
converted, err := e.Convert(ctx, project, "yaml")
if err != nil {
return err
}
@ -69,7 +69,7 @@ func (e ecsLocalSimulation) Up(ctx context.Context, project *types.Project, deta
return cmd.Run()
}
func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project) ([]byte, error) {
func (e ecsLocalSimulation) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
project.Networks["credentials_network"] = types.NetworkConfig{
Driver: "bridge",
Ipam: types.IPAMConfig{

View File

@ -22,29 +22,50 @@ import (
"strings"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/sanathkr/go-yaml"
)
func marshall(template *cloudformation.Template) ([]byte, error) {
raw, err := template.JSON()
func marshall(template *cloudformation.Template, format string) ([]byte, error) {
var (
source func() ([]byte, error)
marshal func(in interface{}) ([]byte, error)
unmarshal func(in []byte, out interface{}) error
)
switch format {
case "yaml":
source = template.YAML
marshal = yaml.Marshal
unmarshal = yaml.Unmarshal
case "json":
source = template.JSON
marshal = func(in interface{}) ([]byte, error) {
return json.MarshalIndent(in, "", " ")
}
unmarshal = json.Unmarshal
default:
return nil, fmt.Errorf("unsupported format %q", format)
}
raw, err := source()
if err != nil {
return nil, err
}
var unmarshalled interface{}
if err := json.Unmarshal(raw, &unmarshalled); err != nil {
if err := unmarshal(raw, &unmarshalled); err != nil {
return nil, fmt.Errorf("invalid JSON: %s", err)
}
if input, ok := unmarshalled.(map[string]interface{}); ok {
if input, ok := unmarshalled.(map[interface{}]interface{}); ok {
if resources, ok := input["Resources"]; ok {
for _, uresource := range resources.(map[string]interface{}) {
if resource, ok := uresource.(map[string]interface{}); ok {
for _, uresource := range resources.(map[interface{}]interface{}) {
if resource, ok := uresource.(map[interface{}]interface{}); ok {
if resource["Type"] == "AWS::ECS::TaskDefinition" {
properties := resource["Properties"].(map[string]interface{})
properties := resource["Properties"].(map[interface{}]interface{})
for _, def := range properties["ContainerDefinitions"].([]interface{}) {
containerDefinition := def.(map[string]interface{})
containerDefinition := def.(map[interface{}]interface{})
if strings.HasSuffix(containerDefinition["Name"].(string), "_InitContainer") {
containerDefinition["Essential"] = "false"
containerDefinition["Essential"] = false
}
}
}
@ -53,9 +74,5 @@ func marshall(template *cloudformation.Template) ([]byte, error) {
}
}
raw, err = json.MarshalIndent(unmarshalled, "", " ")
if err != nil {
return nil, fmt.Errorf("invalid JSON: %s", err)
}
return raw, err
return marshal(unmarshalled)
}

View File

@ -1,332 +1,208 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"CloudMap": {
"Properties": {
"Description": "Service Map for Docker Compose project TestSimpleConvert",
"Name": "TestSimpleConvert.local",
"Vpc": "vpc-123"
},
"Type": "AWS::ServiceDiscovery::PrivateDnsNamespace"
},
"Cluster": {
"Properties": {
"ClusterName": "TestSimpleConvert",
"Tags": [
{
"Key": "com.docker.compose.project",
"Value": "TestSimpleConvert"
}
]
},
"Type": "AWS::ECS::Cluster"
},
"Default80Ingress": {
"Properties": {
"CidrIp": "0.0.0.0/0",
"Description": "simple:80/tcp on default network",
"FromPort": 80,
"GroupId": {
"Ref": "DefaultNetwork"
},
"IpProtocol": "TCP",
"ToPort": 80
},
"Type": "AWS::EC2::SecurityGroupIngress"
},
"DefaultNetwork": {
"Properties": {
"GroupDescription": "TestSimpleConvert Security Group for default network",
"Tags": [
{
"Key": "com.docker.compose.project",
"Value": "TestSimpleConvert"
},
{
"Key": "com.docker.compose.network",
"Value": "default"
}
],
"VpcId": "vpc-123"
},
"Type": "AWS::EC2::SecurityGroup"
},
"DefaultNetworkIngress": {
"Properties": {
"Description": "Allow communication within network default",
"GroupId": {
"Ref": "DefaultNetwork"
},
"IpProtocol": "-1",
"SourceSecurityGroupId": {
"Ref": "DefaultNetwork"
}
},
"Type": "AWS::EC2::SecurityGroupIngress"
},
"LoadBalancer": {
"Properties": {
"Scheme": "internet-facing",
"SecurityGroups": [
{
"Ref": "DefaultNetwork"
}
],
"Subnets": [
"subnet1",
"subnet2"
],
"Tags": [
{
"Key": "com.docker.compose.project",
"Value": "TestSimpleConvert"
}
],
"Type": "application"
},
"Type": "AWS::ElasticLoadBalancingV2::LoadBalancer"
},
"LogGroup": {
"Properties": {
"LogGroupName": "/docker-compose/TestSimpleConvert"
},
"Type": "AWS::Logs::LogGroup"
},
"SimpleService": {
"DependsOn": [
"SimpleTCP80Listener"
],
"Properties": {
"Cluster": {
"Fn::GetAtt": [
"Cluster",
"Arn"
]
},
"DeploymentConfiguration": {
"MaximumPercent": 200,
"MinimumHealthyPercent": 100
},
"DeploymentController": {
"Type": "ECS"
},
"DesiredCount": 1,
"LaunchType": "FARGATE",
"LoadBalancers": [
{
"ContainerName": "simple",
"ContainerPort": 80,
"TargetGroupArn": {
"Ref": "SimpleTCP80TargetGroup"
}
}
],
"NetworkConfiguration": {
"AwsvpcConfiguration": {
"AssignPublicIp": "ENABLED",
"SecurityGroups": [
{
"Ref": "DefaultNetwork"
}
],
"Subnets": [
"subnet1",
"subnet2"
]
}
},
"PlatformVersion": "1.4.0",
"PropagateTags": "SERVICE",
"SchedulingStrategy": "REPLICA",
"ServiceRegistries": [
{
"RegistryArn": {
"Fn::GetAtt": [
"SimpleServiceDiscoveryEntry",
"Arn"
]
}
}
],
"Tags": [
{
"Key": "com.docker.compose.project",
"Value": "TestSimpleConvert"
},
{
"Key": "com.docker.compose.service",
"Value": "simple"
}
],
"TaskDefinition": {
"Ref": "SimpleTaskDefinition"
}
},
"Type": "AWS::ECS::Service"
},
"SimpleServiceDiscoveryEntry": {
"Properties": {
"Description": "\"simple\" service discovery entry in Cloud Map",
"DnsConfig": {
"DnsRecords": [
{
"TTL": 60,
"Type": "A"
}
],
"RoutingPolicy": "MULTIVALUE"
},
"HealthCheckCustomConfig": {
"FailureThreshold": 1
},
"Name": "simple",
"NamespaceId": {
"Ref": "CloudMap"
}
},
"Type": "AWS::ServiceDiscovery::Service"
},
"SimpleTCP80Listener": {
"Properties": {
"DefaultActions": [
{
"ForwardConfig": {
"TargetGroups": [
{
"TargetGroupArn": {
"Ref": "SimpleTCP80TargetGroup"
}
}
]
},
"Type": "forward"
}
],
"LoadBalancerArn": {
"Ref": "LoadBalancer"
},
"Port": 80,
"Protocol": "HTTP"
},
"Type": "AWS::ElasticLoadBalancingV2::Listener"
},
"SimpleTCP80TargetGroup": {
"Properties": {
"Port": 80,
"Protocol": "HTTP",
"Tags": [
{
"Key": "com.docker.compose.project",
"Value": "TestSimpleConvert"
}
],
"TargetType": "ip",
"VpcId": "vpc-123"
},
"Type": "AWS::ElasticLoadBalancingV2::TargetGroup"
},
"SimpleTaskDefinition": {
"Properties": {
"ContainerDefinitions": [
{
"Command": [
".compute.internal",
"TestSimpleConvert.local"
],
"Essential": "false",
"Image": "docker/ecs-searchdomain-sidecar:1.0",
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": {
"Ref": "LogGroup"
},
"awslogs-region": {
"Ref": "AWS::Region"
},
"awslogs-stream-prefix": "TestSimpleConvert"
}
},
"Name": "Simple_ResolvConf_InitContainer"
},
{
"DependsOn": [
{
"Condition": "SUCCESS",
"ContainerName": "Simple_ResolvConf_InitContainer"
}
],
"Essential": true,
"Image": "nginx",
"LinuxParameters": {},
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-group": {
"Ref": "LogGroup"
},
"awslogs-region": {
"Ref": "AWS::Region"
},
"awslogs-stream-prefix": "TestSimpleConvert"
}
},
"Name": "simple",
"PortMappings": [
{
"ContainerPort": 80,
"HostPort": 80,
"Protocol": "tcp"
}
]
}
],
"Cpu": "256",
"ExecutionRoleArn": {
"Ref": "SimpleTaskExecutionRole"
},
"Family": "TestSimpleConvert-simple",
"Memory": "512",
"NetworkMode": "awsvpc",
"RequiresCompatibilities": [
"FARGATE"
]
},
"Type": "AWS::ECS::TaskDefinition"
},
"SimpleTaskExecutionRole": {
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Condition": {},
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
}
}
],
"Version": "2012-10-17"
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
"arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
],
"Tags": [
{
"Key": "com.docker.compose.project",
"Value": "TestSimpleConvert"
},
{
"Key": "com.docker.compose.service",
"Value": "simple"
}
]
},
"Type": "AWS::IAM::Role"
}
}
}
AWSTemplateFormatVersion: 2010-09-09
Resources:
CloudMap:
Properties:
Description: Service Map for Docker Compose project TestSimpleConvert
Name: TestSimpleConvert.local
Vpc: vpc-123
Type: AWS::ServiceDiscovery::PrivateDnsNamespace
Cluster:
Properties:
ClusterName: TestSimpleConvert
Tags:
- Key: com.docker.compose.project
Value: TestSimpleConvert
Type: AWS::ECS::Cluster
Default80Ingress:
Properties:
CidrIp: 0.0.0.0/0
Description: simple:80/tcp on default network
FromPort: 80
GroupId:
Ref: DefaultNetwork
IpProtocol: TCP
ToPort: 80
Type: AWS::EC2::SecurityGroupIngress
DefaultNetwork:
Properties:
GroupDescription: TestSimpleConvert Security Group for default network
Tags:
- Key: com.docker.compose.project
Value: TestSimpleConvert
- Key: com.docker.compose.network
Value: default
VpcId: vpc-123
Type: AWS::EC2::SecurityGroup
DefaultNetworkIngress:
Properties:
Description: Allow communication within network default
GroupId:
Ref: DefaultNetwork
IpProtocol: "-1"
SourceSecurityGroupId:
Ref: DefaultNetwork
Type: AWS::EC2::SecurityGroupIngress
LoadBalancer:
Properties:
Scheme: internet-facing
SecurityGroups:
- Ref: DefaultNetwork
Subnets:
- subnet1
- subnet2
Tags:
- Key: com.docker.compose.project
Value: TestSimpleConvert
Type: application
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
LogGroup:
Properties:
LogGroupName: /docker-compose/TestSimpleConvert
Type: AWS::Logs::LogGroup
SimpleService:
DependsOn:
- SimpleTCP80Listener
Properties:
Cluster:
Fn::GetAtt:
- Cluster
- Arn
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
DeploymentController:
Type: ECS
DesiredCount: 1
LaunchType: FARGATE
LoadBalancers:
- ContainerName: simple
ContainerPort: 80
TargetGroupArn:
Ref: SimpleTCP80TargetGroup
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- Ref: DefaultNetwork
Subnets:
- subnet1
- subnet2
PlatformVersion: 1.4.0
PropagateTags: SERVICE
SchedulingStrategy: REPLICA
ServiceRegistries:
- RegistryArn:
Fn::GetAtt:
- SimpleServiceDiscoveryEntry
- Arn
Tags:
- Key: com.docker.compose.project
Value: TestSimpleConvert
- Key: com.docker.compose.service
Value: simple
TaskDefinition:
Ref: SimpleTaskDefinition
Type: AWS::ECS::Service
SimpleServiceDiscoveryEntry:
Properties:
Description: '"simple" service discovery entry in Cloud Map'
DnsConfig:
DnsRecords:
- TTL: 60
Type: A
RoutingPolicy: MULTIVALUE
HealthCheckCustomConfig:
FailureThreshold: 1
Name: simple
NamespaceId:
Ref: CloudMap
Type: AWS::ServiceDiscovery::Service
SimpleTCP80Listener:
Properties:
DefaultActions:
- ForwardConfig:
TargetGroups:
- TargetGroupArn:
Ref: SimpleTCP80TargetGroup
Type: forward
LoadBalancerArn:
Ref: LoadBalancer
Port: 80
Protocol: HTTP
Type: AWS::ElasticLoadBalancingV2::Listener
SimpleTCP80TargetGroup:
Properties:
Port: 80
Protocol: HTTP
Tags:
- Key: com.docker.compose.project
Value: TestSimpleConvert
TargetType: ip
VpcId: vpc-123
Type: AWS::ElasticLoadBalancingV2::TargetGroup
SimpleTaskDefinition:
Properties:
ContainerDefinitions:
- Command:
- .compute.internal
- TestSimpleConvert.local
Essential: false
Image: docker/ecs-searchdomain-sidecar:1.0
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group:
Ref: LogGroup
awslogs-region:
Ref: AWS::Region
awslogs-stream-prefix: TestSimpleConvert
Name: Simple_ResolvConf_InitContainer
- DependsOn:
- Condition: SUCCESS
ContainerName: Simple_ResolvConf_InitContainer
Essential: true
Image: nginx
LinuxParameters: {}
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group:
Ref: LogGroup
awslogs-region:
Ref: AWS::Region
awslogs-stream-prefix: TestSimpleConvert
Name: simple
PortMappings:
- ContainerPort: 80
HostPort: 80
Protocol: tcp
Cpu: "256"
ExecutionRoleArn:
Ref: SimpleTaskExecutionRole
Family: TestSimpleConvert-simple
Memory: "512"
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Type: AWS::ECS::TaskDefinition
SimpleTaskExecutionRole:
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- sts:AssumeRole
Condition: {}
Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Version: 2012-10-17
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
Tags:
- Key: com.docker.compose.project
Value: TestSimpleConvert
- Key: com.docker.compose.service
Value: simple
Type: AWS::IAM::Role

View File

@ -32,7 +32,7 @@ func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, detach b
return err
}
template, err := b.Convert(ctx, project)
template, err := b.Convert(ctx, project, "yaml")
if err != nil {
return err
}

View File

@ -158,6 +158,6 @@ func (cs *composeService) Logs(ctx context.Context, project string, w io.Writer)
return errdefs.ErrNotImplemented
}
func (cs *composeService) Convert(ctx context.Context, project *types.Project) ([]byte, error) {
func (cs *composeService) Convert(ctx context.Context, project *types.Project, format string) ([]byte, error) {
return nil, errdefs.ErrNotImplemented
}