compose/ecs/pkg/convert/convert.go

309 lines
8.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package convert
import (
"strings"
"time"
ecsapi "github.com/aws/aws-sdk-go/service/ecs"
"github.com/awslabs/goformation/v4/cloudformation/ecs"
"github.com/compose-spec/compose-go/types"
"github.com/docker/cli/opts"
"github.com/docker/ecs-plugin/pkg/compose"
)
func Convert(project *compose.Project, service types.ServiceConfig) (*ecs.TaskDefinition, error) {
_, err := toCPULimits(service)
if err != nil {
return nil, err
}
return &ecs.TaskDefinition{
ContainerDefinitions: []ecs.TaskDefinition_ContainerDefinition{
// Here we can declare sidecars and init-containers using https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definition_dependson
{
Command: service.Command,
Cpu: 256,
DisableNetworking: service.NetworkMode == "none",
DnsSearchDomains: service.DNSSearch,
DnsServers: service.DNS,
DockerLabels: nil,
DockerSecurityOptions: service.SecurityOpt,
EntryPoint: service.Entrypoint,
Environment: toKeyValuePair(service.Environment),
Essential: true,
ExtraHosts: toHostEntryPtr(service.ExtraHosts),
FirelensConfiguration: nil,
HealthCheck: toHealthCheck(service.HealthCheck),
Hostname: service.Hostname,
Image: service.Image,
Interactive: false,
Links: nil,
LinuxParameters: toLinuxParameters(service),
Memory: toMemoryLimits(service.Deploy),
MemoryReservation: toMemoryReservation(service.Deploy),
MountPoints: nil,
Name: service.Name,
PortMappings: toPortMappings(service.Ports),
Privileged: service.Privileged,
PseudoTerminal: service.Tty,
ReadonlyRootFilesystem: service.ReadOnly,
RepositoryCredentials: nil,
ResourceRequirements: nil,
Secrets: nil,
StartTimeout: 0,
StopTimeout: durationToInt(service.StopGracePeriod),
SystemControls: nil,
Ulimits: toUlimits(service.Ulimits),
User: service.User,
VolumesFrom: nil,
WorkingDirectory: service.WorkingDir,
},
},
Cpu: toCPU(service),
Family: project.Name,
IpcMode: service.Ipc,
Memory: toMemory(service),
NetworkMode: ecsapi.NetworkModeAwsvpc, // FIXME could be set by service.NetworkMode, Fargate only supports network mode awsvpc.
PidMode: service.Pid,
PlacementConstraints: toPlacementConstraints(service.Deploy),
ProxyConfiguration: nil,
RequiresCompatibilities: []string{ecsapi.LaunchTypeFargate},
Tags: nil,
Volumes: []ecs.TaskDefinition_Volume{},
}, nil
}
func toCPU(service types.ServiceConfig) string {
// FIXME based on service's memory/cpu requirements, select the adequate Fargate CPU
return "256"
}
func toMemory(service types.ServiceConfig) string {
// FIXME based on service's memory/cpu requirements, select the adequate Fargate CPU
return "512"
}
func toCPULimits(service types.ServiceConfig) (*int64, error) {
if service.Deploy == nil {
return nil, nil
}
res := service.Deploy.Resources.Limits
if res == nil {
return nil, nil
}
if res.NanoCPUs == "" {
return nil, nil
}
v, err := opts.ParseCPUs(res.NanoCPUs)
if err != nil {
return nil, err
}
return &v, nil
}
func toRequiresCompatibilities(isolation string) []*string {
if isolation == "" {
return nil
}
return []*string{&isolation}
}
func hasMemoryOrMemoryReservation(service types.ServiceConfig) bool {
if service.Deploy == nil {
return false
}
if service.Deploy.Resources.Reservations != nil {
return true
}
if service.Deploy.Resources.Limits != nil {
return true
}
return false
}
func toPlacementConstraints(deploy *types.DeployConfig) []ecs.TaskDefinition_TaskDefinitionPlacementConstraint {
if deploy == nil || deploy.Placement.Constraints == nil || len(deploy.Placement.Constraints) == 0 {
return nil
}
pl := []ecs.TaskDefinition_TaskDefinitionPlacementConstraint{}
for _, c := range deploy.Placement.Constraints {
pl = append(pl, ecs.TaskDefinition_TaskDefinitionPlacementConstraint{
Expression: c,
Type: "",
})
}
return pl
}
func toPortMappings(ports []types.ServicePortConfig) []ecs.TaskDefinition_PortMapping {
if len(ports) == 0 {
return nil
}
m := []ecs.TaskDefinition_PortMapping{}
for _, p := range ports {
m = append(m, ecs.TaskDefinition_PortMapping{
ContainerPort: int(p.Target),
HostPort: int(p.Published),
Protocol: p.Protocol,
})
}
return m
}
func toUlimits(ulimits map[string]*types.UlimitsConfig) []ecs.TaskDefinition_Ulimit {
if len(ulimits) == 0 {
return nil
}
u := []ecs.TaskDefinition_Ulimit{}
for k, v := range ulimits {
u = append(u, ecs.TaskDefinition_Ulimit{
Name: k,
SoftLimit: v.Soft,
HardLimit: v.Hard,
})
}
return u
}
func uint32Toint64Ptr(i uint32) *int64 {
v := int64(i)
return &v
}
func intToInt64Ptr(i int) *int64 {
v := int64(i)
return &v
}
const Mb = 1024 * 1024
func toMemoryLimits(deploy *types.DeployConfig) int {
if deploy == nil {
return 0
}
res := deploy.Resources.Limits
if res == nil {
return 0
}
v := int(res.MemoryBytes) / Mb
return v
}
func toMemoryReservation(deploy *types.DeployConfig) int {
if deploy == nil {
return 0
}
res := deploy.Resources.Reservations
if res == nil {
return 0
}
v := int(res.MemoryBytes) / Mb
return v
}
func toLinuxParameters(service types.ServiceConfig) *ecs.TaskDefinition_LinuxParameters {
return &ecs.TaskDefinition_LinuxParameters{
Capabilities: toKernelCapabilities(service.CapAdd, service.CapDrop),
Devices: nil,
InitProcessEnabled: service.Init != nil && *service.Init,
MaxSwap: 0,
// FIXME SharedMemorySize: service.ShmSize,
Swappiness: 0,
Tmpfs: toTmpfs(service.Tmpfs),
}
}
func toTmpfs(tmpfs types.StringList) []ecs.TaskDefinition_Tmpfs {
if tmpfs == nil || len(tmpfs) == 0 {
return nil
}
o := []ecs.TaskDefinition_Tmpfs{}
for _, path := range tmpfs {
o = append(o, ecs.TaskDefinition_Tmpfs{
ContainerPath: path,
MountOptions: nil,
Size: 0,
})
}
return o
}
func toKernelCapabilities(add []string, drop []string) *ecs.TaskDefinition_KernelCapabilities {
if len(add) == 0 && len(drop) == 0 {
return nil
}
return &ecs.TaskDefinition_KernelCapabilities{
Add: add,
Drop: drop,
}
}
func toHealthCheck(check *types.HealthCheckConfig) *ecs.TaskDefinition_HealthCheck {
if check == nil {
return nil
}
retries := 0
if check.Retries != nil {
retries = int(*check.Retries)
}
return &ecs.TaskDefinition_HealthCheck{
Command: check.Test,
Interval: durationToInt(check.Interval),
Retries: retries,
StartPeriod: durationToInt(check.StartPeriod),
Timeout: durationToInt(check.Timeout),
}
}
func uint64ToInt64Ptr(i *uint64) *int64 {
if i == nil {
return nil
}
v := int64(*i)
return &v
}
func durationToInt(interval *types.Duration) int {
if interval == nil {
return 0
}
v := int(time.Duration(*interval).Seconds())
return v
}
func toHostEntryPtr(hosts types.HostsList) []ecs.TaskDefinition_HostEntry {
if hosts == nil || len(hosts) == 0 {
return nil
}
e := []ecs.TaskDefinition_HostEntry{}
for _, h := range hosts {
parts := strings.SplitN(h, ":", 2) // FIXME this should be handled by compose-go
e = append(e, ecs.TaskDefinition_HostEntry{
Hostname: parts[0],
IpAddress: parts[1],
})
}
return e
}
func toKeyValuePair(environment types.MappingWithEquals) []ecs.TaskDefinition_KeyValuePair {
if environment == nil || len(environment) == 0 {
return nil
}
pairs := []ecs.TaskDefinition_KeyValuePair{}
for k, v := range environment {
name := k
var value string
if v != nil {
value = *v
}
pairs = append(pairs, ecs.TaskDefinition_KeyValuePair{
Name: name,
Value: value,
})
}
return pairs
}