compose/ecs/pkg/amazon/cloudformation.go

124 lines
3.6 KiB
Go
Raw Normal View History

package amazon
import (
"fmt"
"strings"
"github.com/compose-spec/compose-go/types"
"github.com/sirupsen/logrus"
ecsapi "github.com/aws/aws-sdk-go/service/ecs"
"github.com/awslabs/goformation/v4/cloudformation"
"github.com/awslabs/goformation/v4/cloudformation/ec2"
"github.com/awslabs/goformation/v4/cloudformation/ecs"
"github.com/docker/ecs-plugin/pkg/compose"
"github.com/docker/ecs-plugin/pkg/convert"
)
func (c client) Convert(project *compose.Project) (*cloudformation.Template, error) {
template := cloudformation.NewTemplate()
vpc, err := c.api.GetDefaultVPC()
if err != nil {
return nil, err
}
subnets, err := c.api.GetSubNets(vpc)
if err != nil {
return nil, err
}
var ingresses = []ec2.SecurityGroup_Ingress{}
for _, service := range project.Services {
for _, port := range service.Ports {
ingresses = append(ingresses, ec2.SecurityGroup_Ingress{
CidrIp: "0.0.0.0/0",
Description: fmt.Sprintf("%s:%d/%s", service.Name, port.Target, port.Protocol),
FromPort: int(port.Target),
IpProtocol: strings.ToUpper(port.Protocol),
ToPort: int(port.Target),
})
}
}
securityGroup := fmt.Sprintf("%s Security Group", project.Name)
template.Resources["SecurityGroup"] = &ec2.SecurityGroup{
GroupDescription: securityGroup,
GroupName: securityGroup,
SecurityGroupIngress: ingresses,
VpcId: vpc,
}
for _, service := range project.Services {
definition, err := convert.Convert(project, service)
if err != nil {
return nil, err
}
role, err := c.GetEcsTaskExecutionRole(service)
if err != nil {
return nil, err
}
definition.TaskRoleArn = role
taskDefinition := fmt.Sprintf("%sTaskDefinition", service.Name)
template.Resources[taskDefinition] = definition
template.Resources[service.Name] = &ecs.Service{
Cluster: c.Cluster,
DesiredCount: 1,
LaunchType: ecsapi.LaunchTypeFargate,
NetworkConfiguration: &ecs.Service_NetworkConfiguration{
AwsvpcConfiguration: &ecs.Service_AwsVpcConfiguration{
AssignPublicIp: ecsapi.AssignPublicIpEnabled,
SecurityGroups: []string{cloudformation.Ref("SecurityGroup")},
Subnets: subnets,
},
},
SchedulingStrategy: ecsapi.SchedulingStrategyReplica,
ServiceName: service.Name,
TaskDefinition: cloudformation.Ref(taskDefinition),
}
}
return template, nil
}
const ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
var defaultTaskExecutionRole string
// GetEcsTaskExecutionRole retrieve the role ARN to apply for task execution
func (c client) GetEcsTaskExecutionRole(spec types.ServiceConfig) (string, error) {
if arn, ok := spec.Extras["x-ecs-TaskExecutionRole"]; ok {
return arn.(string), nil
}
if defaultTaskExecutionRole != "" {
return defaultTaskExecutionRole, nil
}
logrus.Debug("Retrieve Task Execution Role")
entities, err := c.api.ListRolesForPolicy(ECSTaskExecutionPolicy)
if err != nil {
return "", err
}
if len(entities) == 0 {
return "", fmt.Errorf("no Role is attached to AmazonECSTaskExecutionRole Policy, please provide an explicit task execution role")
}
if len(entities) > 1 {
return "", fmt.Errorf("multiple Roles are attached to AmazonECSTaskExecutionRole Policy, please provide an explicit task execution role")
}
arn, err := c.api.GetRoleArn(entities[0])
if err != nil {
return "", err
}
defaultTaskExecutionRole = arn
return arn, nil
}
type convertAPI interface {
GetDefaultVPC() (string, error)
GetSubNets(vpcID string) ([]string, error)
ListRolesForPolicy(policy string) ([]string, error)
GetRoleArn(name string) (string, error)
}