Split long `Convert` func into smaller, focussed sub-func

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2020-06-04 16:10:34 +02:00
parent f71109be9e
commit 37177e6d7a
No known key found for this signature in database
GPG Key ID: 9858809D6F8F6E7E
1 changed files with 159 additions and 125 deletions

View File

@ -69,17 +69,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
// Create Cluster is `ParameterClusterName` parameter is not set // Create Cluster is `ParameterClusterName` parameter is not set
template.Conditions["CreateCluster"] = cloudformation.Equals("", cloudformation.Ref(ParameterClusterName)) template.Conditions["CreateCluster"] = cloudformation.Equals("", cloudformation.Ref(ParameterClusterName))
template.Resources["Cluster"] = &ecs.Cluster{ cluster := c.createCluster(project, template)
ClusterName: project.Name,
Tags: []tags.Tag{
{
Key: ProjectTag,
Value: project.Name,
},
},
AWSCloudFormationCondition: "CreateCluster",
}
cluster := cloudformation.If("CreateCluster", cloudformation.Ref("Cluster"), cloudformation.Ref(ParameterClusterName))
networks := map[string]string{} networks := map[string]string{}
for _, net := range project.Networks { for _, net := range project.Networks {
@ -92,34 +82,8 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
} }
// Private DNS namespace will allow DNS name for the services to be <service>.<project>.local // Private DNS namespace will allow DNS name for the services to be <service>.<project>.local
template.Resources["CloudMap"] = &cloudmap.PrivateDnsNamespace{ c.createCloudMap(project, template)
Description: fmt.Sprintf("Service Map for Docker Compose project %s", project.Name), loadBalancer := c.createLoadBalancer(project, template)
Name: fmt.Sprintf("%s.local", project.Name),
Vpc: cloudformation.Ref(ParameterVPCId),
}
loadBalancerType := "network"
loadBalancerName := fmt.Sprintf(
"%s%sLB",
strings.Title(project.Name),
strings.ToUpper(loadBalancerType[0:1]),
)
loadBalancer := &elasticloadbalancingv2.LoadBalancer{
Name: loadBalancerName,
Scheme: "internet-facing",
Subnets: []string{
cloudformation.Ref(ParameterSubnet1Id),
cloudformation.Ref(ParameterSubnet2Id),
},
Tags: []tags.Tag{
{
Key: ProjectTag,
Value: project.Name,
},
},
Type: loadBalancerType,
}
template.Resources[loadBalancerName] = loadBalancer
for _, service := range project.Services { for _, service := range project.Services {
definition, err := Convert(project, service) definition, err := Convert(project, service)
@ -127,30 +91,13 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
return nil, err return nil, err
} }
taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", normalizeResourceName(service.Name)) taskExecutionRole, err := c.createTaskExecutionRole(service, err, definition, template)
policy, err := c.getPolicy(definition)
if err != nil { if err != nil {
return nil, err return template, err
}
rolePolicies := []iam.Role_Policy{}
if policy != nil {
rolePolicies = append(rolePolicies, iam.Role_Policy{
PolicyDocument: policy,
PolicyName: fmt.Sprintf("%sGrantAccessToSecrets", service.Name),
})
} }
definition.ExecutionRoleArn = cloudformation.Ref(taskExecutionRole) definition.ExecutionRoleArn = cloudformation.Ref(taskExecutionRole)
taskDefinition := fmt.Sprintf("%sTaskDefinition", normalizeResourceName(service.Name)) taskDefinition := fmt.Sprintf("%sTaskDefinition", normalizeResourceName(service.Name))
template.Resources[taskExecutionRole] = &iam.Role{
AssumeRolePolicyDocument: assumeRolePolicyDocument,
Policies: rolePolicies,
ManagedPolicyArns: []string{
ECSTaskExecutionPolicy,
ECRReadOnlyPolicy,
},
}
template.Resources[taskDefinition] = definition template.Resources[taskDefinition] = definition
var healthCheck *cloudmap.Service_HealthCheckConfig var healthCheck *cloudmap.Service_HealthCheckConfig
@ -158,26 +105,7 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
// FIXME ECS only support HTTP(s) health checks, while Docker only support CMD // FIXME ECS only support HTTP(s) health checks, while Docker only support CMD
} }
serviceRegistration := fmt.Sprintf("%sServiceDiscoveryEntry", normalizeResourceName(service.Name)) serviceRegistry := c.createServiceRegistry(service, template, healthCheck)
serviceRegistry := ecs.Service_ServiceRegistry{
RegistryArn: cloudformation.GetAtt(serviceRegistration, "Arn"),
}
template.Resources[serviceRegistration] = &cloudmap.Service{
Description: fmt.Sprintf("%q service discovery entry in Cloud Map", service.Name),
HealthCheckConfig: healthCheck,
Name: service.Name,
NamespaceId: cloudformation.Ref("CloudMap"),
DnsConfig: &cloudmap.Service_DnsConfig{
DnsRecords: []cloudmap.Service_DnsRecord{
{
TTL: 60,
Type: cloudmapapi.RecordTypeA,
},
},
RoutingPolicy: cloudmapapi.RoutingPolicyMultivalue,
},
}
serviceSecurityGroups := []string{} serviceSecurityGroups := []string{}
for net := range service.Networks { for net := range service.Networks {
@ -188,54 +116,10 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
serviceLB := []ecs.Service_LoadBalancer{} serviceLB := []ecs.Service_LoadBalancer{}
if len(service.Ports) > 0 { if len(service.Ports) > 0 {
for _, port := range service.Ports { for _, port := range service.Ports {
protocol := strings.ToUpper(port.Protocol)
protocolType := strings.ToUpper(port.Protocol) targetGroupName := c.createTargetGroup(project, service, port, template, protocol)
targetGroupName := fmt.Sprintf( listenerName := c.createListener(service, port, template, targetGroupName, loadBalancer, protocol)
"%s%s%sTargetGroup",
normalizeResourceName(service.Name),
strings.ToUpper(port.Protocol),
string(port.Published),
)
template.Resources[targetGroupName] = &elasticloadbalancingv2.TargetGroup{
Name: targetGroupName,
Port: int(port.Target),
Protocol: protocolType,
Tags: []tags.Tag{
{
Key: ProjectTag,
Value: project.Name,
},
},
VpcId: cloudformation.Ref(ParameterVPCId),
TargetType: elbv2.TargetTypeEnumIp,
}
listenerName := fmt.Sprintf(
"%s%s%sListener",
normalizeResourceName(service.Name),
strings.ToUpper(port.Protocol),
string(port.Published),
)
//add listener to dependsOn
//https://stackoverflow.com/questions/53971873/the-target-group-does-not-have-an-associated-load-balancer
dependsOn = append(dependsOn, listenerName) dependsOn = append(dependsOn, listenerName)
template.Resources[listenerName] = &elasticloadbalancingv2.Listener{
DefaultActions: []elasticloadbalancingv2.Listener_Action{
{
ForwardConfig: &elasticloadbalancingv2.Listener_ForwardConfig{
TargetGroups: []elasticloadbalancingv2.Listener_TargetGroupTuple{
{
TargetGroupArn: cloudformation.Ref(targetGroupName),
},
},
},
Type: elbv2.ActionTypeEnumForward,
},
},
LoadBalancerArn: cloudformation.Ref(loadBalancerName),
Protocol: protocolType,
Port: int(port.Published),
}
serviceLB = append(serviceLB, ecs.Service_LoadBalancer{ serviceLB = append(serviceLB, ecs.Service_LoadBalancer{
ContainerName: service.Name, ContainerName: service.Name,
ContainerPort: int(port.Published), ContainerPort: int(port.Published),
@ -287,6 +171,156 @@ func (c client) Convert(project *compose.Project) (*cloudformation.Template, err
return template, nil return template, nil
} }
func (c client) createLoadBalancer(project *compose.Project, template *cloudformation.Template) string {
loadBalancerType := "network"
loadBalancerName := fmt.Sprintf(
"%s%sLB",
strings.Title(project.Name),
strings.ToUpper(loadBalancerType[0:1]),
)
loadBalancer := &elasticloadbalancingv2.LoadBalancer{
Name: loadBalancerName,
Scheme: "internet-facing",
Subnets: []string{
cloudformation.Ref(ParameterSubnet1Id),
cloudformation.Ref(ParameterSubnet2Id),
},
Tags: []tags.Tag{
{
Key: ProjectTag,
Value: project.Name,
},
},
Type: loadBalancerType,
}
template.Resources[loadBalancerName] = loadBalancer
return loadBalancerName
}
func (c client) createListener(service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, targetGroupName string, loadBalancerName string, protocol string) string {
listenerName := fmt.Sprintf(
"%s%s%sListener",
normalizeResourceName(service.Name),
strings.ToUpper(port.Protocol),
string(port.Published),
)
//add listener to dependsOn
//https://stackoverflow.com/questions/53971873/the-target-group-does-not-have-an-associated-load-balancer
template.Resources[listenerName] = &elasticloadbalancingv2.Listener{
DefaultActions: []elasticloadbalancingv2.Listener_Action{
{
ForwardConfig: &elasticloadbalancingv2.Listener_ForwardConfig{
TargetGroups: []elasticloadbalancingv2.Listener_TargetGroupTuple{
{
TargetGroupArn: cloudformation.Ref(targetGroupName),
},
},
},
Type: elbv2.ActionTypeEnumForward,
},
},
LoadBalancerArn: cloudformation.Ref(loadBalancerName),
Protocol: protocol,
Port: int(port.Published),
}
return listenerName
}
func (c client) createTargetGroup(project *compose.Project, service types.ServiceConfig, port types.ServicePortConfig, template *cloudformation.Template, protocol string) string {
targetGroupName := fmt.Sprintf(
"%s%s%sTargetGroup",
normalizeResourceName(service.Name),
strings.ToUpper(port.Protocol),
string(port.Published),
)
template.Resources[targetGroupName] = &elasticloadbalancingv2.TargetGroup{
Name: targetGroupName,
Port: int(port.Target),
Protocol: protocol,
Tags: []tags.Tag{
{
Key: ProjectTag,
Value: project.Name,
},
},
VpcId: cloudformation.Ref(ParameterVPCId),
TargetType: elbv2.TargetTypeEnumIp,
}
return targetGroupName
}
func (c client) createServiceRegistry(service types.ServiceConfig, template *cloudformation.Template, healthCheck *cloudmap.Service_HealthCheckConfig) ecs.Service_ServiceRegistry {
serviceRegistration := fmt.Sprintf("%sServiceDiscoveryEntry", normalizeResourceName(service.Name))
serviceRegistry := ecs.Service_ServiceRegistry{
RegistryArn: cloudformation.GetAtt(serviceRegistration, "Arn"),
}
template.Resources[serviceRegistration] = &cloudmap.Service{
Description: fmt.Sprintf("%q service discovery entry in Cloud Map", service.Name),
HealthCheckConfig: healthCheck,
Name: service.Name,
NamespaceId: cloudformation.Ref("CloudMap"),
DnsConfig: &cloudmap.Service_DnsConfig{
DnsRecords: []cloudmap.Service_DnsRecord{
{
TTL: 60,
Type: cloudmapapi.RecordTypeA,
},
},
RoutingPolicy: cloudmapapi.RoutingPolicyMultivalue,
},
}
return serviceRegistry
}
func (c client) createTaskExecutionRole(service types.ServiceConfig, err error, definition *ecs.TaskDefinition, template *cloudformation.Template) (string, error) {
taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", normalizeResourceName(service.Name))
policy, err := c.getPolicy(definition)
if err != nil {
return taskExecutionRole, err
}
rolePolicies := []iam.Role_Policy{}
if policy != nil {
rolePolicies = append(rolePolicies, iam.Role_Policy{
PolicyDocument: policy,
PolicyName: fmt.Sprintf("%sGrantAccessToSecrets", service.Name),
})
}
template.Resources[taskExecutionRole] = &iam.Role{
AssumeRolePolicyDocument: assumeRolePolicyDocument,
Policies: rolePolicies,
ManagedPolicyArns: []string{
ECSTaskExecutionPolicy,
ECRReadOnlyPolicy,
},
}
return taskExecutionRole, nil
}
func (c client) createCluster(project *compose.Project, template *cloudformation.Template) string {
template.Resources["Cluster"] = &ecs.Cluster{
ClusterName: project.Name,
Tags: []tags.Tag{
{
Key: ProjectTag,
Value: project.Name,
},
},
AWSCloudFormationCondition: "CreateCluster",
}
cluster := cloudformation.If("CreateCluster", cloudformation.Ref("Cluster"), cloudformation.Ref(ParameterClusterName))
return cluster
}
func (c client) createCloudMap(project *compose.Project, template *cloudformation.Template) {
template.Resources["CloudMap"] = &cloudmap.PrivateDnsNamespace{
Description: fmt.Sprintf("Service Map for Docker Compose project %s", project.Name),
Name: fmt.Sprintf("%s.local", project.Name),
Vpc: cloudformation.Ref(ParameterVPCId),
}
}
func convertNetwork(project *compose.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string { func convertNetwork(project *compose.Project, net types.NetworkConfig, vpc string, template *cloudformation.Template) string {
if sg, ok := net.Extras[ExtensionSecurityGroup]; ok { if sg, ok := net.Extras[ExtensionSecurityGroup]; ok {
logrus.Debugf("Security Group for network %q set by user to %q", net.Name, sg) logrus.Debugf("Security Group for network %q set by user to %q", net.Name, sg)