From b70f01d2f4d172ec537be41427c7f33c692d5005 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Tue, 21 Apr 2020 11:38:52 +0200 Subject: [PATCH] Adopt CloudFormation to create ECS app from compose.yaml Signed-off-by: Nicolas De Loof --- ecs/cmd/main/main.go | 31 +++- ecs/go.mod | 1 + ecs/go.sum | 26 ++- ecs/pkg/amazon/client.go | 33 ++-- ecs/pkg/amazon/cloudformation.go | 80 +++++++++ ecs/pkg/amazon/down.go | 54 +----- ecs/pkg/amazon/ecs.go | 1 - ecs/pkg/amazon/loadBalancer.go | 5 +- ecs/pkg/amazon/logs.go | 1 + ecs/pkg/amazon/network.go | 13 +- ecs/pkg/amazon/roles.go | 3 +- ecs/pkg/amazon/up.go | 132 ++++----------- ecs/pkg/compose/api.go | 5 +- ecs/pkg/compose/opts.go | 1 - ecs/pkg/compose/project.go | 8 +- ecs/pkg/compose/project_test.go | 3 +- ecs/pkg/convert/convert.go | 278 +++++++++++++------------------ 17 files changed, 314 insertions(+), 361 deletions(-) diff --git a/ecs/cmd/main/main.go b/ecs/cmd/main/main.go index 183404768..cde9be3bd 100644 --- a/ecs/cmd/main/main.go +++ b/ecs/cmd/main/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli-plugins/plugin" "github.com/docker/cli/cli/command" @@ -70,6 +71,7 @@ func ComposeCommand(clusteropts *clusterOptions) *cobra.Command { opts.AddFlags(cmd.Flags()) cmd.AddCommand( + ConvertCommand(clusteropts, opts), UpCommand(clusteropts, opts), DownCommand(clusteropts, opts), ) @@ -87,6 +89,32 @@ func (o upOptions) LoadBalancerArn() *string { return &o.loadBalancerArn } +func ConvertCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command { + opts := upOptions{} + cmd := &cobra.Command{ + Use: "convert", + RunE: compose.WithProject(projectOpts, func(project *compose.Project, args []string) error { + client, err := amazon.NewClient(clusteropts.profile, clusteropts.cluster, clusteropts.region) + if err != nil { + return err + } + template, err := client.Convert(project, opts.LoadBalancerArn()) + if err != nil { + return err + } + + j, err := template.JSON() + if err != nil { + fmt.Printf("Failed to generate JSON: %s\n", err) + } else { + fmt.Printf("%s\n", string(j)) + } + return nil + }), + } + return cmd +} + func UpCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command { opts := upOptions{} cmd := &cobra.Command{ @@ -107,7 +135,6 @@ type downOptions struct { KeepLoadBalancer bool } - func DownCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOptions) *cobra.Command { opts := downOptions{} cmd := &cobra.Command{ @@ -122,4 +149,4 @@ func DownCommand(clusteropts *clusterOptions, projectOpts *compose.ProjectOption } cmd.Flags().BoolVar(&opts.KeepLoadBalancer, "keep-load-balancer", false, "Keep Load Balancer for further use") return cmd -} \ No newline at end of file +} diff --git a/ecs/go.mod b/ecs/go.mod index 1189b1d57..a8f444c52 100644 --- a/ecs/go.mod +++ b/ecs/go.mod @@ -6,6 +6,7 @@ require ( github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/aws/aws-sdk-go v1.28.9 + github.com/awslabs/goformation/v4 v4.8.0 github.com/bitly/go-hostpool v0.1.0 // indirect github.com/bitly/go-simplejson v0.5.0 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect diff --git a/ecs/go.sum b/ecs/go.sum index f2d87a8ca..dc5a0393a 100644 --- a/ecs/go.sum +++ b/ecs/go.sum @@ -21,6 +21,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.28.9 h1:grIuBQc+p3dTRXerh5+2OxSuWFi0iXuxbFdTSg0jaW0= github.com/aws/aws-sdk-go v1.28.9/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/awslabs/goformation/v4 v4.8.0 h1:UiUhyokRy3suEqBXTnipvY8klqY3Eyl4GCH17brraEc= +github.com/awslabs/goformation/v4 v4.8.0/go.mod h1:GcJULxCJfloT+3pbqCluXftdEK2AD/UqpS3hkaaBntg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -46,8 +48,6 @@ github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiK github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bURvoVUSjTo= github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4= github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo= -github.com/compose-spec/compose-go v0.0.0-20200131085702-0b38cc2d8e6b h1:VK0c2Hfrg9FHjvJpWfGwiHPP2UeU0QZ6/5/dN0ehbSQ= -github.com/compose-spec/compose-go v0.0.0-20200131085702-0b38cc2d8e6b/go.mod h1:KoJjdV81vERSyYVuQD63nryyt8ZTlqTWe8JuJIMhRo4= github.com/compose-spec/compose-go v0.0.0-20200409090215-53c0040c9127 h1:mAsQN3s19glh3KBOQjiRYBhqaX1SdzNqhB3/cuqgSbE= github.com/compose-spec/compose-go v0.0.0-20200409090215-53c0040c9127/go.mod h1:1PUpzRF1O/65VOqXZuwpCuYY7pJxbIq1jbAvAf62FGM= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= @@ -139,7 +139,9 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1: github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -180,7 +182,6 @@ github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -205,8 +206,12 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= +github.com/onsi/ginkgo v1.5.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.2.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -244,12 +249,14 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b h1:jUK33OXuZP/l6babJtnLo1qsGvq6G9so9KMflGAm4YA= +github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b/go.mod h1:8458kAagoME2+LN5//WxE71ysZ3B7r22fdgb7qVmXSY= +github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522 h1:fOCp11H0yuyAt2wqlbJtbyPzSgaxHTv8uN1pMpkG1t8= +github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522/go.mod h1:tQTYKOQgxoH3v6dEmdHiz4JG+nbxWwM5fgPQUpSZqVQ= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= @@ -285,9 +292,12 @@ github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v0.0.0-20181112162635-ac52e6811b56/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1 h1:j2hhcujLRHAg872RWAV5yaUrEjHEObwDv3aImCaNLek= @@ -327,6 +337,8 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09 h1:KaQtG+aDELoNmXYas3TVkGNYR golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -385,10 +397,12 @@ gopkg.in/dancannon/gorethink.v3 v3.0.5 h1:/g7PWP7zUS6vSNmHSDbjCHQh1Rqn8Jy6zSMQxA gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc= gopkg.in/fatih/pool.v2 v2.0.0 h1:xIFeWtxifuQJGk/IEPKsTduEKcKvPmhoiVDGpC40nKg= gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/gorethink/gorethink.v3 v3.0.5 h1:e2Uc/Xe+hpcVQFsj6MuHlYog3r0JYpnTzwDj/y2O4MU= gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -396,8 +410,6 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.0 h1:d+tVGRu6X0ZBQ+kyAR8JKi6AXhTP2gmQaoIYaGFz634= -gotest.tools/v3 v3.0.0/go.mod h1:TUP+/YtXl/dp++T+SZ5v2zUmLVBHmptSb/ajDLCJ+3c= gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/ecs/pkg/amazon/client.go b/ecs/pkg/amazon/client.go index 3dceb4ded..ff202fc23 100644 --- a/ecs/pkg/amazon/client.go +++ b/ecs/pkg/amazon/client.go @@ -18,7 +18,6 @@ import ( "github.com/docker/ecs-plugin/pkg/compose" ) - const ( ProjectTag = "com.docker.compose.project" ) @@ -35,27 +34,27 @@ func NewClient(profile string, cluster string, region string) (compose.API, erro } return &client{ Cluster: cluster, - Region: region, - sess: sess, - ECS: ecs.New(sess), - EC2: ec2.New(sess), - ELB: elbv2.New(sess), - CW: cloudwatchlogs.New(sess), - IAM: iam.New(sess), - CF: cloudformation.New(sess), + Region: region, + sess: sess, + ECS: ecs.New(sess), + EC2: ec2.New(sess), + ELB: elbv2.New(sess), + CW: cloudwatchlogs.New(sess), + IAM: iam.New(sess), + CF: cloudformation.New(sess), }, nil } type client struct { Cluster string - Region string - sess *session.Session - ECS ecsiface.ECSAPI - EC2 ec2iface.EC2API - ELB elbv2iface.ELBV2API - CW cloudwatchlogsiface.CloudWatchLogsAPI - IAM iamiface.IAMAPI - CF cloudformationiface.CloudFormationAPI + Region string + sess *session.Session + ECS ecsiface.ECSAPI + EC2 ec2iface.EC2API + ELB elbv2iface.ELBV2API + CW cloudwatchlogsiface.CloudWatchLogsAPI + IAM iamiface.IAMAPI + CF cloudformationiface.CloudFormationAPI } var _ compose.API = &client{} diff --git a/ecs/pkg/amazon/cloudformation.go b/ecs/pkg/amazon/cloudformation.go index 1f74174fe..0b3b0bc41 100644 --- a/ecs/pkg/amazon/cloudformation.go +++ b/ecs/pkg/amazon/cloudformation.go @@ -1 +1,81 @@ package amazon + +import ( + "fmt" + "strings" + + 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, loadBalancerArn *string) (*cloudformation.Template, error) { + template := cloudformation.NewTemplate() + + vpc, err := c.GetDefaultVPC() + if err != nil { + return nil, err + } + + subnets, err := c.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("%d/%s", 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 +} diff --git a/ecs/pkg/amazon/down.go b/ecs/pkg/amazon/down.go index dbd5b6842..ac2648325 100644 --- a/ecs/pkg/amazon/down.go +++ b/ecs/pkg/amazon/down.go @@ -1,64 +1,18 @@ package amazon import ( - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/ecs" + "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/docker/ecs-plugin/pkg/compose" - "github.com/sirupsen/logrus" ) func (c *client) ComposeDown(project *compose.Project, keepLoadBalancer bool) error { - err := c.DeleteLoadBalancer(project, keepLoadBalancer) - if err != nil { - return err - } - - services := []*string{} - // FIXME we should be able to retrieve services by tags, so we don't need the initial compose file to run "down" - for _, service := range project.Services { - logrus.Debugf("Deleting service %q\n", service.Name) - out, err := c.ECS.DeleteService(&ecs.DeleteServiceInput{ - // Force to true so that we don't have to scale down to 0 - // before deleting - Force: aws.Bool(true), - Cluster: aws.String(c.Cluster), - Service: aws.String(service.Name), - }) - if err != nil { - return err - } - services = append(services, out.Service.ServiceArn) - } - - logrus.Info("Stopping services...") - err = c.ECS.WaitUntilServicesInactive(&ecs.DescribeServicesInput{ - Services: services, + _, err := c.CF.DeleteStack(&cloudformation.DeleteStackInput{ + StackName: &project.Name, }) if err != nil { return err } - logrus.Info("All services stopped") - logrus.Debug("Deleting security groups") - groups, err := c.EC2.DescribeSecurityGroups(&ec2.DescribeSecurityGroupsInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("tag:" + ProjectTag), - Values: aws.StringSlice([]string{project.Name}), - }, - }, - }) - if err != nil { - return err - } - for _, g := range groups.SecurityGroups { - _, err = c.EC2.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{ - GroupId: g.GroupId, - }) - if err != nil { - return err - } - } + // TODO monitor progress return nil } diff --git a/ecs/pkg/amazon/ecs.go b/ecs/pkg/amazon/ecs.go index 5366a7a07..423980f3d 100644 --- a/ecs/pkg/amazon/ecs.go +++ b/ecs/pkg/amazon/ecs.go @@ -5,7 +5,6 @@ import ( "github.com/sirupsen/logrus" ) - func (c client) RegisterTaskDefinition(task *ecs.RegisterTaskDefinitionInput) (*string, error) { logrus.Debug("Register Task Definition") def, err := c.ECS.RegisterTaskDefinition(task) diff --git a/ecs/pkg/amazon/loadBalancer.go b/ecs/pkg/amazon/loadBalancer.go index 6972406e4..c8f96cd9b 100644 --- a/ecs/pkg/amazon/loadBalancer.go +++ b/ecs/pkg/amazon/loadBalancer.go @@ -2,9 +2,10 @@ package amazon import ( "fmt" + "strings" + "github.com/docker/ecs-plugin/pkg/compose" "github.com/sirupsen/logrus" - "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elbv2" @@ -132,4 +133,4 @@ func (c client) DeleteListeners(loadBalancer *string) error { } } return nil -} \ No newline at end of file +} diff --git a/ecs/pkg/amazon/logs.go b/ecs/pkg/amazon/logs.go index b42bc8b08..0c06671c2 100644 --- a/ecs/pkg/amazon/logs.go +++ b/ecs/pkg/amazon/logs.go @@ -2,6 +2,7 @@ package amazon import ( "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/docker/ecs-plugin/pkg/compose" diff --git a/ecs/pkg/amazon/network.go b/ecs/pkg/amazon/network.go index a876b4bc3..26b412758 100644 --- a/ecs/pkg/amazon/network.go +++ b/ecs/pkg/amazon/network.go @@ -2,12 +2,13 @@ package amazon import ( "fmt" + "strings" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/compose-spec/compose-go/types" "github.com/docker/ecs-plugin/pkg/compose" "github.com/sirupsen/logrus" - "strings" ) // GetDefaultVPC retrieve the default VPC for AWS account @@ -30,9 +31,8 @@ func (c client) GetDefaultVPC() (*string, error) { return vpcs.Vpcs[0].VpcId, nil } - // GetSubNets retrieve default subnets for a VPC -func (c client) GetSubNets(vpc *string) ([]*string, error) { +func (c client) GetSubNets(vpc *string) ([]string, error) { logrus.Debug("Retrieve SubNets") subnets, err := c.EC2.DescribeSubnets(&ec2.DescribeSubnetsInput{ DryRun: nil, @@ -51,9 +51,9 @@ func (c client) GetSubNets(vpc *string) ([]*string, error) { return nil, err } - ids := []*string{} + ids := []string{} for _, subnet := range subnets.Subnets { - ids = append(ids, subnet.SubnetId) + ids = append(ids, *subnet.SubnetId) } return ids, nil } @@ -91,7 +91,6 @@ func (c client) CreateSecurityGroup(project *compose.Project, vpc *string) (*str return securityGroup.GroupId, nil } - func (c *client) ExposePort(securityGroup *string, port types.ServicePortConfig) error { logrus.Debugf("Authorize ingress port %d/%s\n", port.Published, port.Protocol) _, err := c.EC2.AuthorizeSecurityGroupIngress(&ec2.AuthorizeSecurityGroupIngressInput{ @@ -110,4 +109,4 @@ func (c *client) ExposePort(securityGroup *string, port types.ServicePortConfig) }, }) return err -} \ No newline at end of file +} diff --git a/ecs/pkg/amazon/roles.go b/ecs/pkg/amazon/roles.go index bfa5026a2..7e4594af4 100644 --- a/ecs/pkg/amazon/roles.go +++ b/ecs/pkg/amazon/roles.go @@ -2,6 +2,7 @@ package amazon import ( "fmt" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" "github.com/compose-spec/compose-go/types" @@ -13,7 +14,7 @@ const ECSTaskExecutionPolicy = "arn:aws:iam::aws:policy/service-role/AmazonECSTa var defaultTaskExecutionRole *string // GetEcsTaskExecutionRole retrieve the role ARN to apply for task execution -func (c client) GetEcsTaskExecutionRole(spec *types.ServiceConfig) (*string, error) { +func (c client) GetEcsTaskExecutionRole(spec types.ServiceConfig) (*string, error) { if arn, ok := spec.Extras["x-ecs-TaskExecutionRole"]; ok { s := arn.(string) return &s, nil diff --git a/ecs/pkg/amazon/up.go b/ecs/pkg/amazon/up.go index 0dbc35c92..5f9b8750d 100644 --- a/ecs/pkg/amazon/up.go +++ b/ecs/pkg/amazon/up.go @@ -2,128 +2,54 @@ package amazon import ( "fmt" + "os" + "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" - "github.com/compose-spec/compose-go/types" + "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/docker/ecs-plugin/pkg/compose" - "github.com/docker/ecs-plugin/pkg/convert" - "github.com/sirupsen/logrus" ) func (c *client) ComposeUp(project *compose.Project, loadBalancerArn *string) error { - type mapping struct { - service *types.ServiceConfig - task *ecs.RegisterTaskDefinitionInput - } - mappings := []mapping{} - for _, service := range project.Services { - task, err := convert.Convert(project, service) - if err != nil { - return err - } - mappings = append(mappings, mapping{ - service: &service, - task: task, - }) - } - - vpc, err := c.GetDefaultVPC() - if err != nil { - return err - } - subnets, err := c.GetSubNets(vpc) + template, err := c.Convert(project, loadBalancerArn) if err != nil { return err } - securityGroup, err := c.CreateSecurityGroup(project, vpc) + json, err := template.JSON() if err != nil { return err } - if loadBalancerArn == nil { - loadBalancerArn, err = c.CreateLoadBalancer(project, subnets) - if err != nil { - return err - } - } - - logGroup, err := c.GetOrCreateLogGroup(project) + _, err = c.CF.ValidateTemplate(&cloudformation.ValidateTemplateInput{ + TemplateBody: aws.String(string(json)), + }) if err != nil { return err } - for _, mapping := range mappings { - ingress := []*ecs.LoadBalancer{} - for _, port := range mapping.service.Ports { - name := fmt.Sprintf("%s-%s-%d-%s", project.Name, mapping.service.Name, port.Target, port.Protocol) - targetgroup, err := c.CreateTargetGroup(name, vpc, port) - if err != nil { - return err - } - ingress = append(ingress, &ecs.LoadBalancer{ - ContainerName: aws.String(mapping.service.Name), - ContainerPort: aws.Int64(int64(port.Target)), - TargetGroupArn: targetgroup, - }) + _, err = c.CF.CreateStack(&cloudformation.CreateStackInput{ + OnFailure: aws.String("DELETE"), + StackName: aws.String(project.Name), + TemplateBody: aws.String(string(json)), + TimeoutInMinutes: aws.Int64(10), + }) + if err != nil { + return err + } - err = c.CreateListener(port, loadBalancerArn, targetgroup) - if err != nil { - return err - } - } - - _, err = c.CreateService(project, mapping.service, mapping.task, securityGroup, subnets, logGroup, ingress) - if err != nil { - return err + events, err := c.CF.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{ + StackName: aws.String(project.Name), + }) + if err != nil { + return err + } + for _, event := range events.StackEvents { + fmt.Printf("%s %s\n", *event.LogicalResourceId, *event.ResourceStatus) + if *event.ResourceStatus == "CREATE_FAILED" { + fmt.Fprintln(os.Stderr, event.ResourceStatusReason) } } + + // TODO monitor progress return nil } - -func (c *client) CreateService(project *compose.Project, service *types.ServiceConfig, task *ecs.RegisterTaskDefinitionInput, securityGroup *string, subnets []*string, logGroup *string, ingress []*ecs.LoadBalancer) (*string, error) { - role, err := c.GetEcsTaskExecutionRole(service) - if err != nil { - return nil, err - } - - task.ExecutionRoleArn = role - - for _, def := range task.ContainerDefinitions { - def.LogConfiguration.Options["awslogs-group"] = logGroup - def.LogConfiguration.Options["awslogs-stream-prefix"] = aws.String(service.Name) - def.LogConfiguration.Options["awslogs-region"] = aws.String(c.Region) - } - - arn, err := c.RegisterTaskDefinition(task) - if err != nil { - return nil, err - } - - logrus.Debug("Create Service") - created, err := c.ECS.CreateService(&ecs.CreateServiceInput{ - Cluster: aws.String(c.Cluster), - DesiredCount: aws.Int64(1), // FIXME get from deploy options - LaunchType: aws.String(ecs.LaunchTypeFargate), //FIXME use service.Isolation tro select EC2 vs Fargate - NetworkConfiguration: &ecs.NetworkConfiguration{ - AwsvpcConfiguration: &ecs.AwsVpcConfiguration{ - AssignPublicIp: aws.String(ecs.AssignPublicIpEnabled), - SecurityGroups: []*string{securityGroup}, - Subnets: subnets, - }, - }, - ServiceName: aws.String(service.Name), - SchedulingStrategy: aws.String(ecs.SchedulingStrategyReplica), - TaskDefinition: arn, - LoadBalancers: ingress, - }) - - for _, port := range service.Ports { - err = c.ExposePort(securityGroup, port) - if err != nil { - return nil, err - } - } - - return created.Service.ServiceArn, err -} diff --git a/ecs/pkg/compose/api.go b/ecs/pkg/compose/api.go index ceebacb89..50f62ccf7 100644 --- a/ecs/pkg/compose/api.go +++ b/ecs/pkg/compose/api.go @@ -1,6 +1,9 @@ package compose +import "github.com/awslabs/goformation/v4/cloudformation" + type API interface { + Convert(project *Project, loadBalancerArn *string) (*cloudformation.Template, error) ComposeUp(project *Project, loadBalancerArn *string) error ComposeDown(project *Project, keepLoadBalancer bool) error -} \ No newline at end of file +} diff --git a/ecs/pkg/compose/opts.go b/ecs/pkg/compose/opts.go index 9429fb01a..390ccaa69 100644 --- a/ecs/pkg/compose/opts.go +++ b/ecs/pkg/compose/opts.go @@ -15,7 +15,6 @@ func (o *ProjectOptions) AddFlags(flags *pflag.FlagSet) { flags.StringVarP(&o.name, "project-name", "n", "", "Specify an alternate project name (default: directory name)") } - type ProjectFunc func(project *Project, args []string) error // WithProject wrap a ProjectFunc into a cobra command diff --git a/ecs/pkg/compose/project.go b/ecs/pkg/compose/project.go index c2c6c007a..6f2ef98e9 100644 --- a/ecs/pkg/compose/project.go +++ b/ecs/pkg/compose/project.go @@ -2,14 +2,15 @@ package compose import ( "fmt" - "github.com/compose-spec/compose-go/loader" - "github.com/compose-spec/compose-go/types" - "github.com/sirupsen/logrus" "io/ioutil" "os" "path/filepath" "regexp" "strings" + + "github.com/compose-spec/compose-go/loader" + "github.com/compose-spec/compose-go/types" + "github.com/sirupsen/logrus" ) type Project struct { @@ -32,7 +33,6 @@ func NewProject(config types.ConfigDetails, name string) (*Project, error) { return &p, nil } - // projectFromOptions load a compose project based on command line options func projectFromOptions(options *ProjectOptions) (*Project, error) { configPath, err := getConfigPathFromOptions(options) diff --git a/ecs/pkg/compose/project_test.go b/ecs/pkg/compose/project_test.go index d5f771404..0b44fb33d 100644 --- a/ecs/pkg/compose/project_test.go +++ b/ecs/pkg/compose/project_test.go @@ -1,9 +1,10 @@ package compose import ( - "gotest.tools/v3/assert" "os" "testing" + + "gotest.tools/v3/assert" ) func Test_project_name(t *testing.T) { diff --git a/ecs/pkg/convert/convert.go b/ecs/pkg/convert/convert.go index 502f5089e..33e95c73c 100644 --- a/ecs/pkg/convert/convert.go +++ b/ecs/pkg/convert/convert.go @@ -1,118 +1,87 @@ package convert import ( - "github.com/docker/ecs-plugin/pkg/compose" + "strings" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ecs" + 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.RegisterTaskDefinitionInput, error) { +func Convert(project *compose.Project, service types.ServiceConfig) (*ecs.TaskDefinition, 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{ + 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: 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, - }, + 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: toStringPtr(service.Name), + Name: service.Name, PortMappings: toPortMappings(service.Ports), - Privileged: toBoolPtr(service.Privileged), - PseudoTerminal: toBoolPtr(service.Tty), - ReadonlyRootFilesystem: toBoolPtr(service.ReadOnly), + Privileged: service.Privileged, + PseudoTerminal: service.Tty, + ReadonlyRootFilesystem: service.ReadOnly, RepositoryCredentials: nil, ResourceRequirements: nil, Secrets: nil, - StartTimeout: nil, - StopTimeout: durationToInt64Ptr(service.StopGracePeriod), + StartTimeout: 0, + StopTimeout: durationToInt(service.StopGracePeriod), SystemControls: nil, Ulimits: toUlimits(service.Ulimits), - User: toStringPtr(service.User), + User: service.User, VolumesFrom: nil, - WorkingDirectory: toStringPtr(service.WorkingDir), + WorkingDirectory: service.WorkingDir, }, }, Cpu: toCPU(service), - ExecutionRoleArn: nil, - Family: toStringPtr(project.Name), - IpcMode: toStringPtr(service.Ipc), + Family: project.Name, + IpcMode: 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), + 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: toRequiresCompatibilities(ecs.LaunchTypeFargate), + RequiresCompatibilities: []string{ecsapi.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"), - }, - }, + Volumes: []ecs.TaskDefinition_Volume{}, }, nil } -func toCPU(service types.ServiceConfig) *string { +func toCPU(service types.ServiceConfig) string { // FIXME based on service's memory/cpu requirements, select the adequate Fargate CPU - v := "256" - return &v + return "256" } -func toMemory(service types.ServiceConfig) *string { +func toMemory(service types.ServiceConfig) string { // FIXME based on service's memory/cpu requirements, select the adequate Fargate CPU - v := "512" - return &v + return "512" } func toCPULimits(service types.ServiceConfig) (*int64, error) { @@ -153,45 +122,45 @@ func hasMemoryOrMemoryReservation(service types.ServiceConfig) bool { return false } -func toPlacementConstraints(deploy *types.DeployConfig) []*ecs.TaskDefinitionPlacementConstraint { +func toPlacementConstraints(deploy *types.DeployConfig) []ecs.TaskDefinition_TaskDefinitionPlacementConstraint { if deploy == nil || deploy.Placement.Constraints == nil || len(deploy.Placement.Constraints) == 0 { return nil } - pl := []*ecs.TaskDefinitionPlacementConstraint{} + pl := []ecs.TaskDefinition_TaskDefinitionPlacementConstraint{} for _, c := range deploy.Placement.Constraints { - pl = append(pl, &ecs.TaskDefinitionPlacementConstraint{ - Expression: toStringPtr(c), - Type: nil, + pl = append(pl, ecs.TaskDefinition_TaskDefinitionPlacementConstraint{ + Expression: c, + Type: "", }) } return pl } -func toPortMappings(ports []types.ServicePortConfig) []*ecs.PortMapping { +func toPortMappings(ports []types.ServicePortConfig) []ecs.TaskDefinition_PortMapping { if len(ports) == 0 { return nil } - m := []*ecs.PortMapping{} + m := []ecs.TaskDefinition_PortMapping{} for _, p := range ports { - m = append(m, &ecs.PortMapping{ - ContainerPort: uint32Toint64Ptr(p.Target), - HostPort: uint32Toint64Ptr(p.Published), - Protocol: toStringPtr(p.Protocol), + 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.Ulimit { +func toUlimits(ulimits map[string]*types.UlimitsConfig) []ecs.TaskDefinition_Ulimit { if len(ulimits) == 0 { return nil } - u := []*ecs.Ulimit{} + u := []ecs.TaskDefinition_Ulimit{} for k, v := range ulimits { - u = append(u, &ecs.Ulimit{ - Name: toStringPtr(k), - SoftLimit: intToInt64Ptr(v.Soft), - HardLimit: intToInt64Ptr(v.Hard), + u = append(u, ecs.TaskDefinition_Ulimit{ + Name: k, + SoftLimit: v.Soft, + HardLimit: v.Hard, }) } return u @@ -209,79 +178,82 @@ func intToInt64Ptr(i int) *int64 { const Mb = 1024 * 1024 -func toMemoryLimits(deploy *types.DeployConfig) *int64 { +func toMemoryLimits(deploy *types.DeployConfig) int { if deploy == nil { - return nil + return 0 } res := deploy.Resources.Limits if res == nil { - return nil + return 0 } - v := int64(res.MemoryBytes) / Mb - return &v + v := int(res.MemoryBytes) / Mb + return v } -func toMemoryReservation(deploy *types.DeployConfig) *int64 { +func toMemoryReservation(deploy *types.DeployConfig) int { if deploy == nil { - return nil + return 0 } res := deploy.Resources.Reservations if res == nil { - return nil + return 0 } - v := int64(res.MemoryBytes) / Mb - return &v + v := int(res.MemoryBytes) / Mb + return v } -func toLinuxParameters(service types.ServiceConfig) *ecs.LinuxParameters { - return &ecs.LinuxParameters{ +func toLinuxParameters(service types.ServiceConfig) *ecs.TaskDefinition_LinuxParameters { + return &ecs.TaskDefinition_LinuxParameters{ Capabilities: toKernelCapabilities(service.CapAdd, service.CapDrop), Devices: nil, - InitProcessEnabled: service.Init, - MaxSwap: nil, + InitProcessEnabled: service.Init != nil && *service.Init, + MaxSwap: 0, // FIXME SharedMemorySize: service.ShmSize, - Swappiness: nil, + Swappiness: 0, Tmpfs: toTmpfs(service.Tmpfs), } } -func toTmpfs(tmpfs types.StringList) []*ecs.Tmpfs { +func toTmpfs(tmpfs types.StringList) []ecs.TaskDefinition_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, + o := []ecs.TaskDefinition_Tmpfs{} + for _, path := range tmpfs { + o = append(o, ecs.TaskDefinition_Tmpfs{ + ContainerPath: path, MountOptions: nil, - Size: nil, + Size: 0, }) } return o } -func toKernelCapabilities(add []string, drop []string) *ecs.KernelCapabilities { +func toKernelCapabilities(add []string, drop []string) *ecs.TaskDefinition_KernelCapabilities { if len(add) == 0 && len(drop) == 0 { return nil } - return &ecs.KernelCapabilities{ - Add: toStringPtrSlice(add), - Drop: toStringPtrSlice(drop), + return &ecs.TaskDefinition_KernelCapabilities{ + Add: add, + Drop: drop, } } -func toHealthCheck(check *types.HealthCheckConfig) *ecs.HealthCheck { +func toHealthCheck(check *types.HealthCheckConfig) *ecs.TaskDefinition_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), + 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), } } @@ -293,66 +265,44 @@ func uint64ToInt64Ptr(i *uint64) *int64 { return &v } -func durationToInt64Ptr(interval *types.Duration) *int64 { +func durationToInt(interval *types.Duration) int { if interval == nil { - return nil + return 0 } - v := int64(time.Duration(*interval).Seconds()) - return &v + v := int(time.Duration(*interval).Seconds()) + return v } -func toHostEntryPtr(hosts types.HostsList) []*ecs.HostEntry { +func toHostEntryPtr(hosts types.HostsList) []ecs.TaskDefinition_HostEntry { if hosts == nil || len(hosts) == 0 { return nil } - e := []*ecs.HostEntry{} + e := []ecs.TaskDefinition_HostEntry{} for _, h := range hosts { - host := h - e = append(e, &ecs.HostEntry{ - Hostname: &host, + 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 toKeyValuePairPtr(environment types.MappingWithEquals) []*ecs.KeyValuePair { +func toKeyValuePair(environment types.MappingWithEquals) []ecs.TaskDefinition_KeyValuePair { if environment == nil || len(environment) == 0 { return nil } - pairs := []*ecs.KeyValuePair{} + pairs := []ecs.TaskDefinition_KeyValuePair{} for k, v := range environment { name := k - value := v - pairs = append(pairs, &ecs.KeyValuePair{ - Name: &name, + var value string + if v != nil { + value = *v + } + pairs = append(pairs, ecs.TaskDefinition_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 -} \ No newline at end of file