358 lines
9.0 KiB
Go
358 lines
9.0 KiB
Go
package convert
|
||
|
||
import (
|
||
"github.com/docker/ecs-plugin/pkg/compose"
|
||
"time"
|
||
|
||
"github.com/aws/aws-sdk-go/aws"
|
||
"github.com/aws/aws-sdk-go/service/ecs"
|
||
"github.com/compose-spec/compose-go/types"
|
||
"github.com/docker/cli/opts"
|
||
)
|
||
|
||
func Convert(project *compose.Project, service types.ServiceConfig) (*ecs.RegisterTaskDefinitionInput, error) {
|
||
_, err := toCPULimits(service)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
foo := int64(256)
|
||
logDriver := "awslogs" // FIXME could be set by service.Logging, especially to enable use of firelens
|
||
return &ecs.RegisterTaskDefinitionInput{
|
||
ContainerDefinitions: []*ecs.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: toStringPtrSlice(service.Command),
|
||
Cpu: &foo,
|
||
DependsOn: nil,
|
||
DisableNetworking: toBoolPtr(service.NetworkMode == "none"),
|
||
DnsSearchDomains: toStringPtrSlice(service.DNSSearch),
|
||
DnsServers: toStringPtrSlice(service.DNS),
|
||
DockerLabels: nil,
|
||
DockerSecurityOptions: toStringPtrSlice(service.SecurityOpt),
|
||
EntryPoint: toStringPtrSlice(service.Entrypoint),
|
||
Environment: toKeyValuePairPtr(service.Environment),
|
||
Essential: toBoolPtr(true),
|
||
ExtraHosts: toHostEntryPtr(service.ExtraHosts),
|
||
FirelensConfiguration: nil,
|
||
HealthCheck: toHealthCheck(service.HealthCheck),
|
||
Hostname: toStringPtr(service.Hostname),
|
||
Image: toStringPtr(service.Image),
|
||
Interactive: nil,
|
||
Links: nil,
|
||
LinuxParameters: toLinuxParameters(service),
|
||
LogConfiguration: &ecs.LogConfiguration{
|
||
LogDriver: &logDriver,
|
||
Options: map[string]*string{},
|
||
SecretOptions: nil,
|
||
},
|
||
Memory: toMemoryLimits(service.Deploy),
|
||
MemoryReservation: toMemoryReservation(service.Deploy),
|
||
MountPoints: nil,
|
||
Name: toStringPtr(service.Name),
|
||
PortMappings: toPortMappings(service.Ports),
|
||
Privileged: toBoolPtr(service.Privileged),
|
||
PseudoTerminal: toBoolPtr(service.Tty),
|
||
ReadonlyRootFilesystem: toBoolPtr(service.ReadOnly),
|
||
RepositoryCredentials: nil,
|
||
ResourceRequirements: nil,
|
||
Secrets: nil,
|
||
StartTimeout: nil,
|
||
StopTimeout: durationToInt64Ptr(service.StopGracePeriod),
|
||
SystemControls: nil,
|
||
Ulimits: toUlimits(service.Ulimits),
|
||
User: toStringPtr(service.User),
|
||
VolumesFrom: nil,
|
||
WorkingDirectory: toStringPtr(service.WorkingDir),
|
||
},
|
||
},
|
||
Cpu: toCPU(service),
|
||
ExecutionRoleArn: nil,
|
||
Family: toStringPtr(project.Name),
|
||
IpcMode: toStringPtr(service.Ipc),
|
||
Memory: toMemory(service),
|
||
NetworkMode: toStringPtr("awsvpc"), // FIXME could be set by service.NetworkMode, Fargate only supports network mode ‘awsvpc’.
|
||
PidMode: toStringPtr(service.Pid),
|
||
PlacementConstraints: toPlacementConstraints(service.Deploy),
|
||
ProxyConfiguration: nil,
|
||
RequiresCompatibilities: toRequiresCompatibilities(ecs.LaunchTypeFargate),
|
||
Tags: nil,
|
||
Volumes: []*ecs.Volume{
|
||
{
|
||
/* ONLY supported when using EC2 launch type
|
||
DockerVolumeConfiguration: {
|
||
Autoprovision: nil,
|
||
Driver: nil,
|
||
DriverOpts: nil,
|
||
Labels: nil,
|
||
Scope: nil,
|
||
}, */
|
||
/* Beta and ONLY supported when using EC2 launch type
|
||
EfsVolumeConfiguration: {
|
||
FileSystemId: nil,
|
||
RootDirectory: nil,
|
||
}, */
|
||
/* Bind mount host volume
|
||
Host: {
|
||
SourcePath:
|
||
}, */
|
||
Name: aws.String("MyVolume"),
|
||
},
|
||
},
|
||
}, nil
|
||
|
||
}
|
||
|
||
func toCPU(service types.ServiceConfig) *string {
|
||
// FIXME based on service's memory/cpu requirements, select the adequate Fargate CPU
|
||
v := "256"
|
||
return &v
|
||
}
|
||
|
||
func toMemory(service types.ServiceConfig) *string {
|
||
// FIXME based on service's memory/cpu requirements, select the adequate Fargate CPU
|
||
v := "512"
|
||
return &v
|
||
}
|
||
|
||
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.TaskDefinitionPlacementConstraint {
|
||
if deploy == nil || deploy.Placement.Constraints == nil || len(deploy.Placement.Constraints) == 0 {
|
||
return nil
|
||
}
|
||
pl := []*ecs.TaskDefinitionPlacementConstraint{}
|
||
for _, c := range deploy.Placement.Constraints {
|
||
pl = append(pl, &ecs.TaskDefinitionPlacementConstraint{
|
||
Expression: toStringPtr(c),
|
||
Type: nil,
|
||
})
|
||
}
|
||
return pl
|
||
}
|
||
|
||
func toPortMappings(ports []types.ServicePortConfig) []*ecs.PortMapping {
|
||
if len(ports) == 0 {
|
||
return nil
|
||
}
|
||
m := []*ecs.PortMapping{}
|
||
for _, p := range ports {
|
||
m = append(m, &ecs.PortMapping{
|
||
ContainerPort: uint32Toint64Ptr(p.Target),
|
||
HostPort: uint32Toint64Ptr(p.Published),
|
||
Protocol: toStringPtr(p.Protocol),
|
||
})
|
||
}
|
||
return m
|
||
}
|
||
|
||
func toUlimits(ulimits map[string]*types.UlimitsConfig) []*ecs.Ulimit {
|
||
if len(ulimits) == 0 {
|
||
return nil
|
||
}
|
||
u := []*ecs.Ulimit{}
|
||
for k, v := range ulimits {
|
||
u = append(u, &ecs.Ulimit{
|
||
Name: toStringPtr(k),
|
||
SoftLimit: intToInt64Ptr(v.Soft),
|
||
HardLimit: intToInt64Ptr(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) *int64 {
|
||
if deploy == nil {
|
||
return nil
|
||
}
|
||
res := deploy.Resources.Limits
|
||
if res == nil {
|
||
return nil
|
||
}
|
||
v := int64(res.MemoryBytes) / Mb
|
||
return &v
|
||
}
|
||
|
||
func toMemoryReservation(deploy *types.DeployConfig) *int64 {
|
||
if deploy == nil {
|
||
return nil
|
||
}
|
||
res := deploy.Resources.Reservations
|
||
if res == nil {
|
||
return nil
|
||
}
|
||
v := int64(res.MemoryBytes) / Mb
|
||
return &v
|
||
}
|
||
|
||
func toLinuxParameters(service types.ServiceConfig) *ecs.LinuxParameters {
|
||
return &ecs.LinuxParameters{
|
||
Capabilities: toKernelCapabilities(service.CapAdd, service.CapDrop),
|
||
Devices: nil,
|
||
InitProcessEnabled: service.Init,
|
||
MaxSwap: nil,
|
||
// FIXME SharedMemorySize: service.ShmSize,
|
||
Swappiness: nil,
|
||
Tmpfs: toTmpfs(service.Tmpfs),
|
||
}
|
||
}
|
||
|
||
func toTmpfs(tmpfs types.StringList) []*ecs.Tmpfs {
|
||
if tmpfs == nil || len(tmpfs) == 0 {
|
||
return nil
|
||
}
|
||
o := []*ecs.Tmpfs{}
|
||
for _, t := range tmpfs {
|
||
path := t
|
||
o = append(o, &ecs.Tmpfs{
|
||
ContainerPath: &path,
|
||
MountOptions: nil,
|
||
Size: nil,
|
||
})
|
||
}
|
||
return o
|
||
}
|
||
|
||
func toKernelCapabilities(add []string, drop []string) *ecs.KernelCapabilities {
|
||
if len(add) == 0 && len(drop) == 0 {
|
||
return nil
|
||
}
|
||
return &ecs.KernelCapabilities{
|
||
Add: toStringPtrSlice(add),
|
||
Drop: toStringPtrSlice(drop),
|
||
}
|
||
|
||
}
|
||
|
||
func toHealthCheck(check *types.HealthCheckConfig) *ecs.HealthCheck {
|
||
if check == nil {
|
||
return nil
|
||
}
|
||
return &ecs.HealthCheck{
|
||
Command: toStringPtrSlice(check.Test),
|
||
Interval: durationToInt64Ptr(check.Interval),
|
||
Retries: uint64ToInt64Ptr(check.Retries),
|
||
StartPeriod: durationToInt64Ptr(check.StartPeriod),
|
||
Timeout: durationToInt64Ptr(check.Timeout),
|
||
}
|
||
}
|
||
|
||
func uint64ToInt64Ptr(i *uint64) *int64 {
|
||
if i == nil {
|
||
return nil
|
||
}
|
||
v := int64(*i)
|
||
return &v
|
||
}
|
||
|
||
func durationToInt64Ptr(interval *types.Duration) *int64 {
|
||
if interval == nil {
|
||
return nil
|
||
}
|
||
v := int64(time.Duration(*interval).Seconds())
|
||
return &v
|
||
}
|
||
|
||
func toHostEntryPtr(hosts types.HostsList) []*ecs.HostEntry {
|
||
if hosts == nil || len(hosts) == 0 {
|
||
return nil
|
||
}
|
||
e := []*ecs.HostEntry{}
|
||
for _, h := range hosts {
|
||
host := h
|
||
e = append(e, &ecs.HostEntry{
|
||
Hostname: &host,
|
||
})
|
||
}
|
||
return e
|
||
}
|
||
|
||
func toKeyValuePairPtr(environment types.MappingWithEquals) []*ecs.KeyValuePair {
|
||
if environment == nil || len(environment) == 0 {
|
||
return nil
|
||
}
|
||
pairs := []*ecs.KeyValuePair{}
|
||
for k, v := range environment {
|
||
name := k
|
||
value := v
|
||
pairs = append(pairs, &ecs.KeyValuePair{
|
||
Name: &name,
|
||
Value: value,
|
||
})
|
||
}
|
||
return pairs
|
||
}
|
||
|
||
func toStringPtr(s string) *string {
|
||
if s == "" {
|
||
return nil
|
||
}
|
||
return &s
|
||
}
|
||
|
||
func toStringPtrSlice(s []string) []*string {
|
||
if len(s) == 0 {
|
||
return nil
|
||
}
|
||
v := []*string{}
|
||
for _, x := range s {
|
||
value := x
|
||
v = append(v, &value)
|
||
}
|
||
return v
|
||
}
|
||
|
||
func toBoolPtr(b bool) *bool {
|
||
if !b {
|
||
return nil
|
||
}
|
||
return &b
|
||
} |