cloudformation.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package amazon
  2. import (
  3. "fmt"
  4. "github.com/compose-spec/compose-go/types"
  5. "github.com/sirupsen/logrus"
  6. "strings"
  7. ecsapi "github.com/aws/aws-sdk-go/service/ecs"
  8. "github.com/awslabs/goformation/v4/cloudformation"
  9. "github.com/awslabs/goformation/v4/cloudformation/ec2"
  10. "github.com/awslabs/goformation/v4/cloudformation/ecs"
  11. "github.com/docker/ecs-plugin/pkg/compose"
  12. "github.com/docker/ecs-plugin/pkg/convert"
  13. )
  14. func (c client) Convert(project *compose.Project, loadBalancerArn *string) (*cloudformation.Template, error) {
  15. template := cloudformation.NewTemplate()
  16. vpc, err := c.api.GetDefaultVPC()
  17. if err != nil {
  18. return nil, err
  19. }
  20. subnets, err := c.api.GetSubNets(vpc)
  21. if err != nil {
  22. return nil, err
  23. }
  24. var ingresses = []ec2.SecurityGroup_Ingress{}
  25. for _, service := range project.Services {
  26. for _, port := range service.Ports {
  27. ingresses = append(ingresses, ec2.SecurityGroup_Ingress{
  28. CidrIp: "0.0.0.0/0",
  29. Description: fmt.Sprintf("%s:%d/%s", service.Name, port.Target, port.Protocol),
  30. FromPort: int(port.Target),
  31. IpProtocol: strings.ToUpper(port.Protocol),
  32. ToPort: int(port.Target),
  33. })
  34. }
  35. }
  36. securityGroup := fmt.Sprintf("%s Security Group", project.Name)
  37. template.Resources["SecurityGroup"] = &ec2.SecurityGroup{
  38. GroupDescription: securityGroup,
  39. GroupName: securityGroup,
  40. SecurityGroupIngress: ingresses,
  41. VpcId: vpc,
  42. }
  43. for _, service := range project.Services {
  44. definition, err := convert.Convert(project, service)
  45. if err != nil {
  46. return nil, err
  47. }
  48. role, err := c.GetEcsTaskExecutionRole(service)
  49. if err != nil {
  50. return nil, err
  51. }
  52. definition.TaskRoleArn = role
  53. taskDefinition := fmt.Sprintf("%sTaskDefinition", service.Name)
  54. template.Resources[taskDefinition] = definition
  55. template.Resources[service.Name] = &ecs.Service{
  56. Cluster: c.Cluster,
  57. DesiredCount: 1,
  58. LaunchType: ecsapi.LaunchTypeFargate,
  59. NetworkConfiguration: &ecs.Service_NetworkConfiguration{
  60. AwsvpcConfiguration: &ecs.Service_AwsVpcConfiguration{
  61. AssignPublicIp: ecsapi.AssignPublicIpEnabled,
  62. SecurityGroups: []string{cloudformation.Ref("SecurityGroup")},
  63. Subnets: subnets,
  64. },
  65. },
  66. SchedulingStrategy: ecsapi.SchedulingStrategyReplica,
  67. ServiceName: service.Name,
  68. TaskDefinition: cloudformation.Ref(taskDefinition),
  69. }
  70. }
  71. return template, nil
  72. }
  73. const ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
  74. var defaultTaskExecutionRole string
  75. // GetEcsTaskExecutionRole retrieve the role ARN to apply for task execution
  76. func (c client) GetEcsTaskExecutionRole(spec types.ServiceConfig) (string, error) {
  77. if arn, ok := spec.Extras["x-ecs-TaskExecutionRole"]; ok {
  78. return arn.(string), nil
  79. }
  80. if defaultTaskExecutionRole != "" {
  81. return defaultTaskExecutionRole, nil
  82. }
  83. logrus.Debug("Retrieve Task Execution Role")
  84. entities, err := c.api.ListRolesForPolicy(ECSTaskExecutionPolicy)
  85. if err != nil {
  86. return "", err
  87. }
  88. if len(entities) == 0 {
  89. return "", fmt.Errorf("no Role is attached to AmazonECSTaskExecutionRole Policy, please provide an explicit task execution role")
  90. }
  91. if len(entities) > 1 {
  92. return "", fmt.Errorf("multiple Roles are attached to AmazonECSTaskExecutionRole Policy, please provide an explicit task execution role")
  93. }
  94. arn, err := c.api.GetRoleArn(entities[0])
  95. if err != nil {
  96. return "", err
  97. }
  98. defaultTaskExecutionRole = arn
  99. return arn, nil
  100. }